tap 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. data/History +12 -0
  2. data/MIT-LICENSE +0 -2
  3. data/README +23 -32
  4. data/bin/rap +116 -0
  5. data/bin/tap +6 -9
  6. data/cgi/run.rb +67 -0
  7. data/cmd/console.rb +1 -1
  8. data/cmd/destroy.rb +4 -4
  9. data/cmd/generate.rb +4 -4
  10. data/cmd/manifest.rb +61 -53
  11. data/cmd/run.rb +8 -75
  12. data/doc/Class Reference +130 -121
  13. data/doc/Command Reference +76 -124
  14. data/doc/Syntax Reference +290 -0
  15. data/doc/Tutorial +305 -237
  16. data/lib/tap/app.rb +140 -467
  17. data/lib/tap/constants.rb +2 -2
  18. data/lib/tap/declarations.rb +211 -0
  19. data/lib/tap/env.rb +171 -193
  20. data/lib/tap/exe.rb +100 -21
  21. data/lib/tap/file_task.rb +3 -3
  22. data/lib/tap/generator/base.rb +1 -1
  23. data/lib/tap/generator/destroy.rb +10 -10
  24. data/lib/tap/generator/generate.rb +29 -18
  25. data/lib/tap/generator/generators/command/command_generator.rb +2 -2
  26. data/lib/tap/generator/generators/command/templates/command.erb +2 -2
  27. data/lib/tap/generator/generators/config/config_generator.rb +3 -3
  28. data/lib/tap/generator/generators/config/templates/doc.erb +1 -1
  29. data/lib/tap/generator/generators/file_task/file_task_generator.rb +1 -1
  30. data/lib/tap/generator/generators/file_task/templates/task.erb +1 -1
  31. data/lib/tap/generator/generators/file_task/templates/test.erb +1 -1
  32. data/lib/tap/generator/generators/generator/generator_generator.rb +27 -0
  33. data/lib/tap/generator/generators/generator/templates/task.erb +27 -0
  34. data/lib/tap/generator/generators/root/root_generator.rb +13 -13
  35. data/lib/tap/generator/generators/root/templates/README +0 -0
  36. data/lib/tap/generator/generators/root/templates/Rakefile +2 -2
  37. data/lib/tap/generator/generators/root/templates/gemspec +4 -5
  38. data/lib/tap/generator/generators/root/templates/tapfile +11 -8
  39. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
  40. data/lib/tap/generator/generators/task/task_generator.rb +1 -3
  41. data/lib/tap/generator/generators/task/templates/test.erb +1 -3
  42. data/lib/tap/patches/optparse/summarize.rb +62 -0
  43. data/lib/tap/root.rb +41 -29
  44. data/lib/tap/support/aggregator.rb +16 -3
  45. data/lib/tap/support/assignments.rb +10 -9
  46. data/lib/tap/support/audit.rb +58 -64
  47. data/lib/tap/support/class_configuration.rb +33 -44
  48. data/lib/tap/support/combinator.rb +125 -0
  49. data/lib/tap/support/configurable.rb +13 -14
  50. data/lib/tap/support/configurable_class.rb +21 -43
  51. data/lib/tap/support/configuration.rb +55 -9
  52. data/lib/tap/support/constant.rb +87 -13
  53. data/lib/tap/support/constant_manifest.rb +116 -0
  54. data/lib/tap/support/dependencies.rb +54 -0
  55. data/lib/tap/support/dependency.rb +44 -0
  56. data/lib/tap/support/executable.rb +247 -32
  57. data/lib/tap/support/executable_queue.rb +1 -1
  58. data/lib/tap/support/gems/rake.rb +29 -8
  59. data/lib/tap/support/gems.rb +10 -30
  60. data/lib/tap/support/instance_configuration.rb +29 -3
  61. data/lib/tap/support/intern.rb +46 -0
  62. data/lib/tap/support/join.rb +143 -0
  63. data/lib/tap/support/joins/fork.rb +19 -0
  64. data/lib/tap/support/joins/merge.rb +22 -0
  65. data/lib/tap/support/joins/sequence.rb +21 -0
  66. data/lib/tap/support/joins/switch.rb +25 -0
  67. data/lib/tap/support/joins/sync_merge.rb +63 -0
  68. data/lib/tap/support/joins.rb +15 -0
  69. data/lib/tap/support/lazy_attributes.rb +17 -2
  70. data/lib/tap/support/lazydoc/comment.rb +503 -0
  71. data/lib/tap/support/lazydoc/config.rb +17 -0
  72. data/lib/tap/support/lazydoc/definition.rb +36 -0
  73. data/lib/tap/support/lazydoc/document.rb +152 -0
  74. data/lib/tap/support/lazydoc/method.rb +24 -0
  75. data/lib/tap/support/lazydoc.rb +269 -343
  76. data/lib/tap/support/manifest.rb +121 -103
  77. data/lib/tap/support/minimap.rb +90 -0
  78. data/lib/tap/support/node.rb +56 -0
  79. data/lib/tap/support/parser.rb +436 -0
  80. data/lib/tap/support/schema.rb +359 -0
  81. data/lib/tap/support/shell_utils.rb +3 -5
  82. data/lib/tap/support/string_ext.rb +60 -0
  83. data/lib/tap/support/tdoc.rb +7 -2
  84. data/lib/tap/support/templater.rb +30 -16
  85. data/lib/tap/support/validation.rb +77 -8
  86. data/lib/tap/task.rb +431 -143
  87. data/lib/tap/tasks/dump.rb +15 -10
  88. data/lib/tap/tasks/load.rb +112 -0
  89. data/lib/tap/tasks/rake.rb +4 -41
  90. data/lib/tap/test/assertions.rb +38 -0
  91. data/lib/tap/test/env_vars.rb +1 -1
  92. data/lib/tap/test/extensions.rb +79 -0
  93. data/lib/tap/test/file_test.rb +420 -0
  94. data/lib/tap/test/file_test_class.rb +12 -0
  95. data/lib/tap/test/regexp_escape.rb +87 -0
  96. data/lib/tap/test/script_test.rb +46 -0
  97. data/lib/tap/test/script_tester.rb +115 -0
  98. data/lib/tap/test/subset_test.rb +260 -0
  99. data/lib/tap/test/subset_test_class.rb +99 -0
  100. data/lib/tap/test/{tap_methods.rb → tap_test.rb} +45 -43
  101. data/lib/tap/test/utils.rb +231 -0
  102. data/lib/tap/test.rb +53 -26
  103. data/lib/tap.rb +3 -20
  104. metadata +50 -27
  105. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +0 -15
  106. data/lib/tap/patches/rake/rake_test_loader.rb +0 -8
  107. data/lib/tap/patches/rake/testtask.rb +0 -57
  108. data/lib/tap/patches/ruby19/backtrace_filter.rb +0 -51
  109. data/lib/tap/patches/ruby19/parsedate.rb +0 -16
  110. data/lib/tap/support/batchable.rb +0 -47
  111. data/lib/tap/support/batchable_class.rb +0 -107
  112. data/lib/tap/support/command_line.rb +0 -98
  113. data/lib/tap/support/comment.rb +0 -270
  114. data/lib/tap/support/constant_utils.rb +0 -127
  115. data/lib/tap/support/declarations.rb +0 -111
  116. data/lib/tap/support/framework.rb +0 -83
  117. data/lib/tap/support/framework_class.rb +0 -180
  118. data/lib/tap/support/run_error.rb +0 -39
  119. data/lib/tap/support/summary.rb +0 -30
  120. data/lib/tap/test/file_methods.rb +0 -377
  121. data/lib/tap/test/script_methods/script_test.rb +0 -98
  122. data/lib/tap/test/script_methods.rb +0 -107
  123. data/lib/tap/test/subset_methods.rb +0 -420
  124. data/lib/tap/workflow.rb +0 -200
