smooth_operator 0.4.4 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +9 -9
- data/.gitignore +2 -1
- data/.rspec +4 -0
- data/Gemfile +13 -0
- data/README.md +258 -10
- data/console.rb +44 -0
- data/lib/smooth_operator/array_with_meta_data.rb +31 -0
- data/lib/smooth_operator/attribute_assignment.rb +102 -0
- data/lib/smooth_operator/attribute_methods.rb +87 -0
- data/lib/smooth_operator/attributes/base.rb +107 -0
- data/lib/smooth_operator/attributes/dirty.rb +29 -0
- data/lib/smooth_operator/attributes/normal.rb +15 -0
- data/lib/smooth_operator/delegation.rb +60 -0
- data/lib/smooth_operator/finder_methods.rb +43 -0
- data/lib/smooth_operator/helpers.rb +79 -0
- data/lib/smooth_operator/model_schema.rb +81 -0
- data/lib/smooth_operator/open_struct.rb +37 -0
- data/lib/smooth_operator/operator.rb +145 -0
- data/lib/smooth_operator/operators/faraday.rb +75 -0
- data/lib/smooth_operator/operators/typhoeus.rb +77 -0
- data/lib/smooth_operator/persistence.rb +144 -0
- data/lib/smooth_operator/relation/array_relation.rb +13 -0
- data/lib/smooth_operator/relation/association_reflection.rb +75 -0
- data/lib/smooth_operator/relation/associations.rb +75 -0
- data/lib/smooth_operator/relation/reflection.rb +41 -0
- data/lib/smooth_operator/relation/single_relation.rb +14 -0
- data/lib/smooth_operator/remote_call/base.rb +80 -0
- data/lib/smooth_operator/remote_call/errors/connection_failed.rb +20 -0
- data/lib/smooth_operator/remote_call/errors/timeout.rb +20 -0
- data/lib/smooth_operator/remote_call/faraday.rb +19 -0
- data/lib/smooth_operator/remote_call/typhoeus.rb +19 -0
- data/lib/smooth_operator/serialization.rb +79 -0
- data/lib/smooth_operator/translation.rb +27 -0
- data/lib/smooth_operator/validations.rb +15 -0
- data/lib/smooth_operator/version.rb +1 -1
- data/lib/smooth_operator.rb +26 -5
- data/smooth_operator.gemspec +12 -3
- data/spec/factories/user_factory.rb +34 -0
- data/spec/require_helper.rb +11 -0
- data/spec/smooth_operator/attribute_assignment_spec.rb +351 -0
- data/spec/smooth_operator/attributes_dirty_spec.rb +53 -0
- data/spec/smooth_operator/delegation_spec.rb +139 -0
- data/spec/smooth_operator/finder_methods_spec.rb +105 -0
- data/spec/smooth_operator/model_schema_spec.rb +31 -0
- data/spec/smooth_operator/operator_spec.rb +46 -0
- data/spec/smooth_operator/persistence_spec.rb +424 -0
- data/spec/smooth_operator/remote_call_spec.rb +320 -0
- data/spec/smooth_operator/serialization_spec.rb +80 -0
- data/spec/smooth_operator/validations_spec.rb +42 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/helpers/persistence_helper.rb +38 -0
- data/spec/support/localhost_server.rb +97 -0
- data/spec/support/models/address.rb +14 -0
- data/spec/support/models/comment.rb +3 -0
- data/spec/support/models/post.rb +13 -0
- data/spec/support/models/user.rb +41 -0
- data/spec/support/models/user_with_address_and_posts.rb +89 -0
- data/spec/support/test_server.rb +165 -0
- metadata +108 -18
- data/lib/smooth_operator/base.rb +0 -30
- data/lib/smooth_operator/core.rb +0 -218
- data/lib/smooth_operator/http_handlers/typhoeus/base.rb +0 -58
- data/lib/smooth_operator/http_handlers/typhoeus/orm.rb +0 -34
- data/lib/smooth_operator/http_handlers/typhoeus/remote_call.rb +0 -28
- data/lib/smooth_operator/operator/base.rb +0 -43
- data/lib/smooth_operator/operator/exceptions.rb +0 -64
- data/lib/smooth_operator/operator/orm.rb +0 -118
- data/lib/smooth_operator/operator/remote_call.rb +0 -84
@@ -0,0 +1,424 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
shared_examples_for "successful persistent remote call" do
|
4
|
+
it "it should populate 'internal_data' with the server's response" do
|
5
|
+
execute_method
|
6
|
+
expect(subject.server_response).to be true
|
7
|
+
end
|
8
|
+
|
9
|
+
it "it should populate 'last_remote_call' with the remote_call used on this transaction" do
|
10
|
+
expect(subject.last_remote_call).to be_nil unless method_to_execute.to_s =~ /create/
|
11
|
+
execute_method
|
12
|
+
expect(subject.last_remote_call).to be_a_kind_of(SmoothOperator::RemoteCall::Base)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
shared_examples_for "persistent remote call" do
|
17
|
+
it "should send the extra params set by .query_string method" do
|
18
|
+
execute_method
|
19
|
+
expect(subject.last_remote_call.parsed_response['query_params']).to be true
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when the response is positive" do
|
23
|
+
let(:method_arguments) { ['', { status: 200 }] }
|
24
|
+
|
25
|
+
it "it should return true" do
|
26
|
+
execute_method
|
27
|
+
expect(subject.last_remote_call.ok?).to be true
|
28
|
+
expect(subject.last_remote_call.status).to be true
|
29
|
+
end
|
30
|
+
|
31
|
+
it "it should assert the subject's persistence" do
|
32
|
+
execute_method
|
33
|
+
expect(subject.persisted?).to be(persistence_state[200])
|
34
|
+
end
|
35
|
+
|
36
|
+
it_behaves_like "successful persistent remote call"
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when the response is unprocessed_entity" do
|
40
|
+
let(:method_arguments) { ['', { status: 422 }] }
|
41
|
+
|
42
|
+
it "it should return false" do
|
43
|
+
execute_method
|
44
|
+
expect(subject.last_remote_call.not_processed?).to be true
|
45
|
+
expect(subject.last_remote_call.status).to be false
|
46
|
+
end
|
47
|
+
|
48
|
+
it "it should assert the subject's persistence" do
|
49
|
+
execute_method
|
50
|
+
expect(subject.persisted?).to be(persistence_state[422])
|
51
|
+
end
|
52
|
+
|
53
|
+
it_behaves_like "successful persistent remote call"
|
54
|
+
end
|
55
|
+
|
56
|
+
context "when the response is client side error" do
|
57
|
+
let(:method_arguments) { ['', { status: 404 }] }
|
58
|
+
|
59
|
+
it "it should return nil" do
|
60
|
+
execute_method
|
61
|
+
expect(subject.last_remote_call.client_error?).to be true
|
62
|
+
expect(subject.last_remote_call.error?).to be true
|
63
|
+
expect(subject.last_remote_call.status).to be nil
|
64
|
+
end
|
65
|
+
|
66
|
+
it "it should assert the subject's persistence" do
|
67
|
+
execute_method
|
68
|
+
expect(subject.persisted?).to be(persistence_state[500])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when the there is a connection error ou http 500" do
|
73
|
+
let(:method_arguments) { ['', { status: 500 }] }
|
74
|
+
|
75
|
+
it "it should return nil" do
|
76
|
+
execute_method
|
77
|
+
expect(subject.last_remote_call.server_error?).to be true
|
78
|
+
expect(subject.last_remote_call.error?).to be true
|
79
|
+
expect(subject.last_remote_call.status).to be_nil
|
80
|
+
end
|
81
|
+
|
82
|
+
it "it should NOT alter 'internal_data'" do
|
83
|
+
execute_method
|
84
|
+
expect(subject.respond_to?(:server_response)).to be(false)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "it should populate 'last_remote_call' with the remote_call used on this transaction" do
|
88
|
+
expect(subject.last_remote_call).to be_nil unless method_to_execute.to_s =~ /create/
|
89
|
+
execute_method
|
90
|
+
expect(subject.last_remote_call).to be_a_kind_of(SmoothOperator::RemoteCall::Base)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "it should assert the subject's persistence" do
|
94
|
+
execute_method
|
95
|
+
expect(subject.persisted?).to be(persistence_state[500])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
shared_examples_for "save method" do
|
101
|
+
it "it should make a http call with the contents of 'internal_data'" do
|
102
|
+
execute_method
|
103
|
+
expect(subject.last_remote_call.parsed_response['internal_data_match']).to be true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
shared_examples_for "a method that calls the #make_the_call method" do
|
108
|
+
let(:method_arguments) { ['/custom_path', { "user" => { "first_name" => "nhoJ" }, "extra_params" => 'extra_params' }, { option1: 'option1', option2: 'option2', http_verb: :get, serializable_options: { only: [:first_name] } }] }
|
109
|
+
|
110
|
+
it "it should pass all arguments to #make_the_call" do
|
111
|
+
_method_arguments = method_arguments.dup
|
112
|
+
_method_arguments[0] = SmoothOperator::Helpers.remove_initial_slash(_method_arguments[0])
|
113
|
+
|
114
|
+
expect(subject.class).to receive(:make_the_call).with(:get, *_method_arguments)
|
115
|
+
|
116
|
+
execute_method
|
117
|
+
end
|
118
|
+
|
119
|
+
it "it should pass all arguments to .make_the_call" do
|
120
|
+
_method_arguments = method_arguments.dup
|
121
|
+
_method_arguments[0] = SmoothOperator::Helpers.remove_initial_slash(_method_arguments[0])
|
122
|
+
|
123
|
+
expect(subject.class).to receive(:make_the_call).with(:get, *_method_arguments)
|
124
|
+
|
125
|
+
execute_method
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
describe SmoothOperator::Persistence, helpers: :persistence do
|
131
|
+
|
132
|
+
describe "#reload" do
|
133
|
+
subject { new_user }
|
134
|
+
let(:method_to_execute) { :reload }
|
135
|
+
|
136
|
+
context "before calling #reload" do
|
137
|
+
it "#has_data_from_server and #from_server should return false" do
|
138
|
+
expect(subject.from_server).to be_falsey
|
139
|
+
expect(subject.has_data_from_server).to be_falsey
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "when subject doesn't has an id" do
|
144
|
+
it "it should raise 'UnknownPath'" do
|
145
|
+
expect{ subject.reload }.to raise_error 'UnknownPath'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "when subject has an id" do
|
150
|
+
before do
|
151
|
+
subject.id = 1
|
152
|
+
subject.reload
|
153
|
+
end
|
154
|
+
|
155
|
+
it "it should fetch server data" do
|
156
|
+
expect(subject.attributes).to eq(attributes_for(:user_with_address_and_posts))
|
157
|
+
end
|
158
|
+
|
159
|
+
it "#has_data_from_server and #from_server should return true" do
|
160
|
+
expect(subject.from_server).to be true
|
161
|
+
expect(subject.has_data_from_server).to be true
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context "when calling #reload on a nested object" do
|
166
|
+
|
167
|
+
context "without the option 'ignore_parent: true'" do
|
168
|
+
before do
|
169
|
+
subject.id = 1
|
170
|
+
subject.reload
|
171
|
+
subject.posts.first.reload
|
172
|
+
end
|
173
|
+
|
174
|
+
it "it should fetch server data, with NESTED REST url" do
|
175
|
+
expect(subject.posts.first.attributes).to eq({ id: 1, body: 'from_nested_url' })
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context "with the option 'ignore_parent: true'" do
|
180
|
+
before do
|
181
|
+
subject.id = 1
|
182
|
+
subject.reload
|
183
|
+
subject.posts.first.reload(nil, nil, { ignore_parent: true })
|
184
|
+
end
|
185
|
+
|
186
|
+
it "it should fetch server data, with REST url" do
|
187
|
+
expect(subject.posts.first.attributes).to eq({ id: 1, body: 'from_resource_url' })
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
it_behaves_like "a method that calls the #make_the_call method"
|
194
|
+
end
|
195
|
+
|
196
|
+
describe ".create" do
|
197
|
+
|
198
|
+
subject { created_subject }
|
199
|
+
let(:method_arguments) { [] }
|
200
|
+
|
201
|
+
context "when attributes DON'T contain an ID" do
|
202
|
+
let(:method_to_execute) { :create_without_id }
|
203
|
+
let(:persistence_state) { { 200 => true, 422 => false, 500 => false } }
|
204
|
+
|
205
|
+
it_behaves_like "persistent remote call"
|
206
|
+
|
207
|
+
it "it should make a post http call" do
|
208
|
+
execute_method
|
209
|
+
expect(subject.last_remote_call.parsed_response['http_verb']).to eq('post')
|
210
|
+
end
|
211
|
+
|
212
|
+
it_behaves_like "save method"
|
213
|
+
end
|
214
|
+
|
215
|
+
context "when attributes contain an ID" do
|
216
|
+
let(:method_to_execute) { :create_with_id }
|
217
|
+
let(:persistence_state) { { 200 => true, 422 => true, 500 => true } }
|
218
|
+
|
219
|
+
it_behaves_like "persistent remote call"
|
220
|
+
|
221
|
+
it "it should make a post http call" do
|
222
|
+
execute_method
|
223
|
+
expect(subject.last_remote_call.parsed_response['http_verb']).to eq('put')
|
224
|
+
end
|
225
|
+
|
226
|
+
it_behaves_like "save method"
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "#new_record?" do
|
232
|
+
context "when initializing an instance without an id" do
|
233
|
+
subject { new_user }
|
234
|
+
|
235
|
+
it "it should return true" do
|
236
|
+
expect(subject.new_record?).to be(true)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
context "when initializing an instance with an id" do
|
241
|
+
subject { existing_user }
|
242
|
+
|
243
|
+
it "it should return false" do
|
244
|
+
expect(subject.new_record?).to be(false)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "#destroyed?" do
|
250
|
+
context "before executing #destroy" do
|
251
|
+
subject { existing_user }
|
252
|
+
|
253
|
+
it "it should return false" do
|
254
|
+
expect(subject.destroyed?).to be_falsey
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
context "after a successful execution of #destroy" do
|
259
|
+
subject { existing_user }
|
260
|
+
before { subject.destroy('', { status: 200 }) }
|
261
|
+
|
262
|
+
it "it should return true" do
|
263
|
+
expect(subject.destroyed?).to be(true)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context "after a failed execution of #destroy" do
|
268
|
+
subject { existing_user }
|
269
|
+
before { subject.destroy('', { status: 422 }) }
|
270
|
+
|
271
|
+
it "it should return false" do
|
272
|
+
expect(subject.destroyed?).to be(false)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
describe "#persisted?" do
|
279
|
+
context "when initializing an instance without an id" do
|
280
|
+
subject { new_user }
|
281
|
+
|
282
|
+
it "it should return false" do
|
283
|
+
expect(subject.persisted?).to be(false)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context "when initializing an instance with an id" do
|
288
|
+
|
289
|
+
context "before destroying the instance" do
|
290
|
+
subject { existing_user }
|
291
|
+
|
292
|
+
it "it should return true" do
|
293
|
+
expect(subject.persisted?).to be(true)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
context "after a successful #destroy" do
|
298
|
+
subject { existing_user }
|
299
|
+
before { subject.destroy('', { status: 200 }) }
|
300
|
+
|
301
|
+
it "it should return false" do
|
302
|
+
expect(subject.persisted?).to be(false)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
context "after a failed #destroy" do
|
307
|
+
subject { existing_user }
|
308
|
+
before { subject.destroy('', { status: 422 }) }
|
309
|
+
|
310
|
+
it "it should return true" do
|
311
|
+
expect(subject.persisted?).to be(true)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
describe "#save" do
|
318
|
+
|
319
|
+
let(:method_to_execute) { :save }
|
320
|
+
let(:method_arguments) { [] }
|
321
|
+
|
322
|
+
context "when an instance is NOT persisted" do
|
323
|
+
subject { new_user }
|
324
|
+
let(:persistence_state) { { 200 => true, 422 => false, 500 => false } }
|
325
|
+
|
326
|
+
it "it should make a post http call" do
|
327
|
+
execute_method
|
328
|
+
expect(subject.last_remote_call.parsed_response['http_verb']).to eq('post')
|
329
|
+
end
|
330
|
+
|
331
|
+
it_behaves_like "save method"
|
332
|
+
|
333
|
+
it_behaves_like "persistent remote call"
|
334
|
+
|
335
|
+
it_behaves_like "a method that calls the #make_the_call method"
|
336
|
+
end
|
337
|
+
|
338
|
+
context "when an instance IS persisted" do
|
339
|
+
context "and it uses 'put' http verb to save" do
|
340
|
+
subject { existing_user }
|
341
|
+
let(:persistence_state) { { 200 => true, 422 => true, 500 => true } }
|
342
|
+
|
343
|
+
it "it should make a put http call" do
|
344
|
+
execute_method
|
345
|
+
expect(subject.last_remote_call.parsed_response['http_verb']).to eq('put')
|
346
|
+
end
|
347
|
+
|
348
|
+
it_behaves_like "save method"
|
349
|
+
|
350
|
+
it_behaves_like "persistent remote call"
|
351
|
+
|
352
|
+
it_behaves_like "a method that calls the #make_the_call method"
|
353
|
+
end
|
354
|
+
|
355
|
+
context "and it uses 'patch' http verb to save" do
|
356
|
+
subject { existing_user_with_patch }
|
357
|
+
let(:persistence_state) { { 200 => true, 422 => true, 500 => true } }
|
358
|
+
|
359
|
+
it "it should make a patch http call" do
|
360
|
+
execute_method
|
361
|
+
expect(subject.last_remote_call.parsed_response['http_verb']).to eq('patch')
|
362
|
+
end
|
363
|
+
|
364
|
+
it_behaves_like "save method"
|
365
|
+
|
366
|
+
it_behaves_like "persistent remote call"
|
367
|
+
|
368
|
+
it_behaves_like "a method that calls the #make_the_call method"
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
describe "#save!" do
|
374
|
+
subject { existing_user }
|
375
|
+
|
376
|
+
context "when #save return true" do
|
377
|
+
it "should return true" do
|
378
|
+
expect(subject.save!('', { status: 200 })).to be(true)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
context "when #save return false or nil" do
|
383
|
+
it "should raise an exception" do
|
384
|
+
expect { subject.save!('', { status: 500 }) }.to raise_error
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
describe "#destroy" do
|
390
|
+
|
391
|
+
let(:method_to_execute) { :destroy }
|
392
|
+
let(:method_arguments) { [] }
|
393
|
+
|
394
|
+
context "when an instance is not persisted" do
|
395
|
+
subject { new_user }
|
396
|
+
|
397
|
+
it "it should return false" do
|
398
|
+
expect(execute_method).to be_falsey
|
399
|
+
end
|
400
|
+
|
401
|
+
it "it NOT should make a delete http call" do
|
402
|
+
expect(subject.last_remote_call).to be_nil
|
403
|
+
execute_method
|
404
|
+
expect(subject.last_remote_call).to be_nil
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
context "when an instance is persisted" do
|
409
|
+
subject { existing_user }
|
410
|
+
let(:persistence_state) { { 200 => false, 422 => true, 500 => true } }
|
411
|
+
|
412
|
+
it "it should make a delete http call" do
|
413
|
+
execute_method
|
414
|
+
expect(subject.last_remote_call.parsed_response['http_verb']).to eq('delete')
|
415
|
+
end
|
416
|
+
|
417
|
+
it_behaves_like "persistent remote call"
|
418
|
+
|
419
|
+
it_behaves_like "a method that calls the #make_the_call method"
|
420
|
+
end
|
421
|
+
|
422
|
+
end
|
423
|
+
|
424
|
+
end
|