parallel_ancestry 1.0.0 → 1.0.1
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.
- data/{CHANGELOG.rdoc → CHANGELOG.md} +7 -7
- data/lib/namespaces.rb +3 -0
- data/lib/parallel_ancestry/{parallel_ancestry/inheritance → inheritance}/module_subclass_inheritance.rb +0 -0
- data/lib/parallel_ancestry/{parallel_ancestry/inheritance.rb → inheritance.rb} +0 -0
- data/lib/parallel_ancestry/{parallel_ancestry/module_subclass_inheritance.rb → module_subclass_inheritance.rb} +0 -0
- data/lib/parallel_ancestry.rb +351 -11
- data/lib/requires.rb +15 -0
- metadata +7 -7
- data/README.rdoc +0 -125
- data/lib/parallel_ancestry/parallel_ancestry.rb +0 -355
@@ -1,32 +1,32 @@
|
|
1
1
|
|
2
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
26
|
+
## 6/19/2012 ##
|
27
27
|
|
28
28
|
Changed include/extend hooks to prepend.
|
29
29
|
|
30
|
-
|
30
|
+
## 7/06/2012 ##
|
31
31
|
|
32
32
|
Renamed to parallel_ancestry.
|
data/lib/namespaces.rb
ADDED
File without changes
|
File without changes
|
File without changes
|
data/lib/parallel_ancestry.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
46
|
+
end
|
10
47
|
|
11
|
-
|
48
|
+
#############
|
49
|
+
# parents #
|
50
|
+
#############
|
12
51
|
|
13
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
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.
|
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/
|
62
|
-
- lib/parallel_ancestry/
|
63
|
-
- lib/parallel_ancestry/
|
64
|
-
- lib/parallel_ancestry/
|
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
|
-
-
|
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
|