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.
Files changed (68) hide show
  1. checksums.yaml +9 -9
  2. data/.gitignore +2 -1
  3. data/.rspec +4 -0
  4. data/Gemfile +13 -0
  5. data/README.md +258 -10
  6. data/console.rb +44 -0
  7. data/lib/smooth_operator/array_with_meta_data.rb +31 -0
  8. data/lib/smooth_operator/attribute_assignment.rb +102 -0
  9. data/lib/smooth_operator/attribute_methods.rb +87 -0
  10. data/lib/smooth_operator/attributes/base.rb +107 -0
  11. data/lib/smooth_operator/attributes/dirty.rb +29 -0
  12. data/lib/smooth_operator/attributes/normal.rb +15 -0
  13. data/lib/smooth_operator/delegation.rb +60 -0
  14. data/lib/smooth_operator/finder_methods.rb +43 -0
  15. data/lib/smooth_operator/helpers.rb +79 -0
  16. data/lib/smooth_operator/model_schema.rb +81 -0
  17. data/lib/smooth_operator/open_struct.rb +37 -0
  18. data/lib/smooth_operator/operator.rb +145 -0
  19. data/lib/smooth_operator/operators/faraday.rb +75 -0
  20. data/lib/smooth_operator/operators/typhoeus.rb +77 -0
  21. data/lib/smooth_operator/persistence.rb +144 -0
  22. data/lib/smooth_operator/relation/array_relation.rb +13 -0
  23. data/lib/smooth_operator/relation/association_reflection.rb +75 -0
  24. data/lib/smooth_operator/relation/associations.rb +75 -0
  25. data/lib/smooth_operator/relation/reflection.rb +41 -0
  26. data/lib/smooth_operator/relation/single_relation.rb +14 -0
  27. data/lib/smooth_operator/remote_call/base.rb +80 -0
  28. data/lib/smooth_operator/remote_call/errors/connection_failed.rb +20 -0
  29. data/lib/smooth_operator/remote_call/errors/timeout.rb +20 -0
  30. data/lib/smooth_operator/remote_call/faraday.rb +19 -0
  31. data/lib/smooth_operator/remote_call/typhoeus.rb +19 -0
  32. data/lib/smooth_operator/serialization.rb +79 -0
  33. data/lib/smooth_operator/translation.rb +27 -0
  34. data/lib/smooth_operator/validations.rb +15 -0
  35. data/lib/smooth_operator/version.rb +1 -1
  36. data/lib/smooth_operator.rb +26 -5
  37. data/smooth_operator.gemspec +12 -3
  38. data/spec/factories/user_factory.rb +34 -0
  39. data/spec/require_helper.rb +11 -0
  40. data/spec/smooth_operator/attribute_assignment_spec.rb +351 -0
  41. data/spec/smooth_operator/attributes_dirty_spec.rb +53 -0
  42. data/spec/smooth_operator/delegation_spec.rb +139 -0
  43. data/spec/smooth_operator/finder_methods_spec.rb +105 -0
  44. data/spec/smooth_operator/model_schema_spec.rb +31 -0
  45. data/spec/smooth_operator/operator_spec.rb +46 -0
  46. data/spec/smooth_operator/persistence_spec.rb +424 -0
  47. data/spec/smooth_operator/remote_call_spec.rb +320 -0
  48. data/spec/smooth_operator/serialization_spec.rb +80 -0
  49. data/spec/smooth_operator/validations_spec.rb +42 -0
  50. data/spec/spec_helper.rb +25 -0
  51. data/spec/support/helpers/persistence_helper.rb +38 -0
  52. data/spec/support/localhost_server.rb +97 -0
  53. data/spec/support/models/address.rb +14 -0
  54. data/spec/support/models/comment.rb +3 -0
  55. data/spec/support/models/post.rb +13 -0
  56. data/spec/support/models/user.rb +41 -0
  57. data/spec/support/models/user_with_address_and_posts.rb +89 -0
  58. data/spec/support/test_server.rb +165 -0
  59. metadata +108 -18
  60. data/lib/smooth_operator/base.rb +0 -30
  61. data/lib/smooth_operator/core.rb +0 -218
  62. data/lib/smooth_operator/http_handlers/typhoeus/base.rb +0 -58
  63. data/lib/smooth_operator/http_handlers/typhoeus/orm.rb +0 -34
  64. data/lib/smooth_operator/http_handlers/typhoeus/remote_call.rb +0 -28
  65. data/lib/smooth_operator/operator/base.rb +0 -43
  66. data/lib/smooth_operator/operator/exceptions.rb +0 -64
  67. data/lib/smooth_operator/operator/orm.rb +0 -118
  68. 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