tap 0.9.1 → 0.10.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 (244) hide show
  1. data/History +37 -30
  2. data/MIT-LICENSE +1 -1
  3. data/README +92 -44
  4. data/bin/tap +62 -75
  5. data/cmd/console.rb +42 -0
  6. data/cmd/destroy.rb +16 -0
  7. data/cmd/generate.rb +16 -0
  8. data/cmd/run.rb +126 -0
  9. data/doc/Class Reference +362 -0
  10. data/doc/Command Reference +153 -0
  11. data/doc/Tutorial +237 -0
  12. data/lib/tap.rb +6 -45
  13. data/lib/tap/app.rb +126 -500
  14. data/lib/tap/constants.rb +2 -29
  15. data/lib/tap/env.rb +555 -250
  16. data/lib/tap/file_task.rb +60 -103
  17. data/lib/tap/generator/base.rb +109 -0
  18. data/lib/tap/generator/destroy.rb +37 -0
  19. data/lib/tap/generator/generate.rb +61 -0
  20. data/lib/tap/generator/generators/command/command_generator.rb +16 -12
  21. data/lib/tap/generator/generators/command/templates/command.erb +13 -19
  22. data/lib/tap/generator/generators/config/config_generator.rb +18 -27
  23. data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
  24. data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
  25. data/lib/tap/generator/generators/file_task/file_task_generator.rb +16 -11
  26. data/lib/tap/generator/generators/file_task/templates/file.txt +11 -2
  27. data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
  28. data/lib/tap/generator/generators/file_task/templates/task.erb +24 -31
  29. data/lib/tap/generator/generators/file_task/templates/test.erb +18 -22
  30. data/lib/tap/generator/generators/root/root_generator.rb +45 -31
  31. data/lib/tap/generator/generators/root/templates/Rakefile +64 -41
  32. data/lib/tap/generator/generators/root/templates/gemspec +27 -0
  33. data/lib/tap/generator/generators/root/templates/tapfile +8 -0
  34. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -0
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
  36. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +21 -28
  38. data/lib/tap/generator/generators/task/templates/task.erb +13 -23
  39. data/lib/tap/generator/generators/task/templates/test.erb +15 -18
  40. data/lib/tap/generator/manifest.rb +14 -0
  41. data/lib/tap/patches/rake/rake_test_loader.rb +0 -0
  42. data/lib/tap/patches/rake/testtask.rb +0 -0
  43. data/lib/tap/patches/ruby19/backtrace_filter.rb +0 -0
  44. data/lib/tap/patches/ruby19/parsedate.rb +0 -0
  45. data/lib/tap/root.rb +260 -21
  46. data/lib/tap/support/aggregator.rb +11 -11
  47. data/lib/tap/support/assignments.rb +172 -0
  48. data/lib/tap/support/audit.rb +20 -18
  49. data/lib/tap/support/batchable.rb +21 -10
  50. data/lib/tap/support/batchable_class.rb +107 -0
  51. data/lib/tap/support/class_configuration.rb +154 -239
  52. data/lib/tap/support/command_line.rb +97 -102
  53. data/lib/tap/support/comment.rb +270 -0
  54. data/lib/tap/support/configurable.rb +86 -65
  55. data/lib/tap/support/configurable_class.rb +296 -0
  56. data/lib/tap/support/configuration.rb +122 -0
  57. data/lib/tap/support/constant.rb +70 -0
  58. data/lib/tap/support/constant_utils.rb +127 -0
  59. data/lib/tap/support/declarations.rb +111 -0
  60. data/lib/tap/support/executable.rb +30 -17
  61. data/lib/tap/support/executable_queue.rb +0 -0
  62. data/lib/tap/support/framework.rb +71 -0
  63. data/lib/tap/support/framework_class.rb +199 -0
  64. data/lib/tap/support/instance_configuration.rb +147 -0
  65. data/lib/tap/support/lazydoc.rb +428 -0
  66. data/lib/tap/support/manifest.rb +89 -0
  67. data/lib/tap/support/run_error.rb +0 -0
  68. data/lib/tap/support/shell_utils.rb +33 -9
  69. data/lib/tap/support/summary.rb +30 -0
  70. data/lib/tap/support/tdoc.rb +339 -134
  71. data/lib/tap/support/tdoc/tdoc_html_generator.rb +0 -0
  72. data/lib/tap/support/tdoc/tdoc_html_template.rb +0 -0
  73. data/lib/tap/support/templater.rb +180 -0
  74. data/lib/tap/support/validation.rb +409 -76
  75. data/lib/tap/support/versions.rb +5 -3
  76. data/lib/tap/task.rb +78 -174
  77. data/lib/tap/tasks/dump.rb +56 -0
  78. data/lib/tap/tasks/rake.rb +93 -0
  79. data/lib/tap/test.rb +3 -3
  80. data/lib/tap/test/env_vars.rb +2 -2
  81. data/lib/tap/test/file_methods.rb +19 -20
  82. data/lib/tap/test/script_methods.rb +144 -0
  83. data/lib/tap/test/subset_methods.rb +1 -1
  84. data/lib/tap/test/tap_methods.rb +28 -62
  85. data/lib/tap/workflow.rb +22 -39
  86. metadata +48 -179
  87. data/Basic Overview +0 -151
  88. data/Command Reference +0 -99
  89. data/Rakefile +0 -127
  90. data/Tutorial +0 -287
  91. data/lib/tap/cmd/console.rb +0 -31
  92. data/lib/tap/cmd/destroy.rb +0 -20
  93. data/lib/tap/cmd/generate.rb +0 -20
  94. data/lib/tap/cmd/run.rb +0 -151
  95. data/lib/tap/dump.rb +0 -57
  96. data/lib/tap/generator.rb +0 -91
  97. data/lib/tap/generator/generators/command/USAGE +0 -6
  98. data/lib/tap/generator/generators/config/USAGE +0 -21
  99. data/lib/tap/generator/generators/config/templates/config.erb +0 -1
  100. data/lib/tap/generator/generators/file_task/USAGE +0 -3
  101. data/lib/tap/generator/generators/file_task/templates/file.yml +0 -3
  102. data/lib/tap/generator/generators/generator/USAGE +0 -0
  103. data/lib/tap/generator/generators/generator/generator_generator.rb +0 -21
  104. data/lib/tap/generator/generators/generator/templates/generator.erb +0 -32
  105. data/lib/tap/generator/generators/generator/templates/usage.erb +0 -1
  106. data/lib/tap/generator/generators/root/USAGE +0 -0
  107. data/lib/tap/generator/generators/root/templates/ReadMe.txt +0 -0
  108. data/lib/tap/generator/generators/root/templates/tap.yml +0 -80
  109. data/lib/tap/generator/generators/task/USAGE +0 -3
  110. data/lib/tap/generator/generators/workflow/USAGE +0 -0
  111. data/lib/tap/generator/generators/workflow/templates/task.erb +0 -16
  112. data/lib/tap/generator/generators/workflow/templates/test.erb +0 -7
  113. data/lib/tap/generator/generators/workflow/workflow_generator.rb +0 -6
  114. data/lib/tap/generator/options.rb +0 -26
  115. data/lib/tap/generator/usage.rb +0 -26
  116. data/lib/tap/support/batchable_methods.rb +0 -34
  117. data/lib/tap/support/command_line_methods.rb +0 -76
  118. data/lib/tap/support/configurable_methods.rb +0 -224
  119. data/lib/tap/support/logger.rb +0 -88
  120. data/lib/tap/support/rake.rb +0 -43
  121. data/lib/tap/support/tdoc/config_attr.rb +0 -362
  122. data/test/app/config/another/task.yml +0 -1
  123. data/test/app/config/batch.yml +0 -2
  124. data/test/app/config/empty.yml +0 -0
  125. data/test/app/config/erb.yml +0 -2
  126. data/test/app/config/some/task.yml +0 -1
  127. data/test/app/config/template.yml +0 -2
  128. data/test/app/config/version-0.1.yml +0 -1
  129. data/test/app/config/version.yml +0 -1
  130. data/test/app/lib/app_test_task.rb +0 -3
  131. data/test/app_test.rb +0 -1849
  132. data/test/env/test_configure/recurse_a.yml +0 -2
  133. data/test/env/test_configure/recurse_b.yml +0 -2
  134. data/test/env/test_configure/tap.yml +0 -23
  135. data/test/env/test_load_env_config/dir/tap.yml +0 -3
  136. data/test/env/test_load_env_config/recurse_a.yml +0 -2
  137. data/test/env/test_load_env_config/recurse_b.yml +0 -2
  138. data/test/env/test_load_env_config/tap.yml +0 -3
  139. data/test/env_test.rb +0 -198
  140. data/test/file_task/config/batch.yml +0 -2
  141. data/test/file_task/config/configured.yml +0 -1
  142. data/test/file_task/old_file_one.txt +0 -0
  143. data/test/file_task/old_file_two.txt +0 -0
  144. data/test/file_task_test.rb +0 -1291
  145. data/test/root/alt_lib/alt_module.rb +0 -4
  146. data/test/root/file.txt +0 -0
  147. data/test/root/glob/one.txt +0 -0
  148. data/test/root/glob/two.txt +0 -0
  149. data/test/root/lib/absolute_alt_filepath.rb +0 -2
  150. data/test/root/lib/alternative_filepath.rb +0 -2
  151. data/test/root/lib/another_module.rb +0 -2
  152. data/test/root/lib/nested/some_module.rb +0 -4
  153. data/test/root/lib/no_module_included.rb +0 -0
  154. data/test/root/lib/some/module.rb +0 -4
  155. data/test/root/lib/some_class.rb +0 -2
  156. data/test/root/lib/some_module.rb +0 -3
  157. data/test/root/load_path/load_path_module.rb +0 -2
  158. data/test/root/load_path/skip_module.rb +0 -2
  159. data/test/root/mtime/older.txt +0 -0
  160. data/test/root/unload/full_path.rb +0 -2
  161. data/test/root/unload/loaded_by_nested.rb +0 -2
  162. data/test/root/unload/nested/nested_load.rb +0 -6
  163. data/test/root/unload/nested/nested_with_ext.rb +0 -4
  164. data/test/root/unload/nested/relative_path.rb +0 -4
  165. data/test/root/unload/older.rb +0 -2
  166. data/test/root/unload/unload_base.rb +0 -9
  167. data/test/root/versions/another.yml +0 -0
  168. data/test/root/versions/file-0.1.2.yml +0 -0
  169. data/test/root/versions/file-0.1.yml +0 -0
  170. data/test/root/versions/file.yml +0 -0
  171. data/test/root_test.rb +0 -718
  172. data/test/support/aggregator_test.rb +0 -99
  173. data/test/support/audit_test.rb +0 -445
  174. data/test/support/batchable_test.rb +0 -74
  175. data/test/support/class_configuration_test.rb +0 -331
  176. data/test/support/command_line_test.rb +0 -58
  177. data/test/support/configurable/config/configured.yml +0 -2
  178. data/test/support/configurable_test.rb +0 -295
  179. data/test/support/executable_queue_test.rb +0 -103
  180. data/test/support/executable_test.rb +0 -38
  181. data/test/support/logger_test.rb +0 -31
  182. data/test/support/rake_test.rb +0 -37
  183. data/test/support/shell_utils_test.rb +0 -24
  184. data/test/support/tdoc_test.rb +0 -370
  185. data/test/support/validation_test.rb +0 -54
  186. data/test/support/versions_test.rb +0 -103
  187. data/test/tap_test_helper.rb +0 -57
  188. data/test/tap_test_suite.rb +0 -7
  189. data/test/task/config/batch.yml +0 -2
  190. data/test/task/config/batched.yml +0 -2
  191. data/test/task/config/configured.yml +0 -1
  192. data/test/task/config/example.yml +0 -1
  193. data/test/task_base_test.rb +0 -24
  194. data/test/task_syntax_test.rb +0 -300
  195. data/test/task_test.rb +0 -320
  196. data/test/test/env_vars_test.rb +0 -48
  197. data/test/test/file_methods/test_assert_files/expected/one.txt +0 -1
  198. data/test/test/file_methods/test_assert_files/expected/two.txt +0 -1
  199. data/test/test/file_methods/test_assert_files/input/one.txt +0 -1
  200. data/test/test/file_methods/test_assert_files/input/two.txt +0 -1
  201. data/test/test/file_methods/test_assert_files_can_have_no_expected_files_if_specified/input/one.txt +0 -1
  202. data/test/test/file_methods/test_assert_files_can_have_no_expected_files_if_specified/input/two.txt +0 -1
  203. data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/one.txt +0 -1
  204. data/test/test/file_methods/test_assert_files_fails_for_different_content/expected/two.txt +0 -1
  205. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/one.txt +0 -1
  206. data/test/test/file_methods/test_assert_files_fails_for_different_content/input/two.txt +0 -1
  207. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/expected/one.txt +0 -1
  208. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/one.txt +0 -1
  209. data/test/test/file_methods/test_assert_files_fails_for_missing_expected_file/input/two.txt +0 -1
  210. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/one.txt +0 -1
  211. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/expected/two.txt +0 -1
  212. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/one.txt +0 -1
  213. data/test/test/file_methods/test_assert_files_fails_for_missing_output_file/input/two.txt +0 -1
  214. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/one.txt +0 -1
  215. data/test/test/file_methods/test_assert_files_fails_for_no_expected_files/input/two.txt +0 -1
  216. data/test/test/file_methods/test_method_glob/expected/file.yml +0 -0
  217. data/test/test/file_methods/test_method_glob/expected/file_1.txt +0 -0
  218. data/test/test/file_methods/test_method_glob/expected/file_2.txt +0 -0
  219. data/test/test/file_methods_doc/test_sub/expected/one.txt +0 -1
  220. data/test/test/file_methods_doc/test_sub/expected/two.txt +0 -1
  221. data/test/test/file_methods_doc/test_sub/input/one.txt +0 -1
  222. data/test/test/file_methods_doc/test_sub/input/two.txt +0 -1
  223. data/test/test/file_methods_doc_test.rb +0 -29
  224. data/test/test/file_methods_test.rb +0 -275
  225. data/test/test/subset_methods_test.rb +0 -171
  226. data/test/test/tap_methods/test_assert_files/expected/task/name/a.txt +0 -1
  227. data/test/test/tap_methods/test_assert_files/expected/task/name/b.txt +0 -1
  228. data/test/test/tap_methods/test_assert_files/input/a.txt +0 -1
  229. data/test/test/tap_methods/test_assert_files/input/b.txt +0 -1
  230. data/test/test/tap_methods_test.rb +0 -399
  231. data/test/workflow_test.rb +0 -120
  232. data/vendor/rails_generator.rb +0 -56
  233. data/vendor/rails_generator/base.rb +0 -263
  234. data/vendor/rails_generator/commands.rb +0 -581
  235. data/vendor/rails_generator/generated_attribute.rb +0 -42
  236. data/vendor/rails_generator/lookup.rb +0 -209
  237. data/vendor/rails_generator/manifest.rb +0 -53
  238. data/vendor/rails_generator/options.rb +0 -143
  239. data/vendor/rails_generator/scripts.rb +0 -83
  240. data/vendor/rails_generator/scripts/destroy.rb +0 -7
  241. data/vendor/rails_generator/scripts/generate.rb +0 -7
  242. data/vendor/rails_generator/scripts/update.rb +0 -12
  243. data/vendor/rails_generator/simple_logger.rb +0 -46
  244. data/vendor/rails_generator/spec.rb +0 -44
