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
@@ -0,0 +1,98 @@
1
+ module Tap
2
+ module Support
3
+ # Executable wraps methods to make them executable by App.
4
+ module Executable
5
+
6
+ # The method called when an Executable is executed via _execute
7
+ attr_reader :_method_name
8
+
9
+ # Indicates whether or not to call in multithread mode. Default false.
10
+ attr_accessor :multithread
11
+
12
+ # Stores the on complete block. Default is Executable.default_on_complete_block.
13
+ attr_reader :on_complete_block
14
+
15
+ protected
16
+
17
+ attr_writer :_method_name
18
+
19
+ public
20
+
21
+ def self.initialize(obj, method_name, multithread=false, &on_complete_block)
22
+ obj.extend Executable
23
+ obj.instance_variable_set(:@_method_name, method_name)
24
+ obj.instance_variable_set(:@multithread, multithread)
25
+ obj.instance_variable_set(:@on_complete_block, on_complete_block)
26
+ obj
27
+ end
28
+
29
+ # Sets a block to receive the results of _call. Raises an error
30
+ # if on_complete_block is already set, unless override = true.
31
+ #
32
+ # Note the block recieves an audited result and not
33
+ # the result itself (see Audit for more information).
34
+ def on_complete(override=false, &block) # :yields: _result
35
+ unless on_complete_block == nil || override
36
+ raise "on_complete_block already set: #{self}"
37
+ end
38
+ @on_complete_block = block
39
+ end
40
+
41
+ # Auditing method call. Executes _method_name for self, but audits
42
+ # the result. Sends the audited result to the on_complete_block if set.
43
+ #
44
+ # Audits are initialized in the follwing manner:
45
+ # no inputs:: create a new, empty Audit. The first value of the audit
46
+ # will be the result of call
47
+ # one input:: forks the input if it is an audit, otherwise initializes
48
+ # a new audit using the input
49
+ # multiple inputs:: merges the inputs into a new Audit.
50
+ #
51
+ def _execute(*inputs)
52
+ audit = case inputs.length
53
+ when 0 then Audit.new
54
+ when 1
55
+ audit = inputs.first
56
+ if audit.kind_of?(Audit)
57
+ inputs = [audit._current]
58
+ audit._fork
59
+ else
60
+ Audit.new(audit)
61
+ end
62
+ else
63
+ sources = []
64
+ inputs.collect! do |input|
65
+ if input.kind_of?(Audit)
66
+ sources << input._fork
67
+ input._current
68
+ else
69
+ sources << nil
70
+ input
71
+ end
72
+ end
73
+ Audit.new(inputs, sources)
74
+ end
75
+
76
+ audit._record(self, send(_method_name, *inputs))
77
+ on_complete_block.call(audit) if on_complete_block
78
+
79
+ audit
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ # Tap extends Object with a convenience method to generate methods
86
+ # that can be enqued by App and incorporated into workflows.
87
+ class Object
88
+
89
+ # Makes a Tap::Support::Executable for the Method returned by
90
+ # Object#method, setting multithread and the on_complete block
91
+ # as specified. The method will be called on _execute.
92
+ #
93
+ # Returns nil if Object#method returns nil.
94
+ def _method(method_name, multithread=false, &on_complete_block) # :yields: _result
95
+ return nil unless m = method(method_name)
96
+ Tap::Support::Executable.initialize(m, :call, multithread, &on_complete_block)
97
+ end
98
+ end
@@ -0,0 +1,82 @@
1
+ module Tap
2
+ module Support
3
+
4
+ # ExecutableQueue allows thread-safe enqueing and dequeing of
5
+ # Executable methods and inputs for execution.
6
+ class ExecutableQueue
7
+ include MonitorMixin
8
+
9
+ # Creates a new ExecutableQueue
10
+ def initialize
11
+ # required for MonitorMixin
12
+ super()
13
+ @queue = []
14
+ end
15
+
16
+ # Clears all methods and inputs. Returns the existing queue as an array.
17
+ def clear
18
+ synchronize do
19
+ current = self.queue
20
+ self.queue = []
21
+ current
22
+ end
23
+ end
24
+
25
+ # Returns the number of enqueued methods
26
+ def size
27
+ queue.length
28
+ end
29
+
30
+ # True if no methods are enqueued
31
+ def empty?
32
+ queue.empty?
33
+ end
34
+
35
+ # Enqueues the method and inputs. Raises an error if the
36
+ # method is not an Executable.
37
+ def enq(method, inputs)
38
+ synchronize do
39
+ check_method(method)
40
+ queue.push [method, inputs]
41
+ end
42
+ end
43
+
44
+ # Enqueues the method and inputs, but to the top of the queue.
45
+ # Raises an error if the method is not an Executable.
46
+ def unshift(method, inputs)
47
+ synchronize do
48
+ check_method(method)
49
+ queue.unshift [method, inputs]
50
+ end
51
+ end
52
+
53
+ # Dequeues the next method and inputs as an array like
54
+ # [method, inputs]. Returns nil if the queue is empty.
55
+ def deq
56
+ synchronize { queue.shift }
57
+ end
58
+
59
+ def concat(array)
60
+ synchronize do
61
+ array.each do |method, inputs|
62
+ enq(method, inputs)
63
+ end
64
+ end
65
+ end
66
+
67
+ # Converts self to an array.
68
+ def to_a
69
+ queue.dup
70
+ end
71
+
72
+ protected
73
+
74
+ attr_accessor :queue
75
+
76
+ # Checks if the input method is extended with Executable
77
+ def check_method(method) # :nodoc:
78
+ raise "not Executable: #{method}" unless method.kind_of?(Executable)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -19,6 +19,12 @@ module Tap
19
19
  # Typed logging occurs through +method_missing+. Any type is possible so long as the logger doesn't
