opium 1.1.8 → 1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/opium/extensions/pointer.rb +1 -0
- data/lib/opium/model.rb +2 -0
- data/lib/opium/model/attributable.rb +6 -2
- data/lib/opium/model/batchable.rb +83 -0
- data/lib/opium/model/batchable/batch.rb +1 -2
- data/lib/opium/model/connectable.rb +1 -1
- data/lib/opium/model/criteria.rb +4 -4
- data/lib/opium/model/field.rb +12 -2
- data/lib/opium/model/fieldable.rb +12 -2
- data/lib/opium/model/persistable.rb +1 -1
- data/lib/opium/model/reference.rb +50 -0
- data/lib/opium/model/relatable.rb +64 -0
- data/lib/opium/model/relatable/metadata.rb +32 -0
- data/lib/opium/model/relation.rb +156 -0
- data/lib/opium/version.rb +1 -1
- data/spec/opium/model/attributable_spec.rb +0 -9
- data/spec/opium/model/batchable/batch_spec.rb +4 -1
- data/spec/opium/model/batchable_spec.rb +148 -0
- data/spec/opium/model/fieldable_spec.rb +16 -0
- data/spec/opium/model/reference_spec.rb +73 -0
- data/spec/opium/model/relatable/metadata_spec.rb +31 -0
- data/spec/opium/model/relatable_spec.rb +220 -0
- data/spec/opium/model/relation_spec.rb +275 -0
- data/spec/opium/model_spec.rb +1 -0
- metadata +14 -2
@@ -0,0 +1,275 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::Model::Relation do
|
4
|
+
before do
|
5
|
+
stub_const( 'Model', Class.new do |klass|
|
6
|
+
include Opium::Model
|
7
|
+
stub(:model_name).and_return( ActiveModel::Name.new( klass, nil, 'Model' ) )
|
8
|
+
end )
|
9
|
+
|
10
|
+
stub_const( 'RelatedClass', Class.new do |klass|
|
11
|
+
include Opium::Model
|
12
|
+
field :title, type: String
|
13
|
+
|
14
|
+
def klass.model_name
|
15
|
+
ActiveModel::Name.new( self, nil, 'RelatedClass' )
|
16
|
+
end
|
17
|
+
end )
|
18
|
+
|
19
|
+
stub_request(:get, "https://api.parse.com/1/classes/RelatedClass?count=1").
|
20
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
21
|
+
to_return(status: 200, body: {
|
22
|
+
count: 0,
|
23
|
+
results: []
|
24
|
+
}.to_json, headers: { content_type: 'application/json' })
|
25
|
+
|
26
|
+
stub_request(:get, "https://api.parse.com/1/classes/RelatedClass?count=1&where=%7B%22$relatedTo%22:%7B%22object%22:%7B%22__type%22:%22Pointer%22,%22className%22:%22Model%22,%22objectId%22:%22abcd1234%22%7D,%22key%22:%22related%22%7D%7D").
|
27
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
28
|
+
to_return(status: 200, body: { count: 0, results: [] }.to_json, headers: { content_type: 'application/json' })
|
29
|
+
|
30
|
+
stub_request(:post, "https://api.parse.com/1/classes/RelatedClass").
|
31
|
+
with(body: "{\"title\":null}",
|
32
|
+
headers: {'Content-Type'=>'application/json', 'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
33
|
+
to_return(status: 200, body: {
|
34
|
+
objectId: 'rc1234',
|
35
|
+
createdAt: Time.now.utc
|
36
|
+
}.to_json, headers: { content_type: 'application/json' })
|
37
|
+
|
38
|
+
stub_request(:put, "https://api.parse.com/1/classes/Model/abcd1234").
|
39
|
+
with(body: "{\"related\":{\"__op\":\"AddRelation\",\"objects\":[{\"__type\":\"Pointer\",\"className\":\"RelatedClass\",\"objectId\":\"rc1234\"}]}}",
|
40
|
+
headers: {'Content-Type'=>'application/json', 'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
41
|
+
to_return(status: 200, body: { updatedAt: Time.now.utc }.to_json, headers: { content_type: 'application/json' })
|
42
|
+
end
|
43
|
+
|
44
|
+
after do
|
45
|
+
Opium::Model::Relation.models.clear
|
46
|
+
end
|
47
|
+
|
48
|
+
it { expect( described_class ).to be <= Opium::Model::Criteria }
|
49
|
+
it { expect( described_class ).to be <= ActiveModel::Dirty }
|
50
|
+
|
51
|
+
describe '.to_ruby' do
|
52
|
+
let(:result) { described_class.to_ruby( convert_from ) }
|
53
|
+
|
54
|
+
context 'with a parse Relation object hash' do
|
55
|
+
let(:convert_from) { { '__type' => 'Relation', 'className' => 'RelatedClass' } }
|
56
|
+
|
57
|
+
it { expect { result }.to_not raise_exception }
|
58
|
+
it { expect( result ).to be_a described_class }
|
59
|
+
it { expect( result.class_name ).to eq 'RelatedClass' }
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with a string value' do
|
63
|
+
let(:convert_from) { 'RelatedClass' }
|
64
|
+
|
65
|
+
it { expect( result ).to be_a described_class }
|
66
|
+
it { expect( result.class_name ).to eq 'RelatedClass' }
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with an Opium::Model' do
|
70
|
+
let(:convert_from) { Model.new }
|
71
|
+
|
72
|
+
it { expect( result ).to be_a described_class }
|
73
|
+
it { expect( result.class_name ).to eq 'Model' }
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with an Opium::Model::Relation' do
|
77
|
+
let(:convert_from) { described_class.new( 'RelatedClass' ) }
|
78
|
+
|
79
|
+
it { expect( result ).to be_a described_class }
|
80
|
+
it { expect( result.class_name ).to eq 'RelatedClass' }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'with nil' do
|
84
|
+
let(:convert_from) { nil }
|
85
|
+
|
86
|
+
it { expect { result }.to_not raise_exception }
|
87
|
+
it { expect( result ).to be_nil }
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'with any unconvertable value' do
|
91
|
+
let(:convert_from) { 42 }
|
92
|
+
|
93
|
+
it { expect { result }.to raise_exception }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '.to_parse' do
|
98
|
+
let(:result) { described_class.to_parse( convert_from ) }
|
99
|
+
|
100
|
+
context 'with an Opium::Model::Relation' do
|
101
|
+
let(:convert_from) { described_class.new( 'RelatedClass' ) }
|
102
|
+
|
103
|
+
it { expect { result }.to_not raise_exception }
|
104
|
+
it { expect( result ).to be_a Hash }
|
105
|
+
it { expect( result.keys ).to include( '__type', 'className' ) }
|
106
|
+
it { expect( result ).to eq( { '__type' => 'Relation', 'className' => 'RelatedClass' } ) }
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'with a hash' do
|
110
|
+
let(:convert_from) { { '__type' => 'Relation', 'className' => 'RelatedClass' } }
|
111
|
+
|
112
|
+
it { expect( result ).to eq( { '__type' => 'Relation', 'className' => 'RelatedClass' } ) }
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'with a string value' do
|
116
|
+
let(:convert_from) { 'RelatedClass' }
|
117
|
+
|
118
|
+
it { expect( result ).to eq( { '__type' => 'Relation', 'className' => 'RelatedClass' } ) }
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'with an Opium::Model' do
|
122
|
+
let(:convert_from) { Model.new }
|
123
|
+
|
124
|
+
it { expect( result ).to eq( { '__type' => 'Relation', 'className' => 'Model' } ) }
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'when the class_name cannot be determined' do
|
128
|
+
let(:convert_from) { { '__type' => 'Relation' } }
|
129
|
+
|
130
|
+
it { expect { result }.to raise_exception }
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'with any unconvertable value' do
|
134
|
+
let(:convert_from) { 42 }
|
135
|
+
|
136
|
+
it { expect { result }.to raise_exception }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#to_parse' do
|
141
|
+
let(:result) { subject.to_parse }
|
142
|
+
subject { described_class.new 'RelatedClass' }
|
143
|
+
|
144
|
+
it { expect( result ).to be_a Hash }
|
145
|
+
it { expect( result.keys ).to include( '__type', 'className' ) }
|
146
|
+
it { expect( result ).to eq( { '__type' => 'Relation', 'className' => 'RelatedClass' } ) }
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '#initialize' do
|
150
|
+
let(:result) { described_class.new 'RelatedClass' }
|
151
|
+
|
152
|
+
it { expect( result ).to be_cached }
|
153
|
+
end
|
154
|
+
|
155
|
+
describe '#empty?' do
|
156
|
+
let(:result) { subject.empty? }
|
157
|
+
|
158
|
+
context 'within an new model' do
|
159
|
+
subject { described_class.new( 'RelatedClass' ).tap {|r| r.owner = Model.new } }
|
160
|
+
|
161
|
+
it { expect { result }.to_not raise_exception }
|
162
|
+
it { expect( result ).to be_truthy }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe '#owner' do
|
167
|
+
let(:result) { subject.owner }
|
168
|
+
subject { described_class.new 'RelatedClass' }
|
169
|
+
|
170
|
+
it { expect { result }.to_not raise_exception }
|
171
|
+
end
|
172
|
+
|
173
|
+
describe '#owner=' do
|
174
|
+
let(:result) { subject.owner = Model.new }
|
175
|
+
subject { described_class.new 'RelatedClass' }
|
176
|
+
|
177
|
+
it { expect { result }.to_not raise_exception }
|
178
|
+
end
|
179
|
+
|
180
|
+
describe '#each' do
|
181
|
+
let(:result) { subject.each }
|
182
|
+
subject do
|
183
|
+
described_class.new( 'RelatedClass' ).tap do |r|
|
184
|
+
r.owner = Model.new id: 'm1234'
|
185
|
+
r.metadata = Opium::Model::Relatable::Metadata.new( Model, :has_many, :related, class_name: 'RelatedClass' )
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'with existing items' do
|
190
|
+
before do
|
191
|
+
stub_request(:get, "https://api.parse.com/1/classes/RelatedClass?count=1&where=%7B%22$relatedTo%22:%7B%22object%22:%7B%22__type%22:%22Pointer%22,%22className%22:%22Model%22,%22objectId%22:%22m1234%22%7D,%22key%22:%22related%22%7D%7D").
|
192
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
193
|
+
to_return(status: 200, body: {
|
194
|
+
count: 1,
|
195
|
+
results: [ { objectId: 'rc1234', createdAt: Time.now.utc } ]
|
196
|
+
}.to_json, headers: { content_type: 'application/json' })
|
197
|
+
end
|
198
|
+
|
199
|
+
it { expect( subject.model_name ).to eq 'RelatedClass' }
|
200
|
+
it { expect( subject.model ).to eq RelatedClass }
|
201
|
+
it { expect { result }.to_not raise_exception }
|
202
|
+
it { expect( result ).to all( be_a( RelatedClass ) ) }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe '#push' do
|
207
|
+
let(:result) { subject.push a_related_object }
|
208
|
+
let(:a_related_object) { RelatedClass.new }
|
209
|
+
subject { described_class.new 'RelatedClass' }
|
210
|
+
|
211
|
+
it { expect { result }.to_not raise_exception }
|
212
|
+
it { expect( result ).to eq subject }
|
213
|
+
it { expect( result ).to include( a_related_object ) }
|
214
|
+
end
|
215
|
+
|
216
|
+
describe '#delete' do
|
217
|
+
let(:result) { subject.delete a_related_object }
|
218
|
+
let(:a_related_object) { RelatedClass.new }
|
219
|
+
subject { described_class.new 'RelatedClass' }
|
220
|
+
before(:each) { subject.push a_related_object }
|
221
|
+
|
222
|
+
it { expect { result }.to_not raise_exception }
|
223
|
+
it { expect( result ).to_not include( a_related_object ) }
|
224
|
+
end
|
225
|
+
|
226
|
+
describe '#build' do
|
227
|
+
let(:result) { subject.build object_params }
|
228
|
+
subject { described_class.new 'RelatedClass' }
|
229
|
+
|
230
|
+
context 'with no params' do
|
231
|
+
let(:object_params) { }
|
232
|
+
|
233
|
+
it { expect( result.model_name ).to eq 'RelatedClass' }
|
234
|
+
it { expect( result.persisted? ).to eq false }
|
235
|
+
it { is_expected.to include( result ) }
|
236
|
+
end
|
237
|
+
|
238
|
+
context 'with params' do
|
239
|
+
let(:object_params) { { title: 'Related' } }
|
240
|
+
|
241
|
+
it { expect( result.model_name ).to eq 'RelatedClass' }
|
242
|
+
it { expect( result.persisted? ).to eq false }
|
243
|
+
it { expect( result.title ).to eq object_params[:title] }
|
244
|
+
it { is_expected.to include( result ) }
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe '#create' do
|
249
|
+
end
|
250
|
+
|
251
|
+
describe '#create!' do
|
252
|
+
end
|
253
|
+
|
254
|
+
describe '#save' do
|
255
|
+
# Note: this is mostly a utility method which should be only ever invoked by the owner upon it being saved.
|
256
|
+
# This should trigger a save on all relations within changes which are not persisted?, and then perform a series
|
257
|
+
# of AddRelation and RemoveRelation API calls.
|
258
|
+
let(:result) { subject.save }
|
259
|
+
let(:a_related_object) { subject.first }
|
260
|
+
subject do
|
261
|
+
described_class.new( 'RelatedClass' ).tap do |r|
|
262
|
+
r.owner = Model.new id: 'abcd1234'
|
263
|
+
r.metadata = Opium::Model::Relatable::Metadata.new( Model, :has_many, :related, class_name: 'RelatedClass' )
|
264
|
+
r.build
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
it { expect { result }.to_not raise_exception }
|
269
|
+
it { result && is_expected.to( all( be_persisted ) ) }
|
270
|
+
it { result && expect( subject.map(&:errors) ).to( all( be_empty ) ) }
|
271
|
+
it { expect( result && subject.send(:__additions__) ).to be_empty }
|
272
|
+
it { expect( result && subject.send(:__deletions__) ).to be_empty }
|
273
|
+
it { is_expected.to include( a_related_object ) }
|
274
|
+
end
|
275
|
+
end
|
data/spec/opium/model_spec.rb
CHANGED
@@ -24,6 +24,7 @@ describe Opium::Model do
|
|
24
24
|
it { is_expected.to be <= Opium::Model::Findable }
|
25
25
|
it { is_expected.to be <= Opium::Model::Inheritable }
|
26
26
|
it { is_expected.to be <= Opium::Model::Batchable }
|
27
|
+
it { is_expected.to be <= Opium::Model::Relatable }
|
27
28
|
|
28
29
|
describe '#inspect' do
|
29
30
|
context 'within a blank model' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Bowers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -295,6 +295,10 @@ files:
|
|
295
295
|
- lib/opium/model/kaminari/scopable.rb
|
296
296
|
- lib/opium/model/persistable.rb
|
297
297
|
- lib/opium/model/queryable.rb
|
298
|
+
- lib/opium/model/reference.rb
|
299
|
+
- lib/opium/model/relatable.rb
|
300
|
+
- lib/opium/model/relatable/metadata.rb
|
301
|
+
- lib/opium/model/relation.rb
|
298
302
|
- lib/opium/model/scopable.rb
|
299
303
|
- lib/opium/model/serialization.rb
|
300
304
|
- lib/opium/railtie.rb
|
@@ -334,6 +338,10 @@ files:
|
|
334
338
|
- spec/opium/model/kaminari_spec.rb
|
335
339
|
- spec/opium/model/persistable_spec.rb
|
336
340
|
- spec/opium/model/queryable_spec.rb
|
341
|
+
- spec/opium/model/reference_spec.rb
|
342
|
+
- spec/opium/model/relatable/metadata_spec.rb
|
343
|
+
- spec/opium/model/relatable_spec.rb
|
344
|
+
- spec/opium/model/relation_spec.rb
|
337
345
|
- spec/opium/model/scopable_spec.rb
|
338
346
|
- spec/opium/model/serialization_spec.rb
|
339
347
|
- spec/opium/model_spec.rb
|
@@ -398,6 +406,10 @@ test_files:
|
|
398
406
|
- spec/opium/model/kaminari_spec.rb
|
399
407
|
- spec/opium/model/persistable_spec.rb
|
400
408
|
- spec/opium/model/queryable_spec.rb
|
409
|
+
- spec/opium/model/reference_spec.rb
|
410
|
+
- spec/opium/model/relatable/metadata_spec.rb
|
411
|
+
- spec/opium/model/relatable_spec.rb
|
412
|
+
- spec/opium/model/relation_spec.rb
|
401
413
|
- spec/opium/model/scopable_spec.rb
|
402
414
|
- spec/opium/model/serialization_spec.rb
|
403
415
|
- spec/opium/model_spec.rb
|