@@ -5,14 +5,12 @@ module Tap
5
5
  # which values are assigned to a particular key. A value may only
6
6
  # be assigned to one key at a time.
7
7
  #
8
- # Assignments tracks the order in which keys are declared, and the
8
+ # Assignments tracks the order of key declaration, and the
9
9
  # order in which values are assigned to a key. This behavior is
10
- # used by ClassConfiguration to track the order in which configurations
11
- # are assigned to a class; the order, in turn, is used in the formation
10
+ # used by ClassConfiguration to track the order of configurations
11
+ # in a class; the order, in turn, is used in the formation
12
12
  # of config files, command line documentation, etc.
13
13
  #
14
- # === Example
15
- #
16
14
  # a = Assignments.new
17
15
  # a.assign(:one, 'one')
18
16
  # a.assign(:two, 'two')
@@ -35,6 +33,9 @@ module Tap
35
33
  class Assignments
36
34
  include Enumerable
37
35
 
36
+ # Generates a new Assignments using the parent array of
37
+ # [key, values] pairs. Uses parent.array if parent is
38
+ # an Assignments, or [] if parent is nil.
38
39
  def initialize(parent=nil)
39
40
  existing_array = case parent
40
41
  when Assignments then parent.array
@@ -139,7 +140,7 @@ module Tap
139
140
  nil
