smooth_operator 1.3.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +8 -8
  2. data/Gemfile +0 -5
  3. data/README.md +11 -308
  4. data/console.rb +3 -28
  5. data/lib/smooth_operator.rb +7 -75
  6. data/lib/smooth_operator/array_with_meta_data.rb +21 -20
  7. data/lib/smooth_operator/attribute_assignment.rb +53 -57
  8. data/lib/smooth_operator/delegation.rb +34 -15
  9. data/lib/smooth_operator/finder_methods.rb +17 -33
  10. data/lib/smooth_operator/helpers.rb +7 -37
  11. data/lib/smooth_operator/internal_attribute.rb +49 -0
  12. data/lib/smooth_operator/model_schema.rb +72 -0
  13. data/lib/smooth_operator/open_struct.rb +4 -7
  14. data/lib/smooth_operator/operator.rb +64 -102
  15. data/lib/smooth_operator/persistence.rb +64 -94
  16. data/lib/smooth_operator/remote_call.rb +70 -0
  17. data/lib/smooth_operator/serialization.rb +33 -89
  18. data/lib/smooth_operator/translation.rb +13 -26
  19. data/lib/smooth_operator/type_converter.rb +69 -0
  20. data/lib/smooth_operator/validations.rb +3 -25
  21. data/lib/smooth_operator/version.rb +1 -1
  22. data/smooth_operator.gemspec +5 -9
  23. data/spec/factories/user_factory.rb +4 -5
  24. data/spec/smooth_operator/attribute_assignment_spec.rb +11 -145
  25. data/spec/smooth_operator/delegation_spec.rb +54 -57
  26. data/spec/smooth_operator/finder_methods_spec.rb +2 -91
  27. data/spec/smooth_operator/{resource_name_spec.rb → model_schema_spec.rb} +2 -2
  28. data/spec/smooth_operator/operator_spec.rb +1 -1
  29. data/spec/smooth_operator/persistence_spec.rb +20 -140
  30. data/spec/smooth_operator/serialization_spec.rb +4 -28
  31. data/spec/spec_helper.rb +9 -7
  32. data/spec/support/models/address.rb +0 -9
  33. data/spec/support/models/post.rb +3 -9
  34. data/spec/support/models/user.rb +7 -30
  35. data/spec/support/models/user_with_address_and_posts.rb +12 -20
  36. data/spec/support/test_server.rb +7 -63
  37. metadata +18 -55
  38. data/lib/smooth_operator/associations.rb +0 -110
  39. data/lib/smooth_operator/associations/association_reflection.rb +0 -79
  40. data/lib/smooth_operator/associations/has_many_relation.rb +0 -45
  41. data/lib/smooth_operator/associations/reflection.rb +0 -41
  42. data/lib/smooth_operator/cookie_jar.rb +0 -21
  43. data/lib/smooth_operator/http_methods.rb +0 -17
  44. data/lib/smooth_operator/internal_data.rb +0 -45
  45. data/lib/smooth_operator/operators/connection_wrapper.rb +0 -15
  46. data/lib/smooth_operator/operators/faraday.rb +0 -75
  47. data/lib/smooth_operator/operators/typhoeus.rb +0 -87
  48. data/lib/smooth_operator/options.rb +0 -30
  49. data/lib/smooth_operator/remote_call/base.rb +0 -76
  50. data/lib/smooth_operator/remote_call/errors/connection_failed.rb +0 -20
  51. data/lib/smooth_operator/remote_call/errors/timeout.rb +0 -20
  52. data/lib/smooth_operator/remote_call/faraday.rb +0 -19
  53. data/lib/smooth_operator/remote_call/typhoeus.rb +0 -19
  54. data/lib/smooth_operator/resource_name.rb +0 -46
  55. data/lib/smooth_operator/schema.rb +0 -21
  56. data/lib/smooth_operator/type_casting.rb +0 -127
  57. data/spec/require_helper.rb +0 -11
  58. data/spec/smooth_operator/remote_call_spec.rb +0 -340
  59. data/spec/smooth_operator/validations_spec.rb +0 -42
  60. data/spec/support/models/comment.rb +0 -5
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MmY1MDZiMTA3ZGQ4OThhNWZlNDA1YzgzNmRhZTg3YzE2OWQ0ZjM1ZQ==
4
+ MjU3NWQ2OTMyMjE4ODBjZGMzZWJmMmRlMzcxZDBmMWZjYmU5YjYwOQ==
5
5
  data.tar.gz: !binary |-
