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
data/lib/opium/version.rb
CHANGED
@@ -1,15 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Opium::Model::Attributable do
|
4
|
-
let( :model ) { Class.new { include Opium::Model::Attributable } }
|
5
|
-
|
6
|
-
context 'within an instance' do
|
7
|
-
subject { model.new }
|
8
|
-
|
9
|
-
it { is_expected.to respond_to( :attributes, :attributes= ) }
|
10
|
-
it { is_expected.to respond_to( :attributes_to_parse ) }
|
11
|
-
end
|
12
|
-
|
13
4
|
context 'when included in a model' do
|
14
5
|
before do
|
15
6
|
stub_const( 'Book', Class.new do
|
@@ -107,7 +107,10 @@ describe Opium::Model::Batchable::Batch do
|
|
107
107
|
context 'when #queue is empty' do
|
108
108
|
before { subject.queue.clear }
|
109
109
|
|
110
|
-
it { expect { subject.execute }.to raise_exception }
|
110
|
+
# it { expect { subject.execute }.to raise_exception }
|
111
|
+
|
112
|
+
it { expect { subject.execute }.to_not raise_exception }
|
113
|
+
it { expect( subject.owner ).to_not receive(:http_post) }
|
111
114
|
end
|
112
115
|
|
113
116
|
context 'when #queue has more than MAX_BATCH_SIZE operations' do
|
@@ -2,4 +2,152 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Opium::Model::Batchable do
|
4
4
|
it { expect( described_class.constants ).to include( :Batch, :Operation ) }
|
5
|
+
|
6
|
+
before do
|
7
|
+
stub_const( 'Game', Class.new do
|
8
|
+
include Opium::Model
|
9
|
+
field :title, type: String
|
10
|
+
end )
|
11
|
+
|
12
|
+
stub_request(:get, "https://api.parse.com/1/classes/Game/abcd1234").
|
13
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
14
|
+
to_return(:status => 200, :body => { objectId: 'abcd1234', title: 'Never Alone' }.to_json, :headers => {})
|
15
|
+
|
16
|
+
stub_request(:get, "https://api.parse.com/1/classes/Game/efgh5678").
|
17
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
18
|
+
to_return(:status => 200, :body => { objectId: 'efgh5678', title: 'Guacamelee' }.to_json, :headers => {})
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.batched?' do
|
22
|
+
let(:result) { Game.batched? }
|
23
|
+
|
24
|
+
context 'when there is no batch job' do
|
25
|
+
it { expect( result ).to be_falsey }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when there is a batch job' do
|
29
|
+
before(:each) { Game.create_batch }
|
30
|
+
after(:each) { Game.delete_batch }
|
31
|
+
|
32
|
+
it { expect( result ).to be_truthy }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '.create_batch' do
|
37
|
+
after { Game.delete_batch }
|
38
|
+
let(:result) { Game.create_batch }
|
39
|
+
|
40
|
+
context 'when there is no existing batch job' do
|
41
|
+
it { expect { result }.to_not raise_exception }
|
42
|
+
it { expect( result ).to be_a Opium::Model::Batchable::Batch }
|
43
|
+
it { expect( result.depth ).to be 0 }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when there is a current batch job' do
|
47
|
+
before { Game.create_batch }
|
48
|
+
after { Game.delete_batch }
|
49
|
+
|
50
|
+
it { expect { result }.to_not raise_exception }
|
51
|
+
it( 'increments the depth' ) { expect( result.depth ).to eq 1 }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '.delete_batch' do
|
56
|
+
let(:result) { Game.delete_batch }
|
57
|
+
|
58
|
+
context 'when there is no existing batch job' do
|
59
|
+
it { expect { result }.to raise_exception }
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when there is a current batch job' do
|
63
|
+
before { Game.create_batch }
|
64
|
+
|
65
|
+
context 'with depth greater than 0' do
|
66
|
+
before { Game.create_batch }
|
67
|
+
after { Game.delete_batch }
|
68
|
+
|
69
|
+
it { expect { result }.to_not raise_exception }
|
70
|
+
it { expect( result ).to be_a Opium::Model::Batchable::Batch }
|
71
|
+
it( 'decrements the depth' ) { expect( result.depth ).to eq 0 }
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'with depth of 0' do
|
75
|
+
it { expect { result }.to_not raise_exception }
|
76
|
+
it { expect( result ).to be_nil }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '.current_batch_job' do
|
82
|
+
let(:result) { Game.current_batch_job }
|
83
|
+
|
84
|
+
context 'when there is no batch job' do
|
85
|
+
it { expect { result }.to_not raise_exception }
|
86
|
+
it { expect( result ).to be_nil }
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when a batch job has been created' do
|
90
|
+
before { Game.create_batch }
|
91
|
+
after { Game.delete_batch }
|
92
|
+
|
93
|
+
it { expect { result }.to_not raise_exception }
|
94
|
+
it { expect( result ).to be_a Opium::Model::Batchable::Batch }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '.batch' do
|
99
|
+
let(:batch) { Game.batch( batch_options, &batch_block ) }
|
100
|
+
let(:batch_options) { { } }
|
101
|
+
let(:batch_block) { -> { } }
|
102
|
+
|
103
|
+
context 'when given a block' do
|
104
|
+
it { expect { batch }.to_not raise_exception }
|
105
|
+
it { expect {|b| Game.batch( batch_options, &b ) }.to yield_control.at_least(1).times }
|
106
|
+
it 'is batched within the block' do
|
107
|
+
Game.batch( batch_options ) do
|
108
|
+
expect( Game ).to be_batched
|
109
|
+
|
110
|
+
Game.batch( batch_options ) do
|
111
|
+
expect( Game ).to be_batched
|
112
|
+
expect( Game.current_batch_job.depth ).to eq 1
|
113
|
+
end
|
114
|
+
|
115
|
+
expect( Game ).to be_batched
|
116
|
+
expect( Game.current_batch_job.depth ).to eq 0
|
117
|
+
end
|
118
|
+
expect( Game ).to_not be_batched
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'without a block' do
|
123
|
+
let(:batch_block) { }
|
124
|
+
|
125
|
+
it { expect { batch }.to raise_exception }
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'with mode: :mixed' do
|
129
|
+
let(:batch_options) { { mode: :mixed } }
|
130
|
+
let(:batch_block) do
|
131
|
+
-> do
|
132
|
+
Game.find('abcd1234').save
|
133
|
+
Game.new( title: 'Skyrim' ).save
|
134
|
+
Game.find('efgh5678').destroy
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
it { expect { batch }.to_not raise_exception }
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'with mode: :ordered' do
|
143
|
+
let(:batch_options) { { mode: :ordered } }
|
144
|
+
let(:batch_block) do
|
145
|
+
-> do
|
146
|
+
Game.find('abcd1234').save
|
147
|
+
Game.new( title: 'Skyrim' ).save
|
148
|
+
Game.find('efgh5678').destroy
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
5
153
|
end
|
@@ -147,6 +147,22 @@ describe Opium::Model::Fieldable do
|
|
147
147
|
expect( Model.default_attributes ).to include( 'symbolic_with_string_default' => :value, 'symbolic_with_symbol_default' => :value )
|
148
148
|
end
|
149
149
|
|
150
|
+
describe '.has_field?' do
|
151
|
+
let(:result) { subject.has_field? field_name }
|
152
|
+
|
153
|
+
context 'with a valid field_name' do
|
154
|
+
let(:field_name) { :symbolic_with_string_default }
|
155
|
+
|
156
|
+
it { expect( result ).to be_truthy }
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'with an invalid field_name' do
|
160
|
+
let(:field_name) { :not_defined }
|
161
|
+
|
162
|
+
it { expect( result ).to be_falsey }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
150
166
|
describe '.field' do
|
151
167
|
subject { Model.new }
|
152
168
|
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::Model::Reference do
|
4
|
+
before do
|
5
|
+
stub_const( 'Tree', Class.new do |klass|
|
6
|
+
include Opium::Model
|
7
|
+
field :trees
|
8
|
+
|
9
|
+
class << klass
|
10
|
+
stub(:model_name).and_return( ActiveModel::Name.new( self, nil, 'Tree' ) )
|
11
|
+
end
|
12
|
+
end )
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:child) { Tree.new id: 'c1234' }
|
16
|
+
let(:metadata) { Opium::Model::Relatable::Metadata.new( Tree, :belongs_to, :parent, class_name: 'Tree' ) }
|
17
|
+
|
18
|
+
describe '.to_ruby' do
|
19
|
+
let(:result) { described_class.to_ruby( subject ) }
|
20
|
+
|
21
|
+
context 'when given a hash' do
|
22
|
+
subject { { metadata: metadata, context: child } }
|
23
|
+
|
24
|
+
it { expect( result ).to be_a described_class }
|
25
|
+
it { expect( result.metadata ).to eq metadata }
|
26
|
+
it { expect( result.context ).to eq child }
|
27
|
+
it { expect( result ).to be_a Delegator }
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when given a Reference' do
|
31
|
+
subject { described_class.new( metadata, child ) }
|
32
|
+
|
33
|
+
it { expect( result ).to be_a described_class }
|
34
|
+
it { expect( result.metadata ).to eq metadata }
|
35
|
+
it { expect( result.context ).to eq child }
|
36
|
+
it { expect( result ).to be_a Delegator }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '.to_parse' do
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.__getobj__' do
|
44
|
+
subject { described_class.new( metadata, child ) }
|
45
|
+
let(:result) { subject.__getobj__ }
|
46
|
+
|
47
|
+
context 'when parse returns no results' do
|
48
|
+
before do
|
49
|
+
stub_request(:get, "https://api.parse.com/1/classes/Tree?count=1&where=%7B%22trees%22:%7B%22__type%22:%22Pointer%22,%22className%22:%22Tree%22,%22objectId%22:%22c1234%22%7D%7D").
|
50
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
51
|
+
to_return(status: 200, body: { results: [], count: 0 }.to_json, headers: { content_type: 'application/json' })
|
52
|
+
end
|
53
|
+
|
54
|
+
it { expect { result }.to_not raise_exception }
|
55
|
+
it { expect( result ).to be_nil }
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when parse returns a result' do
|
59
|
+
before do
|
60
|
+
stub_request(:get, "https://api.parse.com/1/classes/Tree?count=1&where=%7B%22trees%22:%7B%22__type%22:%22Pointer%22,%22className%22:%22Tree%22,%22objectId%22:%22c1234%22%7D%7D").
|
61
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
62
|
+
to_return(status: 200, body: { results: [
|
63
|
+
{ objectId: 'abcd1234' }
|
64
|
+
], count: 1 }.to_json, headers: { content_type: 'application/json' })
|
65
|
+
allow(Tree).to receive(:model_name).and_return( ActiveModel::Name.new( Tree, nil, 'Tree' ) )
|
66
|
+
end
|
67
|
+
|
68
|
+
it { expect { result }.to_not raise_exception }
|
69
|
+
it { expect( result ).to_not be_nil }
|
70
|
+
it { expect( result ).to be_a Opium::Model }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::Model::Relatable::Metadata do
|
4
|
+
before do
|
5
|
+
stub_const( 'Model', Class.new do |klass|
|
6
|
+
stub('model_name').and_return( ActiveModel::Name.new( klass, nil, 'Model' ) )
|
7
|
+
end )
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'inverse_relation_name' do
|
11
|
+
let(:result) { subject.inverse_relation_name }
|
12
|
+
|
13
|
+
context 'when created with an :inverse_of key' do
|
14
|
+
subject { described_class.new( Model, :belongs_to, :parent, class_name: 'Model', inverse_of: :children ) }
|
15
|
+
|
16
|
+
it( 'uses the :inverse_of value' ) { expect( result ).to eq 'children' }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when inferred from a :has_many context' do
|
20
|
+
subject { described_class.new( Model, :has_many, :articles ) }
|
21
|
+
|
22
|
+
it( 'uses the singular of its model name' ) { expect( result ).to eq 'model' }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when inferred from a :belongs_to context' do
|
26
|
+
subject { described_class.new( Model, :belongs_to, :article ) }
|
27
|
+
|
28
|
+
it( 'uses the plural of its model name' ) { expect( result ).to eq 'models' }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::Model::Relatable do
|
4
|
+
before do
|
5
|
+
stub_const( 'Game', Class.new do |klass|
|
6
|
+
include Opium::Model
|
7
|
+
stub(:model_name).and_return( ActiveModel::Name.new( klass, nil, 'Game' ) )
|
8
|
+
|
9
|
+
field :title, type: String
|
10
|
+
has_and_belongs_to_many :players
|
11
|
+
end )
|
12
|
+
|
13
|
+
stub_const( 'Player', Class.new do |klass|
|
14
|
+
include Opium::Model
|
15
|
+
stub(:model_name).and_return( ActiveModel::Name.new( klass, nil, 'Player' ) )
|
16
|
+
|
17
|
+
field :tag, type: String
|
18
|
+
has_and_belongs_to_many :games
|
19
|
+
end )
|
20
|
+
|
21
|
+
stub_const( 'Article', Class.new do |klass|
|
22
|
+
include Opium::Model
|
23
|
+
|
24
|
+
instance_eval do
|
25
|
+
def model_name
|
26
|
+
ActiveModel::Name.new( self, nil, 'Article' )
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
field :title, type: String
|
31
|
+
has_many :comments
|
32
|
+
belongs_to :author, class_name: 'User'
|
33
|
+
end )
|
34
|
+
|
35
|
+
stub_const( 'Comment', Class.new do |klass|
|
36
|
+
include Opium::Model
|
37
|
+
|
38
|
+
instance_eval do
|
39
|
+
def model_name
|
40
|
+
ActiveModel::Name.new( self, nil, 'Comment' )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
field :body
|
45
|
+
belongs_to :article
|
46
|
+
end )
|
47
|
+
|
48
|
+
stub_const( 'User', Class.new(Opium::User) do |klass|
|
49
|
+
stub(:model_name).and_return( ActiveModel::Name.new( klass, nil, 'User' ) )
|
50
|
+
|
51
|
+
has_many :articles
|
52
|
+
has_one :profile
|
53
|
+
end )
|
54
|
+
|
55
|
+
stub_const( 'Profile', Class.new do |klass|
|
56
|
+
include Opium::Model
|
57
|
+
stub(:model_name).and_return( ActiveModel::Name.new( klass, nil, 'Profile' ) )
|
58
|
+
|
59
|
+
field :first_name, type: String
|
60
|
+
belongs_to :user
|
61
|
+
end )
|
62
|
+
|
63
|
+
stub_const( 'Event', Class.new do |klass|
|
64
|
+
include Opium::Model
|
65
|
+
stub(:model_name).and_return( ActiveModel::Name.new( klass, nil, 'Event' ) )
|
66
|
+
|
67
|
+
field :title, type: String
|
68
|
+
end )
|
69
|
+
|
70
|
+
stub_request(:get, "https://api.parse.com/1/classes/Comment?count=1&where=%7B%22$relatedTo%22:%7B%22object%22:%7B%22__type%22:%22Pointer%22,%22className%22:%22Article%22,%22objectId%22:%22abcd1234%22%7D,%22key%22:%22comments%22%7D%7D").
|
71
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
72
|
+
to_return(status: 200, body: {
|
73
|
+
count: 2,
|
74
|
+
results: [
|
75
|
+
{ objectId: 'c1234', body: 'A Moose once bit my sister...' },
|
76
|
+
{ objectId: 'c5678', body: 'No realli! She was Karving her initials on the moose' }
|
77
|
+
]
|
78
|
+
}.to_json, headers: { content_type: 'application/json' })
|
79
|
+
|
80
|
+
stub_request(:get, "https://api.parse.com/1/classes/Comment?count=1&where=%7B%22$relatedTo%22:%7B%22object%22:%7B%22__type%22:%22Pointer%22,%22className%22:%22Article%22,%22objectId%22:%22a1234%22%7D,%22key%22:%22comments%22%7D%7D").
|
81
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
82
|
+
to_return(status: 200, body: {
|
83
|
+
count: 1,
|
84
|
+
results: [
|
85
|
+
{ objectId: 'c2345', body: 'Seems plausible.' }
|
86
|
+
]
|
87
|
+
}.to_json, headers: { content_type: 'application/json' })
|
88
|
+
|
89
|
+
stub_request(:get, "https://api.parse.com/1/classes/Article?count=1&where=%7B%22comments%22:%7B%22__type%22:%22Pointer%22,%22className%22:%22Comment%22,%22objectId%22:%22c1234%22%7D%7D").
|
90
|
+
with(headers: {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
91
|
+
to_return(status: 200, body: {
|
92
|
+
count: 1,
|
93
|
+
results: [
|
94
|
+
{ objectId: 'abcd1234', title: 'Funny Subtitles' }
|
95
|
+
]
|
96
|
+
}.to_json, headers: { content_type: 'application/json' })
|
97
|
+
|
98
|
+
stub_request(:post, "https://api.parse.com/1/classes/Article").
|
99
|
+
with(body: "{\"title\":\"A new approach to Sandboxes\"}",
|
100
|
+
headers: {'Content-Type'=>'application/json', 'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
101
|
+
to_return(status: 200, body: { objectId: 'a1234', createdAt: Time.now.utc }.to_json, headers: { content_type: 'appliction/json' })
|
102
|
+
|
103
|
+
stub_request(:post, "https://api.parse.com/1/classes/Comment").
|
104
|
+
with(body: "{\"body\":\"Do. Not. Want.\"}",
|
105
|
+
headers: {'Content-Type'=>'application/json', 'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
106
|
+
to_return(status: 200, body: { objectId: 'c7896', createdAt: Time.now.utc }.to_json, headers: { content_type: 'application/json' })
|
107
|
+
|
108
|
+
stub_request(:put, "https://api.parse.com/1/classes/Article/a1234").
|
109
|
+
with(body: "{\"comments\":{\"__op\":\"AddRelation\",\"objects\":[{\"__type\":\"Pointer\",\"className\":\"Comment\",\"objectId\":\"c7896\"}]}}",
|
110
|
+
headers: {'Content-Type'=>'application/json', 'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
111
|
+
to_return(status: 200, body: { updatedAt: Time.now.utc }.to_json, headers: { content_type: 'application/json' })
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '.relations' do
|
115
|
+
let(:result) { subject.relations }
|
116
|
+
|
117
|
+
context 'within a model with multiple relations' do
|
118
|
+
subject { User }
|
119
|
+
|
120
|
+
it { expect { result }.to_not raise_exception }
|
121
|
+
it { expect( result ).to be_a Hash }
|
122
|
+
it { expect( result.keys ).to include( 'articles', 'profile' ) }
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'within a model with no relations' do
|
126
|
+
subject { Event }
|
127
|
+
|
128
|
+
it { expect { result }.to_not raise_exception }
|
129
|
+
it { expect( result ).to be_a Hash }
|
130
|
+
it { expect( result ).to be_empty }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '.has_many' do
|
135
|
+
subject { Article }
|
136
|
+
let(:result) { subject.relations[relation_name] }
|
137
|
+
let(:relation_name) { :comments }
|
138
|
+
|
139
|
+
it { expect( result ).to be_a Opium::Model::Relatable::Metadata }
|
140
|
+
|
141
|
+
it { is_expected.to have_field :comments }
|
142
|
+
it { expect( subject.fields[:comments].type ).to eq Opium::Model::Relation }
|
143
|
+
it { expect( subject.fields[:comments].default ).to_not be_nil }
|
144
|
+
|
145
|
+
context 'within a new model' do
|
146
|
+
subject { Article.new }
|
147
|
+
|
148
|
+
it { expect( subject.comments ).to_not be_nil }
|
149
|
+
it { expect( subject.comments ).to be_a Opium::Model::Relation }
|
150
|
+
it('adds a constraint for the owner of the relation') { expect( subject.comments.constraints['where'] ).to include( '$relatedTo' => { 'object' => subject.to_parse, 'key' => 'comments' } ) }
|
151
|
+
it { expect( subject.comments ).to be_empty }
|
152
|
+
it { expect( subject.comments.owner ).to eq subject }
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'when a model has existing relations' do
|
156
|
+
subject { Article.new id: 'abcd1234' }
|
157
|
+
|
158
|
+
it { expect( subject.comments ).to_not be_nil }
|
159
|
+
it { expect( subject.comments ).to be_a Opium::Model::Relation }
|
160
|
+
it('adds a constraint for the owner of the relation') { expect( subject.comments.constraints['where'] ).to include( '$relatedTo' => { 'object' => subject.to_parse, 'key' => 'comments' } ) }
|
161
|
+
it { expect( subject.comments ).to_not be_empty }
|
162
|
+
it { expect( subject.comments.owner ).to eq subject }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe '.has_one' do
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '.belongs_to' do
|
170
|
+
subject { Comment }
|
171
|
+
let(:result) { subject.relations[relation_name] }
|
172
|
+
let(:relation_name) { :article }
|
173
|
+
|
174
|
+
it { expect( result ).to be_a Opium::Model::Relatable::Metadata }
|
175
|
+
|
176
|
+
it { is_expected.to have_field :article }
|
177
|
+
it { expect( subject.fields[:article].type ).to eq Opium::Model::Reference }
|
178
|
+
it { expect( subject.fields[:article].default ).to be_a Hash }
|
179
|
+
|
180
|
+
context 'within a new model' do
|
181
|
+
subject { Comment.new }
|
182
|
+
|
183
|
+
it { expect( subject.article ).to_not be_nil }
|
184
|
+
it { expect( subject.article ).to be_a Opium::Model::Reference }
|
185
|
+
it { expect( subject.article.context ).to eq subject }
|
186
|
+
it { expect { subject.article.__getobj__ }.to_not raise_exception }
|
187
|
+
it { expect( subject.article.__getobj__ ).to be_nil }
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'within an existing model' do
|
191
|
+
subject { Comment.new id: 'c1234' }
|
192
|
+
|
193
|
+
it { expect( subject.article ).to_not be_nil }
|
194
|
+
it { expect( subject.article ).to be_a Opium::Model::Reference }
|
195
|
+
it { expect( subject.article.context ).to eq subject }
|
196
|
+
it( 'loads the proper model id' ) { expect( subject.article.id ).to eq 'abcd1234' }
|
197
|
+
it( 'loads a properly typed model') { expect( subject.article.model_name ).to eq 'Article' }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '.has_and_belongs_to_many' do
|
202
|
+
end
|
203
|
+
|
204
|
+
describe '#save' do
|
205
|
+
let(:result) { subject.save }
|
206
|
+
|
207
|
+
context 'within a model with a has_many relation' do
|
208
|
+
subject { Article.new title: 'A new approach to Sandboxes' }
|
209
|
+
before { subject.comments.build body: 'Do. Not. Want.' }
|
210
|
+
|
211
|
+
it { result; expect( subject.errors ).to be_empty }
|
212
|
+
it { expect { result }.to_not raise_exception }
|
213
|
+
it { expect( result ).to be_truthy }
|
214
|
+
it { result && is_expected.to( be_persisted ) }
|
215
|
+
it { result && expect( subject.comments ).to( be_a Opium::Model::Relation ) }
|
216
|
+
it { result && expect( subject.comments ).to( all( be_a( Opium::Model ) ) ) }
|
217
|
+
it { result && expect( subject.comments ).to( all( be_persisted ) ) }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|