@@ -1,279 +1,194 @@
1
- autoload(:GetOptLong, 'getoptlong')
1
+ require 'tap/support/assignments'
2
+ require 'tap/support/instance_configuration'
3
+ require 'tap/support/configuration'
2
4
 
3
5
  module Tap
4
6
  module Support
5
- # == UNDER CONSTRUCTION
6
- #--
7
- #
8
- # ClassConfiguration holds the class configurations defined in a Tap::Task.
9
- # The configurations are stored as an array of declarations_array like:
10
- # [name, default, msg, declaration_class]. In addition, ClassConfiguration
11
- # collapse the array of declarations_array into a hash, which acts as the default
12
- # task configuration.
13
- #
14
- # Storing metadata about the configurations, such as the declaration_class,
15
- # allows the creation of more user-friendly configuration files and facilitates
16
- # incorporation into command-line applications.
17
- #
18
- # In general, users will not have to interact with ClassConfigurations directly.
19
- #
20
- # === Example
21
- #
22
- # class BaseTask < Tap::Configurable
23
- # class_configurations [:one, 1]
24
- # end
25
- #
26
- # BaseTask.configurations.hash # => {:one => 1}
27
- #
28
- # class SubTask < BaseTask
29
- # class_configurations(
30
- # [:one, 'one', "the first configuration"],
31
- # [:two, 'two', "the second configuration"])
32
- # end
33
- #
34
- # SubTask.configurations.hash # => {:one => 'one', :two => 'two'}
35
- #
36
- # Now you can see how the comments and declaring classes get used in the
37
- # configuration files. Note that configuration keys are stringified
38
- # for clarity (this is ok -- they will be symbolized when loaded by a
39
- # task).
40
- #
41
- # [BaseTask.configurations.format_yaml]
42
- # # BaseTask configuration
43
- # one: 1
44
- #
45
- # [SubTask.configurations.format_yaml]
46
- # # BaseTask configuration
47
- # one: one # the first configuration
48
- #
49
- # # SubTask configuration
50
- # two: two # the second configuration
51
- #
52
- #--
53
- # TODO -
54
- # Revisit config formatting... right now it's a bit jacked.
55
- #++
7
+
8
+ # ClassConfiguration tracks and handles the class configurations defined in a
9
+ # Configurable class.
56
10
  class ClassConfiguration
