compo 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/compo.svg)](http://badge.fury.io/rb/compo)
|
4
|
+
[![Build Status](https://travis-ci.org/CaptainHayashi/compo.svg)](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
|