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.
@@ -0,0 +1,208 @@
1
+ require 'compo'
2
+ require 'composite_shared_examples'
3
+
4
+ shared_examples 'a hash 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 occupied' do
19
+ before(:each) { subject.add(:a, child1) }
20
+ it 'returns the new child' do
21
+ expect(subject.add(:a, child2)).to eq(child2)
22
+ end
23
+
24
+ it 'replaces the old child in the child hash' do
25
+ expect(subject.children).to eq(a: child1)
26
+
27
+ subject.add(:a, child2)
28
+ expect(subject.children).to eq(a: child2)
29
+ end
30
+
31
+ it 'calls #update_parent on the new child with itself and an ID proc' do
32
+ expect(child2).to receive(:update_parent).once do |parent, proc|
33
+ expect(parent).to eq(subject)
34
+ expect(proc.call).to eq(:a)
35
+ end
36
+ subject.add(:a, child2)
37
+ end
38
+
39
+ it 'calls #update_parent on the old child with a Parentless' do
40
+ expect(child1).to receive(:update_parent).once do |parent, _|
41
+ expect(parent).to be_a(Compo::Parentless)
42
+ end
43
+ subject.add(:a, child2)
44
+ end
45
+
46
+ it 'calls #update_parent on the old child with nil-returning ID proc' do
47
+ expect(child1).to receive(:update_parent).once do |_, idp|
48
+ expect(idp.call).to be_nil
49
+ end
50
+ subject.add(:a, child2)
51
+ end
52
+ end
53
+
54
+ context 'when the ID is not occupied' do
55
+ it 'returns the child' do
56
+ expect(subject.add(:a, child1)).to eq(child1)
57
+ expect(subject.add(:b, child2)).to eq(child2)
58
+ expect(subject.add(:c, child3)).to eq(child3)
59
+ end
60
+
61
+ it 'adds to the child hash' do
62
+ expect(subject.children).to eq({})
63
+
64
+ subject.add(:a, child1)
65
+ expect(subject.children).to eq(a: child1)
66
+
67
+ subject.add(:b, child2)
68
+ expect(subject.children).to eq(a: child1, b: child2)
69
+
70
+ subject.add(:c, child3)
71
+ expect(subject.children).to eq(a: child1, b: child2, c: child3)
72
+ end
73
+
74
+ it 'calls #update_parent on the child with itself and an ID proc' do
75
+ expect(child1).to receive(:update_parent) do |parent, proc|
76
+ expect(parent).to eq(subject)
77
+ expect(proc.call).to eq(:a)
78
+ end
79
+ subject.add(:a, child1)
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#remove' do
85
+ context 'when the child exists in the hash' do
86
+ before(:each) { subject.add(:a, child1) }
87
+
88
+ it 'returns the child' do
89
+ expect(subject.remove(child1)).to eq(child1)
90
+ end
91
+
92
+ it 'calls #update_parent on the child with a Parentless' do
93
+ expect(child1).to receive(:update_parent).once do |parent, _|
94
+ expect(parent).to be_a(Compo::Parentless)
95
+ end
96
+ subject.remove(child1)
97
+ end
98
+
99
+ it 'calls #update_parent on the child with a nil-returning ID proc' do
100
+ expect(child1).to receive(:update_parent).once do |_, idp|
101
+ expect(idp.call).to be_nil
102
+ end
103
+ subject.remove(child1)
104
+ end
105
+
106
+ it 'removes the child, and only the child, from the children hash' do
107
+ subject.add(:b, child2)
108
+ subject.add(:c, child3)
109
+ expect(subject.children).to eq(a: child1, b: child2, c: child3)
110
+ subject.remove(child2)
111
+ expect(subject.children).to eq(a: child1, c: child3)
112
+ end
113
+ end
114
+
115
+ context 'when the child does not exist in the hash' do
116
+ specify { expect(subject.remove(child1)).to be_nil }
117
+
118
+ it 'does not change the children' do
119
+ expect(subject.children).to eq({})
120
+ subject.remove(child1)
121
+ expect(subject.children).to eq({})
122
+
123
+ subject.add(:a, child1)
124
+ subject.add(:b, child2)
125
+ expect(subject.children).to eq(a: child1, b: child2)
126
+ subject.remove(child3)
127
+ expect(subject.children).to eq(a: child1, b: child2)
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '#remove_id' do
133
+ context 'when the ID exists' do
134
+ before(:each) { subject.add(:a, child1) }
135
+
136
+ it 'returns the child' do
137
+ expect(subject.remove_id(:a)).to eq(child1)
138
+ end
139
+
140
+ it 'calls #update_parent on the child with a Parentless' do
141
+ expect(child1).to receive(:update_parent).once do |parent, _|
142
+ expect(parent).to be_a(Compo::Parentless)
143
+ end
144
+ subject.remove_id(:a)
145
+ end
146
+
147
+ it 'calls #update_parent on the child with a nil-returning ID proc' do
148
+ expect(child1).to receive(:update_parent).once do |_, idp|
149
+ expect(idp.call).to be_nil
150
+ end
151
+ subject.remove_id(:a)
152
+ end
153
+
154
+ it 'does not change the IDs of other children' do
155
+ subject.add(:b, child2)
156
+ subject.add(:c, child3)
157
+ expect(subject.children).to eq(a: child1, b: child2, c: child3)
158
+ subject.remove_id(:b)
159
+ expect(subject.children).to eq(a: child1, c: child3)
160
+ end
161
+ end
162
+
163
+ context 'when the ID does not exist' do
164
+ specify { expect(subject.remove_id(:a)).to be_nil }
165
+
166
+ it 'does not change the children' do
167
+ expect(subject.children).to eq({})
168
+ subject.remove_id(:a)
169
+ expect(subject.children).to eq({})
170
+
171
+ subject.add(:a, child1)
172
+ subject.add(:b, child2)
173
+ expect(subject.children).to eq(a: child1, b: child2)
174
+ subject.remove_id(:c)
175
+ expect(subject.children).to eq(a: child1, b: child2)
176
+ end
177
+ end
178
+ end
179
+
180
+ describe '#children' do
181
+ context 'when the hash has no children' do
182
+ it 'returns the empty hash' do
183
+ expect(subject.children).to eq({})
184
+ end
185
+ end
186
+
187
+ context 'when the hash has children' do
188
+ it 'returns the current hash' do
189
+ expect(subject.children).to eq({})
190
+
191
+ subject.add(:a, child1)
192
+ expect(subject.children).to eq(a: child1)
193
+
194
+ subject.add(:b, child2)
195
+ expect(subject.children).to eq(a: child1, b: child2)
196
+
197
+ subject.add(:c, child3)
198
+ expect(subject.children).to eq(a: child1, b: child2, c: child3)
199
+
200
+ subject.remove(child2)
201
+ expect(subject.children).to eq(a: child1, c: child3)
202
+
203
+ subject.add(:a, child2)
204
+ expect(subject.children).to eq(a: child2, c: child3)
205
+ end
206
+ end
207
+ end
208
+ end
@@ -1,184 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'compo'
3
+ require 'hash_composite_shared_examples'
3
4
 