140
141
  end
141
142
 
142
- # Yields each key, value pair in the order in which
143
+ # Yields each [key, value] pair in the order in which
143
144
  # the keys were declared. Keys with no values are
144
145
  # skipped.
145
146
  def each
@@ -148,7 +149,7 @@ module Tap
148
149
  end
149
150
  end
150
151
 
151
- # Yields each key, values pair in the order in which
152
+ # Yields each [key, values] pair in the order in which
152
153
  # the keys were declared.
153
154
  def each_pair
154
155
  array.each do |key, values|
@@ -156,7 +157,7 @@ module Tap
156
157
  end
157
158
  end
158
159
 
159
- # Returns the [key, values] as an array
160
+ # Returns self as an array
160
161
  def to_a
161
162
  array.collect {|key, values| [key, values.dup] }
162
163
  end
@@ -164,7 +165,7 @@ module Tap
164
165
  protected
165
166
 
166
167
  # An array of [key, values] arrays tracking the
167
- # key and order in which values were assigned.
168
+ # order in which values are assigned.
168
169
  attr_reader :array
169
170
 
170
171
  end
@@ -1,47 +1,43 @@
1
- autoload(:PP, 'pp')
2
-
3
1
  module Tap
4
2
  module Support
5
3
 
6
4
  # Marks the merge of multiple Audit trails
7
5
  class AuditMerge < Array
6
+
7
+ # True if another is an AuditMerge and passes Array#==
8
8
  def ==(another)
9
9
  another.kind_of?(AuditMerge) && super
10
10
  end
11
11
  end
12
12
 
13
- # Marks a split in an Audit trail
14
- class AuditSplit
15
- attr_reader :block
16
- def initialize(block) @block = block end
17
-
18
- def ==(another)
19
- another.kind_of?(AuditSplit) && another.block == block
20
- end
21
- end
22
-
23
13
  # Marks the expansion of an Audit trail
24
- class AuditExpand
14
+ class AuditIterate
25
15
  attr_reader :index
26
16
  def initialize(index) @index = index end
27
-
17
+
18
+ # True if another is an AuditIterate with the same index.
28
19
  def ==(another)
29
- another.kind_of?(AuditExpand) && another.index == index
20
+ another.kind_of?(AuditIterate) && another.index == index
21
+ end
22
+
23
+ # Returns a string like '_iterate(<index>)'.
24
+ def to_s
25
+ "_iterate(#{index})"
30
26
  end
31
27
  end
32
28
 
