opium 1.0.0.beta
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 +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +24 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +10 -0
- data/lib/generators/opium/config_generator.rb +15 -0
- data/lib/generators/opium/model_generator.rb +33 -0
- data/lib/generators/opium/templates/config.yml +27 -0
- data/lib/generators/opium/templates/model.rb +10 -0
- data/lib/opium/config.rb +44 -0
- data/lib/opium/extensions/array.rb +10 -0
- data/lib/opium/extensions/boolean.rb +13 -0
- data/lib/opium/extensions/date.rb +18 -0
- data/lib/opium/extensions/date_time.rb +18 -0
- data/lib/opium/extensions/false_class.rb +7 -0
- data/lib/opium/extensions/float.rb +13 -0
- data/lib/opium/extensions/geo_point.rb +37 -0
- data/lib/opium/extensions/hash.rb +43 -0
- data/lib/opium/extensions/integer.rb +13 -0
- data/lib/opium/extensions/numeric.rb +7 -0
- data/lib/opium/extensions/object.rb +15 -0
- data/lib/opium/extensions/pointer.rb +20 -0
- data/lib/opium/extensions/regexp.rb +12 -0
- data/lib/opium/extensions/string.rb +20 -0
- data/lib/opium/extensions/time.rb +19 -0
- data/lib/opium/extensions/true_class.rb +7 -0
- data/lib/opium/extensions.rb +16 -0
- data/lib/opium/model/attributable.rb +37 -0
- data/lib/opium/model/callbacks.rb +38 -0
- data/lib/opium/model/connectable.rb +155 -0
- data/lib/opium/model/criteria.rb +123 -0
- data/lib/opium/model/dirty.rb +35 -0
- data/lib/opium/model/field.rb +31 -0
- data/lib/opium/model/fieldable.rb +57 -0
- data/lib/opium/model/findable.rb +20 -0
- data/lib/opium/model/kaminari/queryable.rb +46 -0
- data/lib/opium/model/kaminari/scopable.rb +15 -0
- data/lib/opium/model/kaminari.rb +4 -0
- data/lib/opium/model/persistable.rb +153 -0
- data/lib/opium/model/queryable.rb +150 -0
- data/lib/opium/model/scopable.rb +58 -0
- data/lib/opium/model/serialization.rb +13 -0
- data/lib/opium/model.rb +47 -0
- data/lib/opium/railtie.rb +14 -0
- data/lib/opium/user.rb +44 -0
- data/lib/opium/version.rb +3 -0
- data/lib/opium.rb +9 -0
- data/opium.gemspec +40 -0
- data/spec/opium/config/opium.yml +5 -0
- data/spec/opium/config_spec.rb +56 -0
- data/spec/opium/extensions/array_spec.rb +34 -0
- data/spec/opium/extensions/boolean_spec.rb +28 -0
- data/spec/opium/extensions/date_spec.rb +55 -0
- data/spec/opium/extensions/date_time_spec.rb +55 -0
- data/spec/opium/extensions/float_spec.rb +42 -0
- data/spec/opium/extensions/geo_point_spec.rb +55 -0
- data/spec/opium/extensions/hash_spec.rb +76 -0
- data/spec/opium/extensions/integer_spec.rb +42 -0
- data/spec/opium/extensions/object_spec.rb +24 -0
- data/spec/opium/extensions/pointer_spec.rb +28 -0
- data/spec/opium/extensions/regexp_spec.rb +23 -0
- data/spec/opium/extensions/string_spec.rb +65 -0
- data/spec/opium/extensions/time_spec.rb +55 -0
- data/spec/opium/model/attributable_spec.rb +45 -0
- data/spec/opium/model/callbacks_spec.rb +59 -0
- data/spec/opium/model/connectable_spec.rb +218 -0
- data/spec/opium/model/criteria_spec.rb +285 -0
- data/spec/opium/model/dirty_spec.rb +39 -0
- data/spec/opium/model/fieldable_spec.rb +133 -0
- data/spec/opium/model/findable_spec.rb +57 -0
- data/spec/opium/model/kaminari/queryable_spec.rb +22 -0
- data/spec/opium/model/kaminari/scopable_spec.rb +20 -0
- data/spec/opium/model/kaminari_spec.rb +104 -0
- data/spec/opium/model/persistable_spec.rb +367 -0
- data/spec/opium/model/queryable_spec.rb +338 -0
- data/spec/opium/model/scopable_spec.rb +115 -0
- data/spec/opium/model/serialization_spec.rb +51 -0
- data/spec/opium/model_spec.rb +49 -0
- data/spec/opium/user_spec.rb +195 -0
- data/spec/opium_spec.rb +5 -0
- data/spec/spec_helper.rb +25 -0
- metadata +400 -0
@@ -0,0 +1,338 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Opium::Model::Queryable do
|
4
|
+
let( :model ) { Class.new { include Opium::Model::Queryable } }
|
5
|
+
|
6
|
+
describe 'the class' do
|
7
|
+
subject { model }
|
8
|
+
|
9
|
+
it { should respond_to( :all, :all_in ).with(1).argument }
|
10
|
+
it { should respond_to( :and ).with(1).argument }
|
11
|
+
it { should respond_to( :between ).with(1).argument }
|
12
|
+
it { should respond_to( :exists ).with(1).argument }
|
13
|
+
it { should respond_to( :gt, :gte ).with(1).argument }
|
14
|
+
it { should respond_to( :lt, :lte ).with(1).argument }
|
15
|
+
it { should respond_to( :in, :any_in, :nin ).with(1).argument }
|
16
|
+
it { should respond_to( :ne ).with(1).argument }
|
17
|
+
it { should respond_to( :or ).with(1).argument }
|
18
|
+
it { should respond_to( :select, :dont_select ).with(1).argument }
|
19
|
+
it { should respond_to( :keys, :pluck ).with(1).argument }
|
20
|
+
it { should respond_to( :where ).with(1).argument }
|
21
|
+
it { should respond_to( :order ).with(1).argument }
|
22
|
+
it { should respond_to( :limit, :skip ).with(1).argument }
|
23
|
+
it { should respond_to( :cache, :uncache, :cached? ) }
|
24
|
+
it { should respond_to( :count, :total_count ) }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'within a model' do
|
28
|
+
before do
|
29
|
+
stub_const( 'Game', Class.new do
|
30
|
+
include Opium::Model
|
31
|
+
field :title, type: String
|
32
|
+
field :price, type: Float
|
33
|
+
|
34
|
+
stub(:model_name).and_return( 'Game' )
|
35
|
+
|
36
|
+
default_scope order( title: :asc )
|
37
|
+
end )
|
38
|
+
end
|
39
|
+
|
40
|
+
after do
|
41
|
+
Opium::Model::Criteria.models.clear
|
42
|
+
end
|
43
|
+
|
44
|
+
subject { Game }
|
45
|
+
|
46
|
+
describe ':where' do
|
47
|
+
it 'should return a criteria' do
|
48
|
+
subject.where( price: { '$lte' => 5 } ).should be_a( Opium::Model::Criteria )
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should set the "where" constraint to the provided value' do
|
52
|
+
subject.where( price: { '$lte' => 5 } ).tap do |criteria|
|
53
|
+
criteria.constraints.should have_key( 'where' )
|
54
|
+
criteria.constraints['where'].should =~ { 'price' => { '$lte' => 5 } }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should deep merge the "where" constraint on successive calls' do
|
59
|
+
subject.where( price: { '$lte' => 5 } ).where( price: { '$gte' => 1 } ).tap do |criteria|
|
60
|
+
criteria.constraints['where'].should =~ { 'price' => { '$lte' => 5, '$gte' => 1 } }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should ensure that specified fields exist on the model' do
|
65
|
+
expect { subject.where( does_not_exist: true ) }.to raise_exception
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should map ruby names to parse names and ruby values to parse values' do
|
69
|
+
time = Time.now - 1000
|
70
|
+
subject.where( created_at: { '$gte' => time } ).tap do |criteria|
|
71
|
+
criteria.constraints['where'].should =~ { 'createdAt' => { '$gte' => time.to_parse } }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
shared_examples_for 'a chainable criteria clause' do |method|
|
77
|
+
describe ":#{method}" do
|
78
|
+
it 'should return a criteria' do
|
79
|
+
subject.send( method, price: 5, title: 'Skyrim' ).should be_a( Opium::Model::Criteria )
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should add a \"$#{method}\" clause to the where constraint for each member of the hash" do
|
83
|
+
subject.send( method, price: 5, title: 'Skyrim' ).tap do |criteria|
|
84
|
+
criteria.constraints.should have_key( 'where' )
|
85
|
+
criteria.constraints['where'].should =~ { 'price' => { "$#{method}" => 5 }, 'title' => { "$#{method}" => 'Skyrim' } }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it_should_behave_like 'a chainable criteria clause', :gte
|
92
|
+
it_should_behave_like 'a chainable criteria clause', :lte
|
93
|
+
it_should_behave_like 'a chainable criteria clause', :gt
|
94
|
+
it_should_behave_like 'a chainable criteria clause', :lt
|
95
|
+
it_should_behave_like 'a chainable criteria clause', :ne
|
96
|
+
|
97
|
+
shared_examples_for 'a chainable array-valued criteria clause' do |method|
|
98
|
+
describe ":#{method}" do
|
99
|
+
it 'should return a criteria' do
|
100
|
+
subject.send( method, price: [1, 2, 3, 4] ).should be_a( Opium::Model::Criteria )
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should add a \"$#{method}\" clause to the where constraint for each member of the hash, converting pair values to arrays" do
|
104
|
+
subject.send( method, price: 1..4, title: ['Skyrim', 'Oblivion'] ).tap do |criteria|
|
105
|
+
criteria.constraints.should have_key( 'where' )
|
106
|
+
criteria.constraints['where'].should =~ { 'price' => { "$#{method}" => [1, 2, 3, 4] }, 'title' => { "$#{method}" => ['Skyrim', 'Oblivion'] } }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it_should_behave_like 'a chainable array-valued criteria clause', :all
|
113
|
+
it_should_behave_like 'a chainable array-valued criteria clause', :in
|
114
|
+
it_should_behave_like 'a chainable array-valued criteria clause', :nin
|
115
|
+
|
116
|
+
shared_examples_for 'an aliased method' do |aliased, method|
|
117
|
+
describe ":#{aliased}" do
|
118
|
+
it "should be an alias for :#{method}" do
|
119
|
+
subject.method( aliased ).should == subject.method( method )
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it_should_behave_like 'an aliased method', :and, :where
|
125
|
+
it_should_behave_like 'an aliased method', :all_in, :all
|
126
|
+
it_should_behave_like 'an aliased method', :any_in, :in
|
127
|
+
|
128
|
+
describe ':exists' do
|
129
|
+
it 'should return a criteria' do
|
130
|
+
subject.exists( price: true ).should be_a( Opium::Model::Criteria )
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should add a "$exist" clause for each hash pair, set to the truthiness of the pair value' do
|
134
|
+
subject.exists( price: true, title: 'no' ).tap do |criteria|
|
135
|
+
criteria.constraints.should have_key( 'where' )
|
136
|
+
criteria.constraints['where'].should =~ { 'price' => { '$exists' => true }, 'title' => { '$exists' => false } }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#between' do
|
142
|
+
context 'with a inclusive range' do
|
143
|
+
subject { Game.between( price: 5..10 ) }
|
144
|
+
|
145
|
+
it { is_expected.to be_an( Opium::Model::Criteria ) }
|
146
|
+
|
147
|
+
it { expect( subject.criteria.constraints ).to include(:where) }
|
148
|
+
|
149
|
+
it 'adds a clause on the key' do
|
150
|
+
expect( subject.criteria.constraints[:where] ).to include(:price)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'adds a "$gte" clause to the key' do
|
154
|
+
expect( subject.criteria.constraints[:where][:price] ).to include( '$gte' => 5 )
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'adds an "$lte" clause to the key' do
|
158
|
+
expect( subject.criteria.constraints[:where][:price] ).to include( '$lte' => 10 )
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'does not add an "$lt" clause to the key' do
|
162
|
+
expect( subject.criteria.constraints[:where][:price].keys ).to_not include( '$lt' )
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'with an exclusive range' do
|
167
|
+
subject { Game.between( price: 5...10 ) }
|
168
|
+
|
169
|
+
it 'adds a "$gte" clause to the key' do
|
170
|
+
expect( subject.criteria.constraints[:where][:price] ).to include( '$gte' => 5 )
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'adds an "$lt" clause to the key' do
|
174
|
+
expect( subject.criteria.constraints[:where][:price] ).to include( '$lt' => 10 )
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'does not add an "$lte" clause to the key' do
|
178
|
+
expect( subject.criteria.constraints[:where][:price].keys ).to_not include( '$lte' )
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe ':select' do
|
184
|
+
it 'should return a criteria' do
|
185
|
+
subject.select( title: subject.between( price: 5..10 ).keys( :title ) ).should be_a( Opium::Model::Criteria )
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'should add a "$select" clause for each hash key, setting the value to a query expression based off the pair-value criteria' do
|
189
|
+
subject.select( title: subject.between( price: 5..10 ).keys( :title ) ).tap do |criteria|
|
190
|
+
criteria.constraints.should have_key( 'where' )
|
191
|
+
criteria.constraints['where'].should =~ {
|
192
|
+
'title' => { '$select' => {
|
193
|
+
'query' => { 'className' => 'Game', 'where' => { 'price' => { '$gte' => 5, '$lte' => 10 } } },
|
194
|
+
'key' => 'title'
|
195
|
+
} }
|
196
|
+
}
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe ':dont_select' do
|
202
|
+
it 'should return a criteria' do
|
203
|
+
subject.dont_select( title: subject.between( price: 5..10 ).keys( :title ) ).should be_a( Opium::Model::Criteria )
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should add a "$donSelect" clause for each hash key, setting the value to a query expression based off the pair-value criteria' do
|
207
|
+
subject.dont_select( title: subject.between( price: 5..10 ).keys( :title ) ).tap do |criteria|
|
208
|
+
criteria.constraints.should have_key( 'where' )
|
209
|
+
criteria.constraints['where'].should =~ {
|
210
|
+
'title' => { '$dontSelect' => {
|
211
|
+
'query' => { 'className' => 'Game', 'where' => { 'price' => { '$gte' => 5, '$lte' => 10 } } },
|
212
|
+
'key' => 'title'
|
213
|
+
} }
|
214
|
+
}
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe ':or' do
|
220
|
+
it 'should return a criteria' do
|
221
|
+
subject.or( { title: 'Skyrim' }, { title: 'Oblivion' } ).should be_a( Opium::Model::Criteria )
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should add an "$or" clause to the "where" constraint, whose contents are an array of all specified subqueries' do
|
225
|
+
subject.or( { title: 'Skyrim' }, { title: 'Oblivion' } ).tap do |criteria|
|
226
|
+
criteria.constraints.should have_key( 'where' )
|
227
|
+
criteria.constraints['where'].should =~ {
|
228
|
+
'$or' => [ { 'title' => 'Skyrim' }, { 'title' => 'Oblivion' } ]
|
229
|
+
}
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe ':keys' do
|
235
|
+
it 'should return a criteria' do
|
236
|
+
subject.keys( :price ).should be_a( Opium::Model::Criteria )
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'should set the "keys" constraint to the provided set of fields, whose names should be parsized' do
|
240
|
+
subject.keys( :price, :updated_at ).tap do |criteria|
|
241
|
+
criteria.constraints.should have_key( 'keys' )
|
242
|
+
criteria.constraints['keys'].should == 'price,updatedAt'
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should raise if a specified field does not exist' do
|
247
|
+
expect { subject.keys( :does_not_exist ) }.to raise_exception
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe ':limit' do
|
252
|
+
it 'should return a criteria' do
|
253
|
+
subject.limit( 5 ).should be_a( Opium::Model::Criteria )
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should set the "limit" constraint to the provided value' do
|
257
|
+
subject.limit( 100 ).tap do |criteria|
|
258
|
+
criteria.constraints.should have_key( 'limit' )
|
259
|
+
criteria.constraints['limit'].should == 100
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe ':skip' do
|
265
|
+
it 'should return a criteria' do
|
266
|
+
subject.skip( 5 ).should be_a( Opium::Model::Criteria )
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'should set the "skip" constraint to the provided value' do
|
270
|
+
subject.skip( 100 ).tap do |criteria|
|
271
|
+
criteria.constraints.should have_key( 'skip' )
|
272
|
+
criteria.constraints['skip'].should == 100
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe ':order' do
|
278
|
+
it 'should return a criteria' do
|
279
|
+
subject.order( title: :asc ).should be_a( Opium::Model::Criteria )
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'should set the "order" constraint to a string' do
|
283
|
+
subject.unscoped.order( title: :asc ).tap do |criteria|
|
284
|
+
criteria.constraints.should have_key( 'order' )
|
285
|
+
criteria.constraints['order'].should == 'title'
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'should negate the field if given something which evaluates to "desc", "-1", or "-"' do
|
290
|
+
[:desc, -1, '-'].each do |direction|
|
291
|
+
subject.unscoped.order( title: direction ).tap do |criteria|
|
292
|
+
criteria.constraints['order'].should == '-title'
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'should combine multiple orderings via a comma' do
|
298
|
+
subject.unscoped.order( title: 1, price: -1 ).tap do |criteria|
|
299
|
+
criteria.constraints['order'].should == 'title,-price'
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'should concatenate successive orderings' do
|
304
|
+
subject.unscoped.order( title: 1 ).order( price: -1 ).tap do |criteria|
|
305
|
+
criteria.constraints['order'].should == 'title,-price'
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
describe ':cached?' do
|
311
|
+
it 'should be false on a non-cached criteria' do
|
312
|
+
Game.criteria.cached?.should be_falsey
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe ':cache' do
|
317
|
+
after { subject.uncache }
|
318
|
+
|
319
|
+
it 'should return a criteria' do
|
320
|
+
subject.cache.should be_an( Opium::Model::Criteria )
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'should cause :cached? to return true' do
|
324
|
+
subject.cache.cached?.should == true
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe ':uncache' do
|
329
|
+
it 'should return a criteria' do
|
330
|
+
subject.uncache.should be_an( Opium::Model::Criteria )
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'should cause :cached? to return false' do
|
334
|
+
subject.uncache.cached?.should == false
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Opium::Model::Scopable do
|
4
|
+
let( :model ) { Class.new { include Opium::Model::Scopable } }
|
5
|
+
|
6
|
+
describe 'the class' do
|
7
|
+
subject { model }
|
8
|
+
|
9
|
+
it { should respond_to( :criteria ) }
|
10
|
+
it { should respond_to( :scope ).with(2).arguments }
|
11
|
+
it { should respond_to( :default_scope ).with(1).argument }
|
12
|
+
it { should respond_to( :scoped ) }
|
13
|
+
it { should respond_to( :unscoped ) }
|
14
|
+
it { should respond_to( :with_scope ).with(1).argument }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'within a model' do
|
18
|
+
before do
|
19
|
+
stub_const( 'Game', Class.new do |klass|
|
20
|
+
include Opium::Model
|
21
|
+
field :title, type: String
|
22
|
+
field :release_price, type: Float
|
23
|
+
|
24
|
+
stub('model_name').and_return(ActiveModel::Name.new(klass, nil, 'Game'))
|
25
|
+
|
26
|
+
default_scope order( title: :asc )
|
27
|
+
|
28
|
+
scope :under_10, where( release_price: { '$lte' => 10.0 } )
|
29
|
+
scope :recent, -> { where( created_at: { '$gte' => Time.now - 3600 } ) }
|
30
|
+
scope :under do |limit|
|
31
|
+
where( release_price: { '$lte' => limit } )
|
32
|
+
end
|
33
|
+
end )
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ':criteria' do
|
37
|
+
it 'should be the :default_scope' do
|
38
|
+
Game.criteria.should == Game.default_scope
|
39
|
+
Game.criteria.should == Game.criteria
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should not be duplicated between calls' do
|
43
|
+
Game.criteria.should equal( Game.criteria )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe ':unscoped' do
|
48
|
+
it 'should return an empty Criteria' do
|
49
|
+
criteria = Game.unscoped
|
50
|
+
criteria.should be_a( Opium::Model::Criteria )
|
51
|
+
criteria.should_not be_constraints
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'if passed a block, should make scope calls within the block be unscoped, and return the result of the block' do
|
55
|
+
Game.default_scope.constraints.keys.should include('order')
|
56
|
+
criteria = Game.unscoped do
|
57
|
+
Game.limit( 10 ).tap do |inner|
|
58
|
+
inner.constraints.should =~ { 'limit' => 10, 'count' => 1 }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
criteria.should be_a( Opium::Model::Criteria )
|
62
|
+
criteria.constraints.should =~ { 'limit' => 10, 'count' => 1 }
|
63
|
+
criteria.constraints.keys.should_not include('order')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ':scope' do
|
68
|
+
it 'should create a method for each scope' do
|
69
|
+
Game.should respond_to( :under_10, :recent )
|
70
|
+
Game.should respond_to( :under ).with(1).argument
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'each scope should be a criteria' do
|
74
|
+
Game.under_10.should be_a( Opium::Model::Criteria )
|
75
|
+
Game.recent.should be_a( Opium::Model::Criteria )
|
76
|
+
Game.under( 20 ).should be_a( Opium::Model::Criteria )
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should not allow scopes without a criteria or block' do
|
80
|
+
expect { Game.scope :no_criteria_or_block }.to raise_exception(ArgumentError)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe ':default_scope' do
|
85
|
+
it 'should return a criteria if not given a parameter' do
|
86
|
+
Game.default_scope.should be_a( Opium::Model::Criteria )
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should have a :model equal to its creating model' do
|
90
|
+
Game.default_scope.model_name.should == 'Game'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should set a criteria if passed one' do
|
94
|
+
expected = Game.unscoped
|
95
|
+
Game.default_scope( expected )
|
96
|
+
Game.default_scope.should == expected
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should accept procs, which yield a criteria' do
|
100
|
+
expected = Game.unscoped.limit( 5 )
|
101
|
+
Game.default_scope( -> { Game.limit( 5 ) } ).should be_a( Opium::Model::Criteria )
|
102
|
+
Game.default_scope.should == expected
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should accept a block, which yields a criteria' do
|
106
|
+
expected = Game.unscoped.limit( 5 )
|
107
|
+
Game.default_scope { Game.limit( 5 ) }.should be_a( Opium::Model::Criteria )
|
108
|
+
Game.default_scope.should == expected
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe ':with_scope' do
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::Model::Serialization do
|
4
|
+
let( :model ) do
|
5
|
+
Class.new do
|
6
|
+
include Opium::Model
|
7
|
+
field :name
|
8
|
+
field :price
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it { model.should respond_to( :include_root_in_json ) }
|
13
|
+
it "include_root_in_json should default to false" do
|
14
|
+
model.include_root_in_json.should == false
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "instance" do
|
18
|
+
describe "with no data" do
|
19
|
+
let( :params ) { { "id" => nil, "created_at" => nil, "updated_at" => nil, "name" => nil, "price" => nil } }
|
20
|
+
subject { model.new }
|
21
|
+
its(:as_json) { should == params }
|
22
|
+
describe "to_json" do
|
23
|
+
it "should have no values in the JSON data" do
|
24
|
+
JSON.parse(subject.to_json).should include( params )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "with partial data" do
|
30
|
+
let( :params ) { { "name" => "test", "price" => nil } }
|
31
|
+
subject { model.new( name: "test" ) }
|
32
|
+
its(:as_json) { should include( params ) }
|
33
|
+
describe "to_json" do
|
34
|
+
it "should have the set values in the JSON data" do
|
35
|
+
JSON.parse(subject.to_json).should include( params )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "with full data" do
|
41
|
+
let( :params ) { { "name" => "test", "price" => 75.0 } }
|
42
|
+
subject { model.new( params ) }
|
43
|
+
its(:as_json) { should include( params ) }
|
44
|
+
describe "to_json" do
|
45
|
+
it "should have all values in the JSON data" do
|
46
|
+
JSON.parse(subject.to_json).should include( params )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::Model do
|
4
|
+
before do
|
5
|
+
stub_const( 'Event', Class.new do
|
6
|
+
include Opium::Model
|
7
|
+
field :name, type: String
|
8
|
+
field :occurred_at, type: DateTime
|
9
|
+
field :severe, type: Boolean
|
10
|
+
end )
|
11
|
+
end
|
12
|
+
|
13
|
+
subject { Event }
|
14
|
+
|
15
|
+
it { should respond_to( :model_name, :validates, :define_model_callbacks ) }
|
16
|
+
it { should respond_to( :field ).with(2).arguments }
|
17
|
+
it { should respond_to( :delete_all, :find ).with(1).argument }
|
18
|
+
it { should respond_to( :connection ) }
|
19
|
+
it { should respond_to( :human_attribute_name, :lookup_ancestors ) }
|
20
|
+
|
21
|
+
describe 'instance' do
|
22
|
+
subject { Event.new }
|
23
|
+
|
24
|
+
it { should respond_to( :attributes ) }
|
25
|
+
it { should respond_to( :serializable_hash, :as_json, :from_json ) }
|
26
|
+
it { should respond_to( :changes, :changed? ) }
|
27
|
+
it { should respond_to( :inspect ) }
|
28
|
+
it { should respond_to( :to_key, :to_model ) }
|
29
|
+
|
30
|
+
its(:attributes) do
|
31
|
+
should_not be_nil
|
32
|
+
should be_a_kind_of( Hash )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'inspect' do
|
37
|
+
describe 'within a blank model' do
|
38
|
+
subject { Event.new }
|
39
|
+
|
40
|
+
it { subject.inspect.should == '#<Event id: nil, created_at: nil, updated_at: nil, name: nil, occurred_at: nil, severe: nil>' }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'within a model with data' do
|
44
|
+
let(:event_time) { Time.now }
|
45
|
+
subject { Event.new id: 'abc123', name: 'ping', occurred_at: event_time, severe: false }
|
46
|
+
it { subject.inspect.should == "#<Event id: \"abc123\", created_at: nil, updated_at: nil, name: \"ping\", occurred_at: #{event_time.to_datetime.inspect}, severe: false>" }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|