20
20
  # already have a method of the input type.
21
21
  #++
22
+ #
23
+ #--
24
+ # TODO
25
+ # multiplex logger so that logging can be directed to two locations; a log file and the console for
26
+ # example, or log files in multiple locations.
27
+ #++
22
28
  module Logger
23
29
  Format = " %s[%s] %18s %s\n"
24
30
 
@@ -39,20 +45,8 @@ module Tap
39
45
  @logdev
40
46
  end
41
47
 
42
- def tick(mark=".")
43
- @logdev.write mark
44
- end
45
-
46
- def monitor(action="beginning", msg="", &block)
47
- begin_time = Time.now
48
- format_add(INFO, msg, action) {|format| format.chomp("\n")}
49
-
50
- result = yield
51
-
52
- format_add(INFO, "#{Time.now-begin_time}s", "completed") {|format| "\n" + format}
53
- result
54
- end
55
-
48
+ # Same as add, but uses the format provided by the block.
49
+ # BUG: not thread safe
56
50
  def format_add(level, msg, action=nil, &block)
57
51
  current_format = self.format
58
52
  self.format = yield(current_format)
@@ -65,7 +59,7 @@ module Tap
65
59
  # I[H:M:S] type message
66
60
  #
67
61
  # If no progname is specified, '--' is used.
68
- def format_message(severity, timestamp, progname, msg)
62
+ def format_message(severity, timestamp, progname, msg, format=self.format)
69
63
  if timestamp.respond_to?(:strftime) && self.datetime_format
70
64
  timestamp = timestamp.strftime(self.datetime_format)
71
65
  end