33
- # Audit provides a way to track the values (inputs and results) passed
34
- # among tasks or, more generally, any Executable method. Audits allow
35
- # you to track inputs as they make their way through a workflow, and
36
- # have great utility in debugging and record keeping.
29
+ # Audit provides a way to track the values (inputs and results) passed among
30
+ # tasks or, more generally, any Executable. Audits allow you to track inputs
31
+ # as they make their way through a workflow, and have great utility in
32
+ # debugging and record keeping.
37
33
  #
38
- # During execution, the group of inputs for a task are used to initialize
39
- # an Audit. These inputs mark the begining of an audit trail; every
40
- # task that processes them (including the first) adds to the trail by
41
- # recording it's result using itself as the 'source' of the result.
34
+ # During execution, the inputs to a task are used to initialize an Audit.
35
+ # These inputs are the original value of the audit and mark the begining
36
+ # of an audit trail; every task adds to the trail by recording it's result
37
+ # and itself as the 'source' of the result.
42
38
  #
43
- # Since Audits are meant to be fairly general structures, they can take
44
- # any object as a source, so for illustration lets use some symbols:
39
+ # Audits can take any object as a source, so for illustration lets use some
40
+ # symbols:
45
41
  #
46
42
  # # initialize a new audit
47
43
  # a = Audit.new(1, nil)
@@ -50,8 +46,8 @@ module Tap
50
46
  # a._record(:A, 2)
51
47
  # a._record(:B, 3)
52
48
  #
53
- # Now you can pull up the source and value trails, as well as
54
- # information like the current and original values:
49
+ # Now you can pull up the source and value trails, as well as the current
50
+ # and original values:
55
51
  #
56
52
  # a._source_trail # => [nil, :A, :B]
57
53
  # a._value_trail # => [1, 2, 3]
@@ -62,9 +58,9 @@ module Tap
62
58
  # a._current # => 3
63
59
  # a._current_source # => :B
64
60
  #
65
- # Merges are supported by using an array of the merging trails (internally
66
- # an AuditMerge) as the source, and an array of the merging values as the
67
- # initial value.
61
+ # Merges are supported by using an array of the merged trails (actually
62
+ # an AuditMerge) as the source, and an array of the merged values as the
63
+ # original value.
68
64
  #
69
65
  # b = Audit.new(10, nil)
70
66
  # b._record(:C, 11)
@@ -83,9 +79,9 @@ module Tap
83
79
  # c._value_trail # => [ [[1,2,3], [10, 11, 12]], "a string value", {'a' => 'hash value'}, ['an', 'array', 'value']]
84
80
  #
85
81
  # Audit supports forks by duplicating the source and value trails. Forks
86
- # can be developed independently. Importantly, Audits are forked during
87
- # a merge; notice the additional record in +a+ doesn't change the source
88
- # trail for +c+:
82
+ # can be developed independently. Audits are also forked during a merge;
83
+ # notice the additional record in 'a' doesn't change the source trail for
84
+ # 'c':
89
85
  #
90
86
  # a1 = a._fork
91
87
  #
@@ -101,23 +97,23 @@ module Tap
101
97
  # to help gain access, as well as a printing method to visualize the
102
98
  # audit trail:
103
99
  #
104
- # [c._to_s]
105
- # o-[] 1
106
- # o-[A] 2
107
- # o-[B] 3
108
- # |
109
- # | o-[] 10
110
- # | o-[C] 11
111
- # | o-[D] 12
112
- # | |
113
- # `-`-o-[E] "a string value"
114
- # o-[F] {"a"=>"hash value"}
115
- # o-[G] ["an", "array", "value"]
100
+ # c._to_s
101
+ # # =>
102
+ # # o-[] 1
103
+ # # o-[A] 2
104
+ # # o-[B] 3
105
+ # # |
106
+ # # | o-[] 10
107
+ # # | o-[C] 11
108
+ # # | o-[D] 12
109
+ # # | |
110
+ # # `-`-o-[E] "a string value"
111
+ # # o-[F] {"a"=>"hash value"}
112
+ # # o-[G] ["an", "array", "value"]
116
113
  #
