opium 1.0.3 → 1.1.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 +9 -0
- data/lib/generators/opium/model_generator.rb +2 -0
- data/lib/opium/config.rb +1 -1
- data/lib/opium/extensions/symbol.rb +13 -0
- data/lib/opium/extensions.rb +1 -0
- data/lib/opium/file.rb +95 -0
- data/lib/opium/model/attributable.rb +5 -5
- data/lib/opium/model/batchable/batch.rb +43 -0
- data/lib/opium/model/batchable/operation.rb +33 -0
- data/lib/opium/model/batchable.rb +10 -0
- data/lib/opium/model/connectable.rb +7 -0
- data/lib/opium/model/fieldable.rb +1 -1
- data/lib/opium/model/persistable.rb +7 -4
- data/lib/opium/model/queryable.rb +2 -2
- data/lib/opium/model.rb +3 -0
- data/lib/opium/version.rb +1 -1
- data/lib/opium.rb +10 -9
- data/opium.gemspec +2 -0
- data/spec/opium/extensions/symbol_spec.rb +54 -0
- data/spec/opium/file_spec.rb +274 -0
- data/spec/opium/model/attributable_spec.rb +30 -16
- data/spec/opium/model/batchable/batch_spec.rb +145 -0
- data/spec/opium/model/batchable/operation_spec.rb +73 -0
- data/spec/opium/model/batchable_spec.rb +5 -0
- data/spec/opium/model/connectable_spec.rb +27 -0
- data/spec/opium/model/fieldable_spec.rb +35 -0
- data/spec/opium/model/persistable_spec.rb +35 -34
- data/spec/opium/model/queryable_spec.rb +35 -25
- data/spec/opium/model_spec.rb +1 -0
- data/spec/opium_spec.rb +1 -1
- metadata +33 -3
@@ -0,0 +1,274 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::File do
|
4
|
+
it { expect( described_class ).to be <= Opium::Model::Connectable }
|
5
|
+
let!(:gif_file) do
|
6
|
+
Tempfile.new(['test', '.gif']).tap do |f|
|
7
|
+
f.binmode
|
8
|
+
f.write Base64.decode64('R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:location) { 'http://files.example.com/1234-file.gif' }
|
13
|
+
|
14
|
+
it { expect( described_class ).to respond_to(:upload, :to_ruby, :to_parse).with(1).argument }
|
15
|
+
it { is_expected.to respond_to( :delete, :name, :url, :mime_type, :to_parse ) }
|
16
|
+
|
17
|
+
describe '.upload' do
|
18
|
+
subject { described_class }
|
19
|
+
let(:upload_options) { {} }
|
20
|
+
let(:result) { subject.upload( gif_file, upload_options ) }
|
21
|
+
|
22
|
+
before do
|
23
|
+
stub_request( :post, %r{https://api\.parse\.com/1/files/.+} ).
|
24
|
+
with(
|
25
|
+
body: "9a\x15\x00\x04\x00\x80\x00\x00#-0\xFF\xFF\xFF!\xF9\x04\x01\x00\x00\x01\x00,\x00\x00\x00\x00\x15\x00\x04\x00\x00\x02\r\x8C\x8F\x01\xC9\xAD\xB0\x9C\x83T2\x8AY\x01\x00;".force_encoding('ASCII-8BIT'),
|
26
|
+
headers: {content_type: 'image/gif', x_parse_application_id: 'PARSE_APP_ID', x_parse_rest_api_key: 'PARSE_API_KEY'}
|
27
|
+
).to_return(status: 201, body: ->(request) { { url: location, name: "1234-#{ ::File.basename(request.uri) }" }.to_json }, headers: { content_type: 'application/json', location: location })
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when the upload completes' do
|
31
|
+
it { expect { result }.to_not raise_exception }
|
32
|
+
it { expect( result ).to be_a Opium::File }
|
33
|
+
it { expect( result.url ).to_not be_nil }
|
34
|
+
it { expect( result.name ).to_not be_nil }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with a :original_filename option' do
|
38
|
+
let(:upload_options) { { original_filename: 'chunky_bacon.jpg' } }
|
39
|
+
|
40
|
+
it { expect { result }.to_not raise_exception }
|
41
|
+
it 'preferentially uses the :original_filename' do
|
42
|
+
expect( result.name ).to end_with 'chunky_bacon.jpg'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with a :content_type option' do
|
47
|
+
let(:upload_options) { { content_type: 'image/png', sent_headers: true } }
|
48
|
+
|
49
|
+
it { expect { result }.to_not raise_exception }
|
50
|
+
it { expect( result ).to have_key( 'Content-Type') }
|
51
|
+
it 'sends the given :content_type value' do
|
52
|
+
expect( result['Content-Type'] ).to eq 'image/png'
|
53
|
+
end
|
54
|
+
it { expect( result.keys ).to include( 'X-Parse-Rest-Api-Key' )}
|
55
|
+
it { expect( result.keys ).to_not include( 'X-Parse-Master-Key' )}
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'without a :content_type option' do
|
59
|
+
let(:upload_options) { { sent_headers: true } }
|
60
|
+
|
61
|
+
it { expect { result }.to_not raise_exception }
|
62
|
+
it { expect( result ).to have_key( 'Content-Type' ) }
|
63
|
+
it 'sends the proper content_type' do
|
64
|
+
expect( result['Content-Type'] ).to eq 'image/gif'
|
65
|
+
end
|
66
|
+
it { expect( result.keys ).to include( 'X-Parse-Rest-Api-Key' )}
|
67
|
+
it { expect( result.keys ).to_not include( 'X-Parse-Master-Key' )}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '.to_ruby' do
|
72
|
+
subject { described_class }
|
73
|
+
|
74
|
+
let(:result) { subject.to_ruby( object ) }
|
75
|
+
|
76
|
+
context 'when given a hash with __type: "File"' do
|
77
|
+
let(:object) { { __type: 'File', url: location, name: 'chunky_bacon.jpg' } }
|
78
|
+
|
79
|
+
it { expect { result }.to_not raise_exception }
|
80
|
+
it { expect( result ).to be_a Opium::File }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when given a hash with just a :url and :name' do
|
84
|
+
let(:object) { { url: location, name: 'chunky_bacon.jpg' } }
|
85
|
+
|
86
|
+
it { expect { result }.to_not raise_exception }
|
87
|
+
it { expect( result ).to be_a Opium::File }
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'when given a hash with __type != "File"' do
|
91
|
+
let(:object) { { __type: 'Pointer' } }
|
92
|
+
|
93
|
+
it { expect { result }.to raise_exception }
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when given an Opium::File' do
|
97
|
+
let(:object) { Opium::File.new( url: location, name: 'chunky_bacon.jpg' ) }
|
98
|
+
|
99
|
+
it { expect { result }.to_not raise_exception }
|
100
|
+
it { expect( result ).to be_a Opium::File }
|
101
|
+
it { expect( result ).to eq object }
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'when given nil' do
|
105
|
+
let(:object) { nil }
|
106
|
+
|
107
|
+
it { expect { result }.to_not raise_exception }
|
108
|
+
it { expect( result ).to be_nil }
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'when not given a hash' do
|
112
|
+
let(:object) { 42 }
|
113
|
+
|
114
|
+
it { expect { result }.to raise_exception }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '.to_parse' do
|
119
|
+
subject { described_class }
|
120
|
+
|
121
|
+
let(:result) { described_class.to_parse( object ) }
|
122
|
+
|
123
|
+
context 'when given an Opium::File' do
|
124
|
+
let(:object) { subject.new( url: location, name: 'chunky_bacon.jpg' ) }
|
125
|
+
|
126
|
+
it { expect { result }.to_not raise_exception }
|
127
|
+
it { expect( result ).to be_a Hash }
|
128
|
+
it { expect( result ).to have_key '__type' }
|
129
|
+
it { expect( result ).to have_key 'name' }
|
130
|
+
it 'has the proper values for :__type and :name' do
|
131
|
+
expect( result ).to eq( '__type' => 'File', 'name' => 'chunky_bacon.jpg' )
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'when given nil' do
|
136
|
+
let(:object) { nil }
|
137
|
+
|
138
|
+
it { expect { result }.to_not raise_exception }
|
139
|
+
it { expect( result ).to be_nil }
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'when given anything else' do
|
143
|
+
let(:object) { 42 }
|
144
|
+
|
145
|
+
it { expect { result }.to raise_exception }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context '#initialize' do
|
150
|
+
let(:filename) { 'file.png' }
|
151
|
+
let(:location) { 'http://files.example.com/1234-file.png' }
|
152
|
+
subject { described_class.new( __type: 'File', url: location, name: filename ) }
|
153
|
+
|
154
|
+
it 'sets the url' do
|
155
|
+
expect( subject.url ).to eq location
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'sets the name' do
|
159
|
+
expect( subject.name ).to eq filename
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'infers the mime_type' do
|
163
|
+
expect( subject.mime_type ).to eq 'image/png'
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#delete' do
|
168
|
+
subject { described_class.new( url: location, name: 'chunky_bacon.jpg' ) }
|
169
|
+
|
170
|
+
before do
|
171
|
+
stub_request(:delete, "https://api.parse.com/1/files/chunky_bacon.jpg").
|
172
|
+
with(:headers => {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Master-Key'=>'PARSE_MASTER_KEY'}).
|
173
|
+
to_return(:status => 200, :body => "{}", :headers => { content_type: 'application/json' })
|
174
|
+
end
|
175
|
+
|
176
|
+
let(:delete_options) { {} }
|
177
|
+
let(:result) { subject.delete( delete_options ) }
|
178
|
+
|
179
|
+
context 'with a name' do
|
180
|
+
it { expect { result }.to_not raise_exception }
|
181
|
+
it 'freezes the Opium::File' do
|
182
|
+
result
|
183
|
+
expect( subject ).to be_frozen
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'without a name' do
|
188
|
+
subject { described_class.new }
|
189
|
+
|
190
|
+
it { expect { result }.to raise_exception }
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'when executed' do
|
194
|
+
let(:delete_options) { { sent_headers: true } }
|
195
|
+
|
196
|
+
it { expect { result }.to_not raise_exception }
|
197
|
+
it { expect( result.keys ).to include( 'X-Parse-Master-Key' ) }
|
198
|
+
it { expect( result.keys ).to_not include( 'X-Parse-Rest-Api-Key' ) }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '#to_parse' do
|
203
|
+
let(:result) { subject.to_parse }
|
204
|
+
|
205
|
+
context 'when #name has a value' do
|
206
|
+
subject { described_class.new( url: location, name: 'chunky_bacon.jpg' ) }
|
207
|
+
|
208
|
+
it { expect { result }.to_not raise_exception }
|
209
|
+
it { expect( result ).to be_a Hash }
|
210
|
+
it { expect( result ).to have_key '__type' }
|
211
|
+
it { expect( result ).to have_key 'name' }
|
212
|
+
it 'has the proper values for :__type and :name' do
|
213
|
+
expect( result ).to eq( '__type' => 'File', 'name' => 'chunky_bacon.jpg' )
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'when #name is empty' do
|
218
|
+
subject { described_class.new }
|
219
|
+
|
220
|
+
it { expect { result }.to raise_exception }
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#inspect' do
|
225
|
+
context 'with data' do
|
226
|
+
let(:filename) { 'file.gif' }
|
227
|
+
let(:mime) { 'image/gif' }
|
228
|
+
|
229
|
+
subject { described_class.new( url: location, name: filename ) }
|
230
|
+
|
231
|
+
it 'has the values for all components' do
|
232
|
+
expect( subject.inspect ).to eq %{#<Opium::File name="#{ filename }" url="#{ location }" mime_type="#{ mime }">}
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context 'without data' do
|
237
|
+
subject { described_class.new }
|
238
|
+
|
239
|
+
it 'has nil values for all components' do
|
240
|
+
expect( subject.inspect ).to eq '#<Opium::File name=nil url=nil mime_type=nil>'
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
context 'when used as a field type' do
|
246
|
+
before do
|
247
|
+
stub_const( 'Game', Class.new do
|
248
|
+
include Opium::Model
|
249
|
+
field :cover_image, type: Opium::File
|
250
|
+
end )
|
251
|
+
|
252
|
+
stub_request(:get, "https://api.parse.com/1/classes/Game/abcd1234").
|
253
|
+
with(:headers => {'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
254
|
+
to_return(:status => 200, :body => { objectId: 'abcd1234', coverImage: { __type: 'File', name: 'chunky_bacon.jpg', url: location }, createdAt: '2015-01-01T12:00:00Z' }.to_json, :headers => { content_type: 'application/json' })
|
255
|
+
|
256
|
+
stub_request(:post, "https://api.parse.com/1/classes/Game").
|
257
|
+
with(
|
258
|
+
:body => "{\"coverImage\":{\"__type\":\"File\",\"name\":\"chunky_bacon.jpg\"}}",
|
259
|
+
:headers => {'Content-Type'=>'application/json', 'X-Parse-Application-Id'=>'PARSE_APP_ID', 'X-Parse-Rest-Api-Key'=>'PARSE_API_KEY'}).
|
260
|
+
to_return(:status => 200, :body => { objectId: 'abcd1234', createdAt: '2015-01-01T12:00:00Z' }.to_json, :headers => { content_type: 'application/json' })
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'is retrievable as an Opium::File' do
|
265
|
+
game = Game.find('abcd1234')
|
266
|
+
expect( game.cover_image ).to be_a Opium::File
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'is persistable as a Parse File object hash' do
|
270
|
+
game = Game.new cover_image: Opium::File.new( url: location, name: 'chunky_bacon.jpg' )
|
271
|
+
expect { game.save! }.to_not raise_exception
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -3,42 +3,56 @@ require 'spec_helper'
|
|
3
3
|
describe Opium::Model::Attributable do
|
4
4
|
let( :model ) { Class.new { include Opium::Model::Attributable } }
|
5
5
|
|
6
|
-
|
6
|
+
context 'within an instance' do
|
7
7
|
subject { model.new }
|
8
8
|
|
9
|
-
it {
|
10
|
-
it {
|
9
|
+
it { is_expected.to respond_to( :attributes, :attributes= ) }
|
10
|
+
it { is_expected.to respond_to( :attributes_to_parse ) }
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
context 'when included in a model' do
|
14
14
|
before do
|
15
15
|
stub_const( 'Book', Class.new do
|
16
16
|
include Opium::Model
|
17
17
|
field :title, type: String
|
18
|
+
field :genre, type: Symbol
|
19
|
+
attr_accessor :not_a_field
|
18
20
|
end )
|
19
21
|
end
|
20
22
|
|
21
|
-
subject { Book.new( title: 'Little Brother', id: 'abc123', created_at: Time.now ) }
|
23
|
+
subject { Book.new( title: 'Little Brother', id: 'abc123', created_at: Time.now, genre: :sci_fi ) }
|
22
24
|
|
23
|
-
describe 'attributes_to_parse' do
|
24
|
-
|
25
|
-
|
25
|
+
describe '#attributes_to_parse' do
|
26
|
+
context 'when called with no parameters' do
|
27
|
+
it 'has all fields present, with names and values converted to parse' do
|
28
|
+
expect( subject.attributes_to_parse ).to eq( 'objectId' => 'abc123', 'title' => 'Little Brother', 'genre' => 'sci_fi', 'createdAt' => subject.created_at.to_parse, 'updatedAt' => nil )
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
|
-
|
29
|
-
|
32
|
+
context 'when called with except' do
|
33
|
+
it 'excludes the excepted fields' do
|
34
|
+
expect( subject.attributes_to_parse( except: [:id, :updated_at] ) ).to eq( 'title' => 'Little Brother', 'genre' => 'sci_fi', 'createdAt' => subject.created_at.to_parse )
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
|
-
|
33
|
-
|
38
|
+
context 'when called with not_readonly' do
|
39
|
+
it 'excludes all readonly fields' do
|
40
|
+
expect( subject.attributes_to_parse( not_readonly: true ) ).to eq( 'title' => 'Little Brother', 'genre' => 'sci_fi' )
|
41
|
+
end
|
34
42
|
end
|
35
43
|
end
|
36
44
|
|
37
|
-
describe '
|
38
|
-
it '
|
45
|
+
describe '#attributes=' do
|
46
|
+
it 'stores unrecognized fields in the attributes hash' do
|
39
47
|
expect { subject.attributes = { unknownField: 42 } }.to_not raise_exception
|
40
|
-
subject.attributes.
|
41
|
-
subject.attributes['unknownField'].
|
48
|
+
expect( subject.attributes ).to have_key('unknownField')
|
49
|
+
expect( subject.attributes['unknownField'] ).to eq 42
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'calls relevant setters rather for non fields if they exist' do
|
53
|
+
expect { subject.attributes = { not_a_field: 42 } }.to_not raise_exception
|
54
|
+
expect( subject.not_a_field ).to eq 42
|
55
|
+
expect( subject.attributes ).to_not have_key('not_a_field')
|
42
56
|
end
|
43
57
|
end
|
44
58
|
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::Model::Batchable::Batch do
|
4
|
+
let(:update_path) { '/1/classes/Game/abcd1234' }
|
5
|
+
let(:body) { { title: 'Skyrim' } }
|
6
|
+
|
7
|
+
describe '#owner' do
|
8
|
+
it 'is a property' do
|
9
|
+
is_expected.to respond_to(:owner)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#depth' do
|
14
|
+
let(:depth) { subject.depth }
|
15
|
+
|
16
|
+
context 'within a new batch' do
|
17
|
+
subject { described_class.new }
|
18
|
+
|
19
|
+
it { expect(depth).to eq 0 }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#dive' do
|
24
|
+
after { subject.depth = 0 }
|
25
|
+
|
26
|
+
it { expect { subject.dive }.to change( subject, :depth ).by(1) }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#ascend' do
|
30
|
+
after { subject.depth = 0 }
|
31
|
+
|
32
|
+
it { expect { subject.ascend }.to change( subject, :depth ).by(-1) }
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#queue' do
|
36
|
+
let(:queue) { subject.queue }
|
37
|
+
|
38
|
+
context 'within a new batch' do
|
39
|
+
subject { described_class.new }
|
40
|
+
|
41
|
+
it { expect(queue).to eq [] }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#enqueue' do
|
46
|
+
let(:perform) { subject.enqueue( operation ) }
|
47
|
+
|
48
|
+
after { subject.queue.clear }
|
49
|
+
|
50
|
+
context 'with a valid operation hash' do
|
51
|
+
let(:operation) { { method: :put, path: update_path, body: body } }
|
52
|
+
|
53
|
+
it { expect { perform }.to_not raise_exception }
|
54
|
+
it { expect( perform ).to be_a Opium::Model::Batchable::Operation }
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'with an Operation object' do
|
58
|
+
let(:operation) { Opium::Model::Batchable::Operation.new( method: :put, path: update_path, body: body ) }
|
59
|
+
|
60
|
+
it { expect { perform }.to_not raise_exception }
|
61
|
+
it { expect( perform ).to be_a Opium::Model::Batchable::Operation }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'with an invalid operation hash' do
|
65
|
+
let(:operation) { { path: update_path, body: body } }
|
66
|
+
|
67
|
+
it { expect { perform }.to raise_exception }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#execute' do
|
72
|
+
let(:execute) { subject.execute }
|
73
|
+
let(:operation) { Opium::Model::Batchable::Operation.new( method: :put, path: update_path, body: body ) }
|
74
|
+
|
75
|
+
before do
|
76
|
+
subject.owner = double()
|
77
|
+
allow( subject.owner ).to receive(:http_post).and_return( [ { success: { objectId: 'abcd1234', createdAt: Time.now } } ] )
|
78
|
+
subject.enqueue( operation )
|
79
|
+
end
|
80
|
+
|
81
|
+
after do
|
82
|
+
subject.owner = nil
|
83
|
+
subject.queue.clear
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when #depth is 0' do
|
87
|
+
it { expect( subject.depth ).to eq 0 }
|
88
|
+
it { expect { execute }.to_not change( subject, :depth ) }
|
89
|
+
it 'performs a POST of the batch' do
|
90
|
+
expect( subject.owner ).to receive(:http_post).with( subject.to_parse.first )
|
91
|
+
execute
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'when #depth is greater than 0' do
|
96
|
+
before { subject.dive }
|
97
|
+
after { subject.ascend }
|
98
|
+
|
99
|
+
it { expect( subject.depth ).to eq 1 }
|
100
|
+
it { expect { execute }.to change( subject, :depth ).by(-1) }
|
101
|
+
it 'does not POST the batch' do
|
102
|
+
expect( subject.owner ).to_not receive(:http_post)
|
103
|
+
execute
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when #queue is empty' do
|
108
|
+
before { subject.queue.clear }
|
109
|
+
|
110
|
+
it { expect { subject.execute }.to raise_exception }
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when #queue has more than MAX_BATCH_SIZE operations' do
|
114
|
+
before { subject.queue = [operation] * 51 }
|
115
|
+
after { subject.queue.clear }
|
116
|
+
|
117
|
+
it 'performs multiple POSTs' do
|
118
|
+
expect( subject.owner ).to receive(:http_post).twice
|
119
|
+
execute
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#to_parse' do
|
125
|
+
let(:result) { subject.to_parse }
|
126
|
+
|
127
|
+
context 'with Operations in the queue' do
|
128
|
+
before { subject.enqueue( method: :put, path: update_path, body: body ) }
|
129
|
+
after { subject.queue.clear }
|
130
|
+
|
131
|
+
it { expect { result }.to_not raise_exception }
|
132
|
+
it { expect( result ).to be_a Array }
|
133
|
+
|
134
|
+
it { expect( result.first ).to be_a Hash }
|
135
|
+
it { expect( result.first ).to have_key( :requests ) }
|
136
|
+
it { expect( result.first[:requests] ).to be_a Array }
|
137
|
+
it { expect( result.first[:requests] ).to_not be_empty }
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'with an empty queue' do
|
141
|
+
it { expect { result }.to_not raise_exception }
|
142
|
+
it { expect( result ).to eq [] }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Opium::Model::Batchable::Operation do
|
4
|
+
let(:update_path) { '/1/classes/Game/abcd1234' }
|
5
|
+
let(:body) { { title: 'Skyrim' } }
|
6
|
+
|
7
|
+
describe '#initialize' do
|
8
|
+
let(:result) { described_class.new( properties ) }
|
9
|
+
|
10
|
+
context 'with all properties' do
|
11
|
+
let(:properties) { { method: :put, path: update_path, body: body } }
|
12
|
+
|
13
|
+
it { expect { result }.to_not raise_exception }
|
14
|
+
it 'sets the method' do
|
15
|
+
expect( result.method ).to eq :put
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'sets the path' do
|
19
|
+
expect( result.path ).to eq update_path
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sets the body' do
|
23
|
+
expect( result.body ).to eq body
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when :method is missing' do
|
28
|
+
let(:properties) { { path: update_path, body: body } }
|
29
|
+
|
30
|
+
it { expect { result }.to raise_exception }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when :path is missing' do
|
34
|
+
let(:properties) { { method: :put, body: body } }
|
35
|
+
|
36
|
+
it { expect { result }.to raise_exception }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when :body is missing' do
|
40
|
+
let(:properties) { { method: :put, path: update_path } }
|
41
|
+
|
42
|
+
it { expect { result }.to_not raise_exception }
|
43
|
+
it { expect( result.body ).to be_nil }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#to_parse' do
|
48
|
+
let(:result) { subject.to_parse }
|
49
|
+
|
50
|
+
context 'when a body is present' do
|
51
|
+
subject { described_class.new( method: :put, path: update_path, body: body ) }
|
52
|
+
|
53
|
+
it { expect { result }.to_not raise_exception }
|
54
|
+
it { expect( result ).to be_a Hash }
|
55
|
+
it { expect( result.keys ).to include( :method, :path, :body ) }
|
56
|
+
it 'stringifies and upcases the method' do
|
57
|
+
expect( result[:method] ).to eq 'PUT'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'without a body' do
|
62
|
+
subject { described_class.new( method: :put, path: update_path ) }
|
63
|
+
|
64
|
+
it { expect { result }.to_not raise_exception }
|
65
|
+
it { expect( result ).to be_a Hash }
|
66
|
+
it { expect( result.keys ).to include( :method, :path ) }
|
67
|
+
it { expect( result ).to_not have_key( :body ) }
|
68
|
+
it 'stringifies and upcases the method' do
|
69
|
+
expect( result[:method] ).to eq 'PUT'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -215,4 +215,31 @@ describe Opium::Model::Connectable do
|
|
215
215
|
subject.requires_heightened_privileges?.should == true
|
216
216
|
end
|
217
217
|
end
|
218
|
+
|
219
|
+
describe '.with_heightened_privileges' do
|
220
|
+
it { expect {|b| subject.with_heightened_privileges &b}.to yield_control }
|
221
|
+
|
222
|
+
it 'has heightened privileges within the block' do
|
223
|
+
subject.with_heightened_privileges do
|
224
|
+
expect( subject ).to have_heightened_privileges
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'does not have heightened privileges outside the block' do
|
229
|
+
subject.with_heightened_privileges {}
|
230
|
+
expect( subject ).to_not have_heightened_privileges
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'turns off heightened privileges on exceptions' do
|
234
|
+
expect { subject.with_heightened_privileges { raise 'expected' } }.to raise_exception
|
235
|
+
expect( subject ).to_not have_heightened_privileges
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'keeps the previous value of requires_heightened_privileges? after the block' do
|
239
|
+
subject.requires_heightened_privileges!
|
240
|
+
subject.with_heightened_privileges {}
|
241
|
+
expect( subject ).to have_heightened_privileges
|
242
|
+
subject.instance_variable_set :@requires_heightened_privileges, nil
|
243
|
+
end
|
244
|
+
end
|
218
245
|
end
|
@@ -130,4 +130,39 @@ describe Opium::Model::Fieldable do
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
end
|
133
|
+
|
134
|
+
context 'when a model has fields' do
|
135
|
+
before do
|
136
|
+
stub_const( 'Model', Class.new do
|
137
|
+
include Opium::Model
|
138
|
+
field :symbolic_with_string_default, type: Symbol, default: 'value'
|
139
|
+
field :symbolic_with_symbol_default, type: Symbol, default: :value
|
140
|
+
end )
|
141
|
+
end
|
142
|
+
|
143
|
+
subject { Model }
|
144
|
+
let(:instance) { subject.new }
|
145
|
+
|
146
|
+
it 'converts default values correctly' do
|
147
|
+
expect( Model.default_attributes ).to include( 'symbolic_with_string_default' => :value, 'symbolic_with_symbol_default' => :value )
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '.field' do
|
151
|
+
subject { Model.new }
|
152
|
+
|
153
|
+
it 'converts default values' do
|
154
|
+
expect( subject.symbolic_with_string_default ).to be_a Symbol
|
155
|
+
expect( subject.symbolic_with_symbol_default ).to be_a Symbol
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '.field=' do
|
160
|
+
subject { Model.new }
|
161
|
+
|
162
|
+
it 'converts strings appropriately' do
|
163
|
+
expect { subject.symbolic_with_symbol_default = 'updated_value' }.to change( subject, :symbolic_with_symbol_default ).from(:value).to(:updated_value)
|
164
|
+
expect( subject.symbolic_with_symbol_default ).to be_a Symbol
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
133
168
|
end
|