cartograph 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +211 -0
- data/Rakefile +6 -0
- data/cartograph.gemspec +24 -0
- data/examples/collection_representation.rb +41 -0
- data/examples/domains.rb +54 -0
- data/examples/representation_for.rb +24 -0
- data/lib/cartograph.rb +24 -0
- data/lib/cartograph/artist.rb +34 -0
- data/lib/cartograph/dsl.rb +104 -0
- data/lib/cartograph/map.rb +91 -0
- data/lib/cartograph/property.rb +69 -0
- data/lib/cartograph/property_collection.rb +27 -0
- data/lib/cartograph/root_key.rb +19 -0
- data/lib/cartograph/sculptor.rb +40 -0
- data/lib/cartograph/version.rb +3 -0
- data/spec/lib/cartograph/artist_spec.rb +115 -0
- data/spec/lib/cartograph/dsl_spec.rb +257 -0
- data/spec/lib/cartograph/map_spec.rb +160 -0
- data/spec/lib/cartograph/property_collection_spec.rb +15 -0
- data/spec/lib/cartograph/property_spec.rb +235 -0
- data/spec/lib/cartograph/root_key_spec.rb +38 -0
- data/spec/lib/cartograph/sculptor_spec.rb +107 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/support/dsl_contexts.rb +22 -0
- data/spec/support/dummy_comment.rb +2 -0
- data/spec/support/dummy_user.rb +2 -0
- metadata +132 -0
@@ -0,0 +1,257 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cartograph::DSL do
|
4
|
+
describe 'Inclusion' do
|
5
|
+
it 'gives you a class method for .cartograph' do
|
6
|
+
klass = Class.new
|
7
|
+
expect { klass.send(:include, described_class) }.to change { klass.respond_to?(:cartograph) }.to(true)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.cartograph' do
|
12
|
+
subject(:mapping) { Class.new { include Cartograph::DSL } }
|
13
|
+
|
14
|
+
it 'yields a Cartograph::Map instance' do
|
15
|
+
expect {|b| mapping.cartograph(&b) }.to yield_with_args(Cartograph::Map.new)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns the map instance' do
|
19
|
+
expect(mapping.cartograph).to be_kind_of(Cartograph::Map)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '.hash_for' do
|
24
|
+
include_context 'DSL Objects'
|
25
|
+
|
26
|
+
it 'returns the hash representation for an object' do
|
27
|
+
hash = mapped.hash_for(:create, object)
|
28
|
+
expect(hash).to eq({ 'name' => object.name })
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with a root key for the scope' do
|
32
|
+
it 'returns the hash with the root key' do
|
33
|
+
mapped.cartograph do
|
34
|
+
root_key singular: 'user', scopes: [:create]
|
35
|
+
end
|
36
|
+
hash = mapped.hash_for(:create, object)
|
37
|
+
|
38
|
+
expect(hash).to eq(
|
39
|
+
{
|
40
|
+
'user' => {
|
41
|
+
'name' => object.name
|
42
|
+
}
|
43
|
+
}
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '.hash_collection_for' do
|
50
|
+
include_context 'DSL Objects'
|
51
|
+
|
52
|
+
let(:users) { Array.new(3, object) }
|
53
|
+
|
54
|
+
subject(:parsed) do
|
55
|
+
json = mapped.represent_collection_for(:read, users)
|
56
|
+
JSON.parse(json)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns the objects as a collection of hashes' do
|
60
|
+
collection = mapped.hash_collection_for(:read, users)
|
61
|
+
|
62
|
+
expect(collection).to be_an(Array)
|
63
|
+
expect(collection.size).to be(3)
|
64
|
+
|
65
|
+
expect(collection[0]['id']).to eq(users[0].id)
|
66
|
+
expect(collection[0]['name']).to eq(users[0].name)
|
67
|
+
expect(collection[1]['id']).to eq(users[1].id)
|
68
|
+
expect(collection[1]['name']).to eq(users[1].name)
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with a root key' do
|
72
|
+
it "includes the root key" do
|
73
|
+
root_key_name = "the_root_key"
|
74
|
+
|
75
|
+
mapped.cartograph do
|
76
|
+
root_key plural: root_key_name, scopes: [:read]
|
77
|
+
end
|
78
|
+
|
79
|
+
collection = mapped.hash_collection_for(:read, users)
|
80
|
+
|
81
|
+
expect(collection).to be_an(Hash)
|
82
|
+
expect(collection.keys.first).to eq(root_key_name)
|
83
|
+
|
84
|
+
collection_array = collection[root_key_name]
|
85
|
+
expect(collection_array[0]['id']).to eq(users[0].id)
|
86
|
+
expect(collection_array[0]['name']).to eq(users[0].name)
|
87
|
+
expect(collection_array[1]['id']).to eq(users[1].id)
|
88
|
+
expect(collection_array[1]['name']).to eq(users[1].name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '.representation_for' do
|
94
|
+
include_context 'DSL Objects'
|
95
|
+
|
96
|
+
it 'returns the JSON representation for an object' do
|
97
|
+
json = mapped.representation_for(:create, object)
|
98
|
+
expect(json).to eq(
|
99
|
+
{ name: object.name }.to_json
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'with a root key for the scope' do
|
104
|
+
it 'returns the json with the root key' do
|
105
|
+
mapped.cartograph do
|
106
|
+
root_key singular: 'user', scopes: [:create]
|
107
|
+
end
|
108
|
+
json = mapped.representation_for(:create, object)
|
109
|
+
|
110
|
+
expect(json).to eq(
|
111
|
+
{
|
112
|
+
user: {
|
113
|
+
name: object.name
|
114
|
+
}
|
115
|
+
}.to_json
|
116
|
+
)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '.represent_collection_for' do
|
122
|
+
include_context 'DSL Objects'
|
123
|
+
|
124
|
+
let(:users) { Array.new(3, object) }
|
125
|
+
|
126
|
+
subject(:parsed) do
|
127
|
+
json = mapped.represent_collection_for(:read, users)
|
128
|
+
JSON.parse(json)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'returns the objects as a collection' do
|
132
|
+
json = mapped.represent_collection_for(:read, users)
|
133
|
+
parsed = JSON.parse(json)
|
134
|
+
|
135
|
+
expect(parsed).to be_an(Array)
|
136
|
+
expect(parsed.size).to be(3)
|
137
|
+
|
138
|
+
expect(parsed[0]['id']).to eq(users[0].id)
|
139
|
+
expect(parsed[0]['name']).to eq(users[0].name)
|
140
|
+
expect(parsed[1]['id']).to eq(users[1].id)
|
141
|
+
expect(parsed[1]['name']).to eq(users[1].name)
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'with a root key' do
|
145
|
+
it "includes the root key" do
|
146
|
+
root_key_name = "the_root_key"
|
147
|
+
|
148
|
+
mapped.cartograph do
|
149
|
+
root_key plural: root_key_name, scopes: [:read]
|
150
|
+
end
|
151
|
+
|
152
|
+
expect(parsed).to be_an(Hash)
|
153
|
+
expect(parsed.keys.first).to eq(root_key_name)
|
154
|
+
|
155
|
+
parsed_array = parsed[root_key_name]
|
156
|
+
expect(parsed_array[0]['id']).to eq(users[0].id)
|
157
|
+
expect(parsed_array[0]['name']).to eq(users[0].name)
|
158
|
+
expect(parsed_array[1]['id']).to eq(users[1].id)
|
159
|
+
expect(parsed_array[1]['name']).to eq(users[1].name)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '.extract_single' do
|
165
|
+
include_context 'DSL Objects'
|
166
|
+
let(:json) do
|
167
|
+
{ id: 1337, name: 'Paul the octopus' }
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'returns a populated object from a JSON representation' do
|
171
|
+
extracted = mapped.extract_single(json.to_json, :read)
|
172
|
+
|
173
|
+
expect(extracted.id).to eq(1337)
|
174
|
+
expect(extracted.name).to eq('Paul the octopus')
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'with a root key in the JSON' do
|
178
|
+
let(:json) { { test: super() } }
|
179
|
+
|
180
|
+
before do
|
181
|
+
mapped.cartograph do
|
182
|
+
root_key singular: 'test', scopes: [:read]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'traverses into the key and pulls the object from there' do
|
187
|
+
extracted = mapped.extract_single(json.to_json, :read)
|
188
|
+
|
189
|
+
expect(extracted.id).to eq(1337)
|
190
|
+
expect(extracted.name).to eq('Paul the octopus')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe '.extract_into_object' do
|
196
|
+
include_context 'DSL Objects'
|
197
|
+
let(:json) do
|
198
|
+
{ id: 1337, name: 'Paul the octopus' }
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'returns a populated object from a JSON representation' do
|
202
|
+
object = DummyUser.new
|
203
|
+
mapped.extract_into_object(object, json.to_json, :read)
|
204
|
+
|
205
|
+
expect(object.id).to eq(1337)
|
206
|
+
expect(object.name).to eq('Paul the octopus')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '.extract_collection' do
|
211
|
+
include_context 'DSL Objects'
|
212
|
+
let(:json) do
|
213
|
+
[
|
214
|
+
{ id: 1337, name: 'Paul the octopus' },
|
215
|
+
{ id: 1338, name: 'Hank the octopus' }
|
216
|
+
]
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'returns a collection of objects from the json' do
|
220
|
+
extracted = mapped.extract_collection(json.to_json, :read)
|
221
|
+
|
222
|
+
expect(extracted.size).to be(2)
|
223
|
+
expect(extracted).to all(be_kind_of(DummyUser))
|
224
|
+
|
225
|
+
expect(extracted[0].id).to eq(json[0][:id])
|
226
|
+
expect(extracted[0].name).to eq(json[0][:name])
|
227
|
+
|
228
|
+
expect(extracted[1].id).to eq(json[1][:id])
|
229
|
+
expect(extracted[1].name).to eq(json[1][:name])
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'for a nested key' do
|
233
|
+
let(:json) { { users: super() } }
|
234
|
+
|
235
|
+
before do
|
236
|
+
mapped.cartograph do
|
237
|
+
root_key plural: 'users', scopes: [:read]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'returns a collection of objects from the json' do
|
242
|
+
extracted = mapped.extract_collection(json.to_json, :read)
|
243
|
+
|
244
|
+
expect(extracted.size).to be(2)
|
245
|
+
expect(extracted).to all(be_kind_of(DummyUser))
|
246
|
+
|
247
|
+
scoped = json[:users]
|
248
|
+
|
249
|
+
expect(extracted[0].id).to eq(scoped[0][:id])
|
250
|
+
expect(extracted[0].name).to eq(scoped[0][:name])
|
251
|
+
|
252
|
+
expect(extracted[1].id).to eq(scoped[1][:id])
|
253
|
+
expect(extracted[1].name).to eq(scoped[1][:name])
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cartograph::Map do
|
4
|
+
subject(:map) { Cartograph::Map.new }
|
5
|
+
|
6
|
+
describe '#property' do
|
7
|
+
it 'adds a property to the map' do
|
8
|
+
map.property :attribute_name, scopes: [:read]
|
9
|
+
expect(map.properties.size).to be(1)
|
10
|
+
expect(map.properties.first).to be_kind_of(Cartograph::Property)
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'defining multiple properties' do
|
14
|
+
it 'adds multiple properties at the same time for the scope' do
|
15
|
+
map.property :attribute1, :attribute2, scopes: [:read]
|
16
|
+
|
17
|
+
expect(map.properties.size).to be(2)
|
18
|
+
expect(map.properties).to all(be_kind_of(Cartograph::Property))
|
19
|
+
|
20
|
+
expect(map.properties[0].name).to eq(:attribute1)
|
21
|
+
expect(map.properties[0].scopes).to eq([:read])
|
22
|
+
|
23
|
+
expect(map.properties[1].name).to eq(:attribute2)
|
24
|
+
expect(map.properties[1].scopes).to eq([:read])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#properties' do
|
30
|
+
it 'returns a PropertyCollection object' do
|
31
|
+
properties = map.properties
|
32
|
+
expect(properties).to be_kind_of(Cartograph::PropertyCollection)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#mapping' do
|
37
|
+
it 'sets the class we\'re mapping' do
|
38
|
+
map.mapping Class
|
39
|
+
expect(map.mapping).to be(Class)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#root_key' do
|
44
|
+
it 'sets the root keys' do
|
45
|
+
map.root_key singlular: 'test', scopes: [:read]
|
46
|
+
map.root_key singlular: 'test', scopes: [:create]
|
47
|
+
|
48
|
+
expect(map.root_keys.size).to be(2)
|
49
|
+
expect(map.root_keys).to all(be_kind_of(Cartograph::RootKey))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#root_key_for' do
|
54
|
+
it 'returns the first root key for the scope and type' do
|
55
|
+
map.root_key singular: 'test', scopes: [:read]
|
56
|
+
key = map.root_key_for(:read, :singular)
|
57
|
+
|
58
|
+
expect(key).to eq('test')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#dup' do
|
63
|
+
it 'performs a safe duplication of the map' do
|
64
|
+
mapped_class = Class.new
|
65
|
+
|
66
|
+
prop1 = map.property :name, scopes: [:read, :write]
|
67
|
+
prop2 = map.property :id, scopes: [:read]
|
68
|
+
map.mapping mapped_class
|
69
|
+
map.root_key singular: 'woot', plural: 'woots', scopes: [:read]
|
70
|
+
|
71
|
+
cache_key = Proc.new {}
|
72
|
+
map.cache 'hello'
|
73
|
+
map.cache_key &cache_key
|
74
|
+
|
75
|
+
new_map = map.dup
|
76
|
+
|
77
|
+
expect(new_map.properties[0]).to_not be(prop1)
|
78
|
+
expect(new_map.properties[1]).to_not be(prop2)
|
79
|
+
|
80
|
+
expect(new_map.properties).to all(be_kind_of(Cartograph::Property))
|
81
|
+
expect(new_map.properties[0].name).to eq(:name)
|
82
|
+
expect(new_map.properties[1].name).to eq(:id)
|
83
|
+
|
84
|
+
expect(new_map.mapping).to eq(mapped_class)
|
85
|
+
expect(new_map.root_keys).to eq(map.root_keys)
|
86
|
+
|
87
|
+
expect(new_map.cache).to eq(map.cache)
|
88
|
+
expect(new_map.cache_key).to eq(cache_key)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#cache' do
|
93
|
+
it "stores the caching method" do
|
94
|
+
cacher = double
|
95
|
+
map.cache cacher
|
96
|
+
|
97
|
+
expect(map.cache).to be(cacher)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'returns the cartograph cache if set' do
|
101
|
+
cacher = double('cache')
|
102
|
+
Cartograph.default_cache = cacher
|
103
|
+
|
104
|
+
expect(map.cache).to be(cacher)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'goes straight to the object if cache is overridden with false' do
|
108
|
+
cacher = double('cache')
|
109
|
+
Cartograph.default_cache = cacher
|
110
|
+
|
111
|
+
property = Cartograph::Property.new(:bunk) do
|
112
|
+
cache false
|
113
|
+
end
|
114
|
+
|
115
|
+
map.properties << property
|
116
|
+
|
117
|
+
expect(property.map.cache).to be(false)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#cache_key' do
|
122
|
+
it 'stores the cache key block' do
|
123
|
+
cache_key = Proc.new {}
|
124
|
+
map.cache_key(&cache_key)
|
125
|
+
|
126
|
+
expect(map.cache_key).to be(cache_key)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'returns the cartograph cache_key if set' do
|
130
|
+
cache_key = double('cache key')
|
131
|
+
Cartograph.default_cache_key = cache_key
|
132
|
+
|
133
|
+
expect(map.cache_key).to be(cache_key)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'Equality' do
|
138
|
+
specify 'duplicated maps are equal to eachother' do
|
139
|
+
map1 = Cartograph::Map.new
|
140
|
+
map1.property :something, scopes: [:read]
|
141
|
+
|
142
|
+
map2 = map1.dup
|
143
|
+
|
144
|
+
expect(map1).to eq(map2)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe '#scoped' do
|
149
|
+
it 'adds properties with the scopes defined on the block instantiation' do
|
150
|
+
map.scoped :read, :write do
|
151
|
+
property :name
|
152
|
+
property :something
|
153
|
+
end
|
154
|
+
|
155
|
+
expect(map.properties.size).to eq(2)
|
156
|
+
expect(map.properties.filter_by_scope(:read).map(&:name)).to eq([:name, :something])
|
157
|
+
expect(map.properties.filter_by_scope(:write).map(&:name)).to eq([:name, :something])
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cartograph::PropertyCollection do
|
4
|
+
describe '#filter_by_scope' do
|
5
|
+
it 'only returns properties with a certain scope attached' do
|
6
|
+
collection = Cartograph::PropertyCollection.new
|
7
|
+
collection << Cartograph::Property.new(:hello, scopes: [:read, :create])
|
8
|
+
collection << Cartograph::Property.new(:id, scopes: [:read])
|
9
|
+
|
10
|
+
filtered = collection.filter_by_scope(:create)
|
11
|
+
expect(filtered.size).to be(1)
|
12
|
+
expect(filtered.first.name).to eq(:hello)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cartograph::Property do
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'initializes with an attribute name and options' do
|
6
|
+
name = :hey
|
7
|
+
options = { lol: 'whut' }
|
8
|
+
|
9
|
+
property = Cartograph::Property.new(name, options)
|
10
|
+
expect(property.name).to eq(name)
|
11
|
+
expect(property.options).to eq(options)
|
12
|
+
expect(property.key).to eq('hey')
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with a key' do
|
16
|
+
it 'sets the key' do
|
17
|
+
name = :hey
|
18
|
+
options = { key: 'Hey' }
|
19
|
+
property = Cartograph::Property.new(name, options)
|
20
|
+
|
21
|
+
expect(property.key).to eq('Hey')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with a block' do
|
26
|
+
it 'yields a map instance for the property' do
|
27
|
+
expect {|b| Cartograph::Property.new(:hello, &b) }.to yield_with_args(Cartograph::Map.new)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with an include' do
|
32
|
+
it 'sets the map to the included mapped class' do
|
33
|
+
klass = Class.new do
|
34
|
+
include Cartograph::DSL
|
35
|
+
cartograph do
|
36
|
+
property :lol, scopes: [:read]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
property = Cartograph::Property.new(:id, scopes: [:read], include: klass)
|
41
|
+
expect(property.map).to eq(klass.cartograph)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#scopes' do
|
47
|
+
it 'returns the scopes that the property is for' do
|
48
|
+
property = Cartograph::Property.new(:name, scopes: [:read, :create])
|
49
|
+
expect(property.scopes).to include(:read, :create)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns an empty array when no scopes are provided' do
|
53
|
+
property = Cartograph::Property.new(:name)
|
54
|
+
expect(property.scopes).to eq( [] )
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'always casts to an array' do
|
58
|
+
property = Cartograph::Property.new(:name, scopes: :read)
|
59
|
+
expect(property.scopes).to eq [:read]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#plural?' do
|
64
|
+
it 'returns true when set to plural' do
|
65
|
+
property = Cartograph::Property.new(:name, scopes: [:read], plural: true)
|
66
|
+
expect(property).to be_plural
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns false when not set' do
|
70
|
+
property = Cartograph::Property.new(:name, scopes: [:read])
|
71
|
+
expect(property).to_not be_plural
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#value_for' do
|
76
|
+
it 'returns the value when passed an object' do
|
77
|
+
property = Cartograph::Property.new(:sammy)
|
78
|
+
object = double('object', sammy: 'cephalopod')
|
79
|
+
|
80
|
+
expect(property.value_for(object)).to eq('cephalopod')
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'for a nested property set' do
|
84
|
+
it 'returns nested properties' do
|
85
|
+
top_level = Cartograph::Property.new(:sammy) do
|
86
|
+
property :cephalopod
|
87
|
+
end
|
88
|
+
|
89
|
+
child = double('child', cephalopod: 'I will ink you')
|
90
|
+
root = double('root', sammy: child)
|
91
|
+
|
92
|
+
expect(top_level.value_for(root)).to eq('cephalopod' => child.cephalopod)
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'when it is plural' do
|
96
|
+
it 'returns a pluralized representation' do
|
97
|
+
top_level = Cartograph::Property.new(:sammy, plural: true) do
|
98
|
+
property :cephalopod
|
99
|
+
end
|
100
|
+
|
101
|
+
child1 = double('child', cephalopod: 'I will ink you')
|
102
|
+
child2 = double('child', cephalopod: 'I wont because im cool')
|
103
|
+
|
104
|
+
root = double('root', sammy: [child1, child2])
|
105
|
+
|
106
|
+
expect(top_level.value_for(root)).to eq([
|
107
|
+
{ 'cephalopod' => child1.cephalopod },
|
108
|
+
{ 'cephalopod' => child2.cephalopod }
|
109
|
+
])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when the value for the root object is nil' do
|
114
|
+
it 'returns nil' do
|
115
|
+
top_level = Cartograph::Property.new(:sammy) do
|
116
|
+
property :cephalopod
|
117
|
+
end
|
118
|
+
|
119
|
+
root = double(sammy: nil)
|
120
|
+
|
121
|
+
expect(top_level.value_for(root)).to be_nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe '#value_from' do
|
128
|
+
let(:hash) { { hello: 'world' } }
|
129
|
+
|
130
|
+
it 'retrieves the value from a hash for the property' do
|
131
|
+
property = Cartograph::Property.new(:hello)
|
132
|
+
expect(property.value_from(hash)).to eq('world')
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'for a nil object' do
|
136
|
+
it 'bails and does not try to retrieve' do
|
137
|
+
property = Cartograph::Property.new(:hello)
|
138
|
+
value = property.value_from(nil)
|
139
|
+
expect(value).to be_nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'string and symbol agnostic' do
|
144
|
+
let(:hash) { { 'hello' => 'world' } }
|
145
|
+
|
146
|
+
it 'retrieves the value from a hash for the property' do
|
147
|
+
property = Cartograph::Property.new(:hello)
|
148
|
+
expect(property.value_from(hash)).to eq('world')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'for a nested property set' do
|
153
|
+
it 'returns an object with the properties set on it' do
|
154
|
+
dummy_class = OpenStruct
|
155
|
+
|
156
|
+
nested_property = Cartograph::Property.new(:hello) do
|
157
|
+
mapping dummy_class
|
158
|
+
property :id
|
159
|
+
property :name
|
160
|
+
end
|
161
|
+
|
162
|
+
hash = { hello: {
|
163
|
+
'id' => 555,
|
164
|
+
'name' => 'Buckstar'
|
165
|
+
}}
|
166
|
+
|
167
|
+
value = nested_property.value_from(hash)
|
168
|
+
expect(value).to be_kind_of(dummy_class)
|
169
|
+
expect(value.id).to eq(hash[:hello]['id'])
|
170
|
+
expect(value.name).to eq(hash[:hello]['name'])
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'returns a collection of objects when set to plural' do
|
174
|
+
dummy_class = OpenStruct
|
175
|
+
|
176
|
+
nested_property = Cartograph::Property.new(:hello, plural: true) do
|
177
|
+
mapping dummy_class
|
178
|
+
|
179
|
+
property :id
|
180
|
+
property :name
|
181
|
+
end
|
182
|
+
|
183
|
+
hash = {
|
184
|
+
hello: [{
|
185
|
+
'id' => 555,
|
186
|
+
'name' => 'Buckstar'
|
187
|
+
}, {
|
188
|
+
'id' => 556,
|
189
|
+
'name' => 'Starbuck'
|
190
|
+
}]
|
191
|
+
}
|
192
|
+
|
193
|
+
value = nested_property.value_from(hash)
|
194
|
+
expect(value).to be_kind_of(Array)
|
195
|
+
expect(value.size).to be(2)
|
196
|
+
|
197
|
+
expect(value[0].id).to eq(hash[:hello][0]['id'])
|
198
|
+
expect(value[0].name).to eq(hash[:hello][0]['name'])
|
199
|
+
|
200
|
+
expect(value[1].id).to eq(hash[:hello][1]['id'])
|
201
|
+
expect(value[1].name).to eq(hash[:hello][1]['name'])
|
202
|
+
end
|
203
|
+
|
204
|
+
context 'when set to plural but the key is nil' do
|
205
|
+
it 'returns an empty array' do
|
206
|
+
dummy_class = Struct.new(:id, :name)
|
207
|
+
|
208
|
+
nested_property = Cartograph::Property.new(:hello, plural: true) do
|
209
|
+
mapping dummy_class
|
210
|
+
|
211
|
+
property :id
|
212
|
+
property :name
|
213
|
+
end
|
214
|
+
|
215
|
+
hash = { hello: nil }
|
216
|
+
value = nested_property.value_from(hash)
|
217
|
+
|
218
|
+
expect(value).to eq([])
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#dup' do
|
225
|
+
it 'copies the name, options, and map into another property' do
|
226
|
+
instance = Cartograph::Property.new(:id, scopes: [:read])
|
227
|
+
duped = instance.dup
|
228
|
+
|
229
|
+
expect(duped).to be_kind_of(Cartograph::Property)
|
230
|
+
expect(duped.name).to eq(:id)
|
231
|
+
expect(duped.options).to_not be(instance.options)
|
232
|
+
expect(duped.options).to eq(instance.options)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|