tap 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. data/Basic Overview +151 -0
  2. data/Command Reference +99 -0
  3. data/History +24 -0
  4. data/MIT-LICENSE +1 -1
  5. data/README +29 -57
  6. data/Rakefile +30 -37
  7. data/Tutorial +243 -191
  8. data/bin/tap +66 -35
  9. data/lib/tap.rb +47 -29
  10. data/lib/tap/app.rb +700 -342
  11. data/lib/tap/{script → cmd}/console.rb +0 -0
  12. data/lib/tap/{script → cmd}/destroy.rb +0 -0
  13. data/lib/tap/{script → cmd}/generate.rb +0 -0
  14. data/lib/tap/cmd/run.rb +156 -0
  15. data/lib/tap/constants.rb +4 -0
  16. data/lib/tap/dump.rb +57 -0
  17. data/lib/tap/env.rb +316 -0
  18. data/lib/tap/file_task.rb +106 -109
  19. data/lib/tap/generator.rb +4 -1
  20. data/lib/tap/generator/generators/command/USAGE +6 -0
  21. data/lib/tap/generator/generators/command/command_generator.rb +17 -0
  22. data/lib/tap/generator/generators/{script/templates/script.erb → command/templates/command.erb} +10 -10
  23. data/lib/tap/generator/generators/config/USAGE +21 -0
  24. data/lib/tap/generator/generators/config/config_generator.rb +17 -7
  25. data/lib/tap/generator/generators/file_task/USAGE +3 -0
  26. data/lib/tap/generator/generators/file_task/file_task_generator.rb +16 -0
  27. data/lib/tap/generator/generators/file_task/templates/file.txt +2 -0
  28. data/lib/tap/generator/generators/file_task/templates/file.yml +3 -0
  29. data/lib/tap/generator/generators/file_task/templates/task.erb +26 -20
  30. data/lib/tap/generator/generators/file_task/templates/test.erb +20 -10
  31. data/lib/tap/generator/generators/generator/generator_generator.rb +1 -1
  32. data/lib/tap/generator/generators/generator/templates/generator.erb +21 -12
  33. data/lib/tap/generator/generators/root/templates/Rakefile +33 -24
  34. data/lib/tap/generator/generators/root/templates/tap.yml +28 -31
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +1 -0
  36. data/lib/tap/generator/generators/task/USAGE +3 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +18 -5
  38. data/lib/tap/generator/generators/task/templates/task.erb +7 -12
  39. data/lib/tap/generator/generators/task/templates/test.erb +10 -11
  40. data/lib/tap/generator/generators/workflow/templates/task.erb +1 -1
  41. data/lib/tap/generator/generators/workflow/templates/test.erb +1 -1
  42. data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
  43. data/lib/tap/patches/rake/testtask.rb +55 -0
  44. data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
  45. data/lib/tap/patches/ruby19/parsedate.rb +16 -0
  46. data/lib/tap/root.rb +172 -67
  47. data/lib/tap/script.rb +70 -336
  48. data/lib/tap/support/aggregator.rb +55 -0
  49. data/lib/tap/support/audit.rb +281 -280
  50. data/lib/tap/support/batchable.rb +59 -0
  51. data/lib/tap/support/class_configuration.rb +279 -0
  52. data/lib/tap/support/configurable.rb +92 -0
  53. data/lib/tap/support/configurable_methods.rb +296 -0
  54. data/lib/tap/support/executable.rb +98 -0
  55. data/lib/tap/support/executable_queue.rb +82 -0
  56. data/lib/tap/support/logger.rb +9 -15
  57. data/lib/tap/support/rake.rb +43 -54
  58. data/lib/tap/support/run_error.rb +32 -13
  59. data/lib/tap/support/shell_utils.rb +47 -0
  60. data/lib/tap/support/tdoc.rb +9 -8
  61. data/lib/tap/support/tdoc/config_attr.rb +40 -16
  62. data/lib/tap/support/validation.rb +77 -0
  63. data/lib/tap/support/versions.rb +36 -36
  64. data/lib/tap/task.rb +276 -482
  65. data/lib/tap/test.rb +20 -261
  66. data/lib/tap/test/env_vars.rb +7 -5
  67. data/lib/tap/test/file_methods.rb +126 -121
  68. data/lib/tap/test/subset_methods.rb +86 -45
  69. data/lib/tap/test/tap_methods.rb +271 -0
  70. data/lib/tap/workflow.rb +174 -46
  71. data/test/app/config/another/task.yml +1 -0
  72. data/test/app/config/erb.yml +2 -1
  73. data/test/app/config/some/task.yml +1 -0
  74. data/test/app/config/template.yml +2 -6
  75. data/test/app_test.rb +1241 -1008
  76. data/test/env/test_configure/recurse_a.yml +2 -0
  77. data/test/env/test_configure/recurse_b.yml +2 -0
  78. data/test/env/test_configure/tap.yml +23 -0
  79. data/test/env/test_load_env_config/dir/tap.yml +3 -0
  80. data/test/env/test_load_env_config/recurse_a.yml +2 -0
  81. data/test/env/test_load_env_config/recurse_b.yml +2 -0
  82. data/test/env/test_load_env_config/tap.yml +3 -0
  83. data/test/env_test.rb +198 -0
  84. data/test/file_task_test.rb +70 -53
  85. data/{lib/tap/generator/generators/package/USAGE → test/root/file.txt} +0 -0
  86. data/test/root_test.rb +621 -454
  87. data/test/script_test.rb +38 -174
  88. data/test/support/aggregator_test.rb +99 -0
  89. data/test/support/audit_test.rb +409 -416
  90. data/test/support/batchable_test.rb +74 -0
  91. data/test/support/{task_configuration_test.rb → class_configuration_test.rb} +106 -47
  92. data/test/{task/config/overriding.yml → support/configurable/config/configured.yml} +0 -0
  93. data/test/support/configurable_test.rb +295 -0
  94. data/test/support/executable_queue_test.rb +103 -0
  95. data/test/support/executable_test.rb +38 -0
  96. data/test/support/logger_test.rb +17 -17
  97. data/test/support/rake_test.rb +4 -2
  98. data/test/support/shell_utils_test.rb +24 -0
  99. data/test/support/tdoc_test.rb +265 -258
  100. data/test/support/validation_test.rb +54 -0
  101. data/test/support/versions_test.rb +38 -38
  102. data/test/tap_test_helper.rb +19 -5
  103. data/test/tap_test_suite.rb +5 -2
  104. data/test/task_base_test.rb +13 -104
  105. data/test/task_syntax_test.rb +300 -0
  106. data/test/task_test.rb +258 -381
  107. data/test/test/env_vars_test.rb +40 -40
  108. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/one.txt +0 -0
  109. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/expected/two.txt +0 -0
  110. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/one.txt +0 -0
  111. data/test/test/file_methods/{test_assert_output_files_equal → test_assert_files}/input/two.txt +0 -0
  112. data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/one.txt +0 -0
  113. data/test/test/{test_file_task_test → file_methods/test_assert_files_can_have_no_expected_files_if_specified}/input/two.txt +0 -0
  114. data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/one.txt +1 -0
  115. data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_different_content}/expected/two.txt +0 -0
  116. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/one.txt +1 -0
  117. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/two.txt +1 -0
  118. data/test/test/{test_file_task_test → file_methods/test_assert_files_fails_for_missing_expected_file}/expected/one.txt +0 -0
  119. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/one.txt +1 -0
  120. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/two.txt +1 -0
  121. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/one.txt +1 -0
  122. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/two.txt +1 -0
  123. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/one.txt +1 -0
  124. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/two.txt +1 -0
  125. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/one.txt +1 -0
  126. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/two.txt +1 -0
  127. data/test/test/file_methods_doc/test_sub/expected/one.txt +1 -0
  128. data/test/test/file_methods_doc/test_sub/expected/two.txt +1 -0
  129. data/test/test/file_methods_doc/test_sub/input/one.txt +1 -0
  130. data/test/test/file_methods_doc/test_sub/input/two.txt +1 -0
  131. data/test/test/file_methods_doc_test.rb +29 -0
  132. data/test/test/file_methods_test.rb +214 -143
  133. data/test/test/subset_methods_test.rb +111 -115
  134. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/a.txt +0 -0
  135. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/expected/task/name/b.txt +0 -0
  136. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/a.txt +0 -0
  137. data/test/test/{test_assert_expected_result_files → tap_methods/test_assert_files}/input/b.txt +0 -0
  138. data/test/test/tap_methods_test.rb +399 -0
  139. data/test/workflow_test.rb +101 -91
  140. metadata +86 -70
  141. data/lib/tap/generator/generators/package/package_generator.rb +0 -38
  142. data/lib/tap/generator/generators/package/templates/package.erb +0 -186
  143. data/lib/tap/generator/generators/script/USAGE +0 -0
  144. data/lib/tap/generator/generators/script/script_generator.rb +0 -17
  145. data/lib/tap/script/run.rb +0 -154
  146. data/lib/tap/support/batch_queue.rb +0 -162
  147. data/lib/tap/support/combinator.rb +0 -114
  148. data/lib/tap/support/task_configuration.rb +0 -169
  149. data/lib/tap/support/template.rb +0 -81
  150. data/lib/tap/support/templater.rb +0 -155
  151. data/lib/tap/version.rb +0 -4
  152. data/test/app/config/addition_template.yml +0 -6
  153. data/test/app_class_test.rb +0 -33
  154. data/test/check/binding_eval.rb +0 -23
  155. data/test/check/define_method_check.rb +0 -22
  156. data/test/check/dependencies_check.rb +0 -175
  157. data/test/check/inheritance_check.rb +0 -22
  158. data/test/support/batch_queue_test.rb +0 -320
  159. data/test/support/combinator_test.rb +0 -249
  160. data/test/support/template_test.rb +0 -122
  161. data/test/support/templater/erb.txt +0 -2
  162. data/test/support/templater/erb.yml +0 -2
  163. data/test/support/templater/somefile.txt +0 -2
  164. data/test/support/templater_test.rb +0 -192
  165. data/test/task/config/template.yml +0 -4
  166. data/test/task_class_test.rb +0 -170
  167. data/test/task_execute_test.rb +0 -262
  168. data/test/test/file_methods/test_assert_expected/expected/file.txt +0 -1
  169. data/test/test/file_methods/test_assert_expected/expected/folder/file.txt +0 -1
  170. data/test/test/file_methods/test_assert_expected/input/file.txt +0 -1
  171. data/test/test/file_methods/test_assert_expected/input/folder/file.txt +0 -1
  172. data/test/test/file_methods/test_assert_files_exist/input/input_1.txt +0 -0
  173. data/test/test/file_methods/test_assert_files_exist/input/input_2.txt +0 -0
  174. data/test/test/file_methods/test_file_compare/expected/output_1.txt +0 -3
  175. data/test/test/file_methods/test_file_compare/expected/output_2.txt +0 -1
  176. data/test/test/file_methods/test_file_compare/input/input_1.txt +0 -3
  177. data/test/test/file_methods/test_file_compare/input/input_2.txt +0 -3
  178. data/test/test/file_methods/test_infer_glob/expected/file.yml +0 -0
  179. data/test/test/file_methods/test_infer_glob/expected/file_1.txt +0 -0
  180. data/test/test/file_methods/test_infer_glob/expected/file_2.txt +0 -0
  181. data/test/test/file_methods/test_yml_compare/expected/output_1.yml +0 -6
  182. data/test/test/file_methods/test_yml_compare/expected/output_2.yml +0 -6
  183. data/test/test/file_methods/test_yml_compare/input/input_1.yml +0 -4
  184. data/test/test/file_methods/test_yml_compare/input/input_2.yml +0 -4
  185. data/test/test_test.rb +0 -373