57
11
  include Enumerable
58
12
 
59
- # The class receiving the configurations
13
+ # The class receiving new configurations
60
14
  attr_reader :receiver
15
+
16
+ # Tracks the assignment of the config keys to receivers
17
+ attr_reader :assignments
61
18
 
62
- # An array of [receiver, configuration keys] arrays tracking
63
- # the order in which configurations were declared across all
64
- # receivers
65
- attr_reader :declarations_array
66
-
67
- # An array of configuration keys declared by self
68
- attr_reader :declarations
69
-
70
- # A hash of the unprocessed default values
71
- attr_reader :unprocessed_default
72
-
73
- # A hash of the processed default values
74
- attr_reader :default
75
-
76
- # A hash of the processing blocks
77
- attr_reader :process_blocks
19
+ # A map of config keys and instance methods used to set a
20
+ # config (ie the reader and writer for a config)
21
+ attr_reader :map
78
22
 
79
- # A placeholder to indicate when no value
80
- # was specified during a call to add.
81
- NO_VALUE = Object.new
82
-
83
23
  def initialize(receiver, parent=nil)
84
24
  @receiver = receiver
85
- @default = parent != nil ? parent.default.dup : {}
86
- @unprocessed_default = parent != nil ? parent.unprocessed_default.dup : {}
87
- @process_blocks = parent != nil ? parent.process_blocks.dup : {}
88
25
 
