compo 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/CHANGELOG +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +7 -0
- data/compo.gemspec +33 -0
- data/lib/compo/array_composite.rb +103 -0
- data/lib/compo/composite.rb +130 -0
- data/lib/compo/hash_composite.rb +70 -0
- data/lib/compo/leaf.rb +22 -0
- data/lib/compo/movable.rb +53 -0
- data/lib/compo/null_composite.rb +63 -0
- data/lib/compo/parent_tracker.rb +64 -0
- data/lib/compo/version.rb +4 -0
- data/lib/compo.rb +12 -0
- data/spec/array_composite_spec.rb +225 -0
- data/spec/composite_spec.rb +249 -0
- data/spec/hash_composite_spec.rb +184 -0
- data/spec/leaf_spec.rb +14 -0
- data/spec/movable_spec.rb +201 -0
- data/spec/null_composite_spec.rb +67 -0
- data/spec/parent_tracker_spec.rb +63 -0
- data/spec/spec_helper.rb +11 -0
- metadata +192 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 13ca1304045a3fb0ac403495a4c838b53aeecb65
|
4
|
+
data.tar.gz: 06039743fc1c5f084a2906d548ecf322b8a12aaf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6de424684aa210f5f48c2d422dee5e44310ccacf6f2623d180c0f2934fa55ee7191b7a479b56badd9220ef1c0ee2dcb907d3df69e3f5063ea4079e2e8448a823
|
7
|
+
data.tar.gz: 6fd54c3de144b876874596fcc446e9b6a4f922f0c89fbaf4d2f6f423bc98d7f3fc10c58bc4d01fcc6d2a2d9d3a7c4beb1d7936684780965fe0ae6b9b389ea167
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Matt Windsor
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Compo
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'compo'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install compo
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/compo.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'compo/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'compo'
|
8
|
+
spec.version = Compo::VERSION
|
9
|
+
spec.authors = ['Matt Windsor']
|
10
|
+
spec.email = ['matt.windsor@ury.org.uk']
|
11
|
+
spec.description = %q{
|
12
|
+
Compo provides mixins and classes that assist in implementing a variant of
|
13
|
+
the Composite design pattern, in which each child has an ID that uniquely
|
14
|
+
identifies it inside the parent's child set.
|
15
|
+
}
|
16
|
+
spec.summary = 'Composite pattern style mixins with IDs'
|
17
|
+
spec.homepage = 'http://github.com/CaptainHayashi/compo'
|
18
|
+
spec.license = 'MIT'
|
19
|
+
|
20
|
+
spec.files = `git ls-files`.split($/)
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_development_dependency 'backports'
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
27
|
+
spec.add_development_dependency 'fuubar'
|
28
|
+
spec.add_development_dependency 'rake'
|
29
|
+
spec.add_development_dependency 'rspec'
|
30
|
+
spec.add_development_dependency 'simplecov'
|
31
|
+
spec.add_development_dependency 'yard'
|
32
|
+
spec.add_development_dependency 'yardstick'
|
33
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'compo/composite'
|
3
|
+
|
4
|
+
module Compo
|
5
|
+
# Implementation of Composite that stores its children in an Array.
|
6
|
+
#
|
7
|
+
# IDs for items entering a ListComposite must be numeric, and will change if
|
8
|
+
# an item with an ID less than the item in question is deleted or inserted.
|
9
|
+
# This means the ID function for objects in a ListComposite may report
|
10
|
+
# different values at different times.
|
11
|
+
#
|
12
|
+
# Adding an object at an occupied ID moves the occupant and those at
|
13
|
+
# successive IDs up by one.
|
14
|
+
class ArrayComposite
|
15
|
+
include Composite
|
16
|
+
extend Forwardable
|
17
|
+
|
18
|
+
# Initialises an ArrayComposite
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
# @example Initializes an ArrayComposite.
|
22
|
+
# comp = ArrayComposite.new
|
23
|
+
def initialize
|
24
|
+
@children = []
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the ArrayComposite's children, as a Hash
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
# @example Gets the children of an empty ArrayComposite.
|
31
|
+
# comp.children
|
32
|
+
# #=> {}
|
33
|
+
# @example Gets the children of a populated ArrayComposite.
|
34
|
+
# comp.children
|
35
|
+
# #=> {0: :first, 1: :second}
|
36
|
+
#
|
37
|
+
# @return [Hash] The Hash mapping the IDs of children to their values.
|
38
|
+
def children
|
39
|
+
Hash[(0...@children.size).zip(@children)]
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def_delegator :@children, :delete, :remove!
|
45
|
+
def_delegator :@children, :delete_at, :remove_id!
|
46
|
+
|
47
|
+
# Inserts a child into the ArrayComposite with the given ID
|
48
|
+
#
|
49
|
+
# You probably want to use #add instead.
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
#
|
53
|
+
# @param id [Object] The ID under which the child is to be added.
|
54
|
+
# @param child [Object] The child to add to the ArrayComposite.
|
55
|
+
#
|
56
|
+
# @return [Object] The newly added child, or nil if the ID was invalid.
|
57
|
+
def add!(id, child)
|
58
|
+
valid_id?(id) ? do_insert(id, child) : nil
|
59
|
+
end
|
60
|
+
|
61
|
+
# Checks to see if the given ID is valid
|
62
|
+
#
|
63
|
+
# A valid ID for ArrayComposites is a number between 0 and the current
|
64
|
+
# size of the children list.
|
65
|
+
#
|
66
|
+
# @api private
|
67
|
+
#
|
68
|
+
# @param id [Object] The candidate ID.
|
69
|
+
#
|
70
|
+
# @return [Boolean] True if the ID is valid; false if not.
|
71
|
+
def valid_id?(id)
|
72
|
+
id.is_a?(Numeric) && (0..@children.size).cover?(id)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Actually performs the insertion of an item into the array
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
#
|
79
|
+
# @param id [Numeric] The index into the array at which the child is to be
|
80
|
+
# inserted.
|
81
|
+
# @param child [Object] The object to insert into the children array.
|
82
|
+
#
|
83
|
+
# @return [Object] The inserted child.
|
84
|
+
def do_insert(id, child)
|
85
|
+
@children.insert(id, child)
|
86
|
+
child
|
87
|
+
end
|
88
|
+
|
89
|
+
# Creates an ID function for the given child
|
90
|
+
#
|
91
|
+
# The returned proc is O(n), as it checks the child array at each call to
|
92
|
+
# find the current ID of the child.
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
#
|
96
|
+
# @param child [Object] The child whose ID is to be returned by the proc.
|
97
|
+
#
|
98
|
+
# @return [Proc] A proc returning the child's ID.
|
99
|
+
def id_function(object)
|
100
|
+
proc { @children.index(object) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Compo
|
2
|
+
# Mixin for objects that can contain other objects
|
3
|
+
#
|
4
|
+
# Objects implementing this interface should implement add!, remove! or
|
5
|
+
# remove_id!, and id_function:
|
6
|
+
#
|
7
|
+
# add! - Given a desired ID and child, adds the child to the children
|
8
|
+
# of this object; returns the child if successful, nil
|
9
|
+
# otherwise.
|
10
|
+
# remove! - Given a child, removes and returns it from the children; if
|
11
|
+
# not provided, this is implemented in terms of remove_id!.
|
12
|
+
# remove_id! - Given an ID, removes and returns the child with this ID from
|
13
|
+
# the children; if not provided, this is implemented in terms
|
14
|
+
# of remove!.
|
15
|
+
# children - Returns the children, as a Hash mapping from current IDs to
|
16
|
+
# their child values.
|
17
|
+
# id_function - Given a newly inserted child, returns a proc that will
|
18
|
+
# always return the child's current ID so long as it is part
|
19
|
+
# of the Composite.
|
20
|
+
module Composite
|
21
|
+
extend Forwardable
|
22
|
+
include Enumerable
|
23
|
+
|
24
|
+
# Adds a child to this Composite
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
# @example Adds a child with intended id 3.
|
28
|
+
# composite.add_child(3, leaf)
|
29
|
+
#
|
30
|
+
# @param id [Object] The intended ID of the child in this Composite.
|
31
|
+
# The actual ID may not be the same as this; consult the proc supplied
|
32
|
+
# to the child via #update_parent.
|
33
|
+
# @param child [Object] The child to add to this Composite.
|
34
|
+
#
|
35
|
+
# @return [Object] The added child if successful; nil otherwise.
|
36
|
+
def add(id, child)
|
37
|
+
add!(id, child).tap(&method(:assign_parent))
|
38
|
+
end
|
39
|
+
|
40
|
+
# Removes a child from this Composite directly
|
41
|
+
#
|
42
|
+
# This method can fail (for example, if the child does not exist in the
|
43
|
+
# Composite).
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
# @example Removes a child.
|
47
|
+
# composite.remove(child)
|
48
|
+
#
|
49
|
+
# @param child [Object] The child to remove from this object.
|
50
|
+
#
|
51
|
+
# @return [Object] The removed child if successful; nil otherwise.
|
52
|
+
def remove(child)
|
53
|
+
remove!(child).tap(&method(:remove_parent))
|
54
|
+
end
|
55
|
+
|
56
|
+
# Removes a child from this Composite, given its ID
|
57
|
+
#
|
58
|
+
# This method can fail (for example, if the ID does not exist in the
|
59
|
+
# Composite).
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
# @example Removes the child with ID :foo.
|
63
|
+
# composite.remove_id(:foo)
|
64
|
+
#
|
65
|
+
# @param id The ID of the child to remove from this object.
|
66
|
+
#
|
67
|
+
# @return [Object] The removed child if successful; nil otherwise.
|
68
|
+
def remove_id(id)
|
69
|
+
remove_id!(id).tap(&method(:remove_parent))
|
70
|
+
end
|
71
|
+
|
72
|
+
def_delegator :children, :each
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
# Assigns this object to a child as its parent
|
77
|
+
#
|
78
|
+
# This also updates its ID function to point to the child's ID under this
|
79
|
+
# parent.
|
80
|
+
#
|
81
|
+
# @api private
|
82
|
+
#
|
83
|
+
# @param child [Object] The child whose parent assignment is being set.
|
84
|
+
#
|
85
|
+
# @return [void]
|
86
|
+
def assign_parent(child)
|
87
|
+
child.update_parent(self, id_function(child)) unless child.nil?
|
88
|
+
end
|
89
|
+
|
90
|
+
# Removes a child's parent assignment
|
91
|
+
#
|
92
|
+
# This also clears its ID function.
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
#
|
96
|
+
# @param child [Object] The child whose parent assignment is being set.
|
97
|
+
#
|
98
|
+
# @return [void]
|
99
|
+
def remove_parent(child)
|
100
|
+
child.remove_parent unless child.nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
# Default implementation of #remove! in terms of #remove_id!
|
104
|
+
#
|
105
|
+
# Either this or #remove_id! must be overridden by the implementing class.
|
106
|
+
#
|
107
|
+
# @api private
|
108
|
+
#
|
109
|
+
# @param child [Object] The child to remove from this object.
|
110
|
+
#
|
111
|
+
# @return [void]
|
112
|
+
def remove!(child)
|
113
|
+
remove_id!(children.key(child))
|
114
|
+
end
|
115
|
+
|
116
|
+
# Default implementation of #remove_id! in terms of #remove!
|
117
|
+
#
|
118
|
+
# Either this or #remove! must be overridden by the implementing class.
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
#
|
122
|
+
# @param id [Object] The current ID of the child to remove from this
|
123
|
+
# object.
|
124
|
+
#
|
125
|
+
# @return [void]
|
126
|
+
def remove_id!(id)
|
127
|
+
remove!(get_child(id))
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'compo/composite'
|
3
|
+
|
4
|
+
module Compo
|
5
|
+
# Implementation of Composite that stores its children in a Hash.
|
6
|
+
#
|
7
|
+
# IDs for items entering a ListComposite may be any permissible hash.
|
8
|
+
#
|
9
|
+
# Adding an item at an occupied ID removes the occupant.
|
10
|
+
class HashComposite
|
11
|
+
include Composite
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
# Initialises a HashComposite
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
# @example Initializes a HashComposite.
|
18
|
+
# comp = HashComposite.new
|
19
|
+
def initialize
|
20
|
+
@children = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the HashComposite's children, as a Hash
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
# @example Gets the children of an empty HashComposite.
|
27
|
+
# comp.children
|
28
|
+
# #=> {}
|
29
|
+
# @example Gets the children of a populated HashComposite.
|
30
|
+
# comp.children
|
31
|
+
# #=> {foo: 3, bar: 5}
|
32
|
+
#
|
33
|
+
# @return [Hash] The Hash mapping the IDs of children to their values.
|
34
|
+
attr_reader :children
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Inserts a child into the HashComposite with the given ID
|
39
|
+
#
|
40
|
+
# You probably want to use #add instead.
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
#
|
44
|
+
# @param id [Object] The ID under which the child is to be added.
|
45
|
+
# @param child [Object] The child to add to the HashComposite.
|
46
|
+
#
|
47
|
+
# @return [Object] The newly added child.
|
48
|
+
def add!(id, child)
|
49
|
+
remove_id(id)
|
50
|
+
@children[id] = child
|
51
|
+
end
|
52
|
+
|
53
|
+
def_delegator :@children, :delete, :remove_id!
|
54
|
+
|
55
|
+
# Creates an ID function for the given child
|
56
|
+
#
|
57
|
+
# The returned proc is O(1), as it stores the ID assigned to the child at
|
58
|
+
# calling time under the assumption that it will not change until removal.
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
#
|
62
|
+
# @param child [Object] The child whose ID is to be returned by the proc.
|
63
|
+
#
|
64
|
+
# @return [Proc] A proc returning the child's ID.
|
65
|
+
def id_function(child)
|
66
|
+
id = @children.key(child)
|
67
|
+
proc { id }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/compo/leaf.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'compo/movable'
|
2
|
+
require 'compo/null_composite'
|
3
|
+
require 'compo/parent_tracker'
|
4
|
+
|
5
|
+
module Compo
|
6
|
+
# A simple implementation of a leaf
|
7
|
+
#
|
8
|
+
# Leaves have no children, but can be moved to one.
|
9
|
+
class Leaf < NullComposite
|
10
|
+
include Movable
|
11
|
+
include ParentTracker
|
12
|
+
|
13
|
+
# Initialises the Leaf
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
# @example Creates a new Leaf.
|
17
|
+
# leaf.new
|
18
|
+
def initialize
|
19
|
+
remove_parent
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Compo
|
2
|
+
# Helper mixin for objects that can be moved into other objects
|
3
|
+
#
|
4
|
+
# This mixin defines a method, #move_to, which handles removing a child
|
5
|
+
# from its current parent and adding it to a new one.
|
6
|
+
#
|
7
|
+
# It expects the current parent to be reachable from #parent.
|
8
|
+
module Movable
|
9
|
+
# Moves this model object to a new parent with a new ID
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
# @example Moves the object to a new parent.
|
13
|
+
# movable.move_to(parent, :id)
|
14
|
+
# @example Moves the object out of its parent (deleting it, if there are
|
15
|
+
# no other live references).
|
16
|
+
# movable.move_to(nil, nil)
|
17
|
+
#
|
18
|
+
# @param new_parent [ModelObject] The new parent for this object (can be
|
19
|
+
# nil).
|
20
|
+
# @param new_id [Object] The new ID under which the object will exist in
|
21
|
+
# the parent.
|
22
|
+
#
|
23
|
+
# @return [self]
|
24
|
+
def move_to(new_parent, new_id)
|
25
|
+
move_from_old_parent
|
26
|
+
move_to_new_parent(new_parent, new_id)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Performs the move from an old parent, if necessary
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
# @return [void]
|
37
|
+
def move_from_old_parent
|
38
|
+
parent.remove(self) unless parent.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
# Performs the move to a new parent, if necessary
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
#
|
45
|
+
# @param new_parent [Composite] The target parent of the move.
|
46
|
+
# @param new_id [Object] The intended new ID of this child.
|
47
|
+
#
|
48
|
+
# @return [void]
|
49
|
+
def move_to_new_parent(new_parent, new_id)
|
50
|
+
new_parent.add(new_id, self) unless new_parent.nil?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'compo/composite'
|
3
|
+
|
4
|
+
module Compo
|
5
|
+
# Null implementation of Composite
|
6
|
+
#
|
7
|
+
# Add/remove operations on NullComposite always fail, and #children always
|
8
|
+
# returns the empty hash.
|
9
|
+
#
|
10
|
+
# This is useful for leaf classes that still need to expose the composite
|
11
|
+
# API.
|
12
|
+
class NullComposite
|
13
|
+
include Composite
|
14
|
+
|
15
|
+
# Returns the empty hash
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
# @example Gets the children
|
19
|
+
# comp.children
|
20
|
+
# #=> {}
|
21
|
+
#
|
22
|
+
# @return [Hash] The empty hash.
|
23
|
+
def children
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Fails to add a child into the NullComposite
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
#
|
33
|
+
# @param id [Object] Ignored.
|
34
|
+
# @param child [Object] Ignored.
|
35
|
+
#
|
36
|
+
# @return [nil]
|
37
|
+
def add!(_, _)
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Fails to remove the given child
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
#
|
45
|
+
# @param child [Object] Ignored.
|
46
|
+
#
|
47
|
+
# @return [nil]
|
48
|
+
def remove!(_)
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Fails to remove the child with the given ID
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
#
|
56
|
+
# @param id [Object] Ignored.
|
57
|
+
#
|
58
|
+
# @return [nil]
|
59
|
+
def remove_id!(_)
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Compo
|
4
|
+
# Basic implementation of parent tracking as a mixin
|
5
|
+
#
|
6
|
+
# This implements #parent, #update_parent and #remove_parent to track the
|
7
|
+
# current parent and ID function as instance variables. It also implements
|
8
|
+
# #parent, and #id in terms of the ID function.
|
9
|
+
#
|
10
|
+
# Subclasses should call #remove_parent in their #initialize methods, to
|
11
|
+
# set the parent and ID function to their default, empty values.
|
12
|
+
module ParentTracker
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
# Gets this object's current ID
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
# @example Gets the object's parent while it has none.
|
19
|
+
# parent_tracker.parent
|
20
|
+
# #=> nil
|
21
|
+
# @example Gets the object's parent while it has one.
|
22
|
+
# parent_tracker.parent
|
23
|
+
# #=> :the_current_parent
|
24
|
+
#
|
25
|
+
# @return [Composite] The current parent.
|
26
|
+
attr_reader :parent
|
27
|
+
|
28
|
+
# Gets this object's current ID
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
# @example Gets the object's ID while it has no parent.
|
32
|
+
# parent_tracker.id
|
33
|
+
# #=> nil
|
34
|
+
# @example Gets the object's ID while it has a parent.
|
35
|
+
# parent_tracker.id
|
36
|
+
# #=> :the_current_id
|
37
|
+
#
|
38
|
+
# @return [Object] The current ID.
|
39
|
+
def_delegator :@id_function, :call, :id
|
40
|
+
|
41
|
+
# Updates this object's parent and ID function
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
# @example Update this Leaf's parent and ID function.
|
45
|
+
# parent_tracker.update_parent(new_parent, new_id_function)
|
46
|
+
#
|
47
|
+
# @return [void]
|
48
|
+
def update_parent(new_parent, new_id_function)
|
49
|
+
@parent = new_parent
|
50
|
+
@id_function = new_id_function
|
51
|
+
end
|
52
|
+
|
53
|
+
# Blanks out this object's parent and ID function
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
# @example Update this Leaf's parent and ID function.
|
57
|
+
# movable.update_parent(new_parent, new_id_function)
|
58
|
+
#
|
59
|
+
# @return [void]
|
60
|
+
def remove_parent
|
61
|
+
update_parent(nil, ->{ nil })
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/compo.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'compo/array_composite'
|
2
|
+
require 'compo/composite'
|
3
|
+
require 'compo/hash_composite'
|
4
|
+
require 'compo/leaf'
|
5
|
+
require 'compo/movable'
|
6
|
+
require 'compo/null_composite'
|
7
|
+
require 'compo/parent_tracker'
|
8
|
+
require 'compo/version'
|
9
|
+
|
10
|
+
# The main module for Compo.
|
11
|
+
module Compo
|
12
|
+
end
|