tap 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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