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.
- data/CHANGELOG.rdoc +6 -0
- data/README.md +137 -0
- data/README.rdoc +125 -0
- data/lib/parallel-ancestry/ParallelAncestry/Inheritance/ModuleSubclassInheritance.rb +18 -0
- data/lib/parallel-ancestry/ParallelAncestry/Inheritance.rb +138 -0
- data/lib/parallel-ancestry/ParallelAncestry/ModuleSubclassInheritance.rb +18 -0
- data/lib/parallel-ancestry/ParallelAncestry.rb +306 -0
- data/lib/parallel-ancestry.rb +22 -0
- data/spec/ParallelAncestry/Inheritance_spec.rb +262 -0
- data/spec/ParallelAncestry_spec.rb +359 -0
- metadata +81 -0
@@ -0,0 +1,359 @@
|
|
1
|
+
|
2
|
+
require_relative '../lib/parallel-ancestry.rb'
|
3
|
+
|
4
|
+
describe ::ParallelAncestry do
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
module ::ParallelAncestry::Mock
|
8
|
+
extend ::ParallelAncestry
|
9
|
+
end
|
10
|
+
class ::ParallelAncestry::ModuleSubclassMock < ::Module
|
11
|
+
include ::ParallelAncestry
|
12
|
+
end
|
13
|
+
::ParallelAncestry::ModuleSubclassInstanceMock = ::ParallelAncestry::ModuleSubclassMock.new
|
14
|
+
end
|
15
|
+
|
16
|
+
##############
|
17
|
+
# children #
|
18
|
+
##############
|
19
|
+
|
20
|
+
it 'tracks an array of children' do
|
21
|
+
[ ::ParallelAncestry::Mock, ::ParallelAncestry::ModuleSubclassInstanceMock ].each do |this_test|
|
22
|
+
this_test.module_eval do
|
23
|
+
configuration_instance = ::Module.new
|
24
|
+
children_instance = children( configuration_instance )
|
25
|
+
children_instance.should == []
|
26
|
+
children_instance.should == children( configuration_instance )
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#############
|
32
|
+
# parents #
|
33
|
+
#############
|
34
|
+
|
35
|
+
it 'tracks an array of children' do
|
36
|
+
[ ::ParallelAncestry::Mock, ::ParallelAncestry::ModuleSubclassInstanceMock ].each do |this_test|
|
37
|
+
this_test.module_eval do
|
38
|
+
configuration_instance = ::Module.new
|
39
|
+
parents_instance = parents( configuration_instance )
|
40
|
+
parents_instance.should == []
|
41
|
+
parents_instance.should == parents( configuration_instance )
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
###############################
|
47
|
+
# register_child_for_parent #
|
48
|
+
###############################
|
49
|
+
|
50
|
+
it 'can register children for a given instance so that migration is possible' do
|
51
|
+
[ ::ParallelAncestry::Mock, ::ParallelAncestry::ModuleSubclassInstanceMock ].each do |this_test|
|
52
|
+
this_test.module_eval do
|
53
|
+
parent = ::Module.new
|
54
|
+
child = ::Module.new
|
55
|
+
register_child_for_parent( child, parent )
|
56
|
+
children( parent ).should == [ child ]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
##############
|
62
|
+
# ancestor #
|
63
|
+
##############
|
64
|
+
|
65
|
+
it 'can return the next ancestor for instance for configuration name' do
|
66
|
+
[ ::ParallelAncestry::Mock, ::ParallelAncestry::ModuleSubclassInstanceMock ].each do |this_test|
|
67
|
+
this_test.module_eval do
|
68
|
+
parent = ::Module.new do
|
69
|
+
# we have to mock the method bc that's how the module determines if it has a configuration defined
|
70
|
+
# since we don't know whether a configuration value has been set yet
|
71
|
+
attr_accessor :some_configuration
|
72
|
+
end
|
73
|
+
child = ::Module.new
|
74
|
+
register_child_for_parent( child, parent )
|
75
|
+
ancestor( child ) { |this_parent| this_parent == parent } .should == parent
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
####################
|
81
|
+
# ancestor_chain #
|
82
|
+
####################
|
83
|
+
|
84
|
+
it 'can return a chain of instances corresponding to the configuration inheritance chain' do
|
85
|
+
[ ::ParallelAncestry::Mock, ::ParallelAncestry::ModuleSubclassInstanceMock ].each do |this_test|
|
86
|
+
this_test.module_eval do
|
87
|
+
# set up hierarchy
|
88
|
+
instance_A = ::Module.new do
|
89
|
+
# we have to mock the method bc that's how the module determines if it has a configuration defined
|
90
|
+
# since we don't know whether a configuration value has been set yet
|
91
|
+
attr_accessor :some_configuration
|
92
|
+
end
|
93
|
+
instance_B = ::Module.new do
|
94
|
+
# we have to mock the method bc that's how the module determines if it has a configuration defined
|
95
|
+
# since we don't know whether a configuration value has been set yet
|
96
|
+
attr_accessor :some_other_configuration
|
97
|
+
include instance_A
|
98
|
+
end
|
99
|
+
register_child_for_parent( instance_B, instance_A )
|
100
|
+
instance_C1 = ::Module.new do
|
101
|
+
include instance_B
|
102
|
+
attr_accessor :yet_another_configuration
|
103
|
+
end
|
104
|
+
register_child_for_parent( instance_C1, instance_B )
|
105
|
+
instance_C2 = ::Module.new do
|
106
|
+
include instance_B
|
107
|
+
end
|
108
|
+
register_child_for_parent( instance_C2, instance_B )
|
109
|
+
instance_D = ::Module.new do
|
110
|
+
include instance_C1
|
111
|
+
include instance_C2
|
112
|
+
end
|
113
|
+
register_child_for_parent( instance_D, instance_C1 )
|
114
|
+
register_child_for_parent( instance_D, instance_C2 )
|
115
|
+
# test ancestor hierarchy for each
|
116
|
+
ancestor_proc = ::Proc.new do |this_parent|
|
117
|
+
this_parent.method_defined?( :some_configuration )
|
118
|
+
end
|
119
|
+
ancestor_chain( instance_A, & ancestor_proc ).should == [ instance_A ]
|
120
|
+
ancestor_chain( instance_B, & ancestor_proc ).should == [ instance_B, instance_A ]
|
121
|
+
ancestor_chain( instance_C1, & ancestor_proc ).should == [ instance_C1, instance_B, instance_A ]
|
122
|
+
ancestor_chain( instance_C2, & ancestor_proc ).should == [ instance_C2, instance_B, instance_A ]
|
123
|
+
ancestor_chain( instance_D, & ancestor_proc ).should == [ instance_D, instance_C2, instance_B, instance_A ]
|
124
|
+
|
125
|
+
ancestor_proc = ::Proc.new do |this_parent|
|
126
|
+
this_parent.method_defined?( :some_other_configuration )
|
127
|
+
end
|
128
|
+
ancestor_chain( instance_B, & ancestor_proc ).should == [ instance_B ]
|
129
|
+
ancestor_chain( instance_C1, & ancestor_proc ).should == [ instance_C1, instance_B ]
|
130
|
+
ancestor_chain( instance_C2, & ancestor_proc ).should == [ instance_C2, instance_B ]
|
131
|
+
|
132
|
+
ancestor_proc = ::Proc.new do |this_parent|
|
133
|
+
this_parent.method_defined?( :yet_another_configuration )
|
134
|
+
end
|
135
|
+
ancestor_chain( instance_C1, & ancestor_proc ).should == [ instance_C1 ]
|
136
|
+
ancestor_chain( instance_D, & ancestor_proc ).should == [ instance_D, instance_C1 ]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
####################
|
142
|
+
# lowest_parents #
|
143
|
+
####################
|
144
|
+
|
145
|
+
it 'can return the first ancestor on each parent tree matching block condition' do
|
146
|
+
[ ::ParallelAncestry::Mock, ::ParallelAncestry::ModuleSubclassInstanceMock ].each do |this_test|
|
147
|
+
this_test.module_eval do
|
148
|
+
# set up hierarchy
|
149
|
+
instance_A = ::Module.new do
|
150
|
+
# we have to mock the method bc that's how the module determines if it has a configuration defined
|
151
|
+
# since we don't know whether a configuration value has been set yet
|
152
|
+
attr_accessor :some_configuration
|
153
|
+
end
|
154
|
+
instance_B1 = ::Module.new do
|
155
|
+
# we have to mock the method bc that's how the module determines if it has a configuration defined
|
156
|
+
# since we don't know whether a configuration value has been set yet
|
157
|
+
attr_accessor :some_other_configuration
|
158
|
+
include instance_A
|
159
|
+
end
|
160
|
+
register_child_for_parent( instance_B1, instance_A )
|
161
|
+
instance_B2 = ::Module.new do
|
162
|
+
include instance_A
|
163
|
+
end
|
164
|
+
register_child_for_parent( instance_B2, instance_A )
|
165
|
+
instance_C1 = ::Module.new do
|
166
|
+
include instance_B1
|
167
|
+
attr_accessor :yet_another_configuration
|
168
|
+
end
|
169
|
+
register_child_for_parent( instance_C1, instance_B1 )
|
170
|
+
instance_C2 = ::Module.new do
|
171
|
+
include instance_B1
|
172
|
+
end
|
173
|
+
register_child_for_parent( instance_C2, instance_B1 )
|
174
|
+
instance_D = ::Module.new do
|
175
|
+
include instance_B2
|
176
|
+
include instance_C1
|
177
|
+
include instance_C2
|
178
|
+
end
|
179
|
+
instance_E = ::Class.new do
|
180
|
+
include instance_B2
|
181
|
+
include instance_C1
|
182
|
+
include instance_C2
|
183
|
+
include instance_D
|
184
|
+
end
|
185
|
+
register_child_for_parent( instance_D, instance_B2 )
|
186
|
+
register_child_for_parent( instance_D, instance_C1 )
|
187
|
+
register_child_for_parent( instance_D, instance_C2 )
|
188
|
+
register_child_for_parent( instance_E, instance_D )
|
189
|
+
register_child_for_parent( instance_E, instance_B1 )
|
190
|
+
match_proc = ::Proc.new do |this_parent|
|
191
|
+
this_parent.method_defined?( :some_configuration )
|
192
|
+
end
|
193
|
+
lowest_parents( instance_D, & match_proc ).should == [ instance_C2, instance_C1, instance_B2 ]
|
194
|
+
lowest_parents( instance_C2, & match_proc ).should == [ instance_B1 ]
|
195
|
+
lowest_parents( instance_C1, & match_proc ).should == [ instance_B1 ]
|
196
|
+
lowest_parents( instance_B1, & match_proc ).should == [ instance_A ]
|
197
|
+
lowest_parents( instance_B2, & match_proc ).should == [ instance_A ]
|
198
|
+
lowest_parents( instance_E, & match_proc ).should == [ instance_B1, instance_D ]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
######################
|
204
|
+
# highest_children #
|
205
|
+
######################
|
206
|
+
|
207
|
+
it 'can return the first ancestor on each child tree matching block condition' do
|
208
|
+
[ ::ParallelAncestry::Mock, ::ParallelAncestry::ModuleSubclassInstanceMock ].each do |this_test|
|
209
|
+
this_test.module_eval do
|
210
|
+
# set up hierarchy
|
211
|
+
instance_A = ::Module.new do
|
212
|
+
# we have to mock the method bc that's how the module determines if it has a configuration defined
|
213
|
+
# since we don't know whether a configuration value has been set yet
|
214
|
+
attr_accessor :some_configuration
|
215
|
+
end
|
216
|
+
instance_B1 = ::Module.new do
|
217
|
+
# we have to mock the method bc that's how the module determines if it has a configuration defined
|
218
|
+
# since we don't know whether a configuration value has been set yet
|
219
|
+
attr_accessor :some_other_configuration
|
220
|
+
extend instance_A
|
221
|
+
end
|
222
|
+
register_child_for_parent( instance_B1, instance_A )
|
223
|
+
instance_B2 = ::Module.new do
|
224
|
+
extend instance_A
|
225
|
+
end
|
226
|
+
register_child_for_parent( instance_B2, instance_A )
|
227
|
+
instance_C1 = ::Module.new do
|
228
|
+
extend instance_B1
|
229
|
+
extend instance_A
|
230
|
+
attr_accessor :yet_another_configuration
|
231
|
+
end
|
232
|
+
register_child_for_parent( instance_C1, instance_B1 )
|
233
|
+
instance_C2 = ::Module.new do
|
234
|
+
extend instance_B2
|
235
|
+
extend instance_A
|
236
|
+
end
|
237
|
+
register_child_for_parent( instance_C2, instance_B1 )
|
238
|
+
instance_D = ::Module.new do
|
239
|
+
extend instance_B2
|
240
|
+
extend instance_C1
|
241
|
+
extend instance_A
|
242
|
+
end
|
243
|
+
instance_E = ::Class.new do
|
244
|
+
extend instance_C2
|
245
|
+
end
|
246
|
+
register_child_for_parent( instance_D, instance_B2 )
|
247
|
+
register_child_for_parent( instance_D, instance_C1 )
|
248
|
+
register_child_for_parent( instance_D, instance_C2 )
|
249
|
+
register_child_for_parent( instance_E, instance_D )
|
250
|
+
register_child_for_parent( instance_E, instance_B1 )
|
251
|
+
match_proc = ::Proc.new do |this_parent|
|
252
|
+
this_parent.respond_to?( :some_configuration )
|
253
|
+
end
|
254
|
+
highest_children( instance_E, & match_proc ).empty?.should == true
|
255
|
+
highest_children( instance_D, & match_proc ).empty?.should == true
|
256
|
+
highest_children( instance_C2, & match_proc ).should == [ instance_D ]
|
257
|
+
highest_children( instance_C1, & match_proc ).should == [ instance_D ]
|
258
|
+
highest_children( instance_B1, & match_proc ).should == [ instance_C1, instance_C2 ]
|
259
|
+
highest_children( instance_B2, & match_proc ).should == [ instance_D ]
|
260
|
+
highest_children( instance_A, & match_proc ).should == [ instance_B1, instance_B2 ]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
#####################################
|
266
|
+
# match_ancestor_searching_upward #
|
267
|
+
#####################################
|
268
|
+
|
269
|
+
it 'can get the first defined configuration searching up the module configuration inheritance chain' do
|
270
|
+
[ ::ParallelAncestry::Mock, ::ParallelAncestry::ModuleSubclassInstanceMock ].each do |this_test|
|
271
|
+
this_test.module_eval do
|
272
|
+
# set up hierarchy
|
273
|
+
instance_A = ::Module.new do
|
274
|
+
# we have to mock the method bc that's how the module determines if it has a configuration defined
|
275
|
+
# since we don't know whether a configuration value has been set yet
|
276
|
+
class << self
|
277
|
+
attr_accessor :some_configuration
|
278
|
+
end
|
279
|
+
end
|
280
|
+
instance_B = ::Module.new do
|
281
|
+
# we have to mock the method bc that's how the module determines if it has a configuration defined
|
282
|
+
# since we don't know whether a configuration value has been set yet
|
283
|
+
include instance_A
|
284
|
+
class << self
|
285
|
+
attr_accessor :some_configuration
|
286
|
+
attr_accessor :some_other_configuration
|
287
|
+
end
|
288
|
+
end
|
289
|
+
register_child_for_parent( instance_B, instance_A )
|
290
|
+
instance_C1 = ::Module.new do
|
291
|
+
include instance_B
|
292
|
+
class << self
|
293
|
+
attr_accessor :some_configuration
|
294
|
+
attr_accessor :some_other_configuration
|
295
|
+
attr_accessor :yet_another_configuration
|
296
|
+
end
|
297
|
+
end
|
298
|
+
register_child_for_parent( instance_C1, instance_B )
|
299
|
+
instance_C2 = ::Module.new do
|
300
|
+
include instance_B
|
301
|
+
class << self
|
302
|
+
attr_accessor :some_configuration
|
303
|
+
attr_accessor :some_other_configuration
|
304
|
+
attr_accessor :yet_another_configuration
|
305
|
+
end
|
306
|
+
end
|
307
|
+
register_child_for_parent( instance_C2, instance_B )
|
308
|
+
instance_D = ::Module.new do
|
309
|
+
include instance_C1
|
310
|
+
include instance_C2
|
311
|
+
class << self
|
312
|
+
attr_accessor :some_configuration
|
313
|
+
attr_accessor :some_other_configuration
|
314
|
+
attr_accessor :yet_another_configuration
|
315
|
+
end
|
316
|
+
end
|
317
|
+
register_child_for_parent( instance_D, instance_C1 )
|
318
|
+
register_child_for_parent( instance_D, instance_C2 )
|
319
|
+
|
320
|
+
configuration_method = :some_configuration
|
321
|
+
|
322
|
+
# test ancestor hierarchy for each
|
323
|
+
match_proc = ::Proc.new do |this_parent|
|
324
|
+
this_parent.__send__( configuration_method )
|
325
|
+
end
|
326
|
+
|
327
|
+
match_ancestor_proc = ::Proc.new do |this_parent|
|
328
|
+
this_parent.respond_to?( configuration_method )
|
329
|
+
end
|
330
|
+
|
331
|
+
instance_A.some_configuration = :some_value
|
332
|
+
instance_B.some_other_configuration = :some_other_value
|
333
|
+
|
334
|
+
match_ancestor_searching_upward( instance_B, match_ancestor_proc, & match_proc ).should == instance_A
|
335
|
+
match_ancestor_searching_upward( instance_C1, match_ancestor_proc, & match_proc ).should == instance_A
|
336
|
+
|
337
|
+
configuration_method = :some_other_configuration
|
338
|
+
match_ancestor_searching_upward( instance_B, match_ancestor_proc, & match_proc ).should == instance_B
|
339
|
+
match_ancestor_searching_upward( instance_C1, match_ancestor_proc, & match_proc ).should == instance_B
|
340
|
+
|
341
|
+
instance_C2.yet_another_configuration = :another_value
|
342
|
+
|
343
|
+
configuration_method = :yet_another_configuration
|
344
|
+
match_ancestor_searching_upward( instance_C1, match_ancestor_proc, & match_proc ).should == nil
|
345
|
+
match_ancestor_searching_upward( instance_D, match_ancestor_proc, & match_proc ).should == instance_C2
|
346
|
+
|
347
|
+
configuration_method = :some_configuration
|
348
|
+
match_ancestor_searching_upward( instance_C2, match_ancestor_proc, & match_proc ).should == instance_A
|
349
|
+
match_ancestor_searching_upward( instance_D, match_ancestor_proc, & match_proc ).should == instance_A
|
350
|
+
|
351
|
+
configuration_method = :some_other_configuration
|
352
|
+
match_ancestor_searching_upward( instance_C2, match_ancestor_proc, & match_proc ).should == instance_B
|
353
|
+
match_ancestor_searching_upward( instance_D, match_ancestor_proc, & match_proc ).should == instance_B
|
354
|
+
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: parallel-ancestry
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Asher
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: module-cluster
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: Create and track parallel inheritance hierarchies. Hook parallel hierarchies
|
31
|
+
(by including a module) to automatically update/register ancestry at include and
|
32
|
+
extend, or update/register only manually. Manual registration permits definitions
|
33
|
+
of ancestry across, for example, instances of the same type or instances of entirely
|
34
|
+
different types. Implementation is provided by simple child/parent trees with an
|
35
|
+
block to choose which parent. This permits a simple multiple inheritance model very
|
36
|
+
similar to how Ruby handles modules. Conflicts for multiple inheritance are resolved
|
37
|
+
by the parent most recently named for the block match. Used heavily by CascadingConfiguration
|
38
|
+
gem (cascading-configuration) as well as by forthcoming Persistence and Magnets
|
39
|
+
gems (persistence and magnets).
|
40
|
+
email: asher@ridiculouspower.com
|
41
|
+
executables: []
|
42
|
+
extensions: []
|
43
|
+
extra_rdoc_files: []
|
44
|
+
files:
|
45
|
+
- lib/parallel-ancestry/ParallelAncestry/Inheritance/ModuleSubclassInheritance.rb
|
46
|
+
- lib/parallel-ancestry/ParallelAncestry/Inheritance.rb
|
47
|
+
- lib/parallel-ancestry/ParallelAncestry/ModuleSubclassInheritance.rb
|
48
|
+
- lib/parallel-ancestry/ParallelAncestry.rb
|
49
|
+
- lib/parallel-ancestry.rb
|
50
|
+
- spec/ParallelAncestry/Inheritance_spec.rb
|
51
|
+
- spec/ParallelAncestry_spec.rb
|
52
|
+
- README.md
|
53
|
+
- README.rdoc
|
54
|
+
- CHANGELOG.rdoc
|
55
|
+
homepage: http://rubygems.org/gems/parallel-ancestry
|
56
|
+
licenses: []
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ! '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project: parallel-ancestry
|
75
|
+
rubygems_version: 1.8.23
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: Provides parallel implementations of inheritance hierarchies. This is useful
|
79
|
+
both for tracking the existing inheritance tree and for creating trees that function
|
80
|
+
independently of inheritance models determined internal to Ruby.
|
81
|
+
test_files: []
|