6
- M2VlYzc3M2ZlZmZiM2I4ZTI3ZmE4NTUyYzIzMzEwNTMwNTAwZTY5OQ==
6
+ Y2E5MDEyYmYxODcwNmQ4MzY2MWFlMmViYTQ3Y2QxN2FlOWU2ZTJiOA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZmQ5NDFiMDAxYzhhNjQ0NjQzMjdlMTQ4MTM0OTg5MTRmOGZmOTlkM2YwZWJh
10
- YzJhZDBmYjRlMzdjOGJiYmFmNTBkZjBmODk0Mzc4MmRmNTcxZDdhZTUwMTE5
11
- MDQ4ZjU3YTM5YTYwMDI0Yjc4ODI2MjZmYTIwMGU4OGUwMGE5NGY=
9
+ NzlmOGZhNzVhYTVkOTE0YTI4OGZkMjhjNDlkYWE1NWEzZmE0YmIzY2RmMGVk
10
+ NzcyYTlmY2ZhZmNmZmU4MzIyOWY4ZjU2MmFlYTU4ZjRmNzI2OTczYmM3MGNi
11
+ ZDUyMGZkOGYwZDBkZThmNTE5ZTQxMTkxN2JjZWRiYTFlNzg4Nzk=
12
12
  data.tar.gz: !binary |-
13
- NTEzZjYwMzQyN2IxMWRmMDc4ZjFlZGZmY2M4ZjY1Zjk4OTM5NTY3OGRkZTEw
14
- OGJhMjNiYjVjOWRmMzQwZDJiZjM4ZGUwMjQ1YTVkZjZiNDhmMTkwMjEyZjhm
15
- YmIxMmRmMzExMjQ2ZmFjNTNhMThjZjBiZmIwMmRjOGJkNWI0NGM=
13
+ YWI0YjkzNDVmN2JhMmY0MzYxMmE4NGQ1ZDAxMDM1NzZkYThiZjZhMmYxZmE2
14
+ NzIzYTQ5MzA0NDg4NzE5ZmM2ODgzOTFmYjkwNzMxYjZiMTdmZjRjZDUzYjM4
15
+ YzQzZTc3NzJkM2I2OGM5MzFhYTQ0NDc5MWU1MGI0NmE0MzkzNGM=
data/Gemfile CHANGED
@@ -3,15 +3,10 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in smooth_operator.gemspec
4
4
  gemspec
5
5
 
6
- gem 'simplecov', :require => false, :group => :test
7
-
8
6
  group :development, :test do
9
7
  gem "pry"
10
8
  gem "sinatra"
11
- gem "typhoeus"
12
- gem "activesupport"
13
9
  gem "sinatra-contrib"
14
10
  gem "rspec", "~> 3.0.0.beta1"
15
11
  gem "factory_girl", "~> 4.0"
16
- gem "ethon", :git => 'https://github.com/goncalvesjoao/ethon'
17
12
  end
data/README.md CHANGED
@@ -1,23 +1,14 @@
1
- # SmoothOperator [![Code Climate](https://codeclimate.com/repos/536a7b9f6956801228014b02/badges/13f79897976274a9de33/gpa.png)](https://codeclimate.com/repos/536a7b9f6956801228014b02/feed)
1
+ # SmoothOperator
2
2
 
3
3
  Ruby gem, that mimics the ActiveRecord behaviour but through external API's.
4
4
  It's a lightweight and flexible alternative to ActiveResource, that responds to a REST API like you expect it too.
5
5
 
6
- Be sure to check out this micro-services example: https://github.com/goncalvesjoao/micro-services-example
6
+ Depends only on Faraday gem, no need for ActiveSupport or any other Active* gem.
7
7
 
8
- Where a Rails4 app lists/creates/edits and destroys blog posts from a Padrino (aka Sinatra) app, using SmoothOperator::Rails instead of ActiveRecord::Base classes.
8
+ Although if I18n is present it will respond to .human_attribute_name method and if ActiveModel is present it will make use of 'ActiveModel::Name' to improve .model_name method.
9
9
 