117
114
  # In practice, tasks are recored as sources. Thus source trails can be used
118
115
  # to access task configurations and other information that may be useful
119
- # when creating reports or making workflow decisions (ex: raise an
120
- # error after looping to a given task too many times).
116
+ # when creating reports or making workflow decisions.
121
117
  #
122
118
  #--
123
119
  # TODO:
@@ -143,9 +139,9 @@ module Tap
143
139
  class << self
144
140
 
145
141
  # Creates a new Audit by merging the input audits. The value of the new
146
- # Audit will be an array of the _current values of the audits. The source
147
- # will be an AuditMerge whose values are forks of the audits. Non-Audit
148
- # sources can be provided; they are initialized to Audits before merging.
142
+ # Audit will be an array of the _current values of the inputs. The source
143
+ # will be an AuditMerge whose values are forks of the inputs. Non-Audit
144
+ # sources may be provided; they are initialized to Audits before merging.
149
145
  #
150
146
  # a = Audit.new
151
147
  # a._record(:a, 'a')
@@ -181,9 +177,9 @@ module Tap
181
177
  # An array of the values in self
182
178
  attr_reader :_values
183
179
 
184
- # An arbitrary constant used to identify when no inputs have been
185
- # provided to Audit.new. (nil itself cannot be used as nil is a
186
- # valid initial value for an audit trail)
180
+ # An arbitrary object used to identify when no inputs have been
181
+ # provided to Audit.new. (nil cannot be used since nil is a valid
182
+ # initial value)
187
183
  AUDIT_NIL = Object.new
188
184
 
189
185
  # A new audit takes a value and/or source. A nil source is typically given
@@ -270,7 +266,7 @@ module Tap
270
266
  end
271
267
 
272
268
  # Produces a new Audit with duplicate sources and values, suitable for
273
- # separate development along a separate path.
269
+ # independent development.
274
270
  def _fork
275
271
  a = Audit.new
276
272
  a._sources = _sources.dup
@@ -278,18 +274,16 @@ module Tap
278
274
  a
279
275
  end
280
276
 
281
- # _forks self and records the next value as [<return from block>, AuditSplit.new(block)]
282
- def _split(&block) # :yields: _current
283
- _fork._record(AuditSplit.new(block), yield(_current))
284
- end
285
-
286
- # _forks self for each member in _current. Records the next value as
287
- # [item, AuditExpand.new(<index of item>)]. Raises an error if _current
288
- # does not respond to each.
289
- def _expand
277
+ # Produces a fork of self for each item in the current value (_current).
278
+ # Iterate is useful for developing each item of (say) an array along
279
+ # different paths.
280
+ #
281
+ # Records the next value of each fork as [item, AuditIterate.new(<index of item>)].
282
+ # Raises an error if _current does not respond to each.
283
+ def _iterate
290
284
  expanded = []
291
285
  _current.each do |value|
292
- expanded << _fork._record(AuditExpand.new(expanded.length), value)
286
+ expanded << _fork._record(AuditIterate.new(expanded.length), value)
293
287
  end
294
288
  expanded
295
289
  end
@@ -5,20 +5,29 @@ require 'tap/support/configuration'
5
5
  module Tap
6
6
  module Support
7
7
 
8
- # ClassConfiguration tracks and handles the class configurations defined in a
9
- # Configurable class.
8
+ # ClassConfiguration tracks configurations defined by a Configurable class.
10
9
  class ClassConfiguration
11
10
  include Enumerable
12
11
 
13
- # The class receiving new configurations
12
+ config_templates_dir = File.expand_path File.dirname(__FILE__) + "/../generator/generators/config/templates"
13
+
14
+ # The path to the :doc template (see inspect)
15
+ DOC_TEMPLATE_PATH = File.join(config_templates_dir, 'doc.erb')
16
+
17
+ # The path to the :nodoc template (see inspect)
18
+ NODOC_TEMPLATE_PATH = File.join(config_templates_dir, 'nodoc.erb')
19
+
20
+ # The Configurable class receiving new configurations
14
21
  attr_reader :receiver
