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
@@ -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