@@ -1,54 +1,43 @@
1
- require 'rake'
2
-
3
- module Rake # :nodoc:
4
- # Modifies Rake::Task to behave like a Tap::Task.
5
- class Task # :nodoc:
6
- alias_method(:tap_original_execute, :execute)
7
- alias_method(:tap_original_invoke, :invoke)
8
- alias_method(:tap_original_initialize, :initialize)
9
-
10
- def initialize(*args)
11
- tap_original_initialize(*args)
12
- self.extend Tap::Task::Base
13
- self.extend InstanceMethods
14
- end
15
-
16
- def invoke
17
- @lock.synchronize do
18
- if application.options.trace
19
- puts "** Invoke #{name} #{format_trace_flags}"
20
- end
21
- return if @already_invoked
22
- @already_invoked = true
23
- invoke_prerequisites
24
- tap_original_execute if needed?
25
- end
26
- end
27
-
28
- module InstanceMethods # :nodoc:
29
-
30
- def iterate=(input)
31
- raise "iterate cannot be set to true for Rake tasks." if input == true
32
- end
33
-
34
- protected
35
-
36
- def on_execute(audited_inputs)
37
- invoke
38
- audited_inputs._record(self, nil)
39
- end
40
- end
41
- end
42
- end
43
-
44
- module Tap
45
- module Support
46
- # == UNDER CONSTRUCTION
47
- module Rake
48
-
49
- def task(td, config=nil)
50
- Object::Rake.application.lookup(td) || super
51
- end
52
- end
53
- end
54
- end
1
+ require 'rake'
2
+
3
+ module Tap
4
+ module Support
5
+
6
+ # Used to modify an App so that it will lookup Rake tasks as well as Tap tasks. Simply use:
7
+ # app.extend(RakeLookup)
8
+ #--
9
+ # Note: Do not refactor Tap:Support::Rake without attending to the line in 'script/run' that
10
+ # extends app. As it stands, this module is loaded as needed using Dependencies.
11
+ module Rake
12
+ def task(td, config={}, &block)
13
+ Object::Rake.application.lookup(td) || super
14
+ end
15
+
16
+ # Modifies Rake::Task to behave like Tap::Task. The essential code is this:
17
+ #
18
+ # module Tap::Support::Rake::Task
19
+ # def new(*args)
20
+ # task = super
21
+ # Tap::Task::Base.initialize(task, :invoke)
22
+ # task
23
+ # end
24
+ # end
25
+ #
26
+ # Here the new method creates a new Rake task as normal, then initializes the
27
+ # Rake task based on the invoke method. The modifed code is applied to Rake
28
+ # in the following fashion:
29
+ #
30
+ # Rake::Task.extend(Tap::Support::Rake::Task)
31
+ #
32
+ module Task
33
+ def new(*args)
34
+ task = super
35
+ Tap::Task::Base.initialize(task, :invoke)
36
+ task
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ Rake::Task.extend(Tap::Support::Rake::Task)
@@ -1,20 +1,39 @@
1
1
  module Tap
2
- module Support
2
+ module Support
3
+ # Raised when an exception is raised during App#run. All errors generated during
4
+ # termination are collected into the RunError.
3
5
  class RunError < RuntimeError
4
- attr_reader :original_error, :terminate_errors
6
+ attr_reader :errors
5
7
 
6
- def initialize(original_error, terminate_errors)
7
- @original_error = original_error
8
- @terminate_errors = terminate_errors
9
- end
10
-
11
- def message
12
- "#{original_error.class} #{original_error.message}"
13
- end
14
-
15
- def backtrace
16
- original_error.backtrace
8
+ def initialize(errors)
9
+ @errors = errors
10
+ @backtrace = nil
11
+ end
12
+
13
+ #The join of all the error messages.
14
+ def message
15
+ lines = []
16
+ errors.each_with_index do |error, i|
17
+ lines << "\nRunError [#{i}] #{error.class} #{error.message}"
18
+ end
19
+ lines.join + "\n"
17
20
  end
21
+
22
+ #The join of all the error backtraces.
23
+ def backtrace
24
+ # backtrace gets called every time RunError is re-raised, leading to multiple
25
+ # repeats of the error backtraces. This ensures the additional backtrace
26
+ # information is only added once.
27
+ return @backtrace unless @backtrace == nil
28
+ return nil unless @backtrace = super
29
+
30
+ errors.each_with_index do |error, i|
31
+ @backtrace [-1] += "\n\n---------------------- RunError [#{i}] ----------------------\n#{error.class} #{error.message}"
32
+ @backtrace.concat(error.backtrace || ["missing backtrace"])
33
+ end
34
+ @backtrace
35
+ end
36
+
18
37
  end
19
38
  end
20
39
  end
