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.
@@ -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
@@ -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.1.8
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-01 00:00:00.000000000 Z
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