smooth_operator 0.4.4 → 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 +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
|