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,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::Model::Findable do
|
4
|
+
before do
|
5
|
+
stub_const( 'Model', Class.new do
|
6
|
+
include Opium::Model::Findable
|
7
|
+
end )
|
8
|
+
stub_const( 'Game', Class.new do
|
9
|
+
include Opium::Model
|
10
|
+
field :title, type: String
|
11
|
+
field :release_price, type: Float
|
12
|
+
end )
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'the module' do
|
16
|
+
subject { Model }
|
17
|
+
|
18
|
+
it { is_expected.to respond_to( :find ).with(1).argument }
|
19
|
+
it { is_expected.to respond_to( :first, :each, :each_with_index ) }
|
20
|
+
it { is_expected.to respond_to( :map ) }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '.find' do
|
24
|
+
before do
|
25
|
+
stub_request( :get, 'https://api.parse.com/1/classes/Game/abcd1234' ).to_return(
|
26
|
+
body: { objectId: 'abcd1234', title: 'Skyrim', releasePrice: 59.99, createdAt: '2011-11-11T12:00:00Z', updatedAt: '2014-11-11T15:13:47Z' }.to_json,
|
27
|
+
status: 200,
|
28
|
+
headers: { content_type: 'application/json' }
|
29
|
+
)
|
30
|
+
|
31
|
+
stub_request( :get, 'https://api.parse.com/1/classes/Game/deadbeef' ).to_return(
|
32
|
+
body: { code: 404, error: 'Could not locate a "Game" with id "deadbeef"' }.to_json,
|
33
|
+
status: 404,
|
34
|
+
headers: { content_type: 'application/json' }
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:result) { Game.find( id ) }
|
39
|
+
|
40
|
+
context 'when a model exists' do
|
41
|
+
let(:id) { 'abcd1234' }
|
42
|
+
|
43
|
+
it { expect { result }.to_not raise_exception }
|
44
|
+
it { expect( result ).to be_an( Opium::Model ) }
|
45
|
+
it { expect( result ).to be_a( Game ) }
|
46
|
+
it 'has the proper model attributes' do
|
47
|
+
expect( result.attributes ).to include( title: 'Skyrim', release_price: 59.99 )
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when a model does not exist' do
|
52
|
+
let(:id) { 'deadbeef' }
|
53
|
+
|
54
|
+
it { expect { result }.to raise_exception }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if defined?( Kaminari )
|
4
|
+
describe Opium::Model::Queryable do
|
5
|
+
describe 'when included in a class' do
|
6
|
+
before do
|
7
|
+
stub_const( 'Query', Class.new { include Opium::Model::Queryable } )
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { Query }
|
11
|
+
|
12
|
+
it { should <= Opium::Model::Queryable }
|
13
|
+
it { Opium::Model::Queryable::ClassMethods <= ::Kaminari::PageScopeMethods }
|
14
|
+
|
15
|
+
# Really, this just is a sanity check to verify that some of the kaminari methods
|
16
|
+
# successfully were added.
|
17
|
+
it { should respond_to( :total_pages, :current_page, :max_pages, :entry_name ) }
|
18
|
+
it { should respond_to( Kaminari.config.page_method_name ).with(1).argument }
|
19
|
+
it { should respond_to( :limit, :offset ) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if defined?( Kaminari )
|
4
|
+
describe Opium::Model::Scopable do
|
5
|
+
describe 'when included in a class' do
|
6
|
+
before do
|
7
|
+
stub_const( 'Scoped', Class.new { include Opium::Model::Scopable } )
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { Scoped }
|
11
|
+
|
12
|
+
it { should <= Opium::Model::Scopable }
|
13
|
+
it { should <= ::Kaminari::ConfigurationMethods }
|
14
|
+
|
15
|
+
# Really, this just is a sanity check to verify that some of the kaminari methods
|
16
|
+
# successfully were added.
|
17
|
+
it { should respond_to( :max_per_page, :max_pages ) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if defined?( Kaminari )
|
4
|
+
describe 'when Kaminari is present' do
|
5
|
+
before do
|
6
|
+
stub_const( 'Game', Class.new do
|
7
|
+
include Opium::Model
|
8
|
+
field :title, type: String
|
9
|
+
field :price, type: Float
|
10
|
+
end )
|
11
|
+
stub_request(:get, "https://api.parse.com/1/classes/Game?count=1&limit=10&skip=0").
|
12
|
+
with(:headers => {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
13
|
+
to_return(
|
14
|
+
status: 200,
|
15
|
+
body: {
|
16
|
+
count: 100,
|
17
|
+
results: []
|
18
|
+
}.to_json,
|
19
|
+
headers: { content_type: 'application/json' }
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Opium::Model do
|
24
|
+
subject { Game }
|
25
|
+
|
26
|
+
it { is_expected.to respond_to(:page, :per).with(1).argument }
|
27
|
+
|
28
|
+
describe '.page' do
|
29
|
+
let( :query ) { subject.page( 5 ) }
|
30
|
+
it { expect { query }.to_not raise_exception }
|
31
|
+
it { expect( query ).to be_an( Opium::Model::Criteria ) }
|
32
|
+
it { expect( query.offset_value ).to eq 100 }
|
33
|
+
it { expect( query ).to be_cached }
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '.per' do
|
37
|
+
let( :query ) { subject.per( 20 ) }
|
38
|
+
it { expect { query }.to_not raise_exception }
|
39
|
+
it { expect( query.limit_value ).to eq 20 }
|
40
|
+
it { expect( query ).to be_cached }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.limit_value' do
|
44
|
+
let( :query ) { subject.limit_value }
|
45
|
+
it { expect { query }.to_not raise_exception }
|
46
|
+
it { expect( query ).to eq ::Kaminari.config.default_per_page }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '.offset_value' do
|
50
|
+
let( :query ) { subject.offset_value }
|
51
|
+
it { expect { query }.to_not raise_exception }
|
52
|
+
it { expect( query ).to eq 0 }
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when .page is called after .per' do
|
56
|
+
let( :query ) { subject.per( 10 ).page( 2 ) }
|
57
|
+
it { expect { query }.to_not raise_exception }
|
58
|
+
|
59
|
+
it 'sets the offset_value correctly' do
|
60
|
+
expect( query.offset_value ).to eq 10
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'sets the limit_value correctly' do
|
64
|
+
expect( query.limit_value ).to eq 10
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe Opium::Model::Criteria do
|
70
|
+
subject { Game.criteria }
|
71
|
+
|
72
|
+
it { is_expected.to be_a( ::Kaminari::PageScopeMethods ) }
|
73
|
+
it { is_expected.to respond_to(:page, :per).with(1).argument }
|
74
|
+
it { is_expected.to respond_to(:limit_value, :offset_value) }
|
75
|
+
|
76
|
+
describe '#page' do
|
77
|
+
let( :query ) { subject.page( 0 ) }
|
78
|
+
it { expect { query }.to_not raise_exception }
|
79
|
+
it { expect( query.offset_value ).to eq 0 }
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#per' do
|
83
|
+
let( :query ) { subject.page( 2 ).per( 10 ) }
|
84
|
+
it { expect { query }.to_not raise_exception }
|
85
|
+
it { expect( query.max_per_page ).to be_nil }
|
86
|
+
it { expect( query.constraints[:limit] ).to eq 10 }
|
87
|
+
it { expect( query.constraints[:skip] ).to eq 10 }
|
88
|
+
it { expect( query.limit_value ).to eq 10 }
|
89
|
+
it { expect( query.offset_value ).to eq 10 }
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#total_pages' do
|
93
|
+
let( :query ) { subject.page( 0 ).per( 10 ) }
|
94
|
+
it { expect( query.offset_value ).to be_truthy }
|
95
|
+
it { expect { query.total_pages }.to_not raise_exception }
|
96
|
+
it { expect( query.total_pages ).to eq 10 }
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#entry_name' do
|
100
|
+
it { expect( subject.entry_name ).to eq Game.model_name.human.downcase }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,367 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Opium::Model::Persistable do
|
4
|
+
let( :model ) { Class.new { include Opium::Model::Persistable } }
|
5
|
+
|
6
|
+
describe 'in a model' do
|
7
|
+
subject { model }
|
8
|
+
|
9
|
+
it { should respond_to( :destroy_all ).with(1).argument }
|
10
|
+
it { should respond_to( :delete_all ).with(1).argument }
|
11
|
+
it { should respond_to( :create, :create! ).with(1).argument }
|
12
|
+
it { should respond_to( :add_header_to ).with(4).arguments }
|
13
|
+
it { should respond_to( :added_headers ) }
|
14
|
+
it { should respond_to( :get_header_for ).with(2).arguments }
|
15
|
+
|
16
|
+
describe ':added_headers' do
|
17
|
+
it { subject.added_headers.should be_a( Hash ) }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ':add_header_to' do
|
21
|
+
after { subject.added_headers.clear }
|
22
|
+
|
23
|
+
it { expect { subject.add_header_to :put, :x_header, 42 }.to_not raise_exception }
|
24
|
+
it { subject.add_header_to(:delete, :x_header, 37, only: :delete).should be_nil }
|
25
|
+
|
26
|
+
it 'should add header information to :added_headers' do
|
27
|
+
subject.add_header_to( :put, :x_header, 42, except: :update )
|
28
|
+
subject.added_headers.should have_key(:put)
|
29
|
+
subject.added_headers[:put].should include( :header, :value, :options )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe ':get_header_for' do
|
34
|
+
after { subject.added_headers.clear }
|
35
|
+
|
36
|
+
it 'should return an empty hash if a method has no added headers' do
|
37
|
+
subject.get_header_for( :put, :update ).should == {}
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should return an empty hash if a context is not within the only list for a method' do
|
41
|
+
subject.add_header_to( :put, :x_header, 42, only: [:foo, :bar] )
|
42
|
+
subject.get_header_for( :put, :baz ).should == {}
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should return an empty hash if a context is within the except list for a method' do
|
46
|
+
subject.add_header_to( :put, :x_header, 42, except: [:foo, :bar] )
|
47
|
+
subject.get_header_for( :put, :foo ).should == {}
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return a headers hash if context-free' do
|
51
|
+
subject.add_header_to( :put, :x_header, 42 )
|
52
|
+
subject.get_header_for( :put, :update ).should == { headers: { x_header: 42 } }
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should return a headers hash if a context is within the only list for a method' do
|
56
|
+
subject.add_header_to( :put, :x_header, 42, only: :update )
|
57
|
+
subject.get_header_for( :put, :update ).should == { headers: { x_header: 42 } }
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should return a headers hash if a context is not within the except list for a method' do
|
61
|
+
subject.add_header_to( :put, :x_header, 42, except: :foo )
|
62
|
+
subject.get_header_for( :put, :update ).should == { headers: { x_header: 42 } }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'instance' do
|
68
|
+
subject { model.new }
|
69
|
+
|
70
|
+
it { should respond_to( :save ).with(1).argument }
|
71
|
+
it { should respond_to( :save! ).with(0).arguments }
|
72
|
+
it { should respond_to( :update_attributes, :update_attributes! ).with(1).argument }
|
73
|
+
it { should respond_to( :touch ) }
|
74
|
+
it { should respond_to( :destroy ) }
|
75
|
+
it { should respond_to( :delete ) }
|
76
|
+
it { should respond_to( :new_record?, :persisted? ) }
|
77
|
+
it { should respond_to( :pointer, :to_parse ) }
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'within a model' do
|
81
|
+
before do
|
82
|
+
stub_const( 'Game', Class.new do
|
83
|
+
include Opium::Model
|
84
|
+
field :title, type: String
|
85
|
+
field :released_on, type: Date
|
86
|
+
field :release_price, type: Float
|
87
|
+
|
88
|
+
validates :release_price, numericality: { greater_than: 0 }
|
89
|
+
end )
|
90
|
+
end
|
91
|
+
|
92
|
+
after do
|
93
|
+
Opium::Model::Criteria.models.clear
|
94
|
+
end
|
95
|
+
|
96
|
+
describe ':new_record?' do
|
97
|
+
subject { Game.new }
|
98
|
+
|
99
|
+
it 'should be true in a model without an id' do
|
100
|
+
subject.should be_a_new_record
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should be false in a model with an id' do
|
104
|
+
subject.attributes = { id: 'abcd1234' }
|
105
|
+
subject.should_not be_a_new_record
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'when changing the attributes of a model' do
|
110
|
+
subject { Game.new( id: 'abcd1234' ) }
|
111
|
+
|
112
|
+
it { expect { subject.attributes = { title: 'Skyrim' } }.to change( subject, :persisted? ).from(true).to(false) }
|
113
|
+
end
|
114
|
+
|
115
|
+
describe 'a new model can be created with' do
|
116
|
+
before do
|
117
|
+
stub_request( :post, 'https://api.parse.com/1/classes/Game' ).with(
|
118
|
+
body: { title: 'Skyrim', releasedOn: { '__type' => 'Date', 'iso' => '2011-11-11' }, releasePrice: 59.99 },
|
119
|
+
headers: { 'Content-Type' => 'application/json' }
|
120
|
+
).to_return(
|
121
|
+
body: { objectId: 'abcd1234', createdAt: Time.now.to_s }.to_json,
|
122
|
+
status: 200,
|
123
|
+
headers: { 'Content-Type' => 'application/json', Location: 'https://api.parse.com/1/classes/Game/abcd1234' }
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
describe ':create' do
|
128
|
+
it 'which should return a persisted? model' do
|
129
|
+
result = Game.create( title: 'Skyrim', released_on: '2011-11-11', release_price: 59.99 )
|
130
|
+
result.should_not be_a_new_record
|
131
|
+
result.should be_persisted
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe ':create!' do
|
136
|
+
it 'which should return a persisted? model' do
|
137
|
+
result = Game.create( title: 'Skyrim', released_on: '2011-11-11', release_price: 59.99 )
|
138
|
+
result.should_not be_a_new_record
|
139
|
+
result.should be_persisted
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe 'attepting to' do
|
145
|
+
describe ':create an invalid model' do
|
146
|
+
it 'should not raise an exception' do
|
147
|
+
expect { Game.create( release_price: -10.00 ) }.to_not raise_exception
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should have errors' do
|
151
|
+
result = Game.create( release_price: -10.00 )
|
152
|
+
result.should_not be_persisted
|
153
|
+
result.errors.should_not be_empty
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe ':create! an invalid model' do
|
158
|
+
it 'should raise an exception' do
|
159
|
+
expect { Game.create!( release_price: -10.00 ) }.to raise_exception
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe 'when saving a new model' do
|
165
|
+
subject { Game.new( title: 'Skyrim', released_on: '2011-11-11', release_price: '59.99' ) }
|
166
|
+
|
167
|
+
before do
|
168
|
+
stub_request( :post, 'https://api.parse.com/1/classes/Game' ).with(
|
169
|
+
body: { title: 'Skyrim', releasedOn: { '__type' => 'Date', 'iso' => '2011-11-11' }, releasePrice: 59.99 },
|
170
|
+
headers: { 'Content-Type' => 'application/json' }
|
171
|
+
).to_return(
|
172
|
+
body: { objectId: 'abcd1234', createdAt: Time.now.to_s }.to_json,
|
173
|
+
status: 200,
|
174
|
+
headers: { 'Content-Type' => 'application/json', Location: 'https://api.parse.com/1/classes/Game/abcd1234' }
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should have its object_id and created_at fields updated' do
|
179
|
+
subject.id.should be_nil
|
180
|
+
subject.created_at.should be_nil
|
181
|
+
subject.should be_a_new_record
|
182
|
+
subject.should_not be_persisted
|
183
|
+
subject.save.should == true
|
184
|
+
subject.should_not be_a_new_record
|
185
|
+
subject.should be_persisted
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'when saving an existing model' do
|
190
|
+
subject { Game.new( id: 'abcd1234', created_at: Time.now - 3600, title: 'Skyrim' ) }
|
191
|
+
|
192
|
+
before do
|
193
|
+
stub_request( :put, 'https://api.parse.com/1/classes/Game/abcd1234' ).with(
|
194
|
+
body: { releasedOn: { '__type' => 'Date', 'iso' => '2011-11-11' }, releasePrice: 59.99 },
|
195
|
+
headers: { content_type: 'application/json' }
|
196
|
+
).to_return(
|
197
|
+
body: { updatedAt: Time.now.to_s }.to_json,
|
198
|
+
status: 200,
|
199
|
+
headers: { content_type: 'application/json', Location: 'https://api.parse.com/1/classes/Game/abcd1234' }
|
200
|
+
)
|
201
|
+
end
|
202
|
+
|
203
|
+
before :each do
|
204
|
+
subject.attributes = { released_on: '2011-11-11', release_price: 59.99 }
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should have its updated_at fields updated' do
|
208
|
+
subject.save.should == true
|
209
|
+
subject.should be_persisted
|
210
|
+
subject.updated_at.should_not be_nil
|
211
|
+
end
|
212
|
+
|
213
|
+
it ':save! should not raise an exception' do
|
214
|
+
subject.release_price.should_not be_nil
|
215
|
+
expect { subject.save! }.to_not raise_exception
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe 'when saving a model causes a ParseError' do
|
220
|
+
subject { Game.new( id: 'deadbeef', created_at: Time.now - 3600, title: 'Skyrim' ) }
|
221
|
+
|
222
|
+
before do
|
223
|
+
stub_request( :put, 'https://api.parse.com/1/classes/Game/deadbeef' ).with(
|
224
|
+
body: { releasePrice: 599.99 },
|
225
|
+
headers: { 'Content-Type' => 'application/json' }
|
226
|
+
).to_return(
|
227
|
+
body: { code: 404, error: 'Could not locate a "Game" with id of "deadbeef".' }.to_json,
|
228
|
+
status: 404,
|
229
|
+
headers: { 'Content-Type' => 'application/json' }
|
230
|
+
)
|
231
|
+
end
|
232
|
+
|
233
|
+
before(:each) do
|
234
|
+
subject.release_price = 599.99
|
235
|
+
end
|
236
|
+
|
237
|
+
it { expect { subject.save }.to_not raise_exception }
|
238
|
+
|
239
|
+
it ':save should return false and have errors' do
|
240
|
+
subject.save.should == false
|
241
|
+
subject.errors.should_not be_empty
|
242
|
+
end
|
243
|
+
|
244
|
+
it { expect { subject.save! }.to raise_exception }
|
245
|
+
end
|
246
|
+
|
247
|
+
describe 'when saving a model with validates: false' do
|
248
|
+
subject { Game.new( title: 'Skyrim' ) }
|
249
|
+
|
250
|
+
it 'should not receive :valid?, but should receive :create' do
|
251
|
+
subject.should_not receive(:valid?)
|
252
|
+
subject.should receive(:create)
|
253
|
+
subject.save( validates: false )
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe 'when saving an invalid model' do
|
258
|
+
subject { Game.new( title: 'Skyrim', release_price: -10.99 ) }
|
259
|
+
|
260
|
+
it ':save should return false and have :errors' do
|
261
|
+
subject.save.should == false
|
262
|
+
subject.errors.should_not be_empty
|
263
|
+
end
|
264
|
+
|
265
|
+
it ':save! should raise an exception' do
|
266
|
+
expect { subject.save! }.to raise_exception
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
describe ':update_attributes' do
|
271
|
+
subject { Game.new( id: 'abcd1234', title: 'Skyrim' ) }
|
272
|
+
|
273
|
+
let(:new_attributes) { { released_on: '2011-11-11', release_price: 59.99 } }
|
274
|
+
|
275
|
+
it 'should alter the attributes and save the model' do
|
276
|
+
subject.should receive(:attributes=).with( new_attributes )
|
277
|
+
subject.should receive(:save)
|
278
|
+
subject.update_attributes( new_attributes )
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
describe ':update_attributes!' do
|
283
|
+
subject { Game.new( id: 'abcd1234', title: 'Skyrim' ) }
|
284
|
+
|
285
|
+
let(:new_attributes) { { released_on: '2011-11-11', release_price: 59.99 } }
|
286
|
+
|
287
|
+
it 'should alter the attributes and save! the model' do
|
288
|
+
subject.should receive(:attributes=).with( new_attributes )
|
289
|
+
subject.should receive(:save!)
|
290
|
+
subject.update_attributes!( new_attributes )
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe ':touch' do
|
295
|
+
subject { Game.new( id: 'abcd1234', title: 'Skyrim' ) }
|
296
|
+
|
297
|
+
before do
|
298
|
+
stub_request( :put, 'https://api.parse.com/1/classes/Game/abcd1234' ).with(
|
299
|
+
body: { },
|
300
|
+
headers: { 'Content-Type' => 'application/json' }
|
301
|
+
).to_return(
|
302
|
+
body: { updatedAt: '2014-12-18T15:00:00Z' }.to_json,
|
303
|
+
status: 200,
|
304
|
+
headers: { content_type: 'application/json', Location: 'https://api.parse.com/1/classes/Game/abcd1234' }
|
305
|
+
)
|
306
|
+
end
|
307
|
+
|
308
|
+
it do
|
309
|
+
expect { subject.touch }.to change( subject, :updated_at ).from( nil ).to( '2014-12-18T15:00:00Z'.to_datetime )
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe 'when deleting a new model' do
|
314
|
+
subject { Game.new( title: 'Skyrim' ) }
|
315
|
+
|
316
|
+
it 'should not receive :http_delete' do
|
317
|
+
subject.class.should_not receive( :http_delete )
|
318
|
+
subject.delete
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'should be frozen after delete' do
|
322
|
+
subject.delete
|
323
|
+
subject.should be_frozen
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
describe 'when deleting an existing model' do
|
328
|
+
subject { Game.new( id: 'abcd1234', title: 'Skyrim' ) }
|
329
|
+
|
330
|
+
it 'should receive :http_delete' do
|
331
|
+
subject.class.should receive( :http_delete ).with('abcd1234', {})
|
332
|
+
subject.delete
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'should be frozen after delete' do
|
336
|
+
stub_request( :delete, 'https://api.parse.com/1/classes/Game/abcd1234' ).to_return( status: 200 )
|
337
|
+
|
338
|
+
subject.delete
|
339
|
+
subject.should be_frozen
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
describe ':pointer' do
|
344
|
+
subject { Game.new( id: 'abcd1234' ) }
|
345
|
+
|
346
|
+
it 'should be an Opium::Pointer' do
|
347
|
+
subject.pointer.should be_a( Opium::Pointer )
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'should have the :id of the instance' do
|
351
|
+
subject.pointer.id.should == subject.id
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'should have the :model_name of the instance class' do
|
355
|
+
subject.pointer.model_name.should == subject.class.model_name
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
describe ':to_parse' do
|
360
|
+
subject { Game.new( id: 'abcd1234' ) }
|
361
|
+
|
362
|
+
it 'should be a pointer hash' do
|
363
|
+
subject.to_parse.should == subject.pointer.to_parse
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|