15
22
 
16
- # Tracks the assignment of the config keys to receivers
23
+ # An Assignments tracking the assignment of config keys to receivers
17
24
  attr_reader :assignments
18
25
 
19
- # A map of (key, config) pairs.
26
+ # A map of [key, Configuration] pairs
20
27
  attr_reader :map
21
-
28
+
29
+ # Generates a new ClassConfiguration for the receiver. If a parent is
30
+ # provided, configurations will be inherited from it.
22
31
  def initialize(receiver, parent=nil)
23
32
  @receiver = receiver
24
33
 
@@ -34,9 +43,9 @@ module Tap
34
43
  end
35
44
  end
36
45
 
37
- # Initializes a Configuration using the inputs and sets it in self
38
- # using name as a key, overriding the current config by that name,
39
- # if it exists. Returns the new config.
46
+ # Initializes a Configuration using the inputs and sets using name
47
+ # as a key. Any existing config by the same name is overridden.
48
+ # Returns the new config.
40
49
  def add(name, default=nil, attributes={})
41
50
  self[name] = Configuration.new(name.to_sym, default, attributes)
42
51
  end
@@ -46,12 +55,12 @@ module Tap
46
55
  self[key] = nil
47
56
  end
48
57
 
49
- # Gets the configuration specified by key. The key is symbolized.
58
+ # Gets the config specified by key. The key is symbolized.
50
59
  def [](key)
51
60
  map[key.to_sym]
52
61
  end
53
62
 
54
- # Assigns the configuration to key. A nil config unassigns the
63
+ # Assigns the config to key. A nil config unassigns the
55
64
  # configuration key. The key is symbolized.
56
65
  def []=(key, config)
57
66
  key = key.to_sym
@@ -91,7 +100,7 @@ module Tap
91
100
  end
92
101
 
93
102
  # Calls block once for each [receiver, key, config] in self,
94
- # passing those elements as parameters, in the order in
103
+ # passing those elements as parameters in the order in
95
104
  # which they were assigned.
96
105
  def each
97
106
  assignments.each do |receiver, key|
@@ -100,7 +109,7 @@ module Tap
100
109
  end
101
110
 
102
111
  # Calls block once for each [key, config] pair in self,
103
- # passing those elements as parameters, in the order in
112
+ # passing those elements as parameters in the order in
104
113
  # which they were assigned.
105
114
  def each_pair
106
115
  assignments.each do |receiver, key|
@@ -114,7 +123,7 @@ module Tap
114
123
  InstanceConfiguration.new(self, receiver, store)
115
124
  end
116
125
 
117
- # Returns a hash of the (key, config.default) values in self.
126
+ # Returns a hash of the [key, config.default] pairs in self.
118
127
  def to_hash
119
128
  hash = {}
120
129
  each_pair {|key, config| hash[key] = config.default }
@@ -125,41 +134,21 @@ module Tap
125
134
  def code_comments
126
135
  code_comments = []
127
136
  values.each do |config|
128
- code_comments << config.desc if config.desc.kind_of?(Comment)
137
+ code_comments << config.desc if config.desc.kind_of?(Lazydoc::Comment)
129
138
  end
130
139
  code_comments
131
140
  end
132
141
 
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"
138
-
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
- # configurations:: An array of configurations and associated comments
152
- #
153
- # In the template these can be accessed as any ERB locals, for example:
154
- #
155
- # <%= receiver.to_s %>
156
- # <% configurations.each do |key, config, comment| %>
157
- # ...
158
- # <% end %>
142
+ # Inspects the configurations using the specified template. Templates
143
+ # are used for format each [receiver, configurations] pair in self.
144
+ # See DEFAULT_TEMPLATE as a model. The results of each template cycle
145
+ # are pushed to target.
159
146
  #
