parallel-ancestry 1.0.0

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.
@@ -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