10
- This micro-services example will also feature other cool stuff like:
11
- - parallel requests;
12
- - using HTTP PATCH verb for saving instead of PUT;
13
- - form errors with simple_form gem;
14
- - nested objects using cocoon gem;
15
- - endless-pagination with kaminari gem
16
- - and others...
17
10
 
18
- ---
19
-
20
- ## 1) Installation
11
+ ## Installation
21
12
 
22
13
  Add this line to your application's Gemfile:
23
14
 
@@ -31,303 +22,15 @@ Or install it yourself as:
31
22
 
32
23
  $ gem install smooth_operator
33
24
 
34
- ---
35
-
36
- ## 2) Usage and Examples
37
-
38
- ```ruby
39
- class MyBlogResource < SmoothOperator::Base
40
-
41
- # HTTP BASIC AUTH
42
- options endpoint_user: 'admin',
43
- endpoint_pass: 'admin',
44
- endpoint: 'http://myblog.com/api/v0'
45
-
46
- # OR
47
- # smooth_operator_options
48
- end
49
-
50
- class Post < MyBlogResource
51
- end
52
- ```
53
-
54
- ---
55
-
56
- ### 2.1) Creating a .new 'Post' and #save it
57
-
58
- ```ruby
59
- post = Post.new(body: 'my first post', author: 'John Doe')
60
-
61
- post.new_record? # true
62
- post.persisted? # false
63
- post.body # 'my first post'
64
- post.author # 'John Doe'
65
- post.something_else # will raise NoMethodError
66
-
67
- save_result = post.save # will make a http POST call to 'http://myblog.com/api/v0/posts'
68
- # with `{ post: { body: 'my first post', author: 'John Doe' } }`
69
-
70
- post.last_remote_call # will contain a SmoothOperator::RemoteCall instance containing relevant information about the save remote call.
71
-
72
- # If the server response is positive (http code between 200 and 299):
73
- save_result # true
74
- post.new_record? # false
75
- post.persisted? # true
76
- # server response contains { id: 1 } on its body
77
- post.id # 1
78
-
79
- # If the server response is negative (http code between 400 and 499):
80
- save_result # false
81
- post.new_record? # true
82
- post.persisted? # false
83
- # server response contains { errors: { body: ['must be less then 10 letters'] } }
84
- post.errors.body # Array
85
-
86
- # If the server response is an error (http code between 500 and 599), or the connection was broke:
87
- save_result # nil
88
- post.new_record? # true
89
- post.persisted? # false
90
- # server response contains { errors: { body: ['must be less then 10 letters'] } }
91
- post.errors # will raise NoMethodError
92
-
93
- # In the positive and negative server response comes with a json,
94
- # e.g. { id: 1 }, post will reflect that new data
95
- post.id # 1
96
-
97
- # In case of error and the server response contains a json,
98
- # e.g. { id: 1 }, post will NOT reflect that data
99
- post.id # raise NoMethodError
100
-
101
- ```
102
-
103
- ---
104
-
105
- ### 2.2) Editing an existing record
106
- ```ruby
107
- post = Post.find(2)
108
-
109
- post.body = 'editing my second page'
110
-
111
- post.save
112
- ```
113
-
114
- ---
115
-
116
- ### 2.3) Customize #save 'url', 'params' and 'options'
117
- ```ruby
118
- post = Post.new(id: 2, body: 'editing my second page')
119
-
120
- post.new_record? # false
121
- post.persisted? # true
122
-
123
- post.save("save_and_add_to_list", { admin: true, post: { author: 'Agent Smith', list_id: 1 } }, { timeout: 1 })
124
- # Will make a PUT to 'http://myblog.com/api/v0/posts/2/save_and_add_to_list'
125
- # with { admin: true, post: { body: 'editing my second page', list_id: 1 } }
126
- # and will only wait 1sec for the server to respond.
127
-
128
- post.save('/#{post.id}/save_and_add_to_list')
129
- # Will make a PUT to 'http://myblog.com/api/v0/posts/2/save_and_add_to_list'
130
-
131
- post.save('/save_and_add_to_list')
132
- # Will make a PUT to 'http://myblog.com/api/v0/posts/save_and_add_to_list'
133
- ```
134
-
135
- ---
136
-
137
- ### 2.4) Saving using HTTP Patch verb
138
- ```ruby
139
- class Page < MyBlogResource
140
- options update_http_verb: 'patch'
141
- # OR
142
- #smooth_operator_options update_http_verb: 'patch'
143
- end
144
-
145
- page = Page.find(2)
146
-
147
- page.body = 'editing my second page'
148
-
149
- page.save # will make a http PATCH call to 'http://myblog.com/api/v0/pages/2'
150
- # with `{ page: { body: 'editing my second page' } }`
151
- ```
152
-
153
- ---
154
-
155
- ### 2.5) Retrieving remote objects - 'index' REST action
156
-
157
- ```ruby
158
- remote_call = Page.find(:all) # Will make a GET call to 'http://myblog.com/api/v0/pages'
159
- # and will return a SmoothOperator::RemoteCall instance
160
-
161
- pages = remote_call.data
162
-
163
- # If the server response is positive (http code between 200 and 299, or 304):
164
- remote_call.ok? # true
165
- remote_call.not_processed? # false
166
- remote_call.error? # false
167
- remote_call.status # true
168
- pages = remote_call.data # array of Page instances
169
- remote_call.http_status # server_response code
170
-
171
- # If the server response is unprocessed entity (http code 422):
172
- remote_call.ok? # false
173
- remote_call.not_processed? # true
174
- remote_call.error? # false
175
- remote_call.status # false
176
- remote_call.http_status # server_response code
177
-
178
- # If the server response is client error (http code between 400..499, except 422):
179
- remote_call.ok? # false
180
- remote_call.not_processed? # false
181
- remote_call.error? # true
182
- remote_call.status # nil
183
- remote_call.http_status # server_response code
184
-
185
- # If the server response is server error (http code between 500 and 599), or the connection broke:
186
- remote_call.ok? # false
187
- remote_call.not_processed? # false
188
- remote_call.error? # true
189
- remote_call.status # nil
190
- remote_call.http_status # server_response code or 0 if connection broke
191
- ```
192
-
193
- ---
194
-
195
- ### 2.6) Retrieving remote objects - 'show' REST action
196
-
197
- ```ruby
198
- remote_call = Page.find(2) # Will make a GET call to 'http://myblog.com/api/v0/pages/2'
199
- # and will return a SmoothOperator::RemoteCall instance
200
-
201
- service_down = remote_call.error?
202
-
203
- page = remote_call.data
204
- ```
205
-
206
- ---
207
-
208
- ### 2.7) Retrieving remote objects - custom query
209
- ```ruby
210
- remote_call = Page.find('my_pages', { q: body_contains: 'link' }, { endpoint_user: 'admin', endpoint_pass: 'new_password' })
211
- # will make a GET call to 'http://myblog.com/api/v0/pages/my_pages?q={body_contains="link"}'
212
- # and will change the HTTP BASIC AUTH credentials to user: 'admin' and pass: 'new_password' for this connection only.
213
-
214
- @service_down = remote_call.error?
215
-
216
- # If the server json response is an Array [{ id: 1 }, { id: 2 }]
217
- @pages = remote.data # will return an array with 2 Page's instances
218
- @pages[0].id # 1
219
- @pages[1].id # 2
220
-
221
- # If the server json response is a Hash { id: 3 }
222
- @page = remote.data # will return a single Page instance
223
- @page.id # 3
224
-
225
- # If the server json response is Hash with a key called 'pages' { current_page: 1, total_pages: 3, limit_value: 10, pages: [{ id: 4 }, { id: 5 }] }
226
- @pages = remote.data # will return a single ArrayWithMetaData instance, that will allow you to access to both the Page's instances array and the metadata.
227
-
228
- # @pages is now a valid object to work with kaminari
229
- @pages.total_pages # 3
230
- @pages.current_page # 1
231
- @pages.limit_value # 10
232
-
233
- @pages[0].id # 4
234
- @pages[1].id # 5
235
- ```
236
-
237
- ### 2.8) Keeping your session alive - custom HTTP Headers
238
-
239
- Controllers
240
- ApplicationController
241
- ```ruby
242
- ```
243
-
244
- Models
245
- SmoothResource
246
- ```ruby
247
- class SmoothResource < SmoothOperator::Rails
248
-
249
- options headers: :custom_headers
250
-
251
- def self.custom_headers
252
- {
253
- cookie: current_user.blog_cookie,
254
- "X_CSRF_TOKEN" => current_user.blog_auth_token
255
- }
256
- end
257
-
258
- protected ############## PROTECTED #################
259
-
260
- def self.current_user
261
- User.current_user
262
- end
263
-
264
- end
265
- ```
266
-
267
- ---
268
-
269
- ## 3) Methods
270
-
271
- ---
272
-
273
- ### 3.1) Persistence methods
274
-
275
- Methods | Behaviour | Arguments | Return
276
- ------- | --------- | ------ | ---------
277
- .create | Generates a new instance of the class with *attributes and calls #save with the rest of its arguments| Hash attributes = nil, String relative_path = nil, Hash data = {}, Hash options = {} | Class instance
278
- #new_record? | Returns @new_record if defined, else populates it with true if #id is present or false if blank. | - | Boolean
279
- #destroyed?| Returns @destroyed if defined, else populates it with false. | - | Boolean
280
- #persisted?| Returns true if both #new_record? and #destroyed? return false, else returns false. | - | Boolean
281
- #save | if #new_record? makes a HTTP POST, else a PUT call. If !#new_record? and relative_path is blank, sets relative_path = id.to_s. If the server POST response is positive, sets @new_record = false. See 4.2) for more behaviour info. | String relative_path = nil, Hash data = {}, Hash options = {} | Boolean or Nil
282
- #save! | Executes the same behaviour as #save, but will raise RecordNotSaved if the returning value is not true | String relative_path = nil, Hash data = {}, Hash options = {} | Boolean or Nil
283
- #destroy | Does nothing if !persisted? else makes a HTTP DELETE call. If server response it positive, sets @destroyed = true. If relative_path is blank, sets relative_path = id.to_s. See 4.2) for more behaviour info. | String relative_path = nil, Hash data = {}, Hash options = {} | Boolean or Nil
284
-
285
- ---
286
-
287
- ### 3.2) Finder methods
288
-
289
- Methods | Behaviour | Arguments | Return
290
- ------- | --------- | ------ | ---------
291
- .find | If relative_path == :all, sets relative_path = ''. Makes a Get call and initiates Class objects with the server's response data. See 4.3) and 4.4) for more behaviour info. | String relative_path, Hash data = {}, Hash options = {} | Class instance, Array of Class instances or an ArrayWithMetaData instance
292
-
293
- ---
294
-
295
- ### 3.3) Operator methods
296
- ...
297
-
298
- ---
299
-
300
- ### 3.3) Remote call methods
301
- ...
302
-
303
- ---
304
-
305
- ## 4) Behaviours
306
-
307
- ---
308
-
309
- ### 4.1) Delegation behaviour
310
- ...
311
-
312
- ---
313
-
314
- ### 4.2) Persistent operator behaviour
315
- ...
316
-
317
- ---
318
-
319
- ### 4.3) Operator behaviour
320
- ...
321
25
 
