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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +24 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +11 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +71 -0
  9. data/Rakefile +10 -0
  10. data/lib/generators/opium/config_generator.rb +15 -0
  11. data/lib/generators/opium/model_generator.rb +33 -0
  12. data/lib/generators/opium/templates/config.yml +27 -0
  13. data/lib/generators/opium/templates/model.rb +10 -0
  14. data/lib/opium/config.rb +44 -0
  15. data/lib/opium/extensions/array.rb +10 -0
  16. data/lib/opium/extensions/boolean.rb +13 -0
  17. data/lib/opium/extensions/date.rb +18 -0
  18. data/lib/opium/extensions/date_time.rb +18 -0
  19. data/lib/opium/extensions/false_class.rb +7 -0
  20. data/lib/opium/extensions/float.rb +13 -0
  21. data/lib/opium/extensions/geo_point.rb +37 -0
  22. data/lib/opium/extensions/hash.rb +43 -0
  23. data/lib/opium/extensions/integer.rb +13 -0
  24. data/lib/opium/extensions/numeric.rb +7 -0
  25. data/lib/opium/extensions/object.rb +15 -0
  26. data/lib/opium/extensions/pointer.rb +20 -0
  27. data/lib/opium/extensions/regexp.rb +12 -0
  28. data/lib/opium/extensions/string.rb +20 -0
  29. data/lib/opium/extensions/time.rb +19 -0
  30. data/lib/opium/extensions/true_class.rb +7 -0
  31. data/lib/opium/extensions.rb +16 -0
  32. data/lib/opium/model/attributable.rb +37 -0
  33. data/lib/opium/model/callbacks.rb +38 -0
  34. data/lib/opium/model/connectable.rb +155 -0
  35. data/lib/opium/model/criteria.rb +123 -0
  36. data/lib/opium/model/dirty.rb +35 -0
  37. data/lib/opium/model/field.rb +31 -0
  38. data/lib/opium/model/fieldable.rb +57 -0
  39. data/lib/opium/model/findable.rb +20 -0
  40. data/lib/opium/model/kaminari/queryable.rb +46 -0
  41. data/lib/opium/model/kaminari/scopable.rb +15 -0
  42. data/lib/opium/model/kaminari.rb +4 -0
  43. data/lib/opium/model/persistable.rb +153 -0
  44. data/lib/opium/model/queryable.rb +150 -0
  45. data/lib/opium/model/scopable.rb +58 -0
  46. data/lib/opium/model/serialization.rb +13 -0
  47. data/lib/opium/model.rb +47 -0
  48. data/lib/opium/railtie.rb +14 -0
  49. data/lib/opium/user.rb +44 -0
  50. data/lib/opium/version.rb +3 -0
  51. data/lib/opium.rb +9 -0
  52. data/opium.gemspec +40 -0
  53. data/spec/opium/config/opium.yml +5 -0
  54. data/spec/opium/config_spec.rb +56 -0
  55. data/spec/opium/extensions/array_spec.rb +34 -0
  56. data/spec/opium/extensions/boolean_spec.rb +28 -0
  57. data/spec/opium/extensions/date_spec.rb +55 -0
  58. data/spec/opium/extensions/date_time_spec.rb +55 -0
  59. data/spec/opium/extensions/float_spec.rb +42 -0
  60. data/spec/opium/extensions/geo_point_spec.rb +55 -0
  61. data/spec/opium/extensions/hash_spec.rb +76 -0
  62. data/spec/opium/extensions/integer_spec.rb +42 -0
  63. data/spec/opium/extensions/object_spec.rb +24 -0
  64. data/spec/opium/extensions/pointer_spec.rb +28 -0
  65. data/spec/opium/extensions/regexp_spec.rb +23 -0
  66. data/spec/opium/extensions/string_spec.rb +65 -0
  67. data/spec/opium/extensions/time_spec.rb +55 -0
  68. data/spec/opium/model/attributable_spec.rb +45 -0
  69. data/spec/opium/model/callbacks_spec.rb +59 -0
  70. data/spec/opium/model/connectable_spec.rb +218 -0
  71. data/spec/opium/model/criteria_spec.rb +285 -0
  72. data/spec/opium/model/dirty_spec.rb +39 -0
  73. data/spec/opium/model/fieldable_spec.rb +133 -0
  74. data/spec/opium/model/findable_spec.rb +57 -0
  75. data/spec/opium/model/kaminari/queryable_spec.rb +22 -0
  76. data/spec/opium/model/kaminari/scopable_spec.rb +20 -0
  77. data/spec/opium/model/kaminari_spec.rb +104 -0
  78. data/spec/opium/model/persistable_spec.rb +367 -0
  79. data/spec/opium/model/queryable_spec.rb +338 -0
  80. data/spec/opium/model/scopable_spec.rb +115 -0
  81. data/spec/opium/model/serialization_spec.rb +51 -0
  82. data/spec/opium/model_spec.rb +49 -0
  83. data/spec/opium/user_spec.rb +195 -0
  84. data/spec/opium_spec.rb +5 -0
  85. data/spec/spec_helper.rb +25 -0
  86. 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