4
5
  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
6
+ it_behaves_like 'a hash composite'
184
7
  end
data/spec/leaf_spec.rb CHANGED
@@ -1,30 +1,9 @@
1
1
  require 'spec_helper'
2
2
  require 'compo'
3
+ require 'branch_shared_examples'
4
+ require 'null_composite_shared_examples'
3
5
 
4
6
  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
-
15
- describe '#url' do
16
- context 'when the Leaf has no parent' do
17
- it 'returns the empty string' do
18
- expect(subject.url).to eq('')
19
- end
20
- end
21
- context 'when the Leaf is the child of a root' do
22
- let(:parent) { Compo::HashBranch.new }
23
- before(:each) { subject.move_to(parent, :id) }
24
-
25
- it 'returns /ID, where ID is the ID of the Leaf' do
26
- expect(subject.url).to eq('/id')
27
- end
28
- end
29
- end
7
+ it_behaves_like 'a branch'
8
+ it_behaves_like 'a null composite'
30
9
  end
@@ -0,0 +1,154 @@
1
+ require 'compo'
2
+
3
+ shared_examples 'a normal call to #move_to' do
4
+ it 'returns itself' do
5
+ expect(subject.move_to(to, :test)).to eq(subject)
6
+ end
7
+
8
+ it 'calls #parent' do
9
+ expect(subject).to receive(:parent)
10
+ subject.move_to(to, :test)
11
+ end
12
+ end
13
+
14
+ shared_examples 'a removal from the old parent' do
15
+ it 'calls #remove on the old parent with the Movable' do
16
+ expect(from).to receive(:remove).once.with(subject)
17
+ subject.move_to(to, :test)
18
+ end
19
+ end
20
+
21
+ shared_examples 'an addition to the new parent' do
22
+ it 'calls #add on the new parent with the ID and Movable' do
23
+ expect(to).to_not be_nil
24
+ expect(to).to receive(:add).once.with(:test, subject)
25
+ subject.move_to(to, :test)
26
+ end
27
+ end
28
+
29
+ shared_examples 'a movable object' do
30
+ let(:old_parent) { double(:old_parent) }
31
+ let(:new_parent) { double(:new_parent) }
32
+ let(:remove_result) { subject }
33
+ let(:add_result) { subject }
34
+
35
+ before(:each) do
36
+ allow(subject).to receive(:parent).and_return(old_parent)
37
+ allow(old_parent).to receive(:remove).and_return(remove_result)
38
+
39
+ unless new_parent.nil?
40
+ allow(new_parent).to receive(:add).and_return(add_result)
41
+ end
42
+ end
43
+
44
+ describe '#move_to' do
45
+ context 'when the receiving parent is nil' do
46
+ let(:new_parent) { nil }
47
+
48
+ context 'and the Movable has no parent' do
49
+ let(:old_parent) { Compo::Parentless.new }
50
+
51
+ it_behaves_like 'a normal call to #move_to' do
52
+ let(:to) { new_parent }
53
+ end
54
+ end
55
+
56
+ context 'and the Movable has a parent' do
57
+ it_behaves_like 'a normal call to #move_to' do
58
+ let(:to) { new_parent }
59
+ end
60
+
61
+ it_behaves_like 'a removal from the old parent' do
62
+ let(:to) { new_parent }
63
+ let(:from) { old_parent }
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'when the receiving parent refuses to add the Movable' do
69
+ let(:add_result) { nil }
70
+
71
+ context 'and the Movable has no parent' do
72
+ let(:old_parent) { Compo::Parentless.new }
73
+
74
+ it_behaves_like 'a normal call to #move_to' do
75
+ let(:to) { new_parent }
76
+ end
77
+
78
+ it_behaves_like 'an addition to the new parent' do
79
+ let(:to) { new_parent }
80
+ end
81
+ end
82
+
83
+ context 'and the Movable has a parent that refuses to remove it' do
84
+ let(:remove_result) { nil }
85
+
86
+ it_behaves_like 'a normal call to #move_to' do
87
+ let(:to) { new_parent }
88
+ end
89
+
90
+ it_behaves_like 'a removal from the old parent' do
91
+ let(:to) { new_parent }
92
+ let(:from) { old_parent }
93
+ end
94
+ end
95
+
96
+ context 'and the Movable has a parent that allows it to be removed' do
97
+ it_behaves_like 'a normal call to #move_to' do
98
+ let(:to) { new_parent }
99
+ end
100
+
101
+ it_behaves_like 'a removal from the old parent' do
102
+ let(:to) { new_parent }
103
+ let(:from) { old_parent }
104
+ end
105
+
106
+ it_behaves_like 'an addition to the new parent' do
107
+ let(:to) { new_parent }
108
+ end
109
+ end
110
+ end
111
+
112
+ context 'when the receiving parent allows the Movable to be added' do
113
+ context 'and the Movable has no parent' do
114
+ let(:old_parent) { Compo::Parentless.new }
115
+
116
+ it_behaves_like 'a normal call to #move_to' do
117
+ let(:to) { new_parent }
118
+ end
119
+
120
+ it_behaves_like 'an addition to the new parent' do
121
+ let(:to) { new_parent }
122
+ end
123
+ end
124
+
125
+ context 'and the Movable has a parent that refuses to remove it' do
126
+ let(:remove_result) { nil }
127
+
128
+ it_behaves_like 'a normal call to #move_to' do
129
+ let(:to) { new_parent }
130
+ end
131
+
132
+ it_behaves_like 'a removal from the old parent' do
133
+ let(:to) { new_parent }
134
+ let(:from) { old_parent }
135
+ end
136
+ end
137
+
138
+ context 'and the Movable has a parent that allows it to be removed' do
139
+ it_behaves_like 'a normal call to #move_to' do
140
+ let(:to) { new_parent }
141
+ end
142
+
143
+ it_behaves_like 'a removal from the old parent' do
144
+ let(:to) { new_parent }
145
+ let(:from) { old_parent }
146
+ end
147
+
148
+ it_behaves_like 'an addition to the new parent' do
149
+ let(:to) { new_parent }
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end