parallel_ancestry 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,32 +1,32 @@
1
1
 
2
- == 6/10/2012
2
+ ## 6/10/2012 ##
3
3
 
4
4
  Initial release abstracted to be entirely independent from cascading-configuration.
5
5
  Replaces cascading-configuration-inheritance and cascading-configuration-ancestors without any cascading-configuration dependencies.
6
6
 
7
- == 6/11/2012
7
+ ## 6/11/2012 ##
8
8
 
9
9
  Added YARD docs.
10
10
  Shortened :initialize_base_instance_for_include and :initialize_base_instance_for_extend parameters to remove reference to self, since self is already present as block receiver.
11
11
 
12
- == 6/12/2012
12
+ ## 6/12/2012 ##
13
13
 
14
14
  Renamed :match_ancestor_searching_upward to :match_ancestor because ancestors are always found by searching upward.
15
15
  Fixes for ancestor lookup when arriving at Class.
16
16
 
17
- == 6/15/2012
17
+ ## 6/15/2012 ##
18
18
 
19
19
  Added unique-array for children and parents, which means now :include? etc. use hash lookup internally.
20
20
 
21
- == 6/18/2012
21
+ ## 6/18/2012 ##
22
22
 
23
23
  Added is_extending parameter (default = false) to :initialize_inheriting_instance.
24
24
  Fix for subclass inheritance passing subclass instance (self) rather than class instance.
25
25
 
26
- == 6/19/2012
26
+ ## 6/19/2012 ##
27
27
 
28
28
  Changed include/extend hooks to prepend.
29
29
 
30
- == 7/06/2012
30
+ ## 7/06/2012 ##
31
31
 
32
32
  Renamed to parallel_ancestry.
data/lib/namespaces.rb ADDED
@@ -0,0 +1,3 @@
1
+
2
+ module ::ParallelAncestry
3
+ end
@@ -2,23 +2,363 @@
2
2
  require 'module/cluster'
3
3
  require 'array/unique'
4
4
 
5
+ # namespaces that have to be declared ahead of time for proper load order
6
+ require_relative './namespaces'
7
+
8
+ # source file requires
9
+ require_relative './requires.rb'
10
+
5
11
  module ::ParallelAncestry
12
+
13
+ InstanceAncestryStruct = ::Struct.new( :children, :parents )
14
+
15
+ extend ::Module::Cluster
6
16
 
7
- end
17
+ # Initialize any module extended with self for inheritance hooks
18
+ cluster( :parallel_ancestry ).after_extend( :module ) do |ancestors_module|
19
+ ancestors_module.module_eval do
20
+ @ancestors_hash = { }
21
+ end
22
+ end
23
+
24
+ # Initialize any subclass of module including self for inheritance hooks
25
+ cluster( :parallel_ancestry ).after_include( :class ) do |class_instance|
26
+ if class_instance < ::Module
27
+ class_instance.module_eval do
28
+ include( ::ParallelAncestry::ModuleSubclassInheritance )
29
+ end
30
+ end
31
+ end
32
+
33
+ extend self
34
+
35
+ ##############
36
+ # children #
37
+ ##############
38
+
39
+ # Return a list of children for provided object.
40
+ # @param [Object] instance Object instance.
41
+ # @return [Array<Object>] An array containing references to children.
42
+ def children( instance )
43
+
44
+ return ancestor_struct( instance ).children ||= ::Array::Unique.new( self )
8
45
 
9
- basepath = 'parallel_ancestry/parallel_ancestry'
46
+ end
10
47
 
11
- files = [
48
+ #############
49
+ # parents #
50
+ #############
12
51
 
13
- 'inheritance',
14
- 'inheritance/module_subclass_inheritance',
52
+ # Return a list of parents for provided object.
53
+ # @param [Object] instance Object instance.
54
+ # @return [Array<Object>] An array containing references to immediate parents for any configuration.
55
+ def parents( instance )
56
+
57
+ return ancestor_struct( instance ).parents ||= ::Array::Unique.new( self )
15
58
 
16
- 'module_subclass_inheritance'
59
+ end
60
+
61
+ ##################
62
+ # has_parents? #
63
+ ##################
64
+
65
+ # Return whether provided object has parents.
66
+ # @param [Object] instance Object instance.
67
+ # @return [true, false] true or false.
68
+ def has_parents?( instance )
17
69
 
18
- ]
70
+ return ! parents( instance ).empty?
19
71
 