322
- ---
26
+ ## Usage
323
27
 
324
- ### 4.4) Remote call behaviour
325
- ...
28
+ TODO: Write usage instructions here
326
29
 
327
- ---
328
30
 
329
- ## 4) TODO
31
+ ## TODO
330
32
 
331
- 1. Finish "Methods" and "Behaviours" documentation;
332
- 2. ModelSchema specs;
333
- 3. Cache.
33
+ 1. FinderMethods specs
34
+ 2. serialization_specs to test the json options for nested classes
35
+ 3. model_schema_specs
36
+ 4. Cache
data/console.rb CHANGED
@@ -3,34 +3,9 @@
3
3
  $LOAD_PATH << './'
4
4
  $LOAD_PATH << './lib'
5
5
 
6
- require 'spec/require_helper'
6
+ require "spec/spec_helper"
7
7
 
8
- FactoryGirl.find_definitions
9
-
10
- LocalhostServer.new(TestServer.new, 4567)
11
-
12
- # user = nil
13
-
14
- # hydra = Typhoeus::Hydra::hydra
15
-
16
- # user = User::Base.new(id: 1)
17
- # user.reload(nil, nil, { hydra: hydra })
18
-
19
- # User::Base.find(1, nil, { hydra: hydra }) do |remote_call|
20
- # user = remote_call.data
21
- # end
22
-
23
- #User::Base.post('', { user: { age: 1, posts: [{ body: 'post1' }, 2] } })
24
-
25
- #user = UserWithAddressAndPosts::Son.new(FactoryGirl.attributes_for(:user_with_address_and_posts))
26
- #user.save('', { status: 200 })
27
-
28
- # "[{\"patient_id\"=>33, \"messages\"=>[{\"id\"=>\"53722c20cb38247c36000003\", \"title\"=>\"Joao Goncalves\", \"created_at\"=>\"2014-05-13T14:28:48Z\"}, {\"id\"=>\"53722bfccb382485d5000002\", \"title\"=>\"Joao Goncalves\", \"created_at\"=>\"2014-05-13T14:28:12Z\"}, {\"id\"=>\"53722b91cb3824e913000001\", \"title\"=>\"Joao Goncalves\", \"created_at\"=>\"2014-05-13T14:26:25Z\"}]}]"
29
-
30
- post = Post.new(comments: [{ id: 1, name: '1' }, { id: 2, name: '2' }], address: { id: 1, name: 'address' })
31
-
32
- comments_attributes = { "0" => { id: 1, name: '3' }, "1" => { name: '4' } }
33
-
34
- comments_with_errors = { "0" => { id: 1, name: '3', errors: { body: ["can't be blank"] } }, "1" => { name: '4', errors: { body: ["can't be blank"] } } }
8
+ #User.post('', { user: { age: 1, posts: [{ body: 'post1' }, 2] } })
35
9
 
