compo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|