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
data/CHANGELOG.rdoc
ADDED
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_searching_upward( 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_searching_upward( 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 ModuleCluster::Define::Block::ClassOrModule
|
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
|
+
module_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
|
+
class_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_inheriting_instance = { }
|
39
|
+
|
40
|
+
extend( ::ModuleCluster::Define::Block::ClassOrModuleOrInstance )
|
41
|
+
|
42
|
+
# When inheritance module is included in another module:
|
43
|
+
prepend_class_or_module_include do |base_instance|
|
44
|
+
initialize_base_instance_for_include( self, base_instance )
|
45
|
+
end
|
46
|
+
|
47
|
+
# When inheritance module is used to extend another module:
|
48
|
+
prepend_class_or_module_or_instance_extend do |base_instance|
|
49
|
+
initialize_base_instance_for_extend( self, 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( inheritance_module, 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( inheritance_module, 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_inheriting_instance[ instance ]
|
94
|
+
|
95
|
+
inheritance_module = self
|
96
|
+
|
97
|
+
if instance.is_a?( ::Class )
|
98
|
+
|
99
|
+
instance.extend( ::ModuleCluster::Define::Block::Subclass )
|
100
|
+
|
101
|
+
instance.subclass do |inheriting_subclass|
|
102
|
+
inheritance_module.initialize_inheriting_instance( instance, inheriting_subclass, true )
|
103
|
+
end
|
104
|
+
|
105
|
+
else
|
106
|
+
|
107
|
+
instance.extend( ::ModuleCluster::Define::Block::ClassOrModuleOrInstance )
|
108
|
+
|
109
|
+
instance.class_or_module_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.class_or_module_or_instance_extend do |inheriting_module|
|
115
|
+
inheritance_module.initialize_inheriting_instance( instance, inheriting_module )
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
@initialized_inheriting_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 )
|
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
|