parallel-ancestry 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,306 @@
1
+
2
+ module ::ParallelAncestry
3
+
4
+ InstanceAncestryStruct = ::Struct.new( :children_hash, :parents_array, :parents_hash )
5
+
6
+ extend ::ModuleCluster::Define::Block::ClassOrModule
7
+
8
+ # Initialize any module extended with self for inheritance hooks
9
+ module_extend 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
+ class_include 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
+ def children( instance )
31
+
32
+ return children_hash( instance ).keys
33
+
34
+ end
35
+
36
+ #############
37
+ # parents #
38
+ #############
39
+
40
+ def parents( instance )
41
+
42
+ return parents_array( instance )
43
+
44
+ end
45
+
46
+ ##################
47
+ # has_parents? #
48
+ ##################
49
+
50
+ def has_parents?( instance )
51
+
52
+ has_parents = false
53
+
54
+ ancestor_struct = ancestor_struct( instance )
55
+
56
+ if ancestor_struct.parents_array
57
+ has_parents = ! ancestor_struct.parents_array.empty?
58
+ end
59
+
60
+ return has_parents
61
+
62
+ end
63
+
64
+ ###################
65
+ # has_children? #
66
+ ###################
67
+
68
+ def has_children?( instance )
69
+
70
+ has_children = false
71
+
72
+ ancestor_struct = ancestor_struct( instance )
73
+
74
+ if ancestor_struct.children_hash
75
+ has_children = ! ancestor_struct.children_hash.empty?
76
+ end
77
+
78
+ return has_children
79
+
80
+ end
81
+
82
+ ###############################
83
+ # register_child_for_parent #
84
+ ###############################
85
+
86
+ def register_child_for_parent( child, parent )
87
+
88
+ parents_of_child_hash = parents_hash( child )
89
+ children_of_parent_hash = children_hash( parent )
90
+
91
+ unless children_of_parent_hash.has_key?( child )
92
+ # child order shouldn't be relevant
93
+ children_of_parent_hash[ child ] = true
94
+ end
95
+
96
+ unless parents_of_child_hash.has_key?( parent )
97
+ parents_of_child_hash[ parent ] = true
98
+ # parent order determines who wins conflicts, so we keep youngest first
99
+ parents_array( child ).unshift( parent )
100
+ end
101
+
102
+ return self
103
+
104
+ end
105
+
106
+ ##############
107
+ # ancestor #
108
+ ##############
109
+
110
+ def ancestor( instance, & match_ancestor_block )
111
+
112
+ ancestor_instance = nil
113
+
114
+ parents = parents( instance )
115
+
116
+ # If we don't have ancestors explicitly declared for this instance, and if it is not
117
+ # a ::Class or ::Module (both are ::Modules) then we have an instance of a class,
118
+ # so we can use the instance's class
119
+ if parents.empty? and ! instance.is_a?( ::Module )
120
+
121
+ ancestor_instance = instance.class
122
+
123
+ else
124
+
125
+ parents.each do |this_parent|
126
+ # we need a way to go from multiple parents to the one that makes up this chain
127
+ # we use the match_ancestor_block to determine this - it should return true/false
128
+ if match_ancestor_block.call( this_parent )
129
+ ancestor_instance = this_parent
130
+ break
131
+ end
132
+ end
133
+
134
+ end
135
+
136
+ return ancestor_instance
137
+
138
+ end
139
+
140
+ ####################
141
+ # ancestor_chain #
142
+ ####################
143
+
144
+ def ancestor_chain( instance, & match_ancestor_block )
145
+
146
+ ancestor_chain = [ this_ancestor = instance ]
147
+
148
+ while this_ancestor = ancestor( this_ancestor, & match_ancestor_block )
149
+ ancestor_chain.push( this_ancestor )
150
+ end
151
+
152
+ return ancestor_chain
153
+
154
+ end
155
+
156
+ ####################
157
+ # lowest_parents #
158
+ ####################
159
+
160
+ def lowest_parents( instance, & match_ancestor_block )
161
+
162
+ # the first super module available for each tree
163
+
164
+ lowest_parents_array = [ ]
165
+
166
+ parents( instance ).each do |this_parent|
167
+
168
+ # if we match this parent we are done with this branch and can go to the next
169
+ if match_ancestor_block.call( this_parent )
170
+
171
+ lowest_parents_array.push( this_parent )
172
+
173
+ # otherwise our branch expands and we have to finish it before the next parent
174
+ elsif has_parents?( this_parent )
175
+
176
+ parents_for_branch = lowest_parents( this_parent, & match_ancestor_block )
177
+
178
+ lowest_parents_array.concat( parents_for_branch )
179
+
180
+ end
181
+
182
+ end
183
+
184
+ return lowest_parents_array
185
+
186
+ end
187
+
188
+ ######################
189
+ # highest_children #
190
+ ######################
191
+
192
+ def highest_children( instance, & match_ancestor_block )
193
+
194
+ # the first super module available for each tree
195
+
196
+ highest_children_array = [ ]
197
+
198
+ children( instance ).each do |this_child|
199
+
200
+ # if we match this parent we are done with this branch and can go to the next
201
+ if match_ancestor_block.call( this_child )
202
+
203
+ highest_children_array.push( this_child )
204
+
205
+ # otherwise our branch expands and we have to finish it before the next parent
206
+ elsif has_children?( this_child )
207
+
208
+ children_for_branch = highest_children( this_child, & match_ancestor_block )
209
+
210
+ highest_children_array.concat( children_for_branch )
211
+
212
+ end
213
+
214
+ end
215
+
216
+ return highest_children_array
217
+
218
+ end
219
+
220
+ #####################################
221
+ # match_ancestor_searching_upward #
222
+ #####################################
223
+
224
+ def match_ancestor_searching_upward( instance, ancestor_match_block, & match_block )
225
+
226
+ matched_value = nil
227
+
228
+ this_ancestor = instance
229
+
230
+ if this_ancestor.is_a?( ::Module ) or
231
+ has_parents?( this_ancestor )
232
+
233
+ begin
234
+ if match_block.call( this_ancestor )
235
+ matched_value = this_ancestor
236
+ break
237
+ end
238
+ end while this_ancestor = ancestor( this_ancestor, & ancestor_match_block )
239
+
240
+ elsif match_block.call( this_ancestor )
241
+
242
+ matched_value = this_ancestor
243
+
244
+ else
245
+
246
+ matched_value = match_ancestor_searching_upward( this_ancestor.class,
247
+ ancestor_match_block,
248
+ & match_block )
249
+
250
+ end
251
+
252
+ return matched_value
253
+
254
+ end
255
+
256
+ ##################################################################################################
257
+ private ######################################################################################
258
+ ##################################################################################################
259
+
260
+ #####################
261
+ # ancestor_struct #
262
+ #####################
263
+
264
+ def ancestor_struct( instance )
265
+
266
+ unless ancestor_struct = @ancestors_hash[ instance.__id__ ]
267
+ # fill in slots lazily
268
+ ancestor_struct = ::ParallelAncestry::InstanceAncestryStruct.new
269
+ @ancestors_hash[ instance.__id__ ] = ancestor_struct
270
+ end
271
+
272
+ return ancestor_struct
273
+
274
+ end
275
+
276
+ ###################
277
+ # children_hash #
278
+ ###################
279
+
280
+ def children_hash( instance )
281
+
282
+ return ancestor_struct( instance ).children_hash ||= { }
283
+
284
+ end
285
+
286
+ ##################
287
+ # parents_hash #
288
+ ##################
289
+
290
+ def parents_hash( instance )
291
+
292
+ return ancestor_struct( instance ).parents_hash ||= { }
293
+
294
+ end
295
+
296
+ ###################
297
+ # parents_array #
298
+ ###################
299
+
300
+ def parents_array( instance )
301
+
302
+ return ancestor_struct( instance ).parents_array ||= [ ]
303
+
304
+ end
305
+
306
+ end
@@ -0,0 +1,22 @@
1
+
2
+ require 'module-cluster'
3
+
4
+ module ::ParallelAncestry
5
+
6
+ end
7
+
8
+ basepath = 'parallel-ancestry/ParallelAncestry'
9
+
10
+ files = [
11
+
12
+ 'ModuleSubclassInheritance',
13
+ 'Inheritance',
14
+ 'Inheritance/ModuleSubclassInheritance'
15
+
16
+ ]
17
+
18
+ files.each do |this_file|
19
+ require_relative( File.join( basepath, this_file ) + '.rb' )
20
+ end
21
+
22
+ require_relative( basepath + '.rb' )
@@ -0,0 +1,262 @@
1
+
2
+ require_relative '../../lib/parallel-ancestry.rb'
3
+
4
+ describe ::ParallelAncestry::Inheritance do
5
+
6
+ it 'can enable hooks in module extended with self' do
7
+ module ::ParallelAncestry::Inheritance::ModuleMock
8
+
9
+ # mock to ensure call
10
+ def self.initialize_inheritance_for_module!
11
+ super
12
+ @ran_initialize_inheritance_for_module = true
13
+ end
14
+ def self.ran_initialize_inheritance_for_module?
15
+ returning_ran_initialize_inheritance_for_module = ( @ran_initialize_inheritance_for_module ? true : false )
16
+ @ran_initialize_inheritance_for_module = false
17
+ return returning_ran_initialize_inheritance_for_module
18
+ end
19
+ # mock to ensure call
20
+ def self.initialize_base_instance_for_include( inheritance_module, inheriting_instance )
21
+ super
22
+ @ran_initialize_base_instance_for_include = true
23
+ end
24
+ def self.ran_initialize_base_instance_for_include?
25
+ returning_ran_initialize_base_instance_for_include = ( @ran_initialize_base_instance_for_include ? true : false )
26
+ @ran_initialize_base_instance_for_include = false
27
+ return returning_ran_initialize_base_instance_for_include
28
+ end
29
+ # mock to ensure call
30
+ def self.initialize_base_instance_for_extend( inheritance_module, inheriting_instance )
31
+ super
32
+ @ran_initialize_base_instance_for_extend = true
33
+ end
34
+ def self.ran_initialize_base_instance_for_extend?
35
+ returning_ran_initialize_base_instance_for_extend = ( @ran_initialize_base_instance_for_extend ? true : false )
36
+ @ran_initialize_base_instance_for_extend = false
37
+ return returning_ran_initialize_base_instance_for_extend
38
+ end
39
+ # mock to ensure call
40
+ def self.initialize_inheritance( inheriting_instance )
41
+ super
42
+ @ran_initialize_inheritance = true
43
+ end
44
+ def self.ran_initialize_inheritance?
45
+ returning_ran_initialize_inheritance = ( @ran_initialize_inheritance ? true : false )
46
+ @ran_initialize_inheritance = false
47
+ return returning_ran_initialize_inheritance
48
+ end
49
+ # mock to ensure call
50
+ def self.initialize_inheriting_instance( parent_instance, inheriting_instance, is_subclass = false )
51
+ super
52
+ @ran_initialize_inheriting_instance = true
53
+ end
54
+ def self.ran_initialize_inheriting_instance?
55
+ returning_ran_initialize_inheriting_instance = ( @ran_initialize_inheriting_instance ? true : false )
56
+ @ran_initialize_inheriting_instance = false
57
+ return returning_ran_initialize_inheriting_instance
58
+ end
59
+
60
+ extend ::ParallelAncestry::Inheritance
61
+
62
+ ran_initialize_inheritance_for_module?.should == true
63
+ ran_initialize_base_instance_for_include?.should == false
64
+ ran_initialize_base_instance_for_extend?.should == false
65
+ ran_initialize_inheritance?.should == false
66
+ ran_initialize_inheriting_instance?.should == false
67
+
68
+ module IncludingModuleMock
69
+ include ::ParallelAncestry::Inheritance::ModuleMock
70
+ end
71
+
72
+ ran_initialize_inheritance_for_module?.should == false
73
+ ran_initialize_base_instance_for_include?.should == true
74
+ ran_initialize_base_instance_for_extend?.should == false
75
+ ran_initialize_inheritance?.should == true
76
+ ran_initialize_inheriting_instance?.should == false
77
+
78
+ module ExtendingModuleMock
79
+ extend ::ParallelAncestry::Inheritance::ModuleMock
80
+ end
81
+
82
+ ran_initialize_inheritance_for_module?.should == false
83
+ ran_initialize_base_instance_for_include?.should == false
84
+ ran_initialize_base_instance_for_extend?.should == true
85
+ ran_initialize_inheritance?.should == false
86
+ ran_initialize_inheriting_instance?.should == false
87
+
88
+ module SubIncludingModuleMock
89
+ include ::ParallelAncestry::Inheritance::ModuleMock::IncludingModuleMock
90
+ end
91
+
92
+ ran_initialize_inheritance_for_module?.should == false
93
+ ran_initialize_base_instance_for_include?.should == false
94
+ ran_initialize_base_instance_for_extend?.should == false
95
+ ran_initialize_inheritance?.should == true
96
+ ran_initialize_inheriting_instance?.should == true
97
+
98
+ module SubExtendingModuleMock
99
+ extend ::ParallelAncestry::Inheritance::ModuleMock::ExtendingModuleMock
100
+ end
101
+
102
+ ran_initialize_inheritance_for_module?.should == false
103
+ ran_initialize_base_instance_for_include?.should == false
104
+ ran_initialize_base_instance_for_extend?.should == false
105
+ ran_initialize_inheritance?.should == false
106
+ ran_initialize_inheriting_instance?.should == false
107
+
108
+ module SubSubIncludingModuleMock
109
+ include ::ParallelAncestry::Inheritance::ModuleMock::SubIncludingModuleMock
110
+ end
111
+
112
+ ran_initialize_inheritance_for_module?.should == false
113
+ ran_initialize_base_instance_for_include?.should == false
114
+ ran_initialize_base_instance_for_extend?.should == false
115
+ ran_initialize_inheritance?.should == true
116
+ ran_initialize_inheriting_instance?.should == true
117
+
118
+ module SubSubExtendingModuleMock
119
+ extend ::ParallelAncestry::Inheritance::ModuleMock::SubExtendingModuleMock
120
+ end
121
+
122
+ ran_initialize_inheritance_for_module?.should == false
123
+ ran_initialize_base_instance_for_include?.should == false
124
+ ran_initialize_base_instance_for_extend?.should == false
125
+ ran_initialize_inheritance?.should == false
126
+ ran_initialize_inheriting_instance?.should == false
127
+
128
+ end
129
+
130
+ end
131
+
132
+ it 'can enable hooks in module subclass including self' do
133
+ class ::ParallelAncestry::Inheritance::ModuleSubclassMock < ::Module
134
+ include ::ParallelAncestry::Inheritance
135
+ end
136
+
137
+ ::ParallelAncestry::Inheritance::ModuleSubclassInstanceMock = ::ParallelAncestry::Inheritance::ModuleSubclassMock.new
138
+ module ::ParallelAncestry::Inheritance::ModuleSubclassInstanceMock
139
+
140
+ # mock to ensure call
141
+ def self.initialize_inheritance_for_module!
142
+ super
143
+ @ran_initialize_inheritance_for_module = true
144
+ end
145
+ def self.ran_initialize_inheritance_for_module?
146
+ returning_ran_initialize_inheritance_for_module = ( @ran_initialize_inheritance_for_module ? true : false )
147
+ @ran_initialize_inheritance_for_module = false
148
+ return returning_ran_initialize_inheritance_for_module
149
+ end
150
+ # mock to ensure call
151
+ def self.initialize_base_instance_for_include( inheritance_module, inheriting_instance )
152
+ super
153
+ @ran_initialize_base_instance_for_include = true
154
+ end
155
+ def self.ran_initialize_base_instance_for_include?
156
+ returning_ran_initialize_base_instance_for_include = ( @ran_initialize_base_instance_for_include ? true : false )
157
+ @ran_initialize_base_instance_for_include = false
158
+ return returning_ran_initialize_base_instance_for_include
159
+ end
160
+ # mock to ensure call
161
+ def self.initialize_base_instance_for_extend( inheritance_module, inheriting_instance )
162
+ super
163
+ @ran_initialize_base_instance_for_extend = true
164
+ end
165
+ def self.ran_initialize_base_instance_for_extend?
166
+ returning_ran_initialize_base_instance_for_extend = ( @ran_initialize_base_instance_for_extend ? true : false )
167
+ @ran_initialize_base_instance_for_extend = false
168
+ return returning_ran_initialize_base_instance_for_extend
169
+ end
170
+ # mock to ensure call
171
+ def self.initialize_inheritance( inheriting_instance )
172
+ super
173
+ @ran_initialize_inheritance = true
174
+ end
175
+ def self.ran_initialize_inheritance?
176
+ returning_ran_initialize_inheritance = ( @ran_initialize_inheritance ? true : false )
177
+ @ran_initialize_inheritance = false
178
+ return returning_ran_initialize_inheritance
179
+ end
180
+ # mock to ensure call
181
+ def self.initialize_inheriting_instance( parent_instance, inheriting_instance, is_subclass = false )
182
+ super
183
+ @ran_initialize_inheriting_instance = true
184
+ end
185
+ def self.ran_initialize_inheriting_instance?
186
+ returning_ran_initialize_inheriting_instance = ( @ran_initialize_inheriting_instance ? true : false )
187
+ @ran_initialize_inheriting_instance = false
188
+ return returning_ran_initialize_inheriting_instance
189
+ end
190
+
191
+ extend ::ParallelAncestry::Inheritance
192
+
193
+ ran_initialize_inheritance_for_module?.should == true
194
+ ran_initialize_base_instance_for_include?.should == false
195
+ ran_initialize_base_instance_for_extend?.should == false
196
+ ran_initialize_inheritance?.should == false
197
+ ran_initialize_inheriting_instance?.should == false
198
+
199
+ module IncludingModuleMock
200
+ include ::ParallelAncestry::Inheritance::ModuleSubclassInstanceMock
201
+ end
202
+
203
+ ran_initialize_inheritance_for_module?.should == false
204
+ ran_initialize_base_instance_for_include?.should == true
205
+ ran_initialize_base_instance_for_extend?.should == false
206
+ ran_initialize_inheritance?.should == true
207
+ ran_initialize_inheriting_instance?.should == false
208
+
209
+ module ExtendingModuleMock
210
+ extend ::ParallelAncestry::Inheritance::ModuleSubclassInstanceMock
211
+ end
212
+
213
+ ran_initialize_inheritance_for_module?.should == false
214
+ ran_initialize_base_instance_for_include?.should == false
215
+ ran_initialize_base_instance_for_extend?.should == true
216
+ ran_initialize_inheritance?.should == false
217
+ ran_initialize_inheriting_instance?.should == false
218
+
219
+ module SubIncludingModuleMock
220
+ include ::ParallelAncestry::Inheritance::ModuleSubclassInstanceMock::IncludingModuleMock
221
+ end
222
+
223
+ ran_initialize_inheritance_for_module?.should == false
224
+ ran_initialize_base_instance_for_include?.should == false
225
+ ran_initialize_base_instance_for_extend?.should == false
226
+ ran_initialize_inheritance?.should == true
227
+ ran_initialize_inheriting_instance?.should == true
228
+
229
+ module SubExtendingModuleMock
230
+ extend ::ParallelAncestry::Inheritance::ModuleSubclassInstanceMock::ExtendingModuleMock
231
+ end
232
+
233
+ ran_initialize_inheritance_for_module?.should == false
234
+ ran_initialize_base_instance_for_include?.should == false
235
+ ran_initialize_base_instance_for_extend?.should == false
236
+ ran_initialize_inheritance?.should == false
237
+ ran_initialize_inheriting_instance?.should == false
238
+
239
+ module SubSubIncludingModuleMock
240
+ include ::ParallelAncestry::Inheritance::ModuleSubclassInstanceMock::SubIncludingModuleMock
241
+ end
242
+
243
+ ran_initialize_inheritance_for_module?.should == false
244
+ ran_initialize_base_instance_for_include?.should == false
245
+ ran_initialize_base_instance_for_extend?.should == false
246
+ ran_initialize_inheritance?.should == true
247
+ ran_initialize_inheriting_instance?.should == true
248
+
249
+ module SubSubExtendingModuleMock
250
+ extend ::ParallelAncestry::Inheritance::ModuleSubclassInstanceMock::SubExtendingModuleMock
251
+ end
252
+
253
+ ran_initialize_inheritance_for_module?.should == false
254
+ ran_initialize_base_instance_for_include?.should == false
255
+ ran_initialize_base_instance_for_extend?.should == false
256
+ ran_initialize_inheritance?.should == false
257
+ ran_initialize_inheriting_instance?.should == false
258
+
259
+ end
260
+ end
261
+
262
+ end