cartograph 1.0.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,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