File without changes
File without changes
File without changes
@@ -0,0 +1,156 @@
1
+ # = Usage
2
+ # tap run {options} -- {task options} task INPUTS...
3
+ #
4
+ # examples:
5
+ # tap run --help Prints this help
6
+ # tap run -- task --help Prints help for task
7
+ #
8
+
9
+ require 'tap/script'
10
+ env = Tap::Env.instance
11
+ app = Tap::App.instance
12
+
13
+ #
14
+ # handle options
15
+ #
16
+
17
+ opts = [
18
+ ['--help', '-h', GetoptLong::NO_ARGUMENT, "Print this help"],
19
+ ['--debug', '-d', GetoptLong::NO_ARGUMENT, "Trace execution and debug"],
20
+ ['--force', '-f', GetoptLong::NO_ARGUMENT, "Force execution at checkpoints"],
21
+ ['--quiet', '-q', GetoptLong::NO_ARGUMENT, "Suppress logging"]]
22
+
23
+ Tap::Script.handle_options(*opts) do |opt, value|
24
+ case opt
25
+ when '--help'
26
+ puts Tap::Script.usage(__FILE__, "Usage", :keep_headers => false)
27
+ puts
28
+ puts "Options:"
29
+ puts Tap::Script.usage_options(opts)
30
+ exit
31
+
32
+ when '--quiet', '--force', '--debug'
33
+ # simply track these have been set
34
+ opt =~ /^-+(\w+)/
35
+ app.options.send("#{$1}=", true)
36
+
37
+ end
38
+ end
39
+
40
+ #
41
+ # handle options for each specified task
42
+ #
43
+ rounds = Tap::Script.split_argv(ARGV).collect do |argv|
44
+ argv.each do |args|
45
+ ARGV.clear
46
+ ARGV.concat(args)
47
+
48
+ td = Tap::Script.next_arg(ARGV)
49
+ case td
50
+ when 'rake', nil
51
+ # remove --help as this will print the Rake help
52
+ ARGV.delete_if do |arg|
53
+ if arg == "--help"
54
+ env.log(:warn, "ignoring --help config for rake command")
55
+ true
56
+ else
57
+ false
58
+ end
59
+ end
60
+ next if ARGV.empty?
61
+
62
+ rake = env.rake_setup
63
+
64
+ # takes the place of rake.top_level
65
+ if rake.options.show_tasks
66
+ rake.display_tasks_and_comments
67
+ exit
68
+ elsif rake.options.show_prereqs
69
+ rake.display_prerequisites
70
+ exit
71
+ else
72
+ rake.top_level_tasks.each do |task_name|
73
+ app.task(task_name).enq
74
+ end
75
+ end
76
+
77
+ else
78
+ begin
79
+ # attempt lookup the task class
80
+ task_class = app.task_class(td)
81
+ rescue(Tap::App::LookupError)
82
+ end
83
+
84
+ # unless a Tap::Task was found, treat the
85
+ # args as a specification for Rake.
86
+ if task_class == nil || !task_class.include?(Tap::Support::Configurable)
87
+ args.unshift('rake')
88
+ redo
89
+ end
90
+
91
+ # now let the class handle the argv
92
+ ARGV.collect! {|str| Tap::Script.parse_yaml(str) }
93
+ ARGV.unshift(td)
94
+ task_class.argv_enq(app)
95
+ end
96
+ end
97
+
98
+ app.queue.clear
99
+ end
100
+ ARGV.clear
101
+
102
+ rounds.delete_if {|round| round.empty? }
103
+ if rounds.empty?
104
+ puts "no task specified"
105
+ exit
106
+ end
107
+
108
+ #
109
+ # set signals
110
+ #
111
+
112
+ # info signal -- Note: some systems do
113
+ # not support the INFO signal
114
+ # (windows, fedora, at least)
115
+ signals = Signal.list.keys
116
+ if signals.include?("INFO")
117
+ Signal.trap("INFO") do
118
+ puts app.info
119
+ end
120
+
121
+ puts "ctl-i prints information"
122
+ end
123
+
124
+ # interuption signal
125
+ if signals.include?("INT")
126
+ Signal.trap("INT") do
127
+ puts " interrupted!"
128
+ # prompt for decision
129
+ while true
130
+ print "stop, terminate, or resume? (s/t/r):"
131
+ case gets.strip
132
+ when /s(top)?/i
133
+ app.stop
134
+ break
135
+ when /t(erminate)?/i
136
+ app.terminate
137
+ break
138
+ when /r(esume)?/i
139
+ break
140
+ else
141
+ puts "unexpected response..."
142
+ end
143
+ end
144
+ end
145
+
146
+ puts "ctl-c interupts execution"
147
+ end
148
+
149
+ #
150
+ # enque tasks and run!
151
+ #
152
+ puts "beginning run..."
153
+ rounds.each_with_index do |queue, i|
154
+ app.queue.concat(queue)
155
+ app.run
156
+ end
@@ -0,0 +1,4 @@
1
+ module Tap
2
+ VERSION="0.9.0"
3
+ WEBSITE="http://tap.rubyforge.org"
4
+ end
@@ -0,0 +1,57 @@
1
+ module Tap
2
+ # == Description
3
+ # A primitive dump task (still in development) to print
4
+ # application results to a file or IO. The results are
5
+ # printed in a tap-readable format allowing the use of
6
+ # dumped results as inputs to other tasks.
7
+ #
8
+ # Usually dump is used as the final task in a round of
9
+ # tasks executed through 'tap run'.
10
+ # === Usage
11
+ # % tap run -- [your tasks] --+ dump filepath
12
+ #
13
+ # If no filepath is specified, the results are printed
14
+ # to stdout.
15
+ #
16
+ class Dump < Tap::FileTask
17
+
18
+ config :datetime_format, '%Y-%m-%d %H:%M:%S'
19
+ config :print_audit, true
20
+ config :print_date, true
21
+
22
+ #config :overwrite
23
+
24
+ def process(target=$stdout)
25
+ case target
26
+ when IO then dump_to(target)
27
+ else
28
+ log_basename(:dump, target)
29
+ prepare(target)
30
+ File.open(target, "wb") {|file| dump_to(file) }
31
+ end
32
+ end
33
+
34
+ def dump_to(io)
35
+ trails = []
36
+ results = {}
37
+ app.aggregator.to_hash.each_pair do |src, _results|
38
+ name = src.respond_to?(:name) ? src.name : ''
39
+
40
+ results["#{name} (#{src.object_id})"] = _results.collect {|_audit| _audit._current }
41
+ _results.each {|_audit| trails << _audit._to_s }
42
+ end
43
+
44
+ if print_audit
45
+ io.puts "# audit:"
46
+ trails.each {|trail| io.puts "# #{trail.gsub("\n", "\n# ")}"}
47
+ end
48
+
49
+ if print_date
50
+ io.puts "# date: #{Time.now.strftime(datetime_format)}"
51
+ end
52
+
53
+ YAML::dump(results, io)
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,316 @@
1
+ require 'tap/root'
2
+ require 'singleton'
3
+ autoload(:PP, "pp")
4
+
5
+ module Tap
6
+
7
+ # == Under Construction
8
+ #
9
+ # Env manages configuration of the Tap execution environment, including the
10
+ # specification of gems that should be available through the tap command.
11
+ class Env
12
+
13
+ # A variety of configuration loading/handling methods for use in
14
+ # conjuction with Tap::Env, to aid in configuring the running
15
+ # environment for Tap.
16
+ module Configuration
17
+ module_function
18
+
19
+ # Templates the input filepath using ERB then loads it as YAML.
20
+ # Returns an empty hash if the file doesn't exist, or loads to
21
+ # nil or false (as for an empty file). Raises an error if the
22
+ # filepath doesn't load to a hash.
23
+ def read_config(filepath)
24
+ return {} if !File.exists?(filepath) || File.directory?(filepath)
25
+
26
+ input = ERB.new(File.read(filepath)).result
27
+ config = YAML.load(input)
28
+
29
+ case config
30
+ when Hash then config
31
+ when nil, false then {}
32
+ else
33
+ raise "expected hash from config file: #{filepath}"
34
+ end
35
+ end
36
+
37
+ # Partitions a configuration hash into environment, execution,
38
+ # and application configurations, as determined by ENV_CONFIG_KEYS
39
+ # and EXE_CONFIG_KEYS. All non-env, non-exe configurations are
40
+ # considered application configurations.
41
+ def partition_configs(hash, *sets)
42
+ partitions = Array.new(sets.length + 1) { Hash.new }
43
+
44
+ hash.each_pair do |key, value|
45
+ index = 0
46
+ sets.each do |keys|
47
+ break if keys.include?(key)
48
+ index += 1
49
+ end
50
+
51
+ partitions[index][key] = value
52
+ end
53
+
54
+ partitions
55
+ end
56
+
57
+ # Joins the input configuration hashes, concatenating
58
+ # values for matching keys. Values will be made into
59
+ # arrays if they are not so already; duplicate values
60
+ # are removed from the result on a key-per-key basis.
61
+ def join_configs(*configs)
62
+ merge = {}
63
+ configs.each do |hash|
64
+ hash.each_pair do |key, values|
65
+ values = [values] unless values.kind_of?(Array)
66
+ (merge[key] ||= []).concat(values)
67
+ end
68
+ end
69
+ merge.values.each {|values| values.uniq! }
70
+ merge
71
+ end
72
+ end
73
+
74
+ include Configuration
75
+ include Singleton
76
+
77
+ DEFAULT_CONFIG_FILE = "tap.yml"
78
+
79
+ # Currently these are ALWAYS included.
80
+ DEFAULT_CONFIG = {
81
+ "load_paths" => ["lib"],
82
+ "load_once_paths" => [],
83
+ "config_paths" => [],
84
+ "command_paths" => ["cmd"],
85
+ "gems" => [],
86
+ "generator_paths" => ["lib/generators"]
87
+ }
88
+
89
+ attr_reader :config
90
+ attr_accessor :logger
91
+
92
+ def initialize
93
+ @config = nil
94
+ @logger = nil
95
+ # @on_handle_unknown_configs = nil
96
+ reset
97
+ end
98
+
99
+ def debug_setup
100
+ $DEBUG = true
101
+ logger.level = Logger::DEBUG
102
+ end
103
+
104
+ def rails_setup(app=Tap::App.instance)
105
+ Object.const_set('RAILS_ROOT', app.root)
106
+ Object.const_set('RAILS_DEFAULT_LOGGER', app.logger)
107
+ Dependencies.log_activity = app.debug?
108
+ end
109
+
110
+ def rake_setup(argv=ARGV, app=Tap::App.instance)
111
+ Tap::Support.autoload(:Rake, 'tap/support/rake')
112
+
113
+ # setup
114
+ app.extend Tap::Support::Rake
115
+ rake = Rake.application
116
+ options = rake.options
117
+
118
+ # merge options down from app
119
+ app.options.marshal_dump.each_pair do |key, value|
120
+ options.send("#{key}=", value)
121
+ end
122
+ options.silent = true
123
+
124
+ # run as if from command line using argv
125
+ current_argv = ARGV.dup
126
+ begin
127
+ ARGV.concat(argv)
128
+
129
+ # now follow the same protocol as
130
+ # in run, handling options
131
+ rake.init
132
+ rake.load_rakefile
133
+ ensure
134
+ ARGV.clear
135
+ ARGV.concat(current_argv)
136
+ end
137
+
138
+ rake
139
+ end
140
+
141
+ # Resets Env. Load paths (load_paths and load_once_paths) are
142
+ # not reset unless dependencies==true; in which case Dependencies
143
+ # are cleared before load paths are cleared. The load paths added
144
+ # to $LOAD_PATH are not cleared.
145
+ #
146
+ # Generally not recommended.
147
+ def reset
148
+ unless @config == nil
149
+ $LOAD_PATH.delete_if {|path| config['load_paths'].include?(path) }
150
+
151
+ Dependencies.clear
152
+ Dependencies.load_paths.delete_if {|path| config['load_paths'].include?(path) }
153
+ Dependencies.load_once_paths.delete_if {|path| config['load_once_paths'].include?(path) }
154
+ end
155
+
156
+ @config = {}
157
+ DEFAULT_CONFIG.keys.each do |key|
158
+ @config[key] = []
159
+ end
160
+ end
161
+
162
+ # Logs the action and message at the input level (default INFO).
163
+ # Logging is suppressed if no logger is set.
164
+ def log(action, msg="", level=Logger::INFO)
165
+ logger.add(level, msg, action.to_s) if logger
166
+ end
167
+
168
+ # Configures the specified App using the configurations in config_file.
169
+ # Loading of environement configurations occcurs via load_env_config;
170
+ # all environment paths are resolved using the app, after the app has
171
+ # been configured.
172
+
173
+ # Loads environment configurations from the specified path. If a directory
174
+ # is given as path, then the DEFAULT_CONFIG_FILE relative to that location
175
+ # will be loaded. The loading cycle recurses as specified by the configurations.
176
+ #
177
+ # Configuration paths are expanded relative to the parent directory
178
+ # of the loaded file. Raises an error if non-env configuration are
179
+ # found (as determined by Tap::Env::Configurtion::ENV_CONFIG_KEYS).
180
+ def load_config(path, root=Tap::Root.new, &block)
181
+ path = File.join(path, DEFAULT_CONFIG_FILE) if File.directory?(path)
182
+ path = File.expand_path(path)
183
+
184
+ # prevent infinite looping
185
+ config_paths = config['config_paths']
186
+ return false if config_paths.include?(path)
187
+
188
+ # load config
189
+ log(:load_config, path, Logger::DEBUG)
190
+ config_paths << path
191
+
192
+ config = read_config(path)
193
+ config['root'] = File.dirname(path) unless config['root']
194
+
195
+ configure(config, root) do |configured_root, other_configs|
196
+ if block_given?
197
+ yield(configured_root, path, other_configs)
198
+ else
199
+ log(:warn, "ignoring configs: #{path} (#{other_configs.keys.join(',')})", Logger::WARN)
200
+ end
201
+ end
202
+
203
+ end
204
+
205
+ def configure(config, root=Tap::Root.new, &block)
206
+ root_configs, env_configs, other_configs = partition_configs(config, ['root', 'directories', 'absolute_paths'], DEFAULT_CONFIG.keys)
207
+ env_configs = join_configs(DEFAULT_CONFIG, env_configs)
208
+
209
+ # assign root configs
210
+ root.send(:assign_paths,
211
+ root_configs['root'] || root.root,
212
+ root_configs['directories'] || root.directories,
213
+ root_configs['absolute_paths'] || root.absolute_paths)
214
+
215
+ # handle unknown configs (handle before setting
216
+ # env configs in case the configs modify root)
217
+ unless other_configs.empty?
218
+ if block_given?
219
+ yield(root, other_configs)
220
+ else
221
+ log(:warn, "ignoring configs: (#{other_configs.keys.join(',')})", Logger::WARN)
222
+ end
223
+ end
224
+
225
+ # load gems and configurations
226
+ gem_paths = env_configs.delete('gems').collect do |gem_name|
227
+ full_gem_path(gem_name)
228
+ end
229
+ config_paths = env_configs.delete('config_paths') + gem_paths
230
+ config_paths.each {|path| load_config(root[path], &block) }
231
+
232
+ # assign env configs
233
+ env_configs.each_pair do |key, value|
234
+ case key
235
+ when 'load_paths'
236
+ assign_paths(root, value, self.config[key], $LOAD_PATH, Dependencies.load_paths)
237
+ when 'load_once_paths'
238
+ assign_paths(root, value, self.config[key], Dependencies.load_once_paths)
239
+ when /_paths$/
240
+ assign_paths(root, value, self.config[key])
241
+ else
242
+ handle_unknown_config(key, value)
243
+ end
244
+ end
245
+
246
+ true
247
+ end
248
+
249
+ # Loads env configurations from a gem, specifically from
250
+ # gemspec.full_gem_path. A gem version can be specified
251
+ # in the name, like 'gem >= 1.2'.
252
+ def full_gem_path(gem_name)
253
+ # figure the version of the gem, by default >= 0.0.0
254
+ gem_name =~ /^([^<=>]*)(.*)$/
255
+ name, version = $1, $2
256
+ version = ">= 0.0.0" if version.empty?
257
+
258
+ # load the gem and get the spec
259
+ gem(name, version)
260
+ spec = Gem.loaded_specs[name]
261
+
262
+ if spec == nil
263
+ log(:warn, "unknown gem: #{gem_name}", Logger::WARN)
264
+ end
265
+
266
+ spec.full_gem_path
267
+ end
268
+
269
+ def load_gem(gem_name)
270
+ load_config(full_gem_path(gem_name))
271
+ end
272
+
273
+ # Searches for and returns all .rb files under each of the command_paths
274
+ # as well as the default tap commands. Commands with conflicting names
275
+ # raise an error; however, user commands are allowed to override the
276
+ # default tap commands and will NOT raise an error.
277
+ def commands
278
+ commands = {}
279
+ config['command_paths'].each do |path|
280
+ pattern = File.join(path, "**/*.rb")
281
+
282
+ Dir.glob(pattern).each do |file|
283
+ cmd = Tap::App.relative_filepath(path, file).chomp(".rb")
284
+ raise "command name confict: #{cmd}" if commands.include?(cmd)
285
+ commands[cmd] = file
286
+ end
287
+ end
288
+
289
+ # allow all other scripts to override default scripts
290
+ # (hence do this second)
291
+ tap_command_dir = File.expand_path(File.join( File.dirname(__FILE__), "cmd"))
292
+ Dir.glob( tap_command_dir + "/**/*.rb" ).each do |file|
293
+ cmd = Tap::App.relative_filepath(tap_command_dir, file).chomp(".rb")
294
+ commands[cmd] = file unless commands.include?(cmd)
295
+ end
296
+
297
+ commands
298
+ end
299
+
300
+ protected
301
+
302
+ def assign_paths(root, paths, *targets)
303
+ paths = paths.collect {|path| root[path]}
304
+ targets.each do |array|
305
+ paths.reverse_each do |path|
306
+ array.unshift(path)
307
+ end
308
+ array.uniq!
309
+ end
310
+ end
311
+
312
+ def handle_unknown_config(key, value)
313
+ raise "unknown env config: #{key}"
314
+ end
315
+ end
316
+ end