compo 0.1.0

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