89
- # use same declarations array? freeze declarations to ensure order?
90
- # definitely falls out of order if parents are modfied after initialization
91
- @declarations_array = parent != nil ? parent.declarations_array.dup : []
92
- @declarations = []
93
- declarations_array << [receiver, @declarations]
26
+ if parent != nil
27
+ @map = parent.map.inject({}) do |hash, (key, config)|
28
+ hash[key] = config.dup
29
+ hash
30
+ end
31
+ @assignments = Assignments.new(parent.assignments)
32
+ else
33
+ @map = {}
34
+ @assignments = Assignments.new
35
+ end
36
+ end
37
+
38
+ # Initializes a Configuration using the inputs and sets it in self
39
+ # using name as a key, overriding the current config by that name,
40
+ # if it exists. Returns the new config.
41
+ def add(name, default=nil, attributes={})
42
+ self[name] = Configuration.new(name.to_sym, default, attributes)
94
43
  end
95
44
 
96
- # Returns true if the key has been declared by some receiver.
97
- # Note this is distinct from whether or not a particular config
98
- # is currently in the default hash.
99
- def declared?(key)
100
- key = key.to_sym
101
- declarations_array.each do |r,array|
102
- return true if array.include?(key)
103
- end
104
- false
45
+ # Removes the specified configuration.
46
+ def remove(key)
47
+ self[key] = nil
105
48
  end
