parallel_ancestry 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +32 -0
- data/README.md +137 -0
- data/README.rdoc +125 -0
- data/lib/parallel_ancestry/parallel_ancestry/inheritance/module_subclass_inheritance.rb +18 -0
- data/lib/parallel_ancestry/parallel_ancestry/inheritance.rb +138 -0
- data/lib/parallel_ancestry/parallel_ancestry/module_subclass_inheritance.rb +18 -0
- data/lib/parallel_ancestry/parallel_ancestry.rb +355 -0
- data/lib/parallel_ancestry.rb +24 -0
- data/spec/parallel_ancestry/inheritance_spec.rb +262 -0
- data/spec/parallel_ancestry_spec.rb +359 -0
- metadata +98 -0
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
== 6/10/2012
|
3
|
+
|
4
|
+
Initial release abstracted to be entirely independent from cascading-configuration.
|
5
|
+
Replaces cascading-configuration-inheritance and cascading-configuration-ancestors without any cascading-configuration dependencies.
|
6
|
+
|
7
|
+
== 6/11/2012
|
8
|
+
|
9
|
+
Added YARD docs.
|
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
|
+
|
12
|
+
== 6/12/2012
|
13
|
+
|
14
|
+
Renamed :match_ancestor_searching_upward to :match_ancestor because ancestors are always found by searching upward.
|
15
|
+
Fixes for ancestor lookup when arriving at Class.
|
16
|
+
|
17
|
+
== 6/15/2012
|
18
|
+
|
19
|
+
Added unique-array for children and parents, which means now :include? etc. use hash lookup internally.
|
20
|
+
|
21
|
+
== 6/18/2012
|
22
|
+
|
23
|
+
Added is_extending parameter (default = false) to :initialize_inheriting_instance.
|
24
|
+
Fix for subclass inheritance passing subclass instance (self) rather than class instance.
|
25
|
+
|
26
|
+
== 6/19/2012
|
27
|
+
|
28
|
+
Changed include/extend hooks to prepend.
|
29
|
+
|
30
|
+
== 7/06/2012
|
31
|
+
|
32
|
+
Renamed to parallel_ancestry.
|
data/README.md
ADDED
@@ -0,0 +1,137 @@
|
|
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
|
+
```ruby
|
58
|
+
match_block = ::Proc.new do |this_ancestor|
|
59
|
+
|
60
|
+
if this_ancestor.matches_arbitrary_condition
|
61
|
+
true
|
62
|
+
else
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
## ::ParallelAncestry::Inheritance ##
|
70
|
+
|
71
|
+
::ParallelAncestry::Inheritance provides hooks for Ruby inheritance events.
|
72
|
+
|
73
|
+
2 ways to use:
|
74
|
+
|
75
|
+
1. Extend any module with ::ParallelAncestry::Inheritance to enable hooks in module singleton.
|
76
|
+
2. Include ::ParallelAncestry::Inheritance in subclass of ::Module to enable instance of ::Module subclass wit hooks.
|
77
|
+
|
78
|
+
Hooks provided:
|
79
|
+
|
80
|
+
* Initialization
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
def initialize_inheritance!
|
84
|
+
... [ your hook ] ...
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
* First include:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
def initialize_base_instance_for_include
|
92
|
+
... [ your hook ] ...
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
|
97
|
+
* First extend:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
def initialize_base_instance_for_extend
|
101
|
+
... [ your hook ] ...
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
* Subsequent includes:
|
106
|
+
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
def initialize_inheriting_instance
|
110
|
+
... [ your hook ] ...
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
# License #
|
115
|
+
|
116
|
+
(The MIT License)
|
117
|
+
|
118
|
+
Copyright (c) Asher
|
119
|
+
|
120
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
121
|
+
a copy of this software and associated documentation files (the
|
122
|
+
'Software'), to deal in the Software without restriction, including
|
123
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
124
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
125
|
+
permit persons to whom the Software is furnished to do so, subject to
|
126
|
+
the following conditions:
|
127
|
+
|
128
|
+
The above copyright notice and this permission notice shall be
|
129
|
+
included in all copies or substantial portions of the Software.
|
130
|
+
|
131
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
132
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
133
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
134
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
135
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
136
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
137
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,125 @@
|
|
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.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module ::ParallelAncestry::Inheritance::ModuleSubclassInheritance
|
3
|
+
|
4
|
+
################
|
5
|
+
# initialize #
|
6
|
+
################
|
7
|
+
|
8
|
+
# define with *args so we can be inserted anywhere in inheritance chain
|
9
|
+
def initialize( *args )
|
10
|
+
|
11
|
+
# call super if defined so we can be inserted anywhere in inheritance chain
|
12
|
+
super if defined?( super )
|
13
|
+
|
14
|
+
initialize_inheritance_for_module!
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
|
2
|
+
module ::ParallelAncestry::Inheritance
|
3
|
+
|
4
|
+
# Provides hooks for inheritance events:
|
5
|
+
# * Initialization: :initialize_inheritance!
|
6
|
+
# * First include: :initialize_base_instance_for_include
|
7
|
+
# * First extend: :initialize_base_instance_for_extend
|
8
|
+
# * Subsequent includes: :initialize_inheriting_instance
|
9
|
+
|
10
|
+
extend ::Module::Cluster
|
11
|
+
|
12
|
+
##################################################################################################
|
13
|
+
################################## Inheritance Initialization ##################################
|
14
|
+
##################################################################################################
|
15
|
+
|
16
|
+
# We want to enable any module extended with self or any subclass of Module including self
|
17
|
+
|
18
|
+
# Initialize any module extended with self for inheritance hooks
|
19
|
+
cluster( :parallel_ancestry ).after_extend do |inheritance_module|
|
20
|
+
inheritance_module.initialize_inheritance_for_module!
|
21
|
+
end
|
22
|
+
|
23
|
+
# Initialize any subclass of module including self for inheritance hooks
|
24
|
+
cluster( :parallel_ancestry ).after_include do |class_instance|
|
25
|
+
if class_instance < ::Module
|
26
|
+
class_instance.module_eval do
|
27
|
+
include( ::ParallelAncestry::Inheritance::ModuleSubclassInheritance )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
########################################
|
33
|
+
# initialize_inheritance_for_module! #
|
34
|
+
########################################
|
35
|
+
|
36
|
+
def initialize_inheritance_for_module!
|
37
|
+
|
38
|
+
@initialized_inheritance_for_instance = { }
|
39
|
+
|
40
|
+
extend ::Module::Cluster
|
41
|
+
|
42
|
+
# When inheritance module is included in another module:
|
43
|
+
cluster( :parallel_ancestry ).before_include do |base_instance|
|
44
|
+
initialize_base_instance_for_include( base_instance )
|
45
|
+
end
|
46
|
+
|
47
|
+
# When inheritance module is used to extend another module:
|
48
|
+
cluster( :parallel_ancestry ).before_extend do |base_instance|
|
49
|
+
initialize_base_instance_for_extend( base_instance )
|
50
|
+
end
|
51
|
+
|
52
|
+
return self
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
##################################################################################################
|
57
|
+
############################## Base Configuration Initialization ###############################
|
58
|
+
##################################################################################################
|
59
|
+
|
60
|
+
##########################################
|
61
|
+
# initialize_base_instance_for_include #
|
62
|
+
##########################################
|
63
|
+
|
64
|
+
def initialize_base_instance_for_include( inheriting_instance )
|
65
|
+
|
66
|
+
# Initialize for future inheriting instances.
|
67
|
+
return initialize_inheritance( inheriting_instance )
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
#########################################
|
72
|
+
# initialize_base_instance_for_extend #
|
73
|
+
#########################################
|
74
|
+
|
75
|
+
def initialize_base_instance_for_extend( inheriting_instance )
|
76
|
+
|
77
|
+
# Hook for extended module to redefine - nothing to do.
|
78
|
+
|
79
|
+
return inheriting_instance
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
##################################################################################################
|
84
|
+
########################### Inheriting Configuration Initialization ############################
|
85
|
+
##################################################################################################
|
86
|
+
|
87
|
+
############################
|
88
|
+
# initialize_inheritance #
|
89
|
+
############################
|
90
|
+
|
91
|
+
def initialize_inheritance( instance )
|
92
|
+
|
93
|
+
unless @initialized_inheritance_for_instance[ instance ]
|
94
|
+
|
95
|
+
inheritance_module = self
|
96
|
+
|
97
|
+
if instance.is_a?( ::Class )
|
98
|
+
|
99
|
+
instance.extend( ::Module::Cluster )
|
100
|
+
|
101
|
+
instance.cluster( :parallel_ancestry ).subclass do |inheriting_subclass|
|
102
|
+
inheritance_module.initialize_inheriting_instance( self, inheriting_subclass, true )
|
103
|
+
end
|
104
|
+
|
105
|
+
else
|
106
|
+
|
107
|
+
instance.extend( ::Module::Cluster )
|
108
|
+
|
109
|
+
instance.cluster( :parallel_ancestry ).before_include do |inheriting_module|
|
110
|
+
inheritance_module.initialize_inheriting_instance( instance, inheriting_module )
|
111
|
+
inheritance_module.initialize_inheritance( inheriting_module )
|
112
|
+
end
|
113
|
+
|
114
|
+
instance.cluster( :parallel_ancestry ).before_extend do |inheriting_module|
|
115
|
+
inheritance_module.initialize_inheriting_instance( instance, inheriting_module, false, true )
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
@initialized_inheritance_for_instance[ instance ] = true
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
####################################
|
127
|
+
# initialize_inheriting_instance #
|
128
|
+
####################################
|
129
|
+
|
130
|
+
def initialize_inheriting_instance( parent_instance, inheriting_instance, for_subclass = false, is_extending = false )
|
131
|
+
|
132
|
+
# Hook for extended module to redefine - nothing to do.
|
133
|
+
|
134
|
+
return inheriting_instance
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module ::ParallelAncestry::ModuleSubclassInheritance
|
3
|
+
|
4
|
+
################
|
5
|
+
# initialize #
|
6
|
+
################
|
7
|
+
|
8
|
+
# define with *args so we can be inserted anywhere in inheritance chain
|
9
|
+
def initialize( *args )
|
10
|
+
|
11
|
+
# call super if defined so we can be inserted anywhere in inheritance chain
|
12
|
+
super if defined?( super )
|
13
|
+
|
14
|
+
@ancestors_hash = { }
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|