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
@@ -1,114 +0,0 @@
1
- require 'enumerator'
2
-
3
- module Tap
4
- module Support
5
-
6
- # Combinator provides a method for iterating over all possible combinations
7
- # of items in the input sets.
8
- #
9
- # c = Combinator.new [1,2], [3,4]
10
- # c.collect # => [[1,3], [1,4], [2,3], [2,4]]
11
- #
12
- # Combinator works by iteratively combining each element from the first set (a)
13
- # with each element from the second set (b). When more than two sets are given,
14
- # a Combinator will bundle all but the first set into a new Combinator which
15
- # then acts as the second set.
16
- #
17
- # # Note: each element in a set is arrayified
18
- # # to simplify creation of the combination.
19
- #
20
- # c = Combinator.new [1,2], [3,4], [5,6]
21
- # c.a # => [[1],[2]]
22
- # c.b.class # => Combinator
23
- # c.b.a # => [[3],[4]]
24
- # c.b.b # => [[5],[6]]
25
- #
26
- # Thus when iterating, each Combinator makes it's pairwise combinations and
27
- # works outwards to the final multi-set combinations.
28
- class Combinator
29
- include Enumerable
30
-
31
- attr_reader :a, :b
32
-
33
- # Creates a new Combinator. Input sets must be an Array, or nil.
34
- # Within the Array any objects are allowed for combination.
35
- def initialize(*sets)
36
- @a = make_set(sets.shift)
37
- @b = make_set(*sets)
38
- end
39
-
40
- # Returns the sets used to initialize the Combinator, minus any
41
- # nil or empty sets.
42
- def sets
43
- sets_in(a) + sets_in(b)
44
- end
45
-
46
- # True if the Combinator has no combinations.
47
- def empty?
48
- a.empty? && b.empty?
49
- end
50
-
51
- # Returns the number of combinations in the Combinator.
52
- def length
53
- if a.empty? || b.empty?
54
- if !a.empty?
55
- a.length
56
- elsif !b.empty?
57
- b.length
58
- else
59
- 0
60
- end
61
- else
62
- a.length * b.length
63
- end
64
- end
65
-
66
- # Passes each combination as an array to the input block.
67
- def each(&block)
68
- # if either set is empty, iterate only the non-empty set
69
- # note, the two-step check on each ensures that if both
70
- # sets are empty, then neither is iterated.
71
- if a.empty? || b.empty?
72
- if !a.empty?
73
- a.each {|aa| yield aa }
74
- elsif !b.empty?
75
- b.each {|bb| yield bb }
76
- end
77
- else
78
- # otherwise, iterate all combinations of both
79
- a.each do |aa|
80
- b.each do |bb|
81
- yield aa + bb
82
- end
83
- end
84
- end
85
- end
86
-
87
- private
88
-
89
- def sets_in(set)
90
- # recursively de-arrayifies each element in the array
91
- case set
92
- when Combinator then set.sets
93
- when Array then set.empty? ? [] : [set.collect {|s| s.first}]
94
- end
95
- end
96
-
97
- def make_set(*sets)
98
- # recieves an array of arrays or combinators
99
- return Combinator.new(*sets) if sets.length > 1
100
- return [] if sets.empty?
101
-
102
- # recursively arrayifies each element in an array
103
- set = sets.first
104
- case set
105
- when Array then set.collect {|s| [s]}
106
- when nil then []
107
- else
108
- raise ArgumentError.new("sets must be arrays or nil")
109
- end
110
- end
111
-
112
- end
113
- end
114
- end
@@ -1,169 +0,0 @@
1
- module Tap
2
- module Support
3
- # == UNDER CONSTRUCTION
4
- #
5
- # TaskConfiguration holds the class configurations defined in a Tap::Task.
6
- # The configurations are stored as an array of declarations like:
7
- # [name, default, msg, declaration_class]. In addition, TaskConfiguration
8
- # collapse the array of declarations into a hash, which acts as the default
9
- # task configuration.
10
- #
11
- # Storing metadata about the configurations, such as the declaration_class,
12
- # allows the creation of more user-friendly configuration files and facilitates
13
- # incorporation into command-line applications.
14
- #
15
- # In general, users will not have to interact with TaskConfigurations directly.
16
- #
17
- # == Example
18
- #
19
- # class BaseTask < Tap::Task
20
- # class_configurations [:one, 1]
21
- # end
22
- #
23
- # BaseTask.configurations.hash # => {:one => 1}
24
- #
25
- # class SubTask < BaseTask
26
- # class_configurations(
27
- # [:one, 'one', "the first configuration"],
28
- # [:two, 'two', "the second configuration"])
29
- # end
30
- #
31
- # SubTask.configurations.hash # => {:one => 'one', :two => 'two'}
32
- #
33
- # Now you can see how the comments and declaring classes get used in the
34
- # configuration files. Note that configuration keys are stringified
35
- # for clarity (this is ok -- they will be symbolized when loaded by a
36
- # task).
37
- #
38
- # [BaseTask.configurations.format_yaml]
39
- # # BaseTask configuration
40
- # one: 1
41
- #
42
- # [SubTask.configurations.format_yaml]
43
- # # BaseTask configuration
44
- # one: one # the first configuration
45
- #
46
- # # SubTask configuration
47
- # two: two # the second configuration
48
- #
49
- class TaskConfiguration
50
- attr_reader :declarations, :default, :attributes
51
-
52
- include Enumerable
53
-
54
- def initialize
55
- @declarations = {}
56
- @default = {}
57
- @attributes = {}
58
- end
59
-
60
- def dup
61
- # ensure the duplication does not pass along
62
- # new references to the same objects
63
- another = TaskConfiguration.new
64
- another.declarations = self.declarations.dup
65
- another.default = self.default.dup
66
- another.attributes = self.attributes.dup
67
- another
68
- end
69
-
70
- def empty?
71
- @declaration.empty?
72
- end
73
-
74
- def declared?(key)
75
- default.has_key?(key.to_sym)
76
- end
77
-
78
- def declare(key, declaration_class)
79
- key = key.to_sym
80
-
81
- # Check if the key is already declared...
82
- return if declared?(key)
83
-
84
- # Add a declaration array if no declaration have been
85
- # created for the class and append the key
86
- (declarations[declaration_class] ||= [declarations.length]) << key
87
- default[key] = nil
88
- attributes[key] = {}
89
- end
90
-
91
- def set(key, value, attributes={})
92
- key = key.to_sym
93
- raise "configurations cannot be set until they are declared" unless declared?(key)
94
-
95
- default[key] = value
96
- self.attributes[key].merge!(attributes)
97
- end
98
-
99
- def each # :yields: declaration_class, key, default, attributes
100
- declarations.each_pair do |declaration_class, keys|
101
- keys[1..-1].each do |key|
102
- yield(declaration_class, key, default[key], attributes[key])
103
- end
104
- end
105
- end
106
-
107
- # Nicely formats the configurations into yaml with messages and
108
- # declaration class divisions.
109
- def format_yaml
110
- lines = []
111
- declarations.each_pair do |declaration_class, keys|
112
- lines << "# #{declaration_class} configuration#{keys.length > 2 ? 's' : ''}"
113
-
114
- class_name = declaration_class.to_s
115
- class_path = class_name.underscore
116
- source_file = Dependencies.search_for_file(class_path)
117
-
118
- Tap::Support::TDoc.document(source_file)
119
- class_doc = Tap::Support::TDoc[declaration_class]
120
- configurations = (class_doc == nil ? [] : class_doc.configurations)
121
-
122
- keys[1..-1].each do |key|
123
- value = default[key]
124
- attribs = attributes[key]
125
- message = ""
126
- configurations.each do |config|
127
- if config.name == key.to_s
128
- message = config.comment
129
- break
130
- end
131
- end
132
-
133
- # yaml adds a header and a final newline which should be removed:
134
- # {'key' => 'value'}.to_yaml # => "--- \nkey: value\n"
135
- # {'key' => 'value'}.to_yaml[5...-1] # => "key: value"
136
- yaml = {key.to_s => value}.to_yaml[5...-1]
137
- #yaml = "##{yaml}" if value.nil?
138
-
139
- lines << case
140
- when message == nil || message.empty?
141
- # if there is no message, simply add the yaml
142
- yaml
143
- when yaml !~ /\r?\n/ && message !~ /\r?\n/ && yaml.length < 20 && message.length < 30
144
- # shorthand ONLY if the config and message can be expressed in a single line
145
- message = message.gsub(/^#\s*/, "")
146
- "%-20s # %s" % [yaml, message]
147
- else
148
- # comment out new lines and add the message
149
- message = message.gsub(/\n#\s*/, "\n# ")
150
- lines << "# #{message}"
151
- yaml
152
- end
153
- end
154
-
155
- # add a spacer line
156
- lines << ""
157
- end
158
-
159
- lines.join("\n")
160
- end
161
-
162
-
163
- protected
164
-
165
- attr_writer :declarations, :default, :attributes
166
-
167
- end
168
- end
169
- end
@@ -1,81 +0,0 @@
1
- require 'tap/support/combinator'
2
-
3
- module Tap
4
- module Support
5
-
6
- # Template provides the default methods for making hash templates using Templater.
7
- # Methods are provided to make variations of the template, as well as to pattern
8
- # templates based on combinations of possible values.
9
- #
10
- module Template
11
- attr_accessor :templater
12
-
13
- # Provides a standard way of creating a variations of a template and takes an
14
- # array of hashes with the specified variations. The template is treated as the
15
- # default and variations are pushed (<<) to the templater. The template is
16
- # cleared upon complete, so that it will be removed if used in the context of
17
- # a Templater.
18
- #
19
- # t = {"default" => "default value"}.extend Template
20
- # t.templater = []
21
- # t.variations([
22
- # {"default" => "non-default value"},
23
- # {"another" => "value"}])
24
- #
25
- # t.templater
26
- # # => [{"default" => "non-default value"},
27
- # {"default" => "default value", "another" => "value"}]
28
- #
29
- def variations(array)
30
- variations!(array)
31
- self.clear
32
- end
33
-
34
- # Same as variations but does NOT clear the template (ie the template will
35
- # be kept as specified, effectively acting as another variant). Note that
36
- # variations! is called by the 'variations!!' key.
37
- def variations!(array)
38
- array.each { |var| templater << self.merge(var) }
39
- end
40
-
41
- # Provides a standard way of creating variations of a template based on all
42
- # combinations of the keys in the input hash. The template itself is treated
43
- # as the default and variations are pushed (<<) to the templater. The template
44
- # is cleared upon complete, so that it will be removed if used in the context of
45
- # a Templater.
46
- #
47
- # t = {"default" => "default value"}.extend Template
48
- # t.templater = []
49
- # t.combinations(
50
- # "letter" => ["a", "b"],
51
- # "number" => [1, 2])
52
- #
53
- # t.templater
54
- # # => [{"default" => "default value", "letter" => "a", "number" => 1},
55
- # {"default" => "default value", "letter" => "a", "number" => 2},
56
- # {"default" => "default value", "letter" => "b", "number" => 1},
57
- # {"default" => "default value", "letter" => "b", "number" => 2}]
58
- def combinations(hash)
59
- combinations!(hash)
60
- self.clear
61
- end
62
-
63
- # Same as combinations but does NOT clear the template (ie the template will
64
- # be kept as specified, effectively acting as another variant). Note that
65
- # combinations! is called by the 'combinations!!' key.
66
- def combinations!(hash)
67
- keys, values = hash.to_a.transpose
68
- values.collect! do |value|
69
- value.kind_of?(Array) ? value : [value]
70
- end
71
-
72
- Combinator.new(*values).each do |c|
73
- template = {}
74
- keys.each_with_index {|key,i| template[key] = c[i] }
75
-
76
- templater << self.merge(template)
77
- end
78
- end
79
- end
80
- end
81
- end
@@ -1,155 +0,0 @@
1
- require 'tap/support/template'
2
-
3
- module Tap
4
- module Support
5
-
6
- # Templater generates permutations of a hash, treating it as a template for other
7
- # hashes. Templater looks for various flags on the end of keys to signify operations
8
- # like method execution ('!') and replacement ('=').
9
- #
10
- # module TemplateMethods
11
- # def method_call(input)
12
- # self["result"] = "#{input} was processed"
13
- # end
14
- # end
15
- #
16
- # t = Templater.new(
17
- # "key"=> "value",
18
- # "method_call!" => "method input",
19
- # "replacement=" => "replacement input")
20
- # t.run_methods(TemplateMethods)
21
- # t.run_replace do |input|
22
- # "#{input} was replaced"
23
- # end
24
- #
25
- # t.templates
26
- # # => [{"key" => "value",
27
- # "result" => "method input was processed",
28
- # "replacement" => "replacement input was replaced"}]
29
- #
30
- # == Creating Template Methods
31
- #
32
- # The default Template methods may not be sufficient for your needs, but making new
33
- # methods (as above) is easy. Some simple rules apply:
34
- #
35
- # - Any method can be called from run_methods provided it has a single input.
36
- # - Modules with the methods will extend the hash templates so 'self' indicates the
37
- # template itself.
38
- # - New templates should be added by pushing the template to the templater, which
39
- # will already be set by the time the method executes
40
- # (ex: self.templater << new_template).
41
- #
42
- # Also, it's important to note that ANY empty templates at the end of run_methods
43
- # are cleared from the templates array. Thus, if you want to remove a template from
44
- # within a method, simply clear self.
45
- #
46
- # See the Template code for examples.
47
- class Templater
48
-
49
- # Convenience method for making a new Templater, running methods
50
- # with the input modules, and making replacements using the input
51
- # block. Returns the resulting templates.
52
- def self.make_templates(hash, *modules, &block)
53
- t = Templater.new(hash)
54
- t.run_methods(*modules)
55
- t.run_replace(&block)
56
- t.templates
57
- end
58
-
59
- attr_reader :templates
60
-
61
- # Creates a new Templater with the input hash as the initial template.
62
- def initialize(hash)
63
- @templates = []
64
- self << hash
65
- end
66
-
67
- # Shortcut to add a template to the templater, unless the template is empty.
68
- def <<(template)
69
- self.templates << template unless template.empty?
70
- end
71
-
72
- # Iterates through the pairs in each template looking for keys matching the
73
- # regexp (using =~). When a matching key is found, it is removed from the
74
- # template and then the template, the key, and the corresponding value are
75
- # passed to the block for further processing. Iteration restarts after each
76
- # match and empty templates are removed upon completion. All templates are
77
- # extended with Template before iteration.
78
- #
79
- # Notes:
80
- # - run_templater is the foundation of the other run_* methods and may be used
81
- # for custom template processing.
82
- # - since =~ is used for comparison, only String keys will be selected
83
- # under normal circumstances.
84
- def run_templater(regexp) # :yields: template, key, value
85
- templates.each do |template|
86
- unless template.respond_to?(:templater)
87
- template.extend Template
88
- template.templater = self
89
- end
90
-
91
- template.each_pair do |key, value|
92
- next unless key =~ regexp
93
-
94
- # remove the key
95
- template.delete(key)
96
- yield(template, key, value)
97
-
98
- # restart the loop as key/value pairs may have changed
99
- retry
100
- end
101
- end
102
-
103
- templates.delete_if {|t| t.empty?}
104
-
105
- self
106
- end
107
-
108
- # Iterates through the pairs in each template looking for and executing method calls
109
- # as per run_templater. Method calls are indicated by an exclamation attached to the
110
- # key, like 'do_something!'.
111
- #
112
- # Templates are extended with the input modules before the method call. The modules
113
- # should provide the methods (an error will be raised if the specified method is not
114
- # defined). Note, templates will by default be extended with the Template module.
115
- #
116
- # module MethodModule
117
- # def method_call(input)
118
- # self["result"] = "#{input} was processed"
119
- # end
120
- # end
121
- #
122
- # t = Templater.new('method_call!' => 'input')
123
- # t.run_methods(MethodModule) # calls 'do_something' with the input 'some value',
124
- # # after removing the key-value pair
125
- # t.templates # => [{"result" => "input was processed"}]
126
- #
127
- # Notes:
128
- # - The method called is the method minus the exclamation, so 'do_something!' calls
129
- # 'do_something' within the template and 'do_something!!' calls 'do_something!'
130
- # - Iteration restarts after each method call; inserted method calls will be executed
131
- def run_methods(*modules)
132
- run_templater(/!$/) do |template, key, value|
133
- modules.each {|mod| template.extend mod}
134
- template.send(key.chop, value)
135
- end
136
- end
137
-
138
- # Iterates through the pairs in each template looking for replacement keys and
139
- # executing the block to make the appropriate replacement value, as per
140
- # run_templater. Replacement keys are indicated by an equals sign attached to
141
- # the key, like 'key='. The equals sign will be chopped off for these keys, and
142
- # the value replaced with the return value of the block.
143
- #
144
- # t = Templater.new('replacement=' => 'some value')
145
- # t.run_replace {|value| "#{value} was replaced"}
146
- # t.templates # => [{"replacement" => "some value was replaced"}]
147
- #
148
- def run_replace # :yields: value
149
- run_templater(/=$/) do |template, key, value|
150
- template[key.chop] = yield(value)
151
- end
152
- end
153
- end
154
- end
155
- end