106
49
 
107
- # Returns the class (receiver) that first added the config
108
- # indicated by key.
109
- def declaration_class(key)
50
+ # Gets the configuration specified by key. The key is symbolized.
51
+ def [](key)
52
+ map[key.to_sym]
53
+ end
54
+
55
+ # Assigns the configuration to key. A nil config unassigns the
56
+ # configuration key. The key is symbolized.
57
+ def []=(key, config)
110
58
  key = key.to_sym
111
- declarations_array.each do |r,array|
112
- return r if array.include?(key)
59
+
60
+ if config == nil
61
+ assignments.unassign(key)
62
+ map.delete(key)
63
+ else
64
+ assignments.assign(receiver, key) unless assignments.assigned?(key)
65
+ map[key] = config
113
66
  end
114
- nil
115
67
  end
116
68
 
117
- # Returns the configurations first declared by the specified receiver.
118
- def declarations_for(receiver)
119
- declarations_array.each do |r,array|
120
- return array if r == receiver
121
- end
122
- nil
69
+ # Returns true if key is a config key.
70
+ def key?(key)
71
+ map.has_key?(key)
123
72
  end
124
73
 
125
- # Adds a configuration. If specified, the value is processed
126
- # by the process_block and recorded adefault
127
- #
128
- # The existing value and process_block for the
129
- # configuration will not be overwritten unless specified. However,
130
- # if a configuration is added without specifying a value and no previous
131
- # default value exists, the default and unprocessed_default for the
132
- # configuration will be set to nil.
133
- #
134
- #--
135
- # Note -- existing blocks are NOT overwritten unless a new block is provided.
136
- # This allows overide of the default value in subclasses while preserving the
137
- # validation/processing code.
138
- def add(key, value=NO_VALUE, &process_block)
139
- key = key.to_sym
140
- value = unprocessed_default[key] if value == NO_VALUE
141
-
142
- declarations << key unless declared?(key)
143
- process_blocks[key] = process_block if block_given?
144
- unprocessed_default[key] = value
145
- default[key] = process(key, value)
146
-
147
- self
74
+ # Returns all config keys.
75
+ def keys
76
+ map.keys
148
77
  end
149
78
 
150
- def remove(key)
151
- key = key.to_sym
152
-
153
- process_blocks.delete(key)
154
- unprocessed_default.delete(key)
155
- default.delete(key)
156
-
157
- self
79
+ # Returns config keys in order.
80
+ def ordered_keys
81
+ assignments.values
158
82
  end
159
83
 