@@ -0,0 +1,47 @@
1
+ require 'tempfile'
2
+
3
+ module Tap
4
+ module Support
5
+ # Provides several shell utility methods for calling programs.
6
+ module ShellUtils
7
+
8
+ module_function
9
+
10
+ # Run the system command +cmd+, passing the result to the block, if given.
11
+ # Raises an error if the command fails. Uses the same semantics as
12
+ # Kernel::exec and Kernel::system.
13
+ #
14
+ # Based on FileUtils#sh from Rake.
15
+ def sh(*cmd) # :yields: ok, status
16
+ ok = system(*cmd)
17
+
18
+ if block_given?
19
+ yield(ok, $?)
20
+ else
21
+ ok or raise "Command failed with status (#{$?.exitstatus}): [#{ cmd.join(' ')}]"
22
+ end
23
+ end
24
+
25
+ # Runs the system command +cmd+ using sh, redirecting the output to the
26
+ # specified file path. Uses the redirection command:
27
+ #
28
+ # "> \"#{path}\" 2>&1 #{cmd}"
29
+ #
30
+ # This redirection has been tested on Windows, OS X, and Fedora. See
31
+ # http://www.robvanderwoude.com/redirection.html for pointers on
32
+ # redirection. The website notes that this style of redirection SHOULD
33
+ # NOT be used with commands that contain other redirections.
34
+ def redirect_sh(cmd, path, &block) # :yields: ok, status
35
+ sh( "> \"#{path}\" 2>&1 #{cmd}", &block)
36
+ end
37
+
38
+ # Runs the system command +cmd+ and returns the output as a string.
39
+ def capture_sh(cmd, quiet=false, &block) # :yields: ok, status
40
+ tempfile = Tempfile.new('shell_utils')
41
+ tempfile.close
42
+ redirect_sh(cmd, tempfile.path, &block)
43
+ quiet == true ? "" : File.read(tempfile.path)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -10,7 +10,7 @@ module Tap
10
10
  # documentation for Task configurations, when they are present. TDoc provides an extension
11
11
  # to the standard RDoc HTMLGenerator and template.
12
12
  #
13
- # == Usage
13
+ # === Usage
14
14
  # To generate task documentation with configuration information, TDoc must be loaded and
15
15
  # the appropriate flags passed to rdoc . Essentially what you want is:
16
16
  #
@@ -40,7 +40,7 @@ module Tap
40
40
  # TDoc may also be utilized programatically, but you should be aware that RDoc in Ruby
41
41
  # can raise errors and/or cause namespace conflicts (see below).
42
42
  #
43
- # == Implementation
43
+ # === Implementation
44
44
  # RDoc is a beast to utilize in a non-standard way. One way to make RDoc parse unexpected
45
45
  # flags like 'config_accessor' or the 'c' config specifier is to use the '--accessor' option
46
46
  # (see 'rdoc --help' or the RDoc documentation for more details).
@@ -54,7 +54,7 @@ module Tap
54
54
  # Similarly, the configuration attributes will not appear in the output unless you specify a
55
55
  # template that utilizes them.
56
56
  #
57
- # == Namespace conflicts
57
+ # === Namespace conflicts
58
58
  # RDoc creates a namespace conflict with other libraries that define RubyToken and RubyLex
59
59
  # in the Object namespace (the prime example being IRB). TDoc checks for such a conflict
60
60
  # and redfines the RDoc RubyToken and RubyLex within the RDoc namespace. Essentially:
@@ -90,7 +90,7 @@ module Tap
90
90
  @stats = RDoc::Stats.new
91
91
  @options = Options.instance
92
92
  @options.parse(argv, RDoc::RDoc::GENERATORS)
93
- @load_paths = $:
93
+ @load_paths = ($: + Dependencies.load_paths + Dependencies.load_once_paths).uniq
94
94
  end
95
95
 
96
96
  class << self
@@ -106,11 +106,12 @@ module Tap
106
106
  end
107
107
 
108
108
  def search_for_source_files(klass)
109
- source_files = search_for_files(klass.to_s.underscore)
110
- while klass.superclass.kind_of?(Tap::Task)
111
- klass = klass.superclass
112
- break if klass.configurations.empty?
109
+ source_files = []
110
+ # searches back for all configurable source files, so that
111
+ # inherited configs can be documented.
112
+ while klass.include?(Tap::Support::Configurable)
113
113
  source_files.concat(search_for_files(klass.to_s.underscore))
114
+ klass = klass.superclass
114
115
  end
115
116
 
116
117
  source_files.uniq