caruby-core 1.5.5 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. data/Gemfile +9 -0
  2. data/History.md +5 -1
  3. data/lib/caruby.rb +3 -5
  4. data/lib/caruby/caruby-src.tar.gz +0 -0
  5. data/lib/caruby/database.rb +53 -69
  6. data/lib/caruby/database/application_service.rb +25 -0
  7. data/lib/caruby/database/cache.rb +60 -0
  8. data/lib/caruby/database/fetched_matcher.rb +52 -38
  9. data/lib/caruby/database/lazy_loader.rb +4 -4
  10. data/lib/caruby/database/operation.rb +34 -0
  11. data/lib/caruby/database/persistable.rb +171 -86
  12. data/lib/caruby/database/persistence_service.rb +32 -34
  13. data/lib/caruby/database/persistifier.rb +100 -43
  14. data/lib/caruby/database/reader.rb +107 -85
  15. data/lib/caruby/database/reader_template_builder.rb +60 -0
  16. data/lib/caruby/database/saved_matcher.rb +3 -3
  17. data/lib/caruby/database/sql_executor.rb +88 -17
  18. data/lib/caruby/database/writer.rb +213 -177
  19. data/lib/caruby/database/writer_template_builder.rb +334 -0
  20. data/lib/caruby/{util → helpers}/controlled_value.rb +0 -0
  21. data/lib/caruby/{util → helpers}/coordinate.rb +4 -4
  22. data/lib/caruby/{util → helpers}/person.rb +3 -3
  23. data/lib/caruby/{util → helpers}/properties.rb +7 -9
  24. data/lib/caruby/{util → helpers}/roman.rb +2 -2
  25. data/lib/caruby/{util → helpers}/version.rb +1 -1
  26. data/lib/caruby/json/deserializer.rb +2 -2
  27. data/lib/caruby/json/serializer.rb +49 -7
  28. data/lib/caruby/metadata.rb +30 -0
  29. data/lib/caruby/metadata/java_property.rb +21 -0
  30. data/lib/caruby/metadata/propertied.rb +191 -0
  31. data/lib/caruby/metadata/property.rb +22 -0
  32. data/lib/caruby/metadata/property_characteristics.rb +201 -0
  33. data/lib/caruby/migration/migratable.rb +11 -182
  34. data/lib/caruby/rdbi/driver/jdbc.rb +446 -0
  35. data/lib/caruby/resource.rb +20 -823
  36. data/lib/caruby/version.rb +1 -1
  37. data/test/lib/caruby/database/cache_test.rb +54 -0
  38. data/test/lib/caruby/{util → helpers}/controlled_value_test.rb +3 -5
  39. data/test/lib/caruby/{util → helpers}/person_test.rb +4 -6
  40. data/test/lib/caruby/helpers/properties_test.rb +34 -0
  41. data/test/lib/caruby/{util → helpers}/roman_test.rb +2 -3
  42. data/test/lib/caruby/{util → helpers}/version_test.rb +2 -3
  43. data/test/lib/helper.rb +7 -0
  44. metadata +161 -214
  45. data/lib/caruby/cli/application.rb +0 -36
  46. data/lib/caruby/cli/command.rb +0 -202
  47. data/lib/caruby/csv/csv_mapper.rb +0 -159
  48. data/lib/caruby/csv/csvio.rb +0 -203
  49. data/lib/caruby/database/search_template_builder.rb +0 -56
  50. data/lib/caruby/database/store_template_builder.rb +0 -278
  51. data/lib/caruby/domain.rb +0 -193
  52. data/lib/caruby/domain/attribute.rb +0 -584
  53. data/lib/caruby/domain/attributes.rb +0 -628
  54. data/lib/caruby/domain/dependency.rb +0 -225
  55. data/lib/caruby/domain/id_alias.rb +0 -22
  56. data/lib/caruby/domain/importer.rb +0 -183
  57. data/lib/caruby/domain/introspection.rb +0 -176
  58. data/lib/caruby/domain/inverse.rb +0 -172
  59. data/lib/caruby/domain/inversible.rb +0 -90
  60. data/lib/caruby/domain/java_attribute.rb +0 -173
  61. data/lib/caruby/domain/merge.rb +0 -185
  62. data/lib/caruby/domain/metadata.rb +0 -142
  63. data/lib/caruby/domain/mixin.rb +0 -35
  64. data/lib/caruby/domain/properties.rb +0 -95
  65. data/lib/caruby/domain/reference_visitor.rb +0 -428
  66. data/lib/caruby/domain/uniquify.rb +0 -50
  67. data/lib/caruby/import/java.rb +0 -387
  68. data/lib/caruby/migration/migrator.rb +0 -918
  69. data/lib/caruby/migration/resource_module.rb +0 -9
  70. data/lib/caruby/migration/uniquify.rb +0 -17
  71. data/lib/caruby/util/attribute_path.rb +0 -44
  72. data/lib/caruby/util/cache.rb +0 -56
  73. data/lib/caruby/util/class.rb +0 -149
  74. data/lib/caruby/util/collection.rb +0 -1152
  75. data/lib/caruby/util/domain_extent.rb +0 -46
  76. data/lib/caruby/util/file_separator.rb +0 -65
  77. data/lib/caruby/util/inflector.rb +0 -27
  78. data/lib/caruby/util/log.rb +0 -95
  79. data/lib/caruby/util/math.rb +0 -12
  80. data/lib/caruby/util/merge.rb +0 -59
  81. data/lib/caruby/util/module.rb +0 -18
  82. data/lib/caruby/util/options.rb +0 -97
  83. data/lib/caruby/util/partial_order.rb +0 -35
  84. data/lib/caruby/util/pretty_print.rb +0 -204
  85. data/lib/caruby/util/stopwatch.rb +0 -74
  86. data/lib/caruby/util/topological_sync_enumerator.rb +0 -62
  87. data/lib/caruby/util/transitive_closure.rb +0 -55
  88. data/lib/caruby/util/tree.rb +0 -48
  89. data/lib/caruby/util/trie.rb +0 -37
  90. data/lib/caruby/util/uniquifier.rb +0 -30
  91. data/lib/caruby/util/validation.rb +0 -20
  92. data/lib/caruby/util/visitor.rb +0 -365
  93. data/lib/caruby/util/weak_hash.rb +0 -36
  94. data/test/lib/caruby/csv/csv_mapper_test.rb +0 -40
  95. data/test/lib/caruby/csv/csvio_test.rb +0 -69
  96. data/test/lib/caruby/database/persistable_test.rb +0 -92
  97. data/test/lib/caruby/domain/domain_test.rb +0 -112
  98. data/test/lib/caruby/domain/inversible_test.rb +0 -99
  99. data/test/lib/caruby/domain/reference_visitor_test.rb +0 -130
  100. data/test/lib/caruby/import/java_test.rb +0 -80
  101. data/test/lib/caruby/import/mixed_case_test.rb +0 -14
  102. data/test/lib/caruby/migration/test_case.rb +0 -102
  103. data/test/lib/caruby/test_case.rb +0 -230
  104. data/test/lib/caruby/util/cache_test.rb +0 -23
  105. data/test/lib/caruby/util/class_test.rb +0 -61
  106. data/test/lib/caruby/util/collection_test.rb +0 -398
  107. data/test/lib/caruby/util/command_test.rb +0 -55
  108. data/test/lib/caruby/util/domain_extent_test.rb +0 -60
  109. data/test/lib/caruby/util/file_separator_test.rb +0 -30
  110. data/test/lib/caruby/util/inflector_test.rb +0 -12
  111. data/test/lib/caruby/util/lazy_hash_test.rb +0 -34
  112. data/test/lib/caruby/util/merge_test.rb +0 -83
  113. data/test/lib/caruby/util/module_test.rb +0 -25
  114. data/test/lib/caruby/util/options_test.rb +0 -59
  115. data/test/lib/caruby/util/partial_order_test.rb +0 -42
  116. data/test/lib/caruby/util/pretty_print_test.rb +0 -85
  117. data/test/lib/caruby/util/properties_test.rb +0 -50
  118. data/test/lib/caruby/util/stopwatch_test.rb +0 -18
  119. data/test/lib/caruby/util/topological_sync_enumerator_test.rb +0 -69
  120. data/test/lib/caruby/util/transitive_closure_test.rb +0 -67
  121. data/test/lib/caruby/util/tree_test.rb +0 -23
  122. data/test/lib/caruby/util/trie_test.rb +0 -14
  123. data/test/lib/caruby/util/visitor_test.rb +0 -278
  124. data/test/lib/caruby/util/weak_hash_test.rb +0 -45
  125. data/test/lib/examples/clinical_trials/migration/migration_test.rb +0 -58
  126. data/test/lib/examples/clinical_trials/migration/test_case.rb +0 -38