160
- def merge(another)
161
- # check for conflicts
162
- another.each do |receiver, key|
163
- dc = declaration_class(key)
164
- next if dc == nil || dc == receiver
165
-
166
- raise "configuration conflict: #{key} (#{receiver}) already declared by #{dc}"
167
- end
168
-
169
- # add the new configurations
170
- another.each do |receiver, key|
171
- # preserve the declarations for receiver
172
- unless declarations = declarations_for(receiver)
173
- declarations = []
174
- declarations_array << [receiver, declarations]
175
- end
176
- unless declarations.include?(key)
177
- declarations << key
178
- yield(key) if block_given?
179
- end
180
-
181
- add(key, another.unprocessed_default[key], &another.process_blocks[key])
84
+ # Returns all mapped configs.
85
+ def values
86
+ map.values
87
+ end
88
+
89
+ # True if map is empty.
90
+ def empty?
91
+ map.empty?
92
+ end
93
+
94
+ # Calls block once for each [receiver, key, config] in self,
95
+ # passing those elements as parameters, in the order in
96
+ # which they were assigned.
97
+ def each
98
+ assignments.each do |receiver, key|
99
+ yield(receiver, key, map[key])
182
100
  end
183
101
  end
184
-
185
- def each # :yields: receiver, key
186
- declarations_array.each do |receiver, keys|
187
- keys.each {|key| yield(receiver, key) }
102
+
103
+ # Calls block once for each [key, config] pair in self,
104
+ # passing those elements as parameters, in the order in
105
+ # which they were assigned.
106
+ def each_pair
107
+ assignments.each do |receiver, key|
108
+ yield(key, map[key])
188
109
  end
189
110
  end
190
111
 
191
- # Sends value to the process block identified by key and returns the result.
192
- # Returns value if no process block has been set for key.
193
- def process(key, value)
194
- block = process_blocks[key.to_sym]
195
- block ? block.call(value) : value
112
+ # Initializes and returns a new InstanceConfiguration set to self
113
+ # and bound to the receiver, if specified.
114
+ def instance_config(receiver=nil)
115
+ InstanceConfiguration.new(self, receiver)
196
116
  end
