compo 0.4.0 → 0.5.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 +4 -4
- data/.rspec +0 -1
- data/.travis.yml +3 -0
- data/CHANGELOG +11 -0
- data/README.md +3 -0
- data/compo.gemspec +3 -4
- data/lib/compo/composites/composite.rb +16 -0
- data/lib/compo/composites/parentless.rb +14 -15
- data/lib/compo/finders/root.rb +77 -0
- data/lib/compo/finders.rb +1 -0
- data/lib/compo/mixins/parent_tracker.rb +15 -0
- data/lib/compo/mixins/url_referenceable.rb +8 -23
- data/lib/compo/version.rb +1 -1
- data/spec/array_branch_spec.rb +1 -1
- data/spec/array_composite_shared_examples.rb +108 -103
- data/spec/array_composite_spec.rb +1 -1
- data/spec/branch_shared_examples.rb +9 -6
- data/spec/branch_spec.rb +1 -1
- data/spec/composite_shared_examples.rb +64 -47
- data/spec/composite_spec.rb +1 -1
- data/spec/constant_branch_spec.rb +1 -1
- data/spec/hash_branch_spec.rb +1 -1
- data/spec/hash_composite_shared_examples.rb +28 -31
- data/spec/hash_composite_spec.rb +1 -1
- data/spec/leaf_branch_spec.rb +1 -1
- data/spec/leaf_composite_shared_examples.rb +14 -28
- data/spec/leaf_composite_spec.rb +1 -1
- data/spec/movable_shared_examples.rb +4 -4
- data/spec/movable_spec.rb +1 -1
- data/spec/parent_tracker_spec.rb +1 -1
- data/spec/parentless_spec.rb +21 -11
- data/spec/root_finder_spec.rb +66 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/url_finder_shared_examples.rb +1 -1
- data/spec/url_finder_spec.rb +1 -1
- data/spec/url_referenceable_shared_examples.rb +16 -14
- data/spec/url_referenceable_spec.rb +1 -1
- metadata +14 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b36ac99cbd26e363126ccaa0e1a2dd20ba9d4b10
|
4
|
+
data.tar.gz: 3a026f05f3e59917727599e3cc83b2a7888e9bb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 457dec32c46dabfa7e03b79545930df61cd9b9777dab0f8d8c436ec4dc425e091ab04b85af9810302fe9725809edab277c4f90a032676acc30fd3711d8c9032f
|
7
|
+
data.tar.gz: 18fd9981c0e31255bce927311b4c624ec2c08adbc27c9de043353228267230e3f896cfd6ac068ff28b2859dba8ca56a1869847cc57b8d65996aada33bed5af01
|
data/.rspec
CHANGED
data/.travis.yml
ADDED
data/CHANGELOG
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
0.5.0 (2014-07-10) ‘Brimble’
|
2
|
+
- Implement a root finder, which has the ability to enumerate the entire path
|
3
|
+
from a parent-tracking composite object up to its root.
|
4
|
+
- Implement new method #on_node, which executes a block if, and only if, the
|
5
|
+
object is an actual composite node (not a Parentless, etc.).
|
6
|
+
- Implement new method #root?, which returns true if, and only if, the
|
7
|
+
object does not have a valid parent (its parent ignores #on_node).
|
8
|
+
- Refactor UrlReferenceable such that URLs are calculated via the root finder.
|
9
|
+
This is now an iterative algorithm and shouldn't exhaust the stack on large
|
10
|
+
composites.
|
11
|
+
- (BACKWARDS INCOMPATIBILITY) Remove #child_url, as it is no longer needed.
|
1
12
|
0.4.0 (2014-05-22) ‘De Vorzon’
|
2
13
|
Big release with quite a few changes:
|
3
14
|
- (BACKWARDS INCOMPATIBILITY) Reorganise classes into submodules. This has
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Compo
|
2
2
|
|
3
|
+
[](http://badge.fury.io/rb/compo)
|
4
|
+
[](https://travis-ci.org/CaptainHayashi/compo)
|
5
|
+
|
3
6
|
**Compo** is a library providing mixins and base classes for setting up
|
4
7
|
composite objects.
|
5
8
|
|
data/compo.gemspec
CHANGED
@@ -9,11 +9,11 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.version = Compo::VERSION
|
10
10
|
spec.authors = ['Matt Windsor']
|
11
11
|
spec.email = ['matt.windsor@ury.org.uk']
|
12
|
-
spec.description =
|
12
|
+
spec.description = <<-DESC
|
13
13
|
Compo provides mixins and classes that assist in implementing a variant of
|
14
14
|
the Composite design pattern, in which each child has an ID that uniquely
|
15
15
|
identifies it inside the parent's child set.
|
16
|
-
|
16
|
+
DESC
|
17
17
|
spec.summary = 'Composite pattern style mixins with IDs'
|
18
18
|
spec.homepage = 'http://github.com/CaptainHayashi/compo'
|
19
19
|
spec.license = 'MIT'
|
@@ -25,9 +25,8 @@ Gem::Specification.new do |spec|
|
|
25
25
|
|
26
26
|
spec.add_development_dependency 'backports'
|
27
27
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
28
|
-
spec.add_development_dependency 'fuubar'
|
29
28
|
spec.add_development_dependency 'rake'
|
30
|
-
spec.add_development_dependency 'rspec'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3'
|
31
30
|
spec.add_development_dependency 'simplecov'
|
32
31
|
spec.add_development_dependency 'yard'
|
33
32
|
spec.add_development_dependency 'yardstick'
|
@@ -123,6 +123,22 @@ module Compo
|
|
123
123
|
|
124
124
|
def_delegator :children, :each
|
125
125
|
|
126
|
+
# Performs an action on this node, if it is an actual Composite node
|
127
|
+
#
|
128
|
+
# By default, this does indeed run the block provided, and returns the
|
129
|
+
# block's result. Composites that do not represent proper nodes (for
|
130
|
+
# example, Parentless) may override this to ignore the block and return
|
131
|
+
# nil.
|
132
|
+
#
|
133
|
+
# @api public
|
134
|
+
# @example Performs an action on this Parentless.
|
135
|
+
# parentless.on_node { |n| 3 }
|
136
|
+
# #=> 3
|
137
|
+
# @return [false]
|
138
|
+
def on_node
|
139
|
+
yield self
|
140
|
+
end
|
141
|
+
|
126
142
|
protected
|
127
143
|
|
128
144
|
# Assigns this object to a child as its parent
|
@@ -71,21 +71,6 @@ module Compo
|
|
71
71
|
''
|
72
72
|
end
|
73
73
|
|
74
|
-
# Given the ID of a child in this Parentless, returns that child's URL
|
75
|
-
#
|
76
|
-
# This is always the empty string. This is so that children of orphan
|
77
|
-
# objects have URLs starting with /their_id.
|
78
|
-
#
|
79
|
-
# @api public
|
80
|
-
# @example Gets the URL of the child of a Parentless.
|
81
|
-
# parentless.child_url(:child_id)
|
82
|
-
# #=> ''
|
83
|
-
#
|
84
|
-
# @return [Hash] The empty string.
|
85
|
-
def child_url(_)
|
86
|
-
''
|
87
|
-
end
|
88
|
-
|
89
74
|
# Returns the parent of this Parentless
|
90
75
|
#
|
91
76
|
# This is always the same Parentless, for convenience's sake.
|
@@ -100,6 +85,20 @@ module Compo
|
|
100
85
|
self
|
101
86
|
end
|
102
87
|
|
88
|
+
# Performs an action on this node, if it is an actual Composite node
|
89
|
+
#
|
90
|
+
# A Parentless is not, and thus this method returns nil and ignores
|
91
|
+
# any block present.
|
92
|
+
#
|
93
|
+
# @api public
|
94
|
+
# @example (Doesn't) perform an action on this Parentless.
|
95
|
+
# parentless.on_node { |n| 3 }
|
96
|
+
# #=> nil
|
97
|
+
# @return [nil]
|
98
|
+
def on_node
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
|
103
102
|
protected
|
104
103
|
|
105
104
|
# 'Adds' a child to this Parentless
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Compo
|
2
|
+
module Finders
|
3
|
+
# A method object for finding the root of an item in a composite tree
|
4
|
+
#
|
5
|
+
# The RootFinder can be used as an Enumerable, where the enumerated items
|
6
|
+
# are the path of nodes from the composite to its root, inclusive.
|
7
|
+
#
|
8
|
+
# Root finders are *not* thread-safe.
|
9
|
+
class Root
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
# Initialises a root finder
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
# @example Initialises a RootFinder
|
16
|
+
# RootFinder.new(composite)
|
17
|
+
#
|
18
|
+
# @param leaf [Composite] A composite object whose root is to be found.
|
19
|
+
def initialize(leaf)
|
20
|
+
@leaf = leaf
|
21
|
+
end
|
22
|
+
|
23
|
+
# Finds the root of a composite object
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
# @example Finds the root of an object
|
27
|
+
# RootFinder.find(composite) { |root| p root }
|
28
|
+
#
|
29
|
+
# @param (see #initialize)
|
30
|
+
#
|
31
|
+
# @yieldparam (see #run)
|
32
|
+
#
|
33
|
+
# @return [Object] The return value of the block.
|
34
|
+
def self.find(*args, &block)
|
35
|
+
new(*args).run(&block)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Attempts to find the root of this RootFinder's composite object
|
39
|
+
#
|
40
|
+
# When the resource is found, it will be yielded to the attached block.
|
41
|
+
#
|
42
|
+
# @api public
|
43
|
+
# @example Runs an RootFinder, returning the root unchanged.
|
44
|
+
# finder.run { |root| root }
|
45
|
+
# #=> root
|
46
|
+
#
|
47
|
+
# @yieldparam resource [Object] The resource found.
|
48
|
+
#
|
49
|
+
# @return [Object] The return value of the block.
|
50
|
+
def run
|
51
|
+
each { |node| yield node if node.root? }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Performs an action on each node in the path to the root
|
55
|
+
#
|
56
|
+
# This includes both the root and the object whose root is sought, and
|
57
|
+
# runs from the latter to the former.
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
# @example Prints each item from the object to the root.
|
61
|
+
# finder.each { |item| puts item }
|
62
|
+
#
|
63
|
+
# @yieldparam resource [Object] The resource found.
|
64
|
+
#
|
65
|
+
# @return [void]
|
66
|
+
def each
|
67
|
+
node = @leaf
|
68
|
+
done = false
|
69
|
+
until done
|
70
|
+
yield node
|
71
|
+
done = node.root?
|
72
|
+
node = node.parent
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/compo/finders.rb
CHANGED
@@ -29,6 +29,21 @@ module Compo
|
|
29
29
|
Compo::Composites::Parentless.for(self)
|
30
30
|
end
|
31
31
|
|
32
|
+
# Gets whether this ParentTracker is the root of its composite tree
|
33
|
+
#
|
34
|
+
# This is equivalent to the ParentTracker having no parent.
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
# @example Checks if a ParentTracker with no parent is a root.
|
38
|
+
# orphan.root?
|
39
|
+
# #=> true
|
40
|
+
# @example Checks if a ParentTracker with a parent is a root.
|
41
|
+
# parented.root?
|
42
|
+
# #=> false
|
43
|
+
def root?
|
44
|
+
parent.on_node { |p| p }.nil?
|
45
|
+
end
|
46
|
+
|
32
47
|
# Gets this object's current ID
|
33
48
|
#
|
34
49
|
# @api public
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'compo/finders/root'
|
2
|
+
|
1
3
|
module Compo
|
2
4
|
module Mixins
|
3
5
|
# Adds ID-based 'URL's to Compo classes
|
@@ -26,30 +28,13 @@ module Compo
|
|
26
28
|
# #=> ''
|
27
29
|
# @example Gets the URL of an object with a parent.
|
28
30
|
# leaf.url
|
29
|
-
# #=> 'grandparent_id/parent_id/id'
|
31
|
+
# #=> '/grandparent_id/parent_id/id'
|
30
32
|
#
|
31
33
|
# @return [String] The URL of this object.
|
32
34
|
def url
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
# Returns the URL of a child of this object, with the given ID
|
37
|
-
#
|
38
|
-
# This defaults to joining the ID to this object's URL with a slash.
|
39
|
-
#
|
40
|
-
# @api public
|
41
|
-
# @example Gets the URL of the child of an object without a parent.
|
42
|
-
# orphan.child_url(:id)
|
43
|
-
# #=> '/id'
|
44
|
-
# @example Gets the URL of the child of an object with a parent.
|
45
|
-
# leaf.child_url(:id)
|
46
|
-
# #=> 'grandparent_id/parent_id/id'
|
47
|
-
#
|
48
|
-
# @param child_id [Object] The ID of the child whose URL is sought.
|
49
|
-
#
|
50
|
-
# @return [String] The URL of the child with the given ID.
|
51
|
-
def child_url(child_id)
|
52
|
-
[url, child_id].join('/')
|
35
|
+
Compo::Finders::Root.new(self).reverse_each.map do |item|
|
36
|
+
item.root? ? '' : item.id
|
37
|
+
end.join('/')
|
53
38
|
end
|
54
39
|
|
55
40
|
# Returns the URL of this object's parent
|
@@ -57,10 +42,10 @@ module Compo
|
|
57
42
|
# @api public
|
58
43
|
# @example Gets the parent URL of an object with no parent.
|
59
44
|
# orphan.parent_url
|
60
|
-
# #=>
|
45
|
+
# #=> ''
|
61
46
|
# @example Gets the URL of an object with a parent.
|
62
47
|
# leaf.parent_url
|
63
|
-
# #=> 'grandparent_id/parent_id'
|
48
|
+
# #=> '/grandparent_id/parent_id'
|
64
49
|
#
|
65
50
|
# @return [String] The URL of this object's parent, or nil if there is no
|
66
51
|
# parent.
|
data/lib/compo/version.rb
CHANGED
data/spec/array_branch_spec.rb
CHANGED
@@ -1,113 +1,120 @@
|
|
1
1
|
require 'compo'
|
2
2
|
require 'composite_shared_examples'
|
3
3
|
|
4
|
-
shared_examples 'an array composite' do
|
4
|
+
RSpec.shared_examples 'an array composite' do
|
5
5
|
it_behaves_like 'a composite'
|
6
6
|
|
7
|
-
|
8
|
-
let(:
|
9
|
-
let(:
|
7
|
+
# Children
|
8
|
+
let(:c1) { double(:c1) }
|
9
|
+
let(:c2) { double(:c2) }
|
10
|
+
let(:c3) { double(:c3) }
|
10
11
|
|
11
12
|
before(:each) do
|
12
|
-
allow(
|
13
|
-
allow(
|
14
|
-
allow(
|
13
|
+
allow(c1).to receive(:update_parent)
|
14
|
+
allow(c2).to receive(:update_parent)
|
15
|
+
allow(c3).to receive(:update_parent)
|
15
16
|
end
|
16
17
|
|
17
18
|
describe '#add' do
|
18
19
|
context 'when the ID is not Numeric' do
|
19
|
-
specify { expect(subject.add(:mr_flibble,
|
20
|
+
specify { expect(subject.add(:mr_flibble, c1)).to be_nil }
|
20
21
|
|
21
22
|
it 'does not add to the list of children' do
|
22
|
-
subject.add(:rimmer,
|
23
|
-
|
23
|
+
expect { subject.add(:rimmer, c1) }.to_not change { subject.children }
|
24
|
+
.from({})
|
25
|
+
|
26
|
+
subject.add(0, c1)
|
24
27
|
|
25
|
-
subject.add(
|
26
|
-
|
27
|
-
expect(subject.children).to eq(0 => child1)
|
28
|
+
expect { subject.add(:lister, c2) }.to_not change { subject.children }
|
29
|
+
.from(0 => c1)
|
28
30
|
|
29
|
-
subject.add(1,
|
30
|
-
|
31
|
-
expect
|
31
|
+
subject.add(1, c2)
|
32
|
+
|
33
|
+
expect { subject.add(:cat, c3) }.to_not change { subject.children }
|
34
|
+
.from(0 => c1, 1 => c2)
|
32
35
|
end
|
33
36
|
end
|
34
37
|
context 'when the ID is Numeric' do
|
35
38
|
context 'and is equal to the number of children' do
|
36
39
|
it 'returns the child' do
|
37
|
-
expect(subject.add(0,
|
38
|
-
expect(subject.add(1,
|
39
|
-
expect(subject.add(2,
|
40
|
+
expect(subject.add(0, c1)).to eq(c1)
|
41
|
+
expect(subject.add(1, c2)).to eq(c2)
|
42
|
+
expect(subject.add(2, c3)).to eq(c3)
|
40
43
|
end
|
41
44
|
|
42
45
|
it 'adds to the end of the list of children' do
|
43
46
|
expect(subject.children).to eq({})
|
44
47
|
|
45
|
-
subject.add(0,
|
46
|
-
|
47
|
-
|
48
|
-
subject.add(1,
|
49
|
-
|
50
|
-
|
51
|
-
subject.add(2,
|
52
|
-
|
48
|
+
expect { subject.add(0, c1) }.to change { subject.children }
|
49
|
+
.from({})
|
50
|
+
.to(0 => c1)
|
51
|
+
expect { subject.add(1, c2) }.to change { subject.children }
|
52
|
+
.from(0 => c1)
|
53
|
+
.to(0 => c1, 1 => c2)
|
54
|
+
expect { subject.add(2, c3) }.to change { subject.children }
|
55
|
+
.from(0 => c1, 1 => c2)
|
56
|
+
.to(0 => c1, 1 => c2, 2 => c3)
|
53
57
|
end
|
54
58
|
|
55
59
|
it 'calls #update_parent on the child with itself and an ID proc' do
|
56
|
-
expect(
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
subject.add(0,
|
60
|
+
expect(c1).to receive(:update_parent).with(
|
61
|
+
subject,
|
62
|
+
an_object_satisfying { |proc| proc.call == 0 }
|
63
|
+
)
|
64
|
+
subject.add(0, c1)
|
61
65
|
end
|
62
66
|
end
|
63
67
|
|
64
68
|
context 'and is greater than the number of children' do
|
65
69
|
it 'returns nil' do
|
66
|
-
expect(subject.add(1,
|
67
|
-
subject.add(0,
|
68
|
-
expect(subject.add(2,
|
69
|
-
subject.add(1,
|
70
|
-
expect(subject.add(3,
|
70
|
+
expect(subject.add(1, c1)).to be_nil
|
71
|
+
subject.add(0, c1)
|
72
|
+
expect(subject.add(2, c2)).to be_nil
|
73
|
+
subject.add(1, c2)
|
74
|
+
expect(subject.add(3, c3)).to be_nil
|
71
75
|
end
|
72
76
|
|
73
77
|
it 'does not add to the list of children' do
|
74
|
-
subject.add(1,
|
75
|
-
|
78
|
+
expect { subject.add(1, c1) }.to_not change { subject.children }
|
79
|
+
.from({})
|
76
80
|
|
77
|
-
subject.add(0,
|
78
|
-
subject.add(2,
|
79
|
-
|
81
|
+
subject.add(0, c1)
|
82
|
+
expect { subject.add(2, c2) }.to_not change { subject.children }
|
83
|
+
.from(0 => c1)
|
80
84
|
|
81
|
-
subject.add(1,
|
82
|
-
subject.add(3,
|
83
|
-
|
85
|
+
subject.add(1, c2)
|
86
|
+
expect { subject.add(3, c2) }.to_not change { subject.children }
|
87
|
+
.from(0 => c1, 1 => c2)
|
84
88
|
end
|
85
89
|
end
|
86
90
|
|
87
91
|
context 'and is less than the number of children' do
|
88
92
|
it 'returns the child' do
|
89
|
-
subject.add(0,
|
90
|
-
expect(subject.add(0,
|
91
|
-
expect(subject.add(1,
|
93
|
+
subject.add(0, c1)
|
94
|
+
expect(subject.add(0, c2)).to eq(c2)
|
95
|
+
expect(subject.add(1, c3)).to eq(c3)
|
92
96
|
end
|
93
97
|
|
94
98
|
it 'adds to the list of children at the correct position' do
|
95
99
|
expect(subject.children).to eq({})
|
96
|
-
subject.add(0,
|
97
|
-
|
98
|
-
|
99
|
-
expect
|
100
|
-
|
101
|
-
|
100
|
+
expect { subject.add(0, c1) }.to change { subject.children }
|
101
|
+
.from({})
|
102
|
+
.to(0 => c1)
|
103
|
+
expect { subject.add(0, c2) }.to change { subject.children }
|
104
|
+
.from(0 => c1)
|
105
|
+
.to(0 => c2, 1 => c1)
|
106
|
+
expect { subject.add(1, c3) }.to change { subject.children }
|
107
|
+
.from(0 => c2, 1 => c1)
|
108
|
+
.to(0 => c2, 1 => c3, 2 => c1)
|
102
109
|
end
|
103
110
|
|
104
111
|
it 'calls #update_parent on the child with itself and an ID proc' do
|
105
|
-
expect(
|
112
|
+
expect(c1).to receive(:update_parent) do |parent, proc|
|
106
113
|
expect(parent).to eq(subject)
|
107
114
|
expect(proc.call).to eq(0)
|
108
115
|
end
|
109
|
-
subject.add(0,
|
110
|
-
subject.add(0,
|
116
|
+
subject.add(0, c2)
|
117
|
+
subject.add(0, c1)
|
111
118
|
end
|
112
119
|
end
|
113
120
|
end
|
@@ -115,80 +122,80 @@ shared_examples 'an array composite' do
|
|
115
122
|
|
116
123
|
describe '#remove' do
|
117
124
|
context 'when the child exists in the list' do
|
118
|
-
before(:each) { subject.add(0,
|
125
|
+
before(:each) { subject.add(0, c1) }
|
119
126
|
|
120
127
|
it 'returns the child' do
|
121
|
-
expect(subject.remove(
|
128
|
+
expect(subject.remove(c1)).to eq(c1)
|
122
129
|
end
|
123
130
|
|
124
131
|
it 'calls #update_parent on the child with a Parentless' do
|
125
|
-
expect(
|
132
|
+
expect(c1).to receive(:update_parent).once do |parent, _|
|
126
133
|
expect(parent).to be_a(Compo::Composites::Parentless)
|
127
134
|
end
|
128
|
-
subject.remove(
|
135
|
+
subject.remove(c1)
|
129
136
|
end
|
130
137
|
|
131
138
|
it 'calls #update_parent on the child with a nil-returning ID proc' do
|
132
|
-
expect(
|
139
|
+
expect(c1).to receive(:update_parent).once do |_, idp|
|
133
140
|
expect(idp.call).to be_nil
|
134
141
|
end
|
135
|
-
subject.remove(
|
142
|
+
subject.remove(c1)
|
136
143
|
end
|
137
144
|
|
138
145
|
it 'moves succeeding IDs down by one' do
|
139
|
-
subject.add(1,
|
140
|
-
subject.add(2,
|
141
|
-
|
142
|
-
subject.remove(
|
143
|
-
|
146
|
+
subject.add(1, c2)
|
147
|
+
subject.add(2, c3)
|
148
|
+
|
149
|
+
expect { subject.remove(c2) }.to change { subject.children }
|
150
|
+
.from(0 => c1, 1 => c2, 2 => c3)
|
151
|
+
.to(0 => c1, 1 => c3)
|
144
152
|
end
|
145
153
|
end
|
146
154
|
|
147
155
|
context 'when the child does not exist in the list' do
|
148
|
-
specify { expect(subject.remove(
|
156
|
+
specify { expect(subject.remove(c1)).to be_nil }
|
149
157
|
|
150
158
|
it 'does not change the children' do
|
151
159
|
expect(subject.children).to eq({})
|
152
|
-
subject.remove(
|
153
|
-
|
160
|
+
expect { subject.remove(c1) }.to_not change { subject.children }
|
161
|
+
.from({})
|
154
162
|
|
155
|
-
subject.add(0,
|
156
|
-
subject.add(1,
|
157
|
-
expect
|
158
|
-
|
159
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
163
|
+
subject.add(0, c1)
|
164
|
+
subject.add(1, c2)
|
165
|
+
expect { subject.remove(c3) }.to_not change { subject.children }
|
166
|
+
.from eq(0 => c1, 1 => c2)
|
160
167
|
end
|
161
168
|
end
|
162
169
|
end
|
163
170
|
|
164
171
|
describe '#remove_id' do
|
165
172
|
context 'when the ID exists' do
|
166
|
-
before(:each) { subject.add(0,
|
173
|
+
before(:each) { subject.add(0, c1) }
|
167
174
|
|
168
175
|
it 'returns the child' do
|
169
|
-
expect(subject.remove_id(0)).to eq(
|
176
|
+
expect(subject.remove_id(0)).to eq(c1)
|
170
177
|
end
|
171
178
|
|
172
179
|
it 'calls #update_parent on the child with a Parentless' do
|
173
|
-
expect(
|
180
|
+
expect(c1).to receive(:update_parent).once do |parent, _|
|
174
181
|
expect(parent).to be_a(Compo::Composites::Parentless)
|
175
182
|
end
|
176
183
|
subject.remove_id(0)
|
177
184
|
end
|
178
185
|
|
179
186
|
it 'calls #update_parent on the child with a nil-returning ID proc' do
|
180
|
-
expect(
|
187
|
+
expect(c1).to receive(:update_parent).once do |_, idp|
|
181
188
|
expect(idp.call).to be_nil
|
182
189
|
end
|
183
190
|
subject.remove_id(0)
|
184
191
|
end
|
185
192
|
|
186
193
|
it 'moves succeeding IDs down by one' do
|
187
|
-
subject.add(1,
|
188
|
-
subject.add(2,
|
189
|
-
expect
|
190
|
-
|
191
|
-
|
194
|
+
subject.add(1, c2)
|
195
|
+
subject.add(2, c3)
|
196
|
+
expect { subject.remove_id(1) }.to change { subject.children }
|
197
|
+
.from(0 => c1, 1 => c2, 2 => c3)
|
198
|
+
.to(0 => c1, 1 => c3)
|
192
199
|
end
|
193
200
|
end
|
194
201
|
|
@@ -196,15 +203,13 @@ shared_examples 'an array composite' do
|
|
196
203
|
specify { expect(subject.remove_id(0)).to be_nil }
|
197
204
|
|
198
205
|
it 'does not change the children' do
|
199
|
-
expect
|
200
|
-
|
201
|
-
expect(subject.children).to eq({})
|
206
|
+
expect { subject.remove_id(0) }.to_not change { subject.children }
|
207
|
+
.from({})
|
202
208
|
|
203
|
-
subject.add(0,
|
204
|
-
subject.add(1,
|
205
|
-
expect
|
206
|
-
|
207
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
209
|
+
subject.add(0, c1)
|
210
|
+
subject.add(1, c2)
|
211
|
+
expect { subject.remove_id(2) }.to_not change { subject.children }
|
212
|
+
.from(0 => c1, 1 => c2)
|
208
213
|
end
|
209
214
|
end
|
210
215
|
end
|
@@ -220,20 +225,20 @@ shared_examples 'an array composite' do
|
|
220
225
|
it 'returns a hash mapping their current indices to themselves' do
|
221
226
|
expect(subject.children).to eq({})
|
222
227
|
|
223
|
-
subject.add(0,
|
224
|
-
expect(subject.children).to eq(0 =>
|
228
|
+
subject.add(0, c1)
|
229
|
+
expect(subject.children).to eq(0 => c1)
|
225
230
|
|
226
|
-
subject.add(1,
|
227
|
-
expect(subject.children).to eq(0 =>
|
231
|
+
subject.add(1, c2)
|
232
|
+
expect(subject.children).to eq(0 => c1, 1 => c2)
|
228
233
|
|
229
|
-
subject.add(2,
|
230
|
-
expect(subject.children).to eq(0 =>
|
234
|
+
subject.add(2, c3)
|
235
|
+
expect(subject.children).to eq(0 => c1, 1 => c2, 2 => c3)
|
231
236
|
|
232
|
-
subject.remove(
|
233
|
-
expect(subject.children).to eq(0 =>
|
237
|
+
subject.remove(c2)
|
238
|
+
expect(subject.children).to eq(0 => c1, 1 => c3)
|
234
239
|
|
235
|
-
subject.add(0,
|
236
|
-
expect(subject.children).to eq(0 =>
|
240
|
+
subject.add(0, c2)
|
241
|
+
expect(subject.children).to eq(0 => c2, 1 => c1, 2 => c3)
|
237
242
|
end
|
238
243
|
end
|
239
244
|
end
|