compo 0.1.5 → 0.2.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/.gitignore +2 -0
- data/CHANGELOG +8 -0
- data/lib/compo/composite.rb +1 -1
- data/lib/compo/parent_tracker.rb +2 -1
- data/lib/compo/parentless.rb +112 -0
- data/lib/compo/url_referenceable.rb +20 -1
- data/lib/compo/version.rb +1 -1
- data/lib/compo.rb +1 -0
- data/spec/array_branch_spec.rb +4 -9
- data/spec/array_composite_shared_examples.rb +240 -0
- data/spec/array_composite_spec.rb +2 -220
- data/spec/branch_shared_examples.rb +33 -0
- data/spec/composite_shared_examples.rb +283 -0
- data/spec/composite_spec.rb +9 -250
- data/spec/hash_branch_spec.rb +4 -9
- data/spec/hash_composite_shared_examples.rb +208 -0
- data/spec/hash_composite_spec.rb +2 -179
- data/spec/leaf_spec.rb +4 -25
- data/spec/movable_shared_examples.rb +154 -0
- data/spec/movable_spec.rb +2 -191
- data/spec/null_composite_shared_examples.rb +68 -0
- data/spec/null_composite_spec.rb +2 -62
- data/spec/parent_tracker_spec.rb +2 -2
- data/spec/parentless_spec.rb +52 -0
- data/spec/url_referenceable_shared_examples.rb +78 -0
- data/spec/url_referenceable_spec.rb +2 -65
- metadata +18 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6db898fd60a7a46a30a7d851bd634c2be59a1d4
|
4
|
+
data.tar.gz: 78d2ccf08de768e42ca7df43a29f6491b0adbcfb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1694942f4173cd3c464a66d904ff36044ea714c79c8e2c8fc708d7c2d296dd28da42d760ebae89442c3734bf692fbf7ee3feceeebeecab5d00ce0d47ffa75f20
|
7
|
+
data.tar.gz: 1c7db6618f24c57980ad14b6200cd212d516d4c800e5718fe46964576eb9ec6175c06d3a1337997507fe972ba70da7b3bcd92d396026e527a99301217b1e3d17
|
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
0.2.0 (2013-12-29)
|
2
|
+
- (BACKWARDS INCOMPATIBILITY) Implement Parentless as a null object for the
|
3
|
+
result of calling #parent on a parentless child. This case now returns
|
4
|
+
an instance of Parentless, not nil.
|
5
|
+
- #remove_child no longer exists, as Parentless calls #update_child with
|
6
|
+
itself when a parent removes a child.
|
7
|
+
- Numerous test improvements; now using shared examples.
|
8
|
+
|
1
9
|
0.1.5 (2013-12-29)
|
2
10
|
- Add a missing require 'forwardable', and remove one that wasn't needed.
|
3
11
|
|
data/lib/compo/composite.rb
CHANGED
data/lib/compo/parent_tracker.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'forwardable'
|
2
|
+
require 'compo/parentless'
|
2
3
|
|
3
4
|
module Compo
|
4
5
|
# Basic implementation of parent tracking as a mixin
|
@@ -58,7 +59,7 @@ module Compo
|
|
58
59
|
#
|
59
60
|
# @return [void]
|
60
61
|
def remove_parent
|
61
|
-
update_parent(
|
62
|
+
update_parent(Parentless.new, -> { nil })
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'compo'
|
2
|
+
|
3
|
+
module Compo
|
4
|
+
# A Composite that represents the non-existent parent of an orphan
|
5
|
+
#
|
6
|
+
# Parentless is the parent assigned when an object is removed from a
|
7
|
+
# Composite, and should be the default parent of an object that can be
|
8
|
+
# added to one. It exists to make some operations easier, such as URL
|
9
|
+
# creation.
|
10
|
+
class Parentless
|
11
|
+
include Composite
|
12
|
+
|
13
|
+
# 'Removes' a child from this Parentless
|
14
|
+
#
|
15
|
+
# This always succeeds, and never triggers any other action.
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
# @example 'Removes' a child.
|
19
|
+
# parentless.remove(child)
|
20
|
+
#
|
21
|
+
# @param child [Object] The child to 'remove' from this Parentless.
|
22
|
+
#
|
23
|
+
# @return [Object] The child.
|
24
|
+
def remove(child)
|
25
|
+
child
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the empty hash
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
# @example Gets the children
|
32
|
+
# parentless.children
|
33
|
+
# #=> {}
|
34
|
+
#
|
35
|
+
# @return [Hash] The empty hash.
|
36
|
+
def children
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the URL of this Parentless
|
41
|
+
#
|
42
|
+
# This is always the empty string.
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
# @example Gets the URL of a Parentless
|
46
|
+
# parentless.url
|
47
|
+
# #=> ''
|
48
|
+
#
|
49
|
+
# @return [Hash] The empty string.
|
50
|
+
def url
|
51
|
+
''
|
52
|
+
end
|
53
|
+
|
54
|
+
# Given the ID of a child in this Parentless, returns that child's URL
|
55
|
+
#
|
56
|
+
# This is always the empty string. This is so that children of orphan
|
57
|
+
# objects have URLs starting with /their_id.
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
# @example Gets the URL of the child of a Parentless.
|
61
|
+
# parentless.child_url(:child_id)
|
62
|
+
# #=> ''
|
63
|
+
#
|
64
|
+
# @return [Hash] The empty string.
|
65
|
+
def child_url(_)
|
66
|
+
''
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns the parent of this Parentless
|
70
|
+
#
|
71
|
+
# This is always the same Parentless, for convenience's sake. Technically,
|
72
|
+
# as a null object, Parentless has no parent.
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
# @example Gets the 'parent' of a Parentless.
|
76
|
+
# parentless.parent
|
77
|
+
#
|
78
|
+
# @return [self]
|
79
|
+
def parent
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
# 'Adds' a child to this Parentless
|
86
|
+
#
|
87
|
+
# This always succeeds.
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
#
|
91
|
+
# @param id [Object] Ignored.
|
92
|
+
# @param child [Object] The object to 'add' to this Parentless.
|
93
|
+
#
|
94
|
+
# @return [Object] The child.
|
95
|
+
def add!(_, child)
|
96
|
+
child
|
97
|
+
end
|
98
|
+
|
99
|
+
# Creates an ID function for the given child
|
100
|
+
#
|
101
|
+
# The returned proc is O(1), and always returns nil.
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
#
|
105
|
+
# @param child [Object] The child whose ID is to be returned by the proc.
|
106
|
+
#
|
107
|
+
# @return [Proc] A proc returning nil.
|
108
|
+
def id_function(_)
|
109
|
+
-> { nil }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -27,7 +27,26 @@ module Compo
|
|
27
27
|
#
|
28
28
|
# @return [String] The URL of this object.
|
29
29
|
def url
|
30
|
-
parent.
|
30
|
+
parent.child_url(id)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the URL of a child of this object, with the given ID
|
34
|
+
#
|
35
|
+
# This defaults to joining the ID to this object's URL with a slash.
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
# @example Gets the URL of the child of an object without a parent.
|
39
|
+
# orphan.child_url(:id)
|
40
|
+
# #=> '/id'
|
41
|
+
# @example Gets the URL of the child of an object with a parent.
|
42
|
+
# leaf.child_url(:id)
|
43
|
+
# #=> 'grandparent_id/parent_id/id'
|
44
|
+
#
|
45
|
+
# @param child_id [Object] The ID of the child whose URL is sought.
|
46
|
+
#
|
47
|
+
# @return [String] The URL of the child with the given ID.
|
48
|
+
def child_url(child_id)
|
49
|
+
[url, child_id].join('/')
|
31
50
|
end
|
32
51
|
|
33
52
|
# Returns the URL of this object's parent
|
data/lib/compo/version.rb
CHANGED
data/lib/compo.rb
CHANGED
data/spec/array_branch_spec.rb
CHANGED
@@ -1,14 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'compo'
|
3
|
+
require 'array_composite_shared_examples'
|
4
|
+
require 'branch_shared_examples'
|
3
5
|
|
4
6
|
describe Compo::ArrayBranch do
|
5
|
-
|
6
|
-
|
7
|
-
expect(subject.parent).to be_nil
|
8
|
-
end
|
9
|
-
|
10
|
-
it 'initialises with an ID function returning nil' do
|
11
|
-
expect(subject.id).to be_nil
|
12
|
-
end
|
13
|
-
end
|
7
|
+
it_behaves_like 'a branch'
|
8
|
+
it_behaves_like 'an array composite'
|
14
9
|
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
require 'compo'
|
2
|
+
require 'composite_shared_examples'
|
3
|
+
|
4
|
+
shared_examples 'an array composite' do
|
5
|
+
it_behaves_like 'a composite'
|
6
|
+
|
7
|
+
let(:child1) { double(:child1) }
|
8
|
+
let(:child2) { double(:child2) }
|
9
|
+
let(:child3) { double(:child3) }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
allow(child1).to receive(:update_parent)
|
13
|
+
allow(child2).to receive(:update_parent)
|
14
|
+
allow(child3).to receive(:update_parent)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#add' do
|
18
|
+
context 'when the ID is not Numeric' do
|
19
|
+
specify { expect(subject.add(:mr_flibble, child1)).to be_nil }
|
20
|
+
|
21
|
+
it 'does not add to the list of children' do
|
22
|
+
subject.add(:rimmer, child1)
|
23
|
+
expect(subject.children).to eq({})
|
24
|
+
|
25
|
+
subject.add(0, child1)
|
26
|
+
subject.add(:lister, child2)
|
27
|
+
expect(subject.children).to eq(0 => child1)
|
28
|
+
|
29
|
+
subject.add(1, child2)
|
30
|
+
subject.add(:cat, child3)
|
31
|
+
expect(subject.children).to eq(0 => child1, 1 => child2)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
context 'when the ID is Numeric' do
|
35
|
+
context 'and is equal to the number of children' do
|
36
|
+
it 'returns the child' do
|
37
|
+
expect(subject.add(0, child1)).to eq(child1)
|
38
|
+
expect(subject.add(1, child2)).to eq(child2)
|
39
|
+
expect(subject.add(2, child3)).to eq(child3)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'adds to the end of the list of children' do
|
43
|
+
expect(subject.children).to eq({})
|
44
|
+
|
45
|
+
subject.add(0, child1)
|
46
|
+
expect(subject.children).to eq(0 => child1)
|
47
|
+
|
48
|
+
subject.add(1, child2)
|
49
|
+
expect(subject.children).to eq(0 => child1, 1 => child2)
|
50
|
+
|
51
|
+
subject.add(2, child3)
|
52
|
+
expect(subject.children).to eq(0 => child1, 1 => child2, 2 => child3)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'calls #update_parent on the child with itself and an ID proc' do
|
56
|
+
expect(child1).to receive(:update_parent) do |parent, proc|
|
57
|
+
expect(parent).to eq(subject)
|
58
|
+
expect(proc.call).to eq(0)
|
59
|
+
end
|
60
|
+
subject.add(0, child1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'and is greater than the number of children' do
|
65
|
+
it 'returns nil' do
|
66
|
+
expect(subject.add(1, child1)).to be_nil
|
67
|
+
subject.add(0, child1)
|
68
|
+
expect(subject.add(2, child2)).to be_nil
|
69
|
+
subject.add(1, child2)
|
70
|
+
expect(subject.add(3, child3)).to be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'does not add to the list of children' do
|
74
|
+
subject.add(1, child1)
|
75
|
+
expect(subject.children).to eq({})
|
76
|
+
|
77
|
+
subject.add(0, child1)
|
78
|
+
subject.add(2, child2)
|
79
|
+
expect(subject.children).to eq(0 => child1)
|
80
|
+
|
81
|
+
subject.add(1, child2)
|
82
|
+
subject.add(3, child3)
|
83
|
+
expect(subject.children).to eq(0 => child1, 1 => child2)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'and is less than the number of children' do
|
88
|
+
it 'returns the child' do
|
89
|
+
subject.add(0, child1)
|
90
|
+
expect(subject.add(0, child2)).to eq(child2)
|
91
|
+
expect(subject.add(1, child3)).to eq(child3)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'adds to the list of children at the correct position' do
|
95
|
+
expect(subject.children).to eq({})
|
96
|
+
subject.add(0, child1)
|
97
|
+
expect(subject.children).to eq(0 => child1)
|
98
|
+
subject.add(0, child2)
|
99
|
+
expect(subject.children).to eq(0 => child2, 1 => child1)
|
100
|
+
subject.add(1, child3)
|
101
|
+
expect(subject.children).to eq(0 => child2, 1 => child3, 2 => child1)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'calls #update_parent on the child with itself and an ID proc' do
|
105
|
+
expect(child1).to receive(:update_parent) do |parent, proc|
|
106
|
+
expect(parent).to eq(subject)
|
107
|
+
expect(proc.call).to eq(0)
|
108
|
+
end
|
109
|
+
subject.add(0, child2)
|
110
|
+
subject.add(0, child1)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#remove' do
|
117
|
+
context 'when the child exists in the list' do
|
118
|
+
before(:each) { subject.add(0, child1) }
|
119
|
+
|
120
|
+
it 'returns the child' do
|
121
|
+
expect(subject.remove(child1)).to eq(child1)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'calls #update_parent on the child with a Parentless' do
|
125
|
+
expect(child1).to receive(:update_parent).once do |parent, _|
|
126
|
+
expect(parent).to be_a(Compo::Parentless)
|
127
|
+
end
|
128
|
+
subject.remove(child1)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'calls #update_parent on the child with a nil-returning ID proc' do
|
132
|
+
expect(child1).to receive(:update_parent).once do |_, idp|
|
133
|
+
expect(idp.call).to be_nil
|
134
|
+
end
|
135
|
+
subject.remove(child1)
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'moves succeeding IDs down by one' do
|
139
|
+
subject.add(1, child2)
|
140
|
+
subject.add(2, child3)
|
141
|
+
expect(subject.children).to eq(0 => child1, 1 => child2, 2 => child3)
|
142
|
+
subject.remove(child2)
|
143
|
+
expect(subject.children).to eq(0 => child1, 1 => child3)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'when the child does not exist in the list' do
|
148
|
+
specify { expect(subject.remove(child1)).to be_nil }
|
149
|
+
|
150
|
+
it 'does not change the children' do
|
151
|
+
expect(subject.children).to eq({})
|
152
|
+
subject.remove(child1)
|
153
|
+
expect(subject.children).to eq({})
|
154
|
+
|
155
|
+
subject.add(0, child1)
|
156
|
+
subject.add(1, child2)
|
157
|
+
expect(subject.children).to eq(0 => child1, 1 => child2)
|
158
|
+
subject.remove(child3)
|
159
|
+
expect(subject.children).to eq(0 => child1, 1 => child2)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#remove_id' do
|
165
|
+
context 'when the ID exists' do
|
166
|
+
before(:each) { subject.add(0, child1) }
|
167
|
+
|
168
|
+
it 'returns the child' do
|
169
|
+
expect(subject.remove_id(0)).to eq(child1)
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'calls #update_parent on the child with a Parentless' do
|
173
|
+
expect(child1).to receive(:update_parent).once do |parent, _|
|
174
|
+
expect(parent).to be_a(Compo::Parentless)
|
175
|
+
end
|
176
|
+
subject.remove_id(0)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'calls #update_parent on the child with a nil-returning ID proc' do
|
180
|
+
expect(child1).to receive(:update_parent).once do |_, idp|
|
181
|
+
expect(idp.call).to be_nil
|
182
|
+
end
|
183
|
+
subject.remove_id(0)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'moves succeeding IDs down by one' do
|
187
|
+
subject.add(1, child2)
|
188
|
+
subject.add(2, child3)
|
189
|
+
expect(subject.children).to eq(0 => child1, 1 => child2, 2 => child3)
|
190
|
+
subject.remove_id(1)
|
191
|
+
expect(subject.children).to eq(0 => child1, 1 => child3)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'when the ID does not exist' do
|
196
|
+
specify { expect(subject.remove_id(0)).to be_nil }
|
197
|
+
|
198
|
+
it 'does not change the children' do
|
199
|
+
expect(subject.children).to eq({})
|
200
|
+
subject.remove_id(0)
|
201
|
+
expect(subject.children).to eq({})
|
202
|
+
|
203
|
+
subject.add(0, child1)
|
204
|
+
subject.add(1, child2)
|
205
|
+
expect(subject.children).to eq(0 => child1, 1 => child2)
|
206
|
+
subject.remove_id(2)
|
207
|
+
expect(subject.children).to eq(0 => child1, 1 => child2)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe '#children' do
|
213
|
+
context 'when the list has no children' do
|
214
|
+
it 'returns the empty hash' do
|
215
|
+
expect(subject.children).to eq({})
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'when the list has children' do
|
220
|
+
it 'returns a hash mapping their current indices to themselves' do
|
221
|
+
expect(subject.children).to eq({})
|
222
|
+
|
223
|
+
subject.add(0, child1)
|
224
|
+
expect(subject.children).to eq(0 => child1)
|
225
|
+
|
226
|
+
subject.add(1, child2)
|
227
|
+
expect(subject.children).to eq(0 => child1, 1 => child2)
|
228
|
+
|
229
|
+
subject.add(2, child3)
|
230
|
+
expect(subject.children).to eq(0 => child1, 1 => child2, 2 => child3)
|
231
|
+
|
232
|
+
subject.remove(child2)
|
233
|
+
expect(subject.children).to eq(0 => child1, 1 => child3)
|
234
|
+
|
235
|
+
subject.add(0, child2)
|
236
|
+
expect(subject.children).to eq(0 => child2, 1 => child1, 2 => child3)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -1,225 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'compo'
|
3
|
+
require 'array_composite_shared_examples'
|
3
4
|
|
4
5
|
describe Compo::ArrayComposite do
|
5
|
-
|
6
|
-
let(:child2) { double(:child2) }
|
7
|
-
let(:child3) { double(:child3) }
|
8
|
-
|
9
|
-
before(:each) do
|
10
|
-
allow(child1).to receive(:update_parent)
|
11
|
-
allow(child1).to receive(:remove_parent)
|
12
|
-
|
13
|
-
allow(child2).to receive(:update_parent)
|
14
|
-
allow(child2).to receive(:remove_parent)
|
15
|
-
|
16
|
-
allow(child3).to receive(:update_parent)
|
17
|
-
allow(child3).to receive(:remove_parent)
|
18
|
-
end
|
19
|
-
|
20
|
-
describe '#add' do
|
21
|
-
context 'when the ID is not Numeric' do
|
22
|
-
specify { expect(subject.add(:mr_flibble, child1)).to be_nil }
|
23
|
-
|
24
|
-
it 'does not add to the list of children' do
|
25
|
-
subject.add(:rimmer, child1)
|
26
|
-
expect(subject.children).to eq({})
|
27
|
-
|
28
|
-
subject.add(0, child1)
|
29
|
-
subject.add(:lister, child2)
|
30
|
-
expect(subject.children).to eq(0 => child1)
|
31
|
-
|
32
|
-
subject.add(1, child2)
|
33
|
-
subject.add(:cat, child3)
|
34
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
context 'when the ID is Numeric' do
|
38
|
-
context 'and is equal to the number of children' do
|
39
|
-
it 'returns the child' do
|
40
|
-
expect(subject.add(0, child1)).to eq(child1)
|
41
|
-
expect(subject.add(1, child2)).to eq(child2)
|
42
|
-
expect(subject.add(2, child3)).to eq(child3)
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'adds to the end of the list of children' do
|
46
|
-
expect(subject.children).to eq({})
|
47
|
-
|
48
|
-
subject.add(0, child1)
|
49
|
-
expect(subject.children).to eq(0 => child1)
|
50
|
-
|
51
|
-
subject.add(1, child2)
|
52
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
53
|
-
|
54
|
-
subject.add(2, child3)
|
55
|
-
expect(subject.children).to eq(0 => child1, 1 => child2, 2 => child3)
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'calls #update_parent on the child with itself and an ID proc' do
|
59
|
-
expect(child1).to receive(:update_parent) do |parent, proc|
|
60
|
-
expect(parent).to eq(subject)
|
61
|
-
expect(proc.call).to eq(0)
|
62
|
-
end
|
63
|
-
subject.add(0, child1)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context 'and is greater than the number of children' do
|
68
|
-
it 'returns nil' do
|
69
|
-
expect(subject.add(1, child1)).to be_nil
|
70
|
-
subject.add(0, child1)
|
71
|
-
expect(subject.add(2, child2)).to be_nil
|
72
|
-
subject.add(1, child2)
|
73
|
-
expect(subject.add(3, child3)).to be_nil
|
74
|
-
end
|
75
|
-
|
76
|
-
it 'does not add to the list of children' do
|
77
|
-
subject.add(1, child1)
|
78
|
-
expect(subject.children).to eq({})
|
79
|
-
|
80
|
-
subject.add(0, child1)
|
81
|
-
subject.add(2, child2)
|
82
|
-
expect(subject.children).to eq(0 => child1)
|
83
|
-
|
84
|
-
subject.add(1, child2)
|
85
|
-
subject.add(3, child3)
|
86
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
context 'and is less than the number of children' do
|
91
|
-
it 'returns the child' do
|
92
|
-
subject.add(0, child1)
|
93
|
-
expect(subject.add(0, child2)).to eq(child2)
|
94
|
-
expect(subject.add(1, child3)).to eq(child3)
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'adds to the list of children at the correct position' do
|
98
|
-
expect(subject.children).to eq({})
|
99
|
-
subject.add(0, child1)
|
100
|
-
expect(subject.children).to eq(0 => child1)
|
101
|
-
subject.add(0, child2)
|
102
|
-
expect(subject.children).to eq(0 => child2, 1 => child1)
|
103
|
-
subject.add(1, child3)
|
104
|
-
expect(subject.children).to eq(0 => child2, 1 => child3, 2 => child1)
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'calls #update_parent on the child with itself and an ID proc' do
|
108
|
-
expect(child1).to receive(:update_parent) do |parent, proc|
|
109
|
-
expect(parent).to eq(subject)
|
110
|
-
expect(proc.call).to eq(0)
|
111
|
-
end
|
112
|
-
subject.add(0, child2)
|
113
|
-
subject.add(0, child1)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
describe '#remove' do
|
120
|
-
context 'when the child exists in the list' do
|
121
|
-
before(:each) { subject.add(0, child1) }
|
122
|
-
|
123
|
-
it 'returns the child' do
|
124
|
-
expect(subject.remove(child1)).to eq(child1)
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'calls #remove_parent on the child' do
|
128
|
-
expect(child1).to receive(:remove_parent).once.with(no_args)
|
129
|
-
subject.remove(child1)
|
130
|
-
end
|
131
|
-
|
132
|
-
it 'moves succeeding IDs down by one' do
|
133
|
-
subject.add(1, child2)
|
134
|
-
subject.add(2, child3)
|
135
|
-
expect(subject.children).to eq(0 => child1, 1 => child2, 2 => child3)
|
136
|
-
subject.remove(child2)
|
137
|
-
expect(subject.children).to eq(0 => child1, 1 => child3)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
context 'when the child does not exist in the list' do
|
142
|
-
specify { expect(subject.remove(child1)).to be_nil }
|
143
|
-
|
144
|
-
it 'does not change the children' do
|
145
|
-
expect(subject.children).to eq({})
|
146
|
-
subject.remove(child1)
|
147
|
-
expect(subject.children).to eq({})
|
148
|
-
|
149
|
-
subject.add(0, child1)
|
150
|
-
subject.add(1, child2)
|
151
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
152
|
-
subject.remove(child3)
|
153
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
describe '#remove_id' do
|
159
|
-
context 'when the ID exists' do
|
160
|
-
before(:each) { subject.add(0, child1) }
|
161
|
-
|
162
|
-
it 'returns the child' do
|
163
|
-
expect(subject.remove_id(0)).to eq(child1)
|
164
|
-
end
|
165
|
-
|
166
|
-
it 'calls #remove_parent on the child' do
|
167
|
-
expect(child1).to receive(:remove_parent).once.with(no_args)
|
168
|
-
subject.remove_id(0)
|
169
|
-
end
|
170
|
-
|
171
|
-
it 'moves succeeding IDs down by one' do
|
172
|
-
subject.add(1, child2)
|
173
|
-
subject.add(2, child3)
|
174
|
-
expect(subject.children).to eq(0 => child1, 1 => child2, 2 => child3)
|
175
|
-
subject.remove_id(1)
|
176
|
-
expect(subject.children).to eq(0 => child1, 1 => child3)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
context 'when the ID does not exist' do
|
181
|
-
specify { expect(subject.remove_id(0)).to be_nil }
|
182
|
-
|
183
|
-
it 'does not change the children' do
|
184
|
-
expect(subject.children).to eq({})
|
185
|
-
subject.remove_id(0)
|
186
|
-
expect(subject.children).to eq({})
|
187
|
-
|
188
|
-
subject.add(0, child1)
|
189
|
-
subject.add(1, child2)
|
190
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
191
|
-
subject.remove_id(2)
|
192
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
describe '#children' do
|
198
|
-
context 'when the list has no children' do
|
199
|
-
it 'returns the empty hash' do
|
200
|
-
expect(subject.children).to eq({})
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
context 'when the list has children' do
|
205
|
-
it 'returns a hash mapping their current indices to themselves' do
|
206
|
-
expect(subject.children).to eq({})
|
207
|
-
|
208
|
-
subject.add(0, child1)
|
209
|
-
expect(subject.children).to eq(0 => child1)
|
210
|
-
|
211
|
-
subject.add(1, child2)
|
212
|
-
expect(subject.children).to eq(0 => child1, 1 => child2)
|
213
|
-
|
214
|
-
subject.add(2, child3)
|
215
|
-
expect(subject.children).to eq(0 => child1, 1 => child2, 2 => child3)
|
216
|
-
|
217
|
-
subject.remove(child2)
|
218
|
-
expect(subject.children).to eq(0 => child1, 1 => child3)
|
219
|
-
|
220
|
-
subject.add(0, child2)
|
221
|
-
expect(subject.children).to eq(0 => child2, 1 => child1, 2 => child3)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
6
|
+
it_behaves_like 'an array composite'
|
225
7
|
end
|