197
-
198
- # Nicely formats the configurations into yaml with messages and
199
- # declaration class divisions.
200
- def format_yaml
201
- lines = []
202
- declarations_array.each do |receiver, keys|
203
-
204
- # do not consider keys that have been removed
205
- keys = keys.delete_if {|key| !self.default.has_key?(key) }
206
- next if keys.empty?
207
-
208
- lines << "# #{receiver} configuration#{keys.length > 1 ? 's' : ''}"
209
-
210
- class_doc = Tap::Support::TDoc[receiver]
211
- configurations = (class_doc == nil ? [] : class_doc.configurations)
212
- keys.each do |key|
213
- tdoc_config = configurations.find {|config| config.name == key.to_s }
214
-
215
- # yaml adds a header and a final newline which should be removed:
216
- # {'key' => 'value'}.to_yaml # => "--- \nkey: value\n"
217
- # {'key' => 'value'}.to_yaml[5...-1] # => "key: value"
218
- yaml = {key.to_s => unprocessed_default[key]}.to_yaml[5...-1]
219
- message = tdoc_config ? tdoc_config.comment : ""
220
-
221
- lines << case
222
- when message == nil || message.empty?
223
- # if there is no message, simply add the yaml
224
- yaml
225
- when yaml !~ /\r?\n/ && message !~ /\r?\n/ && yaml.length < 25 && message.length < 30
226
- # shorthand ONLY if the config and message can be expressed in a single line
227
- message = message.gsub(/^#\s*/, "")
228
- "%-25s # %s" % [yaml, message]
229
- else
230
- lines << ""
231
- # comment out new lines and add the message
232
- message.split(/\n/).each do |msg|
233
- lines << "# #{msg.strip.gsub(/^#\s*/, '')}"
234
- end
235
- yaml
236
- end
237
- end
238
-
239
- # add a spacer line
240
- lines << ""
241
- end
242
117
 
243
- lines.compact.join("\n")
118
+ # Returns a hash of the (key, config.default) values in self.
119
+ def to_hash
120
+ hash = {}
121
+ each_pair {|key, config| hash[key] = config.default }
122
+ hash
244
123
  end
245
-
246
- def opt_map(long_option)
247
- raise ArgumentError.new("not a long option: #{long_option}") unless long_option =~ /^--(.*)$/
248
- long = $1
249
124
 
250
- each do |receiver, key|
251
- return key if long == key.to_s
252
- end
253
- nil
125
+ def code_comments
126
+ code_comments = []
127
+ values.each do |config|
128
+ code_comments << config.desc if config.desc.kind_of?(Comment)
129
+ end
130
+ code_comments
254
131
  end
255
-
256
- def to_opts
257
- collect do |receiver, key|
258
- # Note the receiver is used as a placeholder for desc,
259
- # to be resolved using TDoc.
260
- attributes = {
261
- :long => key,
262
- :short => nil,
263
- :opt_type => GetoptLong::REQUIRED_ARGUMENT,
264
- :desc => receiver
265
- }
266
-
267
- long = attributes[:long]
268
- attributes[:long] = "--#{long}" unless long =~ /^-{2}/
269
-
270
- short = attributes[:short].to_s
271
- attributes[:short] = "-#{short}" unless short.empty? || short =~ /^-/
132
+
133
+ # The path to the :doc template (see format_str)
134
+ DOC_TEMPLATE_PATH = File.expand_path File.dirname(__FILE__) + "/../generator/generators/config/templates/doc.erb"
135
+
136
+ # The path to the :nodoc template (see format_str)
137
+ NODOC_TEMPLATE_PATH = File.expand_path File.dirname(__FILE__) + "/../generator/generators/config/templates/nodoc.erb"
272
138
 
273
- [attributes[:long], attributes[:short], attributes[:opt_type], attributes[:desc]]
274
- end
139
+ # Formats the configurations using the specified template. Two default
140
+ # templates are defined, <tt>:doc</tt> and <tt>:nodoc</tt>. These map
141
+ # to the contents of DOC_TEMPLATE_PATH and NODOC_TEMPLATE_PATH and
142
+ # correspond to the documented and undocumented config generator templates.
143
+ #
144
+ # == Custom Templates
145
+ #
146
+ # format_str initializes a Templater which formats each [receiver, configurations]
147
+ # pair in turn, and puts the output to the target using <tt><<</tt>. The
148
+ # templater is assigned the following attributes for use in formatting:
149
+ #
150
+ # receiver:: The receiver
151
+ # class_doc:: The TDoc for the receiver, from Tap::Support::TDoc[receiver]
152
+ # configurations:: An array of configurations and associated comments
153
+ #
154
+ # In the template these can be accessed as any ERB locals, for example:
155
+ #
156
+ # <%= receiver.to_s %>
157
+ # <% configurations.each do |key, config, comment| %>
158
+ # ...
159
+ # <% end %>
160
+ #
161
+ # The input template may be a String or an ERB; either may be used to
162
+ # initialize the templater.
163
+ def format_str(template=:doc, target="")
164
+ Lazydoc.resolve(code_comments)
165
+
166
+ template = case template
167
+ when :doc then File.read(DOC_TEMPLATE_PATH)
168
+ when :nodoc then File.read(NODOC_TEMPLATE_PATH)
169
+ else template
170
+ end
171
+
172
+ templater = Templater.new(template)
173
+ assignments.each_pair do |receiver, keys|
174
+ next if keys.empty?
175
+
176
+ # set the template attributes
177
+ templater.receiver = receiver
178
+ templater.configurations = keys.collect do |key|
179
+ # duplicate config so that any changes to it
180
+ # during templation will not propogate back
181
+ # into self
182
+ [key, map[key].dup]
183
+ end.compact
184
+
185
+ yield(templater) if block_given?
186
+ target << templater.build
187
+ end
188
+
189
+ target
275
190
  end
276
-
277
191
  end
278
192
  end
279
- end
193
+ end
194
+
@@ -1,103 +1,98 @@
1
- module Tap
2
- module Support
3
-
4
- # Under Construction
5
- module CommandLine
6
- module_function
7
-
8
- # Parses the input string as YAML, if the string matches the YAML document
9
- # specifier (ie it begins with "---\s*\n"). Otherwise returns the string.
10
- #
11
- # str = {'key' => 'value'}.to_yaml # => "--- \nkey: value\n"
12
- # Tap::Script.parse_yaml(str) # => {'key' => 'value'}
13
- # Tap::Script.parse_yaml("str") # => "str"
14
- def parse_yaml(str)
15
- str =~ /\A---\s*\n/ ? YAML.load(str) : str
16
- end
17
-
18
- def split_argv(argv)
19
- current = []
20
- current_split = []
21
- splits = [current_split]
22
-
23
- argv.each do |arg|
24
- if arg =~ /\A-{2}(\+*)\z/
25
- current_split << current unless current.empty?
26
- current = []
27
- current_split = (splits[$1.length] ||= [])
28
- else
29
- current << arg
30
- end
31
- end
32
-
33
- current_split << current unless current.empty?
34
- splits.delete_if {|split| split.nil? || split.empty? }
35
- splits
36
- end
37
-
38
- def next_arg(argv)
39
- index = nil
40
- argv.each_with_index do |arg, i|
41
- if arg !~ /\A-/
42
- index = i
43
- break
44
- end
45
- end
46
- index == nil ? nil : argv.delete_at(index)
47
- end
48
-
49
- # Handles options using GetoptLong, and passes each option and
50
- # value in ARGV to the block.
51
- #
52
- #--
53
- # expect [long, <short>, type, desc]
54
- #++
55
- def handle_options(*options)
56
- options = options.collect do |opt|
57
- opt = opt[0..-2]
58
- opt.compact
59
- end
60
-
61
- opts = GetoptLong.new(*options)
62
- opts.quiet = true
63
- opts.each do |opt, value|
64
- yield(opt, value)
65
- end
66
- end
67
-
68
- def command_help(program_file, opts)
69
- lines = []
70
- lines << usage(program_file, "Usage", "Description", :keep_headers => false)
71
- unless opts.empty?
72
- lines.concat ["Options:", usage_options(opts)]
73
- end
74
- lines.join("\n")
75
- end
76
-
77
- def usage(program_file, *sections)
78
- options = sections.last.kind_of?(Hash) ? sections.pop : {}
79
- options = {:keep_headers => true}.merge(options)
80
- comment = Tap::Support::TDoc.usage(program_file, sections, options[:keep_headers])
81
- comment.rstrip + "\n"
82
- end
83
-
84
- def usage_options(opts)
85
- opt_lines = []
86
- opts.each do |long, short, mode, desc|
87
-
88
- if desc.kind_of?(Class) && desc.include?(Tap::Support::Configurable)
89
- key = desc.configurations.opt_map(long)
90
- default = PP.singleline_pp(desc.configurations.default[key], "")
91
- config_attr = desc.tdoc.find_configuration_named(key.to_s)
92
- desc = config_attr.desc
93
- end
94
-
95
- short = short == nil ? " " : "(#{short})"
96
- opt_lines << " %-25s %s %s" % [long, short, desc]
97
- end
98
- opt_lines.join("\n")
99
- end
100
-
101
- end
102
- end
1
+ require 'optparse'
2
+
3
+ module Tap
4
+ module Support
5
+
6
+ # Under Construction
7
+ module CommandLine
8
+ module_function
9
+
10
+ # Parses the input string as YAML, if the string matches the YAML document
11
+ # specifier (ie it begins with "---\s*\n"). Otherwise returns the string.
12
+ #
13
+ # str = {'key' => 'value'}.to_yaml # => "--- \nkey: value\n"
14
+ # Tap::Script.parse_yaml(str) # => {'key' => 'value'}
15
+ # Tap::Script.parse_yaml("str") # => "str"
16
+ def parse_yaml(str)
17
+ str =~ /\A---\s*\n/ ? YAML.load(str) : str
18
+ end
19
+
20
+ SPLIT_ARGV_REGEXP = /\A-{2}(\+*)\z/
21
+
22
+ def split(argv)
23
+ current = []
24
+ current_split = []
25
+ splits = [current_split]
26
+
27
+ argv.each do |arg|
28
+ if arg =~ SPLIT_ARGV_REGEXP
29
+ current_split << current unless current.empty?
30
+ current = []
31
+ current_split = (splits[$1.length] ||= [])
32
+ else
33
+ current << arg
34
+ end
35
+ end
36
+
37
+ current_split << current unless current.empty?
38
+ splits.delete_if {|split| split.nil? || split.empty? }
39
+ splits
40
+ end
41
+
42
+ def shift(argv)
43
+ index = nil
44
+ argv.each_with_index do |arg, i|
45
+ if arg !~ /\A-/
46
+ index = i
47
+ break
48
+ end
49
+ end
50
+ index == nil ? nil : argv.delete_at(index)
51
+ end
52
+
53
+ def usage(path, cols=80)
54
+ parse_usage(File.read(path), cols)
55
+ end
56
+
57
+ def parse_usage(str, cols=80)
58
+ scanner = StringScanner.new(str)
59
+ scanner.scan(/^#!.*?$/)
60
+ Comment.parse(scanner, false).wrap(cols, 2).strip
61
+ end
62
+
63
+ def configv(config)
64
+ desc = config.desc
65
+ desc.extend(OptParseComment) if desc.kind_of?(Comment)
66
+
67
+ [config.short, argtype(config), desc].compact
68
+ end
69
+
70
+ def argtype(config)
71
+ case config.arg_type
72
+ when :optional
73
+ "#{config.long} [#{config.arg_name}]"
74
+ when :switch
75
+ config.long(true)
76
+ when :flag
77
+ config.long
78
+ when :list
79
+ "#{config.long} a,b,c"
80
+ when :mandatory, nil
81
+ "#{config.long} #{config.arg_name}"
82
+ else
83
+ raise "unknown arg_type: #{config.arg_type}"
84
+ end
85
+ end
86
+
87
+ module OptParseComment
88
+ def empty?
89
+ to_str.empty?
90
+ end
91
+
92
+ def to_str
93
+ subject.to_s =~ /#(.*)$/ ? $1.strip : ""
94
+ end
95
+ end
96
+ end
97
+ end
103
98
  end