@@ -1,37 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'tree')
2
-
3
- # A Trie[http://en.wikipedia.org/wiki/Trie] is an associative access tree structure.
4
- #
5
- # @example
6
- # trie = Trie.new
7
- # trie[:a, :b] = 1
8
- # trie[:a, :b] #=> 1
9
- # trie[:a, :c] #=> nil
10
- class Trie
11
- # Creates a new empty Trie.
12
- def initialize
13
- @tree = Tree.new
14
- end
15
-
16
- # @return the value at the given trie path
17
- def [](*path)
18
- tree = @tree[nil, *path]
19
- tree.value if tree
20
- end
21
-
22
- # @return the top_level Tree for this trie
23
- def to_tree
24
- @tree
25
- end
26
-
27
- # Sets the value for a node path.
28
- #
29
- # @example
30
- # trie = Trie.new
31
- # trie[:a, :b] = 1
32
- # trie[:a, :b] #=> 1
33
- def []=(*path_and_value)
34
- value = path_and_value.pop
35
- @tree.fill(nil, *path_and_value).value = value
36
- end
37
- end
@@ -1,30 +0,0 @@
1
- # A test utility class to generate id qualifiers.
2
- class Uniquifier
3
- # Returns a relatively unique integral qualifier. Successive calls to this method
4
- # within the same time zone spaced more than a millisecond apart return different
5
- # integers. Each generated qualifier is greater than the previous by an unspecified
6
- # amount.
7
- def self.qualifier
8
- # the first date that this method could be called
9
- @first ||= Date.new(2000, 01, 01)
10
- # days as integer + milliseconds as fraction since the first date
11
- diff = DateTime.now - @first
12
- # shift a tenth of a milli up into the integer portion
13
- decimillis = diff * 24 * 60 * 60 * 10000
14
- # truncate the fraction
15
- decimillis.truncate
16
- end
17
- end
18
-
19
- class String
20
- # Returns a relatively unique value obtained from the specified base value.
21
- # Spaces are removed, e.g.:
22
- # Uniquifier.uniquify('Test Name')
23
- # might produce:
24
- # Test_Name_3309388006
25
- #
26
- # The suffix is generated by {Uniquifier.qualifier}.
27
- def uniquify
28
- gsub(' ', '_') + "_#{Uniquifier.qualifier}"
29
- end
30
- end
@@ -1,20 +0,0 @@
1
- # Raised when an object fails a validation test.
2
- class ValidationError < RuntimeError; end
3
-
4
- class Object
5
- # Returns whether this object is nil, false, empty, or a whitespace string.
6
- # For example, "", " ", +nil+, [], and {} are blank.
7
- #
8
- # This method is borrowed from Rails ActiveSupport.
9
- def blank?
10
- respond_to?(:empty?) ? empty? : !self
11
- end
12
-
13
- # Returns whether this object is nil, empty, or a whitespace string.
14
- # For example, "", " ", +nil+, [], and {} return +true+.
15
- #
16
- # This method differs from blank? in that +false+ is an allowed value.
17
- def nil_or_empty?
18
- nil? or (respond_to?(:empty?) and empty?)
19
- end
20
- end
@@ -1,365 +0,0 @@
1
- require 'caruby/util/collection'
2
- require 'caruby/util/options'
3
-
4
- # Enumerator overwrites to_enum, so include it first
5
- require 'enumerator'
6
- require 'generator'
7
-
8
- module CaRuby
9
- # Error raised on a visit failure.
10
- class VisitError < RuntimeError; end
11
-
12
- # Visitor traverses items and applies an operation, e.g.:
13
- # class Node
14
- # attr_accessor :children, :value
15
- # def initialize(value, parent=nil)
16
- # @value = value
17
- # @children = []
18
- # @parent = parent
19
- # @parent.children << self if @parent
20
- # end
21
- # end
22
- # parent = Node.new(1)
23
- # child = Node.new(2, parent)
24
- # multiplier = 2
25
- # CaRuby::Visitor.new { |node| node.children }.visit(parent) { |node| node.value *= multiplier } #=> 2
26
- # parent.value #=> 2
27
- # child.value #=> 4
28
- #
29
- # The visit result is the result of evaluating the operation block on the initial visited node.
30
- # Visiting a collection returns an array of the result of visiting each member of the collection,
31
- # e.g. augmenting the preceding example:
32
- # parent2 = Node.new(3)
33
- # child2 = Node.new(4, parent2)
34
- # CaRuby::Visitor.new { |node| node.children }.visit([parent, parent2]) { |node| node.value *= multiplier } #=> [2, 6]
35
- # Each visit captures the visit result in the +visited+ hash, e.g.:
36
- # parent = Node.new(1)
37
- # child = Node.new(2, parent)
38
- # visitor = CaRuby::Visitor.new { |node| node.children }
39
- # visitor.visit([parent]) { |node| node.value += 1 }
40
- # parent.value #=> 2
41
- # visitor.visited[parent] #=> 2
42
- # child.value #=> 3
43
- # visitor.visited[child] #=> 3
44
- #
45
- # A +return+ from the operation block terminates the visit and exits from the defining scope with the block return value,
46
- # e.g. given the preceding example:
47
- # def increment(parent, limit)
48
- # CaRuby::Visitor.new { |node| node.children }.visit(parent) { |node| node.value < limit ? node.value += 1 : return }
49
- # end
50
- # increment(parent, 2) #=> nil
51
- # parent.value #=> 2
52
- # child.value #=> 2
53
- #
54
- # The to_enum method allows navigator iteration, e.g.:
55
- # CaRuby::Visitor.new { |node| node.children }.to_enum(parent).detect { |node| node.value == 2 }
56
- class Visitor
57
-
58
- attr_reader :options, :visited, :lineage, :cycles
59
-
60
- # Creates a new Visitor which traverses the child objects returned by the navigator block.
61
- # The navigator block takes a parent argument and returns the children to visit. If the block
62
- # return value is not nil and not a collection, then the returned object is visited. A nil or
63
- # empty child is not visited.
64
- #
65
- # options is a symbol => value hash. A Symbol argument _symbol_ is the same as +{+_symbol_+=>true}+.
66
- # Supported options include the follwing:
67
- #
68
- # The value of :depth_first can be +true+, +false+ or a Proc. If the value is a Proc, then
69
- # value determines whether a child is visited depth-first. See the {#visit} method for more information.
70
- #
71
- # If the the :visited option is set, then the visited nodes are recorded in the :visited option hash.
72
- # In that case, the {#visit} call does not clear the visited hash.
73
- #
74
- # If the :operator option is set, then the visit operator block is called when a node is visited.
75
- # The operator block argument is the visited node.
76
- #
77
- # @param [Symbol, {Symbol => Object}] opts the visit options. A symbol argument is the same
78
- # as symbol => true
79
- # @option opts [String] :depth_first depth-first traversal
80
- # @option opts [Hash] :visited the hash to use when recording visited node => value associations
81
- # @option opts [Proc] :operator the visit operator block
82
- # @option opts [String] :prune_cycle flag indicating whether to exclude cycles to the root in a visit
83
- # @yield [parent] the parent being visited
84
- def initialize(opts=nil, &navigator)
85
- @navigator = navigator
86
- @options = Options.to_hash(opts)
87
- @depth_first_flag = @options[:depth_first]
88
- @visited = @options[:visited] || {}
89
- @prune_cycle_flag = @options[:prune_cycle]
90
- @lineage = []
91
- @cycles = []
92
- @exclude = Set.new
93
- end
94
-
95
- # Navigates to node and the children returned by this Visitor's navigator block.
96
- # Applies the optional operator block to each child node if the block is given to this method.
97
- # Returns the result of the operator block if given, or the node itself otherwise.
98
- #
99
- # The nodes to visit from a parent node are determined in the following sequence:
100
- # * Return if the parent node has already been visited.
101
- # * If depth_first, then call the navigator block defined in the initializer on
102
- # the parent node and visit each child node.
103
- # * Visit the parent node.
104
- # * If not depth-first, then call the navigator block defined in the initializer
105
- # on the parent node and visit each child node.
106
- # The :depth option value constrains child traversal to that number of levels.
107
- #
108
- # This method first clears the _visited_ hash, unless the :visited option was set in the initializer.
109
- #
110
- # @param node the root object to visit
111
- # @yield [visited] an operator applied to each visited object
112
- # @yieldparam visited the object currently being visited
113
- # @return the result of the yield block on node, or node itself if no block is given
114
- def visit(node, &operator)
115
- visit_root(node, &operator)
116
- end
117
-
118
- # @param node the node to check
119
- # @return whether the node was visited
120
- def visited?(node)
121
- @visited.has_key?(node)
122
- end
123
-
124
- # @return the top node visited
125
- def root
126
- @lineage.first
127
- end
128
-
129
- # @return the current node being visited
130
- def current
131
- @lineage.last
132
- end
133
-
134
- # @return the node most recently passed as an argument to this visitor's navigator block,
135
- # or nil if visiting the first node
136
- def parent
137
- @lineage[-2]
138
- end
139
-
140
- # @return [Enumerable] iterator over each visited node
141
- def to_enum(node)
142
- # JRuby could use Generator instead, but that results in dire behavior on any error
143
- # by crashing with an elided Java lineage trace.
144
- VisitorEnumerator.new(self, node)
145
- end
146
-
147
- # Returns a new visitor that traverses a collection of parent nodes in lock-step fashion using
148
- # this visitor. The synced {#visit} method applies the visit operator block to an array of child
149
- # nodes taken from each parent node, e.g.:
150
- # parent1 = Node.new(1)
151
- # child11 = Node.new(2, parent1)
152
- # child12 = Node.new(3, parent1)
153
- # parent2 = Node.new(1)
154
- # child21 = Node.new(3, parent2)
155
- # CaRuby::Visitor.new { |node| node.children }.sync.to_enum.to_a #=> [
156
- # [parent1, parent2],
157
- # [child11, child21],
158
- # [child12, nil]
159
- # ]
160
- #
161
- # By default, the children are grouped in enumeration order. If a block is given to this method,
162
- # then the block is called to match child nodes, e.g. using the above example:
163
- # visitor = CaRuby::Visitor.new { |node| node.children }
164
- # synced = visitor.sync { |nodes, others| nodes.to_compact_hash { others.detect { |other| node.value == other.value } } }
165
- # synced.to_enum.to_a #=> [
166
- # [parent1, parent2],
167
- # [child11, nil],
168
- # [child12, child21]
169
- # ]
170
- #
171
- # @yield [nodes, others] matches node in others (optional)
172
- # @yieldparam [<Resource>] nodes the visited nodes to match
173
- # @yieldparam [<Resource>] others the candidates for matching the node
174
- def sync(&matcher)
175
- SyncVisitor.new(self, &matcher)
176
- end
177
-
178
- # Returns a new Visitor which determines which nodes to visit by applying the given block
179
- # to this visitor, e.g.:
180
- # CaRuby::Visitor.new { |node| node.children }.filter { |parent, children| children.first if parent.age >= 18 }
181
- # navigates to the first child of parents 18 or older.
182
- #
183
- # The filter block arguments consist of a parent node and an array of children nodes for the parent.
184
- # The block can return nil, a single node to visit or a collection of nodes to visit.
185
- #
186
- # @return [Visitor] the filter visitor
187
- # @yield [parent, children] the filter to select which of the children to visit next
188
- # @yieldparam parent the currently visited node
189
- # @yieldparam children the nodes slated by this Visitor to visit next
190
- # @raise [ArgumentError] if a block is not given to this method
191
- def filter
192
- raise ArgumentError.new("Filter block not given to visitor filter method") unless block_given?
193
- Visitor.new(@options) { |node| yield(node, node_children(node)) }
194
- end
195
-
196
- protected
197
-
198
- # Resets this visitor's state in preparation for a new visit.
199
- def clear
200
- # clear the lineage
201
- @lineage.clear
202
- # if the visited hash is not shared, then clear it
203
- @visited.clear unless @options.has_key?(:visited)
204
- # clear the cycles
205
- @cycles.clear
206
- end
207
-
208
- # Sets the visited hash.
209
- def visited=(hash)
210
- @visited = hash ||= {}
211
- end
212
-
213
- # Visits the given node using the block given to this method.
214
- # The default block returns node.
215
- def visit_node(node)
216
- @visited[node] = block_given? ? yield(node) : node
217
- end
218
-
219
- # Returns the children to visit for the given node.
220
- def node_children(node)
221
- children = @navigator.call(node)
222
- return Array::EMPTY_ARRAY if children.nil?
223
- Enumerable === children ? children.to_a.compact : [children]
224
- end
225
-
226
- private
227
-
228
- def depth_first?
229
- @depth_first_flag
230
- end
231
-
232
- # Visits the root node and all descendants.
233
- def visit_root(node, &operator)
234
- clear
235
- prune_cycle_nodes(node) if @prune_cycle_flag
236
- # visit the root node
237
- visit_recursive(node, &operator)
238
- end
239
-
240
- # Excludes the internal nodes in cycles starting and ending at the given root.
241
- def prune_cycle_nodes(root)
242
- @exclude.clear
243
- # visit the root, which will detect cycles, and remove the visited nodes afterwords
244
- @prune_cycle_flag = false
245
- to_enum(root).collect.each { |node| @visited.delete(node) }
246
- @prune_cycle_flag = true
247
- # add each cyclic internal node to the exclude list
248
- @cycles.each { |cycle| cycle[1...-1].each { |node| @exclude << node } if cycle.first == root }
249
- end
250
-
251
- def visit_recursive(node, &operator)
252
- # bail if no node or the node is specifically excluded
253
- return if node.nil? or @exclude.include?(node)
254
- # return the visited value if the node has already been visited
255
- if @visited.has_key?(node) then
256
- #capture a cycle
257
- index = @lineage.index(node)
258
- if index then
259
- cycle = @lineage[index..-1] << node
260
- @cycles << cycle
261
- end
262
- return @visited[node]
263
- end
264
- # return nil if the node has not been visited but has been navigated in a depth-first visit
265
- return if @lineage.include?(node)
266
- # all systems go: visit the node graph
267
- visit_node_and_children(node, &operator)
268
- end
269
-
270
- def visit_node_and_children(node, &operator)
271
- # set the current node
272
- @lineage.push(node)
273
- # if depth-first, then visit the children before the current node
274
- visit_children(node, &operator) if depth_first?
275
- # visit the current node
276
- result = visit_node(node, &operator)
277
- # if not depth-first, then visit the children after the current node
278
- visit_children(node, &operator) unless depth_first?
279
- @lineage.pop
280
- # return the visit result
281
- result
282
- end
283
-
284
- def visit_children(node, &operator)
285
- children = node_children(node)
286
- children.each { |child| visit_recursive(child, &operator) }
287
- end
288
-
289
- class VisitorEnumerator
290
- include Enumerable
291
-
292
- def initialize(visitor, node)
293
- @visitor = visitor
294
- @root = node
295
- end
296
-
297
- def each
298
- @visitor.visit(@root) { |node| yield(node) }
299
- end
300
- end
301
-
302
- class SyncVisitor < Visitor
303
- # @param [Visitor] visitor the Visitor which will visit synchronized input
304
- # @yield (see Visitor#sync)
305
- def initialize(visitor, &matcher)
306
- # the next node to visit is an array of child node pairs matched by the given matcher block
307
- super() { |nodes| match_children(visitor, nodes, &matcher) }
308
- end
309
-
310
- # Visits the given pair of nodes.
311
- #
312
- # Raises ArgumentError if nodes does not consist of either two node arguments or one two-item Array
313
- # argument.
314
- def visit(*nodes)
315
- if nodes.size == 1 then
316
- nodes = nodes.first
317
- raise ArgumentError.new("Sync visitor requires a pair of entry nodes") unless nodes.size == 2
318
- end
319
- super(nodes)
320
- end
321
-
322
- # Returns an Enumerable which applies the given block to each matched node starting at the given nodes.
323
- #
324
- # Raises ArgumentError if nodes does not consist of either two node arguments or one two-item Array
325
- # argument.
326
- def to_enum(*nodes)
327
- if nodes.size == 1 then
328
- nodes = nodes.first
329
- raise ArgumentError.new("Sync visitor requires a pair of entry nodes") unless nodes.size == 2
330
- end
331
- super(nodes)
332
- end
333
-
334
- private
335
-
336
- # Returns an array of arrays of matched children from the given parent nodes. The children are matched
337
- # using the block given to this method, if supplied, or by index otherwise.
338
- #
339
- # @see #sync a usage example
340
- # @yield (see Visitor#sync)
341
- def match_children(visitor, nodes)
342
- # the parent nodes
343
- p1, p2 = nodes
344
- # this visitor's children
345
- c1 = visitor.node_children(p1)
346
- c2 = p2 ? visitor.node_children(p2) : []
347
-
348
- # Apply the matcher block on each of this visitor's children and the other children.
349
- # If no block is given, then group the children by index, which is the transpose of the array of
350
- # children arrays.
351
- if block_given? then
352
- # Match each item in the first children array to an item from the second children array using
353
- # then given block.
354
- matches = yield(c1, c2)
355
- c1.map { |c| [c, matches[c]] }
356
- else
357
- # Ensure that both children arrays are the same size.
358
- others = c2.size <= c1.size ? c2.fill(nil, c2.size...c1.size) : c2[0, c1.size]
359
- # The children grouped by index is the transpose of the array of children arrays.
360
- [c1, others].transpose
361
- end
362
- end
363
- end
364
- end
365
- end