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.
@@ -0,0 +1,225 @@
1
+ require 'spec_helper'
2
+ require 'compo'
3
+
4
+ describe Compo::ArrayComposite do
5
+ let(:child1) { double(:child1) }
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
225
+ end
@@ -0,0 +1,249 @@
1
+ require 'spec_helper'
2
+ require 'compo'
3
+
4
+ # Mock implementation of a Composite
5
+ class MockComposite
6
+ include Compo::Composite
7
+ end
8
+
9
+ describe MockComposite do
10
+ let(:id) { double(:id) }
11
+ let(:child) { double(:child) }
12
+
13
+ describe '#add' do
14
+ before(:each) { allow(subject).to receive(:add!) }
15
+
16
+ context 'when #add! returns nil' do
17
+ before(:each) { allow(subject).to receive(:add!).and_return(nil) }
18
+
19
+ specify { expect(subject.add(id, child)).to be_nil }
20
+
21
+ it 'calls #add! with the ID and child given' do
22
+ expect(subject).to receive(:add!).once.with(id, child)
23
+ subject.add(id, child)
24
+ end
25
+ end
26
+
27
+ context 'when #add! returns the child' do
28
+ before(:each) do
29
+ allow(subject).to receive(:add!).and_return(child)
30
+ allow(subject).to receive(:id_function)
31
+ allow(child).to receive(:update_parent)
32
+ end
33
+
34
+ it 'calls #add! with the ID and child given' do
35
+ expect(subject).to receive(:add!).once.with(id, child)
36
+ subject.add(id, child)
37
+ end
38
+
39
+ it 'calls #id_function with the child given' do
40
+ expect(subject).to receive(:id_function).once.with(child)
41
+ subject.add(id, child)
42
+ end
43
+
44
+ it 'calls #update_parent on the child with the parent and ID function' do
45
+ idf = double(:id_function)
46
+ allow(subject).to receive(:id_function).and_return(idf)
47
+ expect(child).to receive(:update_parent).once.with(subject, idf)
48
+ subject.add(id, child)
49
+ end
50
+
51
+ it 'returns the given child' do
52
+ expect(subject.add(id, child)).to eq(child)
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '#remove' do
58
+ context 'when #remove! is defined' do
59
+ before(:each) { allow(subject).to receive(:remove!) }
60
+
61
+ context 'when #remove! returns nil' do
62
+ before(:each) { allow(subject).to receive(:remove!).and_return(nil) }
63
+
64
+ specify { expect(subject.remove(child)).to be_nil }
65
+
66
+ it 'calls #remove! with the child given' do
67
+ expect(subject).to receive(:remove!).once.with(child)
68
+ subject.remove(child)
69
+ end
70
+ end
71
+
72
+ context 'when #remove! returns the child' do
73
+ before(:each) do
74
+ allow(subject).to receive(:remove!).and_return(child)
75
+ allow(child).to receive(:remove_parent)
76
+ end
77
+
78
+ it 'calls #remove! with the child given' do
79
+ expect(subject).to receive(:remove!).once.with(child)
80
+ subject.remove(child)
81
+ end
82
+
83
+ it 'calls #remove_parent on the child with no arguments' do
84
+ expect(child).to receive(:remove_parent).once.with(no_args)
85
+ subject.remove(child)
86
+ end
87
+
88
+ it 'returns the given child' do
89
+ expect(subject.remove(child)).to eq(child)
90
+ end
91
+ end
92
+ end
93
+
94
+ context 'when #remove_id! is defined but #remove! is not' do
95
+ before(:each) do
96
+ allow(subject).to receive(:remove_id!)
97
+ allow(subject).to receive(:children).and_return(id => child)
98
+ end
99
+
100
+ context 'when #remove_id! returns nil' do
101
+ before(:each) do
102
+ allow(subject).to receive(:remove_id!).and_return(nil)
103
+ end
104
+
105
+ specify { expect(subject.remove(child)).to be_nil }
106
+
107
+ it 'calls #remove! with the child given' do
108
+ expect(subject).to receive(:remove!).once.with(child)
109
+ subject.remove(child)
110
+ end
111
+
112
+ it 'calls #remove_id! with the ID of the child' do
113
+ expect(subject).to receive(:remove_id!).once.with(id)
114
+ subject.remove(child)
115
+ end
116
+
117
+ it 'calls #children' do
118
+ expect(subject).to receive(:children).once
119
+ subject.remove(child)
120
+ end
121
+ end
122
+
123
+ context 'when #remove_id! returns the child' do
124
+ before(:each) do
125
+ allow(subject).to receive(:remove_id!).and_return(child)
126
+ allow(child).to receive(:remove_parent)
127
+ end
128
+
129
+ it 'calls #remove_id! with the child given' do
130
+ expect(subject).to receive(:remove!).once.with(child)
131
+ subject.remove(child)
132
+ end
133
+
134
+ it 'calls #remove_parent on the child with no arguments' do
135
+ expect(child).to receive(:remove_parent).once.with(no_args)
136
+ subject.remove(child)
137
+ end
138
+
139
+ it 'returns the given child' do
140
+ expect(subject.remove(child)).to eq(child)
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ describe '#remove_id' do
147
+ let(:id) { double(:id) }
148
+
149
+ context 'when #remove_id! is defined' do
150
+ before(:each) { allow(subject).to receive(:remove_id!) }
151
+
152
+ context 'and #remove_id! returns nil' do
153
+ before(:each) do
154
+ allow(subject).to receive(:remove_id!).and_return(nil)
155
+ end
156
+
157
+ specify { expect(subject.remove_id(id)).to be_nil }
158
+
159
+ it 'calls #remove_id! with the ID given' do
160
+ expect(subject).to receive(:remove_id!).once.with(id)
161
+ subject.remove_id(id)
162
+ end
163
+ end
164
+
165
+ context 'and #remove_id! returns the child' do
166
+ before(:each) do
167
+ allow(subject).to receive(:remove_id!).and_return(child)
168
+ allow(child).to receive(:remove_parent)
169
+ end
170
+
171
+ it 'calls #remove_id! with the ID given' do
172
+ expect(subject).to receive(:remove_id!).once.with(id)
173
+ subject.remove_id(id)
174
+ end
175
+
176
+ it 'calls #remove_parent on the child with no arguments' do
177
+ expect(child).to receive(:remove_parent).once.with(no_args)
178
+ subject.remove_id(id)
179
+ end
180
+
181
+ it 'returns the child' do
182
+ expect(subject.remove_id(id)).to eq(child)
183
+ end
184
+ end
185
+ end
186
+
187
+ context 'when #remove! is defined but #remove_id! is not' do
188
+ before(:each) { allow(subject).to receive(:remove!) }
189
+
190
+ context 'and #remove! returns nil' do
191
+ before(:each) do
192
+ allow(subject).to receive(:remove!).and_return(nil)
193
+ allow(subject).to receive(:get_child).and_return(child)
194
+ end
195
+
196
+ specify { expect(subject.remove_id(id)).to be_nil }
197
+
198
+ it 'calls #remove_id! with the ID given' do
199
+ expect(subject).to receive(:remove_id!).once.with(id)
200
+ subject.remove_id(id)
201
+ end
202
+
203
+ it 'calls #get_child with the ID given' do
204
+ expect(subject).to receive(:get_child).once.with(id)
205
+ subject.remove_id(id)
206
+ end
207
+
208
+ it 'calls #remove! with the child given' do
209
+ expect(subject).to receive(:remove!).once.with(child)
210
+ subject.remove_id(id)
211
+ end
212
+ end
213
+
214
+ context 'and #remove! returns the child' do
215
+ before(:each) do
216
+ allow(subject).to receive(:remove!).and_return(child)
217
+ allow(subject).to receive(:get_child).and_return(child)
218
+ allow(child).to receive(:remove_parent)
219
+ end
220
+
221
+ it 'calls #remove! with the child given' do
222
+ expect(subject).to receive(:remove!).once.with(child)
223
+ subject.remove_id(id)
224
+ end
225
+
226
+ it 'calls #remove_parent on the child with no arguments' do
227
+ expect(child).to receive(:remove_parent).once.with(no_args)
228
+ subject.remove_id(id)
229
+ end
230
+
231
+ it 'returns the given child' do
232
+ expect(subject.remove_id(id)).to eq(child)
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ describe '#each' do
239
+ it 'delegates to the #each implementation of the hash from #children' do
240
+ children = double(:children)
241
+
242
+ allow(subject).to receive(:children).and_return(children)
243
+ expect(subject).to receive(:children).once.with(no_args)
244
+ expect(children).to receive(:each).once
245
+
246
+ subject.each
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,184 @@
1
+ require 'spec_helper'
2
+ require 'compo'
3
+
4
+ describe Compo::HashComposite do
5
+ let(:child1) { double(:child1) }
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 occupied' do
22
+ before(:each) { subject.add(:a, child1) }
23
+ it 'returns the new child' do
24
+ expect(subject.add(:a, child2)).to eq(child2)
25
+ end
26
+
27
+ it 'replaces the old child in the child hash' do
28
+ expect(subject.children).to eq(a: child1)
29
+
30
+ subject.add(:a, child2)
31
+ expect(subject.children).to eq(a: child2)
32
+ end
33
+
34
+ it 'calls #update_parent on the new child with itself and an ID proc' do
35
+ expect(child2).to receive(:update_parent).once do |parent, proc|
36
+ expect(parent).to eq(subject)
37
+ expect(proc.call).to eq(:a)
38
+ end
39
+ subject.add(:a, child2)
40
+ end
41
+
42
+ it 'calls #remove_parent on the old child' do
43
+ expect(child1).to receive(:remove_parent).once.with(no_args)
44
+ subject.add(:a, child2)
45
+ end
46
+ end
47
+
48
+ context 'when the ID is not occupied' do
49
+ it 'returns the child' do
50
+ expect(subject.add(:a, child1)).to eq(child1)
51
+ expect(subject.add(:b, child2)).to eq(child2)
52
+ expect(subject.add(:c, child3)).to eq(child3)
53
+ end
54
+
55
+ it 'adds to the child hash' do
56
+ expect(subject.children).to eq({})
57
+
58
+ subject.add(:a, child1)
59
+ expect(subject.children).to eq(a: child1)
60
+
61
+ subject.add(:b, child2)
62
+ expect(subject.children).to eq(a: child1, b: child2)
63
+
64
+ subject.add(:c, child3)
65
+ expect(subject.children).to eq(a: child1, b: child2, c: child3)
66
+ end
67
+
68
+ it 'calls #update_parent on the child with itself and an ID proc' do
69
+ expect(child1).to receive(:update_parent) do |parent, proc|
70
+ expect(parent).to eq(subject)
71
+ expect(proc.call).to eq(:a)
72
+ end
73
+ subject.add(:a, child1)
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#remove' do
79
+ context 'when the child exists in the hash' do
80
+ before(:each) { subject.add(:a, child1) }
81
+
82
+ it 'returns the child' do
83
+ expect(subject.remove(child1)).to eq(child1)
84
+ end
85
+
86
+ it 'calls #remove_parent on the child' do
87
+ expect(child1).to receive(:remove_parent).once.with(no_args)
88
+ subject.remove(child1)
89
+ end
90
+
91
+ it 'removes the child, and only the child, from the children hash' do
92
+ subject.add(:b, child2)
93
+ subject.add(:c, child3)
94
+ expect(subject.children).to eq(a: child1, b: child2, c: child3)
95
+ subject.remove(child2)
96
+ expect(subject.children).to eq(a: child1, c: child3)
97
+ end
98
+ end
99
+
100
+ context 'when the child does not exist in the hash' do
101
+ specify { expect(subject.remove(child1)).to be_nil }
102
+
103
+ it 'does not change the children' do
104
+ expect(subject.children).to eq({})
105
+ subject.remove(child1)
106
+ expect(subject.children).to eq({})
107
+
108
+ subject.add(:a, child1)
109
+ subject.add(:b, child2)
110
+ expect(subject.children).to eq(a: child1, b: child2)
111
+ subject.remove(child3)
112
+ expect(subject.children).to eq(a: child1, b: child2)
113
+ end
114
+ end
115
+ end
116
+
117
+ describe '#remove_id' do
118
+ context 'when the ID exists' do
119
+ before(:each) { subject.add(:a, child1) }
120
+
121
+ it 'returns the child' do
122
+ expect(subject.remove_id(:a)).to eq(child1)
123
+ end
124
+
125
+ it 'calls #remove_parent on the child' do
126
+ expect(child1).to receive(:remove_parent).once.with(no_args)
127
+ subject.remove_id(:a)
128
+ end
129
+
130
+ it 'does not change the IDs of other children' do
131
+ subject.add(:b, child2)
132
+ subject.add(:c, child3)
133
+ expect(subject.children).to eq(a: child1, b: child2, c: child3)
134
+ subject.remove_id(:b)
135
+ expect(subject.children).to eq(a: child1, c: child3)
136
+ end
137
+ end
138
+
139
+ context 'when the ID does not exist' do
140
+ specify { expect(subject.remove_id(:a)).to be_nil }
141
+
142
+ it 'does not change the children' do
143
+ expect(subject.children).to eq({})
144
+ subject.remove_id(:a)
145
+ expect(subject.children).to eq({})
146
+
147
+ subject.add(:a, child1)
148
+ subject.add(:b, child2)
149
+ expect(subject.children).to eq(a: child1, b: child2)
150
+ subject.remove_id(:c)
151
+ expect(subject.children).to eq(a: child1, b: child2)
152
+ end
153
+ end
154
+ end
155
+
156
+ describe '#children' do
157
+ context 'when the hash has no children' do
158
+ it 'returns the empty hash' do
159
+ expect(subject.children).to eq({})
160
+ end
161
+ end
162
+
163
+ context 'when the hash has children' do
164
+ it 'returns the current hash' do
165
+ expect(subject.children).to eq({})
166
+
167
+ subject.add(:a, child1)
168
+ expect(subject.children).to eq(a: child1)
169
+
170
+ subject.add(:b, child2)
171
+ expect(subject.children).to eq(a: child1, b: child2)
172
+
173
+ subject.add(:c, child3)
174
+ expect(subject.children).to eq(a: child1, b: child2, c: child3)
175
+
176
+ subject.remove(child2)
177
+ expect(subject.children).to eq(a: child1, c: child3)
178
+
179
+ subject.add(:a, child2)
180
+ expect(subject.children).to eq(a: child2, c: child3)
181
+ end
182
+ end
183
+ end
184
+ end
data/spec/leaf_spec.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'compo'
3
+
4
+ describe Compo::Leaf do
5
+ describe '#initialize' do
6
+ it 'initialises with no parent' do
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
14
+ end