36
10
  binding.pry
11
+
@@ -1,94 +1,26 @@
1
- require "smooth_operator/schema"
1
+ # require 'active_model'
2
+
2
3
  require "smooth_operator/version"
3
4
  require "smooth_operator/helpers"
4
5
  require "smooth_operator/operator"
6
+ require "smooth_operator/remote_call"
5
7
  require "smooth_operator/persistence"
6
8
  require "smooth_operator/translation"
7
9
  require "smooth_operator/open_struct"
8
- require "smooth_operator/http_methods"
9
- require "smooth_operator/associations"
10
10
  require "smooth_operator/finder_methods"
11
11
 
12
12
  module SmoothOperator
13
+
13
14
  class Base < OpenStruct
14
15
 
15
- extend Schema
16
- extend HttpMethods
17
- extend Associations
16
+ extend Operator
18
17
  extend FinderMethods
19
18
  extend Translation if defined? I18n
20
19
 
21
- include Operator
22
- include HttpMethods
23
20
  include Persistence
24
- include FinderMethods
25
-
26
- options strict_behaviour: true
27
-
28
- def self.smooth_operator?
29
- true
30
- end
31
-
32
- end
33
-
34
- if defined?(ActiveModel)
35
- class Rails < Base
36
-
37
- include ActiveModel::Validations
38
- include ActiveModel::Validations::Callbacks
39
- include ActiveModel::Conversion
40
-
41
- options unknown_hash_class: SmoothOperator::OpenStruct
42
-
43
- validate :validate_induced_errors, :validate_nested_objects
44
-
45
- def column_for_attribute(attribute_name)
46
- type = self.class.attribute_type(attribute_name)
47
-
48
- ActiveRecord::ConnectionAdapters::Column.new(attribute_name.to_sym, type, type)
49
- end
50
-
51
- def save(relative_path = nil, data = {}, options = {})
52
- return false unless before_save
53
-
54
- clear_induced_errors
55
-
56
- save_result = valid? ? super : false
57
-
58
- after_save if valid? && save_result
59
-
60
- save_result
61
- end
62
-
63
- def before_save
64
- true
65
- end
66
-
67
- def after_save; end
68
-
69
- def self.model_name
70
- smooth_model_name
71
- end
72
-
73
- protected ################# PROTECTED ###################
74
-
75
- def validate_induced_errors
76
- induced_errors.each do |key, value|
77
- [*value].each do |_value|
78
- self.errors.add(key, _value) unless self.errors.added?(key, _value)
79
- end
80
- end
81
-
82
- Helpers.blank?(induced_errors)
83
- end
84
-
85
- def validate_nested_objects
86
- all_nested_objects = self.class.reflections.keys
87
- .map { |association| send(association) }.flatten.compact
88
21
 
89
- all_nested_objects.map { |nested_object| nested_object.valid? }.all?
90
- end
22
+ attr_reader :last_remote_call
91
23
 
92
- end
93
24
  end
25
+
94
26
  end