20
- files.each do |this_file|
21
- require_relative( File.join( basepath, this_file ) + '.rb' )
22
- end
72
+ end
73
+
74
+ ###################
75
+ # has_children? #
76
+ ###################
77
+
78
+ # Return whether provided object has children.
79
+ # @param [Object] instance Object instance.
80
+ # @return [true, false] true or false.
81
+ def has_children?( instance )
82
+
83
+ return ! children( instance ).empty?
84
+
85
+ end
86
+
87
+ ###############################
88
+ # register_child_for_parent #
89
+ ###############################
90
+
91
+ # Register instance as child of another instance.
92
+ # @param [Object] child Child instance.
93
+ # @param [Object] parent Parent instance.
94
+ # @return [Array<Object>] An array containing references to children.
95
+ def register_child_for_parent( child, parent )
96
+
97
+ parents_of_child = parents( child )
98
+ children_of_parent = children( parent )
23
99
 
24
- require_relative( basepath + '.rb' )
100
+ # child order shouldn't be relevant
101
+ children_of_parent.push( child )
102
+
103
+ # parent order determines who wins conflicts, so we keep youngest first
104
+ parents_of_child.unshift( parent )
105
+
106
+ return self
107
+
108
+ end
109
+
110
+ ##############
111
+ # ancestor #
112
+ ##############
113
+
114
+ # Return parent for instance that matches match_ancestor_block.
115
+ # @param [Object] instance Child instance.
116
+ # @yield Block used to match parent. The parameter is the parent instance, the return value true
117
+ # or false, reflecting whether or not block matched ancestor.
118
+ # @example
119
+ # ::ParallelAncestry.ancestor( some_instance ) do |this_parent|
120
+ # if this_parent.matches_arbitrary_condition
121
+ # true
122
+ # else
123
+ # false
124
+ # end
125
+ # end
126
+ # @return [Object] A reference to parent matching block condition.
127
+ def ancestor( instance, & match_ancestor_block )
128
+
129
+ ancestor_instance = nil
130
+
131
+ parents = parents( instance )
132
+
133
+ # If we don't have ancestors explicitly declared for this instance, and if it is not
134
+ # a ::Class or ::Module (both are ::Modules) then we have an instance of a class,
135
+ # so we can use the instance's class
136
+ if parents.empty? and instance != ::Class
137
+
138
+ instance_class = instance.class
139
+ if match_ancestor_block.call( instance_class )
140
+ ancestor_instance = instance.class
141
+ end
142
+
143
+ else
144
+
145
+ parents.each do |this_parent|
146
+ # we need a way to go from multiple parents to the one that makes up this chain
147
+ # we use the match_ancestor_block to determine this - it should return true/false
148
+ if match_ancestor_block.call( this_parent )
149
+ ancestor_instance = this_parent
150
+ break
151
+ end
152
+ end
153
+
154
+ end
155
+
156
+ return ancestor_instance
157
+
158
+ end
159
+
160
+ alias_method :parent, :ancestor
161
+
162
+ ####################
163
+ # ancestor_chain #
164
+ ####################
165
+
166
+ # Returns ancestor chain defined for provided object.
167
+ # @param [Object] instance Instance for which ancestors are being looked up.
168
+ # @yield Block used to match parent. The parameter is the parent instance, the return value true
169
+ # or false, reflecting whether or not block matched ancestor.
170
+ # @example
171
+ # ::ParallelAncestry.ancestor( some_instance ) do |this_parent|
172
+ # if this_parent.matches_arbitrary_condition
173
+ # true
174
+ # else
175
+ # false
176
+ # end
177
+ # end
178
+ # @return [Array<Object>] An array containing references to parents matching block condition.
179
+ def ancestor_chain( instance, & match_ancestor_block )
180
+
181
+ ancestor_chain = [ this_ancestor = instance ]
182
+
183
+ while this_ancestor = ancestor( this_ancestor, & match_ancestor_block )
184
+ ancestor_chain.push( this_ancestor )
185
+ end
186
+
187
+ return ancestor_chain
188
+
189
+ end
190
+
191
+ ####################
192
+ # lowest_parents #
193
+ ####################
194
+
195
+ # Returns the lowest parent in each parent tree matching block condition. For simple linear
196
+ # trees, this is simply the first parent, but more complex trees quickly diverge into multiple
197
+ # branches, each of which then requires a lowest match.
198
+ # @param [Object] instance Instance for which parents are being looked up.
199
+ # @yield Block used to match parent. The parameter is the parent instance, the return value true
200
+ # or false, reflecting whether or not block matched ancestor.
201
+ # @example
202
+ # ::ParallelAncestry.lowest_parents( some_instance ) do |this_parent|
203
+ # if this_parent.matches_arbitrary_condition
204
+ # true
205
+ # else
206
+ # false
207
+ # end
208
+ # end
209
+ # @return [Array<Object>] An array containing references to lowest parent in each parent tree
210
+ # matching block condition.
211
+ def lowest_parents( instance, & match_ancestor_block )
212
+
213
+ # the first super module available for each tree
214
+
215
+ lowest_parents_array = [ ]
216
+
217
+ parents( instance ).each do |this_parent|
218
+
219
+ # if we match this parent we are done with this branch and can go to the next
220
+ if match_ancestor_block.call( this_parent )
221
+
222
+ lowest_parents_array.push( this_parent )
223
+
224
+ # otherwise our branch expands and we have to finish it before the next parent
225
+ elsif has_parents?( this_parent )
226
+
227
+ parents_for_branch = lowest_parents( this_parent, & match_ancestor_block )
228
+
229
+ lowest_parents_array.concat( parents_for_branch )
230
+
231
+ end
232
+
233
+ end
234
+
235
+ return lowest_parents_array
236
+
237
+ end
238
+
239
+ ######################
240
+ # highest_children #
241
+ ######################
242
+
243
+ # Returns the highest parent in each parent tree matching block condition. For simple linear
244
+ # trees, this is simply the first parent, but more complex trees quickly diverge into multiple
245
+ # branches, each of which then requires a highest match.
246
+ # @param [Object] instance Instance for which parents are being looked up.
247
+ # @yield Block used to match parent. The parameter is the parent instance, the return value true
248
+ # or false, reflecting whether or not block matched ancestor.
249
+ # @example
250
+ # ::ParallelAncestry.highest_parents( some_instance ) do |this_parent|
251
+ # if this_parent.matches_arbitrary_condition
252
+ # true
253
+ # else
254
+ # false
255
+ # end
256
+ # end
257
+ # @return [Array<Object>] An array containing references to highest parent in each parent tree
258
+ # matching block condition.
259
+ def highest_children( instance, & match_ancestor_block )
260
+
261
+ # the first super module available for each tree
262
+
263
+ highest_children_array = [ ]
264
+
265
+ children( instance ).each do |this_child|
266
+
267
+ # if we match this parent we are done with this branch and can go to the next
268
+ if match_ancestor_block.call( this_child )
269
+
270
+ highest_children_array.push( this_child )
271
+
272
+ # otherwise our branch expands and we have to finish it before the next parent
273
+ elsif has_children?( this_child )
274
+
275
+ children_for_branch = highest_children( this_child, & match_ancestor_block )
276
+
277
+ highest_children_array.concat( children_for_branch )
278
+
279
+ end
280
+
281
+ end
282
+
283
+ return highest_children_array
284
+
285
+ end
286
+
287
+ ####################
288
+ # match_ancestor #
289
+ ####################
290
+
291
+ # Returns the first ancestor (determined by ancestor_match_block) for which match_block is true.
292
+ # @param [Object] instance Instance for which parents are being looked up.
293
+ # @param [Proc] ancestor_match_block Proc used to match parent. The parameter is the parent
294
+ # instance, the return value true or false, reflecting whether or not block matched
295
+ # ancestor.
296
+ # @yield Block used to match parent. The parameter is the parent instance, the return value true
297
+ # or false, reflecting whether or not block matched.
298
+ # @example
299
+ # ancestor_match_block = ::Proc.new do |this_parent|
300
+ # if this_parent.matches_arbitrary_condition
301
+ # true
302
+ # else
303
+ # false
304
+ # end
305
+ # end
306
+ # ::ParallelAncestry.match_ancestor( some_instance, ancestor_match_block ) do |this_parent|
307
+ # if this_parent.matches_arbitrary_condition
308
+ # true
309
+ # else
310
+ # false
311
+ # end
312
+ # end
313
+ # @return [Object]
314
+ def match_ancestor( instance, ancestor_match_block, & match_block )
315
+
316
+ matched_ancestor = nil
317
+
318
+ this_ancestor = instance
319
+
320
+ if has_parents?( this_ancestor )
321
+
322
+ begin
323
+ if match_block.call( this_ancestor )
324
+ matched_ancestor = this_ancestor
325
+ break
326
+ end
327
+ break if this_ancestor.equal?( ::Object )
328
+ end while this_ancestor = ancestor( this_ancestor, & ancestor_match_block )
329
+
330
+ elsif match_block.call( this_ancestor )
331
+
332
+ matched_ancestor = this_ancestor
333
+
334
+ else
335
+
336
+ matched_ancestor = match_ancestor( this_ancestor.class, ancestor_match_block, & match_block )
337
+
338
+ end
339
+
340
+ return matched_ancestor
341
+
342
+ end
343
+
344
+ ##################################################################################################
345
+ private ######################################################################################
346
+ ##################################################################################################
347
+
348
+ #####################
349
+ # ancestor_struct #
350
+ #####################
351
+
352
+ def ancestor_struct( instance )
353
+
354
+ unless ancestor_struct = @ancestors_hash[ instance.__id__ ]
355
+ # fill in slots lazily
356
+ ancestor_struct = ::ParallelAncestry::InstanceAncestryStruct.new
357
+ @ancestors_hash[ instance.__id__ ] = ancestor_struct
358
+ end
359
+
360
+ return ancestor_struct
361
+
362
+ end
363
+
364
+ end
data/lib/requires.rb ADDED
@@ -0,0 +1,15 @@
1
+
2
+ basepath = 'parallel_ancestry'
3
+
4
+ files = [
5
+
6
+ 'inheritance',
7
+ 'inheritance/module_subclass_inheritance',
8
+
9
+ 'module_subclass_inheritance'
10
+
11
+ ]
12
+
13
+ files.each do |this_file|
14
+ require_relative( File.join( basepath, this_file ) + '.rb' )
15
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_ancestry
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -58,16 +58,16 @@ executables: []
58
58
  extensions: []