160
- # The input template may be a String or an ERB; either may be used to
161
- # initialize the templater.
162
- def format_str(template=:doc, target="")
147
+ # Two default templates are defined, <tt>:doc</tt> and <tt>:nodoc</tt>.
148
+ # These map to the contents of DOC_TEMPLATE_PATH and NODOC_TEMPLATE_PATH
149
+ # and correspond to the documented and undocumented
150
+ # Tap::Generator::Generators::ConfigGenerator templates.
151
+ def inspect(template=:doc, target="")
163
152
  Lazydoc.resolve_comments(code_comments)
164
153
 
165
154
  template = case template
@@ -0,0 +1,125 @@
1
+ require 'enumerator'
2
+
3
+ module Tap
4
+ module Support
5
+
6
+ # Combinator provides a method for iterating over all combinations
7
+ # of items in the input sets.
8
+ #
9
+ # c = Combinator.new [1,2], [3,4]
10
+ # c.to_a # => [[1,3], [1,4], [2,3], [2,4]]
11
+ #
12
+ # Combinators can take any object that responds to each as an
13
+ # input set; normally arrays are used.
14
+ #
15
+ # === Implementation
16
+ #
17
+ # Combinator iteratively combines each element from the first set (a)
18
+ # with each element from the second set (b). When more than two sets
19
+ # are given, the second and remaining sets are bundled into a
20
+ # Combinator, which then acts as the second set.
21
+ #
22
+ # c = Combinator.new [1,2], [3,4], [5,6]
23
+ # c.a # => [[1],[2]]
24
+ # c.b.class # => Combinator
25
+ # c.b.a # => [[3],[4]]
26
+ # c.b.b # => [[5],[6]]
27
+ #
28
+ # Note that internally each item in a set is stored as a single-item
29
+ # array; the arrays are added during combination. Thus when
30
+ # iterating, the combinations are calculated like:
31
+ #
32
+ # ([1] + [3]) + [5] # => [1,3,5]
33
+ #
34
+ # This is probably not the fastest implementation, but it works.
35
+ class Combinator
36
+ include Enumerable
37
+
38
+ # The first set
39
+ attr_reader :a
40
+
41
+ # The second set
42
+ attr_reader :b
43
+
44
+ # Creates a new Combinator. Each input must respond
45
+ # to each, or be nil.
46
+ def initialize(*sets)
47
+ @a = make_set(sets.shift)
48
+ @b = make_set(*sets)
49
+ end
50
+
51
+ # Returns the sets used to initialize the Combinator.
52
+ def sets
53
+ sets_in(a) + sets_in(b)
54
+ end
55
+
56
+ # True if length is zero.
57
+ def empty?
58
+ a.empty? && b.empty?
59
+ end
60
+
61
+ # Returns the number of combinations returned by each.
62
+ def length
63
+ case
64
+ when !(@a.empty? || @b.empty?)
65
+ @a.length * @b.length
66
+ when @a.empty?
67
+ @b.length
68
+ when @b.empty?
69
+ @a.length
70
+ end
71
+ end
72
+
73
+ # Passes each combination as an array to the input block.
74
+ def each # :yields: combination
75
+ case
76
+ when !(@a.empty? || @b.empty?)
77
+ @a.each do |a|
78
+ @b.each do |b|
79
+ yield(a + b)
80
+ end
81
+ end
82
+ when @a.empty?
83
+ @b.each {|b| yield(b) }
84
+ when @b.empty?
85
+ @a.each {|a| yield(a) }
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ # makes a Combinator out of multiple sets or collects the
92
+ # objects of a single set as arrays:
93
+ #
94
+ # make_set([1,2,3], [4,5,6]) # => Combinator.new([1,2,3], [4,5,6])
95
+ # make_set([1,2,3]) # => [[1],[2],[3]]
96
+ #
97
+ def make_set(*sets) # :nodoc:
98
+ # recieves an array of arrays or combinators
99
+ return Combinator.new(*sets) if sets.length > 1
100
+ return sets if sets.empty?
101
+
102
+ set = sets[0]
103
+ return [] if set == nil
104
+
105
+ unless set.respond_to?(:each)
106
+ raise ArgumentError, "does not respond to each: #{set}"
107
+ end
108
+
109
+ # recursively arrayifies each element
110
+ arrayified_set = []
111
+ set.each {|s| arrayified_set << [s]}
112
+ arrayified_set
113
+ end
114
+
115
+ # basically the reverse of make_set
116
+ def sets_in(set) # :nodoc:
117
+ case set
118
+ when Combinator then set.sets
119
+ when Array then set.empty? ? [] : [set.collect {|s| s[0]}]
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+ end
@@ -21,7 +21,7 @@ module Tap
21
21
  # c.config.class # => InstanceConfiguration