59
59
  extra_rdoc_files: []
60
60
  files:
61
- - lib/parallel_ancestry/parallel_ancestry/inheritance/module_subclass_inheritance.rb
62
- - lib/parallel_ancestry/parallel_ancestry/inheritance.rb
63
- - lib/parallel_ancestry/parallel_ancestry/module_subclass_inheritance.rb
64
- - lib/parallel_ancestry/parallel_ancestry.rb
61
+ - lib/namespaces.rb
62
+ - lib/parallel_ancestry/inheritance/module_subclass_inheritance.rb
63
+ - lib/parallel_ancestry/inheritance.rb
64
+ - lib/parallel_ancestry/module_subclass_inheritance.rb
65
65
  - lib/parallel_ancestry.rb
66
+ - lib/requires.rb
66
67
  - spec/parallel_ancestry/inheritance_spec.rb
67
68
  - spec/parallel_ancestry_spec.rb
68
69
  - README.md
69
- - README.rdoc
70
- - CHANGELOG.rdoc
70
+ - CHANGELOG.md
71
71
  homepage: http://rubygems.org/gems/parallel_ancestry
72
72
  licenses: []
73
73
  post_install_message:
data/README.rdoc DELETED
@@ -1,125 +0,0 @@
1
- == Parallel Ancestry
2
-
3
- http://rubygems.org/gems/parallel_ancestry
4
-
5
- == Summary
6
-
7
- Provides parallel implementations of inheritance hierarchies. This is useful both for tracking the existing inheritance tree and for creating trees that function independently of inheritance models determined internal to Ruby.
8
-
9
- == Description
10
-
11
- Create and track parallel inheritance hierarchies.
12
-
13
- Hook parallel hierarchies (by including a module) to automatically update/register ancestry at include and extend, or update/register only manually.
14
-
15
- Manual registration permits definitions of ancestry across, for example, instances of the same type or instances of entirely different types.
16
-
17
- Implementation is provided by simple child/parent trees with an block to choose which parent. This permits a simple multiple inheritance model very similar to how Ruby handles modules. Conflicts for multiple inheritance are resolved by the parent most recently named for the block match.
18
-
19
- Used heavily by CascadingConfiguration gem (cascading-configuration) as well as by forthcoming Persistence and Magnets gems (persistence and magnets).
20
-
21
- == Install
22
-
23
- * sudo gem install parallel_ancestry
24
-
25
- == Usage
26
-
27
- Two modules are provided by Parallel Ancestry:
28
-
29
- 1. ::ParallelAncestry, which provides parallel inheritance.
30
- 2. ::ParallelAncestry::Inheritance, which provides cascading inheritance hooks.
31
-
32
- == ::ParallelAncestry
33
-
34
- ::ParallelAncestry provides parallel inheritance registration and lookup. This is useful for tracking arbitrary trees of ancestry relations (determined by manual registration), and can be combined with ::ParallelAncestry::Inheritance for automatic registration in correspondence with Ruby's inheritance relations.
35
-
36
- 3 ways to use:
37
-
38
- 1. ::ParallelAncestry provides a singleton implementation for parallel inheritance.
39
- 2. Extend any module with ::ParallelAncestry to enable module as singleton implementation for parallel inheritance.
40
- 3. Include ::ParallelAncestry in subclass of ::Module to enable instances of ::Module subclass as individual implementations for parallel inheritance.
41
-
42
- Parallel ancestry instances support registration and lookup of arbitrarily declared ancestor relationships:
43
-
44
- * children( instance )
45
- * parents( instance )
46
- * has_parents?( instance )
47
- * has_children?( instance )
48
- * register_child_for_parent( child, parent )
49
- * ancestor( instance, & match_ancestor_block )
50
- * ancestor_chain( instance, & match_ancestor_block )
51
- * lowest_parents( instance, & match_ancestor_block )
52
- * highest_children( instance, & match_ancestor_block )
53
- * match_ancestor( instance, ancestor_match_block, & match_block )
54
-
55
- match_block is always a proc or lambda that will be passed the next ancestor as the single parameter and is expected to return true or false whether or not the ancestor matches the condition.
56
-
57
- match_block = ::Proc.new do |this_ancestor|
58
-
59
- if this_ancestor.matches_arbitrary_condition
60
- true
61
- else
62
- false
63
- end
64
-
65
- end
66
-
67
- == ::ParallelAncestry::Inheritance
68
-
69
- ::ParallelAncestry::Inheritance provides hooks for Ruby inheritance events.
70
-
71
- 2 ways to use:
72
-
73
- 1. Extend any module with ::ParallelAncestry::Inheritance to enable hooks in module singleton.
74
- 2. Include ::ParallelAncestry::Inheritance in subclass of ::Module to enable instance of ::Module subclass wit hooks.
75
-
76
- Hooks provided:
77
-
78
- * Initialization
79
-
80
- def initialize_inheritance!
81
- ... [ your hook ] ...
82
- end
83
-
84
- * First include:
85
-
86
- def initialize_base_instance_for_include
87
- ... [ your hook ] ...
88
- end
89
-
90
- * First extend:
91
-
92
- def initialize_base_instance_for_extend
93
- ... [ your hook ] ...
94
- end
95
-
96
- * Subsequent includes:
97
-
98
- def initialize_inheriting_instance
99
- ... [ your hook ] ...
100
- end
101
-
102
- == License
103
-
104
- (The MIT License)
105
-
106
- Copyright (c) Asher
107
-
108
- Permission is hereby granted, free of charge, to any person obtaining
109
- a copy of this software and associated documentation files (the
110
- 'Software'), to deal in the Software without restriction, including
111
- without limitation the rights to use, copy, modify, merge, publish,
112
- distribute, sublicense, and/or sell copies of the Software, and to
113
- permit persons to whom the Software is furnished to do so, subject to
114
- the following conditions:
115
-
116
- The above copyright notice and this permission notice shall be
117
- included in all copies or substantial portions of the Software.
118
-
119
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
120
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
121
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
122
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
123
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
124
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
125
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,355 +0,0 @@
1
-
2
- module ::ParallelAncestry
3
-
4
- InstanceAncestryStruct = ::Struct.new( :children, :parents )
5
-
6
- extend ::Module::Cluster
7
-
8
- # Initialize any module extended with self for inheritance hooks
9
- cluster( :parallel_ancestry ).after_extend( :module ) do |ancestors_module|
10
- ancestors_module.module_eval do
11
- @ancestors_hash = { }
12
- end
13
- end
14
-
15
- # Initialize any subclass of module including self for inheritance hooks
16
- cluster( :parallel_ancestry ).after_include( :class ) do |class_instance|
17
- if class_instance < ::Module
18
- class_instance.module_eval do
19
- include( ::ParallelAncestry::ModuleSubclassInheritance )
20
- end
21
- end
22
- end
23
-
24
- extend self
25
-
26
- ##############
27
- # children #
28
- ##############
29
-
30
- # Return a list of children for provided object.
31
- # @param [Object] instance Object instance.
32
- # @return [Array<Object>] An array containing references to children.
33
- def children( instance )
34
-
35
- return ancestor_struct( instance ).children ||= ::Array::Unique.new( self )
36
-
37
- end
38
-
39
- #############
40
- # parents #
41
- #############
42
-
43
- # Return a list of parents for provided object.
44
- # @param [Object] instance Object instance.
45
- # @return [Array<Object>] An array containing references to immediate parents for any configuration.
46
- def parents( instance )
47
-
48
- return ancestor_struct( instance ).parents ||= ::Array::Unique.new( self )
49
-
50
- end
51
-
52
- ##################
53
- # has_parents? #
54
- ##################
55
-
56
- # Return whether provided object has parents.
57
- # @param [Object] instance Object instance.
58
- # @return [true, false] true or false.
59
- def has_parents?( instance )
60
-
61
- return ! parents( instance ).empty?
62
-
63
- end
64
-
65
- ###################
66
- # has_children? #
67
- ###################
68
-
69
- # Return whether provided object has children.
70
- # @param [Object] instance Object instance.
71
- # @return [true, false] true or false.
72
- def has_children?( instance )
73
-
74
- return ! children( instance ).empty?
75
-
76
- end
77
-
78
- ###############################
79
- # register_child_for_parent #
80
- ###############################
81
-
82
- # Register instance as child of another instance.
83
- # @param [Object] child Child instance.
84
- # @param [Object] parent Parent instance.
85
- # @return [Array<Object>] An array containing references to children.
86
- def register_child_for_parent( child, parent )
87
-
88
- parents_of_child = parents( child )
89
- children_of_parent = children( parent )
90
-
91
- # child order shouldn't be relevant
92
- children_of_parent.push( child )
93
-
94
- # parent order determines who wins conflicts, so we keep youngest first
95
- parents_of_child.unshift( parent )
96
-
97
- return self
98
-
99
- end
100
-
101
- ##############
102
- # ancestor #
103
- ##############
104
-
105
- # Return parent for instance that matches match_ancestor_block.
106
- # @param [Object] instance Child instance.
107
- # @yield Block used to match parent. The parameter is the parent instance, the return value true
108
- # or false, reflecting whether or not block matched ancestor.
109
- # @example
110
- # ::ParallelAncestry.ancestor( some_instance ) do |this_parent|
111
- # if this_parent.matches_arbitrary_condition
112
- # true
113
- # else
114
- # false
115
- # end
116
- # end
117
- # @return [Object] A reference to parent matching block condition.
118
- def ancestor( instance, & match_ancestor_block )
119
-
120
- ancestor_instance = nil
121
-
122
- parents = parents( instance )
123
-
124
- # If we don't have ancestors explicitly declared for this instance, and if it is not
125
- # a ::Class or ::Module (both are ::Modules) then we have an instance of a class,
126
- # so we can use the instance's class
127
- if parents.empty? and instance != ::Class
128
-
129
- instance_class = instance.class
130
- if match_ancestor_block.call( instance_class )
131
- ancestor_instance = instance.class
132
- end
133
-
134
- else
135
-
136
- parents.each do |this_parent|
137
- # we need a way to go from multiple parents to the one that makes up this chain
138
- # we use the match_ancestor_block to determine this - it should return true/false
139
- if match_ancestor_block.call( this_parent )
140
- ancestor_instance = this_parent
141
- break
142
- end
143
- end
144
-
145
- end
146
-
147
- return ancestor_instance
148
-
149
- end
150
-
151
- alias_method :parent, :ancestor
152
-
153
- ####################
154
- # ancestor_chain #
155
- ####################
156
-
157
- # Returns ancestor chain defined for provided object.
158
- # @param [Object] instance Instance for which ancestors are being looked up.
159
- # @yield Block used to match parent. The parameter is the parent instance, the return value true
160
- # or false, reflecting whether or not block matched ancestor.
161
- # @example
162
- # ::ParallelAncestry.ancestor( some_instance ) do |this_parent|
163
- # if this_parent.matches_arbitrary_condition
164
- # true
165
- # else
166
- # false
167
- # end
168
- # end
169
- # @return [Array<Object>] An array containing references to parents matching block condition.
170
- def ancestor_chain( instance, & match_ancestor_block )
171
-
172
- ancestor_chain = [ this_ancestor = instance ]
173
-
174
- while this_ancestor = ancestor( this_ancestor, & match_ancestor_block )
175
- ancestor_chain.push( this_ancestor )
176
- end
177
-
178
- return ancestor_chain
179
-
180
- end
181
-
182
- ####################
183
- # lowest_parents #
184
- ####################
185
-
186
- # Returns the lowest parent in each parent tree matching block condition. For simple linear
187
- # trees, this is simply the first parent, but more complex trees quickly diverge into multiple
188
- # branches, each of which then requires a lowest match.
189
- # @param [Object] instance Instance for which parents are being looked up.
190
- # @yield Block used to match parent. The parameter is the parent instance, the return value true
191
- # or false, reflecting whether or not block matched ancestor.
192
- # @example
193
- # ::ParallelAncestry.lowest_parents( some_instance ) do |this_parent|
194
- # if this_parent.matches_arbitrary_condition
195
- # true
196
- # else
197
- # false
198
- # end
199
- # end
200
- # @return [Array<Object>] An array containing references to lowest parent in each parent tree
201
- # matching block condition.
202
- def lowest_parents( instance, & match_ancestor_block )
203
-
204
- # the first super module available for each tree
205
-
206
- lowest_parents_array = [ ]
207
-
208
- parents( instance ).each do |this_parent|
209
-
210
- # if we match this parent we are done with this branch and can go to the next
211
- if match_ancestor_block.call( this_parent )
212
-
213
- lowest_parents_array.push( this_parent )
214
-
215
- # otherwise our branch expands and we have to finish it before the next parent
216
- elsif has_parents?( this_parent )
217
-
218
- parents_for_branch = lowest_parents( this_parent, & match_ancestor_block )
219
-
220
- lowest_parents_array.concat( parents_for_branch )
221
-
222
- end
223
-
224
- end
225
-
226
- return lowest_parents_array
227
-
228
- end
229
-
230
- ######################
231
- # highest_children #
232
- ######################
233
-
234
- # Returns the highest parent in each parent tree matching block condition. For simple linear
235
- # trees, this is simply the first parent, but more complex trees quickly diverge into multiple
236
- # branches, each of which then requires a highest match.
237
- # @param [Object] instance Instance for which parents are being looked up.
238
- # @yield Block used to match parent. The parameter is the parent instance, the return value true
239
- # or false, reflecting whether or not block matched ancestor.
240
- # @example
241
- # ::ParallelAncestry.highest_parents( some_instance ) do |this_parent|
242
- # if this_parent.matches_arbitrary_condition
243
- # true
244
- # else
245
- # false
246
- # end
247
- # end
248
- # @return [Array<Object>] An array containing references to highest parent in each parent tree
249
- # matching block condition.
250
- def highest_children( instance, & match_ancestor_block )
251
-
252
- # the first super module available for each tree
253
-
254
- highest_children_array = [ ]
255
-
256
- children( instance ).each do |this_child|
257
-
258
- # if we match this parent we are done with this branch and can go to the next
259
- if match_ancestor_block.call( this_child )
260
-
261
- highest_children_array.push( this_child )
262
-
263
- # otherwise our branch expands and we have to finish it before the next parent
264
- elsif has_children?( this_child )
265
-
266
- children_for_branch = highest_children( this_child, & match_ancestor_block )
267
-
268
- highest_children_array.concat( children_for_branch )
269
-
270
- end
271
-
272
- end
273
-
274
- return highest_children_array
275
-
276
- end
277
-
278
- ####################
279
- # match_ancestor #
280
- ####################
281
-
282
- # Returns the first ancestor (determined by ancestor_match_block) for which match_block is true.
283
- # @param [Object] instance Instance for which parents are being looked up.
284
- # @param [Proc] ancestor_match_block Proc used to match parent. The parameter is the parent
285
- # instance, the return value true or false, reflecting whether or not block matched
286
- # ancestor.
287
- # @yield Block used to match parent. The parameter is the parent instance, the return value true
288
- # or false, reflecting whether or not block matched.
289
- # @example
290
- # ancestor_match_block = ::Proc.new do |this_parent|
291
- # if this_parent.matches_arbitrary_condition
292
- # true
293
- # else
294
- # false
295
- # end
296
- # end
297
- # ::ParallelAncestry.match_ancestor( some_instance, ancestor_match_block ) do |this_parent|
298
- # if this_parent.matches_arbitrary_condition
299
- # true
300
- # else
301
- # false
302
- # end
303
- # end
304
- # @return [Object]
305
- def match_ancestor( instance, ancestor_match_block, & match_block )
306
-
307
- matched_ancestor = nil
308
-
309
- this_ancestor = instance
310
-
311
- if has_parents?( this_ancestor )
312
-
313
- begin
314
- if match_block.call( this_ancestor )
315
- matched_ancestor = this_ancestor
316
- break
317
- end
318
- break if this_ancestor.equal?( ::Object )
319
- end while this_ancestor = ancestor( this_ancestor, & ancestor_match_block )
320
-
321
- elsif match_block.call( this_ancestor )
322
-
323
- matched_ancestor = this_ancestor
324
-
325
- else
326
-
327
- matched_ancestor = match_ancestor( this_ancestor.class, ancestor_match_block, & match_block )
328
-
329
- end
330
-
331
- return matched_ancestor
332
-
333
- end
334
-
335
- ##################################################################################################
336
- private ######################################################################################
337
- ##################################################################################################
338
-
339
- #####################
340
- # ancestor_struct #
341
- #####################
342
-
343
- def ancestor_struct( instance )
344
-
345
- unless ancestor_struct = @ancestors_hash[ instance.__id__ ]
346
- # fill in slots lazily
347
- ancestor_struct = ::ParallelAncestry::InstanceAncestryStruct.new
348
- @ancestors_hash[ instance.__id__ ] = ancestor_struct
349
- end
350
-
351
- return ancestor_struct
352
-
353
- end
354
-
355
- end