22
22
  # c.config # => {:one => 'one', :two => 'two', :three => 'three'}
23
23
  #
24
- # The <tt>config</tt> object acts as a kind of forwarding hash; declared configurations
24
+ # The <tt>config</tt> object acts as a forwarding hash; declared configurations
25
25
  # map to accessors while undeclared configurations are stored internally:
26
26
  #
27
27
  # c.config[:one] = 'ONE'
@@ -33,8 +33,8 @@ module Tap
33
33
  # c.config[:undeclared] = 'value'
34
34
  # c.config.store # => {:undeclared => 'value'}
35
35
  #
36
- # The writer method for a configuration can be modified by providing a block to config.
37
- # The Validation module provides a number of common validation and string-transform
36
+ # The writer for a configuration can be defined by providing a block to config.
37
+ # The Validation module provides a number of common validation/transform
38
38
  # blocks which can be accessed through the class method 'c':
39
39
  #
40
40
  # class SubClass < ConfigClass
@@ -55,21 +55,21 @@ module Tap
55
55
  # s.two = nil # !> ValidationError
56
56
  # s.two = 'str' # !> ValidationError
57
57
  #
58
- # As shown above, configurations are inherited from the parent and can be
58
+ # As shown above, configurations are inherited from the parent and may be
59
59
  # overridden in subclasses. See ConfigurableClass for more details.
60
60
  #
61
61
  module Configurable
62
62
 
63
63
  # Extends including classes with ConfigurableClass
64
- def self.included(mod)
64
+ def self.included(mod) # :nodoc:
65
65
  mod.extend Support::ConfigurableClass if mod.kind_of?(Class)
66
66
  end
67
67
 
68
- # The instance configurations for self
68
+ # An InstanceConfiguration with configurations for self
69
69
  attr_reader :config
70
70
 
71
- # Reconfigures self with the given configuration overrides. Only
72
- # the specified configs are modified. Override keys are symbolized.
71
+ # Reconfigures self with the given overrides. Only the specified configs
72
+ # are modified. Keys are symbolized.
73
73
  #
74
74
  # Returns self.
75
75
  def reconfigure(overrides={})
@@ -81,9 +81,9 @@ module Tap
81
81
  self
82
82
  end
83
83
 
84
- # Reinitializes config with a copy of orig.config (this assures
85
- # that duplicates have their own copy of configurations,
86
- # separate from the original object).
84
+ # Reinitializes configurations in the copy such that
85
+ # the new object has it's own set of configurations,
86
+ # separate from the original object.
87
87
  def initialize_copy(orig)
88
88
  super
89
89
  initialize_config(orig.config)
@@ -91,9 +91,8 @@ module Tap
91
91
 
92
92
  protected
93
93
 
94
- # Initializes config to an InstanceConfiguration specific for self.
95
- # Default config values are assigned or overridden if specified in
96
- # overrides. Override keys are symbolized.
94
+ # Initializes config to an InstanceConfiguration. Default config values
95
+ # are overridden as specified by overrides. Keys are symbolized.
97
96
  def initialize_config(overrides={})
98
97
  class_config = self.class.configurations
99
98
  @config = class_config.instance_config