acfs 1.3.3 → 1.6.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 +372 -0
- data/LICENSE +22 -0
- data/README.md +321 -0
- data/acfs.gemspec +38 -0
- data/lib/acfs.rb +51 -0
- data/lib/acfs/adapter/base.rb +26 -0
- data/lib/acfs/adapter/typhoeus.rb +82 -0
- data/lib/acfs/collection.rb +28 -0
- data/lib/acfs/collections/paginatable.rb +76 -0
- data/lib/acfs/configuration.rb +120 -0
- data/lib/acfs/errors.rb +147 -0
- data/lib/acfs/global.rb +101 -0
- data/lib/acfs/location.rb +76 -0
- data/lib/acfs/middleware/base.rb +24 -0
- data/lib/acfs/middleware/json.rb +31 -0
- data/lib/acfs/middleware/logger.rb +23 -0
- data/lib/acfs/middleware/msgpack.rb +32 -0
- data/lib/acfs/middleware/print.rb +23 -0
- data/lib/acfs/middleware/serializer.rb +41 -0
- data/lib/acfs/operation.rb +96 -0
- data/lib/acfs/request.rb +32 -0
- data/lib/acfs/request/callbacks.rb +54 -0
- data/lib/acfs/resource.rb +39 -0
- data/lib/acfs/resource/attributes.rb +270 -0
- data/lib/acfs/resource/attributes/base.rb +29 -0
- data/lib/acfs/resource/attributes/boolean.rb +39 -0
- data/lib/acfs/resource/attributes/date_time.rb +32 -0
- data/lib/acfs/resource/attributes/dict.rb +39 -0
- data/lib/acfs/resource/attributes/float.rb +33 -0
- data/lib/acfs/resource/attributes/integer.rb +29 -0
- data/lib/acfs/resource/attributes/list.rb +36 -0
- data/lib/acfs/resource/attributes/string.rb +26 -0
- data/lib/acfs/resource/attributes/uuid.rb +48 -0
- data/lib/acfs/resource/dirty.rb +37 -0
- data/lib/acfs/resource/initialization.rb +31 -0
- data/lib/acfs/resource/loadable.rb +35 -0
- data/lib/acfs/resource/locatable.rb +135 -0
- data/lib/acfs/resource/operational.rb +26 -0
- data/lib/acfs/resource/persistence.rb +258 -0
- data/lib/acfs/resource/query_methods.rb +266 -0
- data/lib/acfs/resource/service.rb +44 -0
- data/lib/acfs/resource/validation.rb +49 -0
- data/lib/acfs/response.rb +30 -0
- data/lib/acfs/response/formats.rb +27 -0
- data/lib/acfs/response/status.rb +33 -0
- data/lib/acfs/rspec.rb +13 -0
- data/lib/acfs/runner.rb +102 -0
- data/lib/acfs/service.rb +94 -0
- data/lib/acfs/service/middleware.rb +58 -0
- data/lib/acfs/service/middleware/stack.rb +65 -0
- data/lib/acfs/singleton_resource.rb +85 -0
- data/lib/acfs/stub.rb +199 -0
- data/lib/acfs/util.rb +22 -0
- data/lib/acfs/version.rb +16 -0
- data/lib/acfs/yard.rb +6 -0
- data/spec/acfs/adapter/typhoeus_spec.rb +55 -0
- data/spec/acfs/collection_spec.rb +157 -0
- data/spec/acfs/configuration_spec.rb +53 -0
- data/spec/acfs/global_spec.rb +140 -0
- data/spec/acfs/location_spec.rb +25 -0
- data/spec/acfs/middleware/json_spec.rb +79 -0
- data/spec/acfs/middleware/msgpack_spec.rb +62 -0
- data/spec/acfs/operation_spec.rb +12 -0
- data/spec/acfs/request/callbacks_spec.rb +48 -0
- data/spec/acfs/request_spec.rb +79 -0
- data/spec/acfs/resource/attributes/boolean_spec.rb +58 -0
- data/spec/acfs/resource/attributes/date_time_spec.rb +51 -0
- data/spec/acfs/resource/attributes/dict_spec.rb +77 -0
- data/spec/acfs/resource/attributes/float_spec.rb +61 -0
- data/spec/acfs/resource/attributes/integer_spec.rb +36 -0
- data/spec/acfs/resource/attributes/list_spec.rb +60 -0
- data/spec/acfs/resource/attributes/uuid_spec.rb +42 -0
- data/spec/acfs/resource/attributes_spec.rb +179 -0
- data/spec/acfs/resource/dirty_spec.rb +49 -0
- data/spec/acfs/resource/initialization_spec.rb +36 -0
- data/spec/acfs/resource/loadable_spec.rb +22 -0
- data/spec/acfs/resource/locatable_spec.rb +118 -0
- data/spec/acfs/resource/persistance_spec.rb +322 -0
- data/spec/acfs/resource/query_methods_spec.rb +548 -0
- data/spec/acfs/resource/validation_spec.rb +129 -0
- data/spec/acfs/response/formats_spec.rb +52 -0
- data/spec/acfs/response/status_spec.rb +71 -0
- data/spec/acfs/runner_spec.rb +95 -0
- data/spec/acfs/service/middleware_spec.rb +35 -0
- data/spec/acfs/service_spec.rb +48 -0
- data/spec/acfs/singleton_resource_spec.rb +17 -0
- data/spec/acfs/stub_spec.rb +345 -0
- data/spec/acfs_spec.rb +205 -0
- data/spec/fixtures/config.yml +14 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/hash.rb +11 -0
- data/spec/support/response.rb +12 -0
- data/spec/support/service.rb +92 -0
- data/spec/support/shared/find_callbacks.rb +50 -0
- metadata +159 -26
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Acfs::Resource
|
4
|
+
# @api private
|
5
|
+
#
|
6
|
+
# Provide methods for creating and processing CRUD operations and
|
7
|
+
# handling responses. That includes error handling as well as
|
8
|
+
# handling stubbed resources.
|
9
|
+
#
|
10
|
+
# Should only be used internal.
|
11
|
+
#
|
12
|
+
module Operational
|
13
|
+
extend ActiveSupport::Concern
|
14
|
+
|
15
|
+
def operation(*args, **kwargs, &block)
|
16
|
+
self.class.operation(*args, **kwargs, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
# Invoke CRUD operation.
|
21
|
+
def operation(action, **opts, &block)
|
22
|
+
Acfs.runner.process ::Acfs::Operation.new(self, action, **opts, &block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Acfs::Resource
|
4
|
+
#
|
5
|
+
# Allow to track the persistence state of a model.
|
6
|
+
#
|
7
|
+
module Persistence
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
# @api public
|
11
|
+
#
|
12
|
+
# Check if the model is persisted. A model is persisted if
|
13
|
+
# it is saved after being created
|
14
|
+
#
|
15
|
+
# @example Newly created resource:
|
16
|
+
# user = User.new name: "John"
|
17
|
+
# user.persisted? # => false
|
18
|
+
# user.save
|
19
|
+
# user.persisted? # => true
|
20
|
+
#
|
21
|
+
# @example Modified resource:
|
22
|
+
# user2 = User.find 5
|
23
|
+
# user2.persisted? # => true
|
24
|
+
# user2.name = 'Amy'
|
25
|
+
# user2.persisted? # => true
|
26
|
+
# user2.save
|
27
|
+
# user2.persisted? # => true
|
28
|
+
#
|
29
|
+
# @return [Boolean] True if resource has been saved
|
30
|
+
#
|
31
|
+
def persisted?
|
32
|
+
!new?
|
33
|
+
end
|
34
|
+
|
35
|
+
# @api public
|
36
|
+
#
|
37
|
+
# Return true if model is a new record and was not saved yet.
|
38
|
+
#
|
39
|
+
# @return [Boolean] True if resource is newly created,
|
40
|
+
# false otherwise.
|
41
|
+
#
|
42
|
+
def new?
|
43
|
+
!loaded?
|
44
|
+
end
|
45
|
+
alias new_record? new?
|
46
|
+
|
47
|
+
# @api public
|
48
|
+
#
|
49
|
+
# Saves the resource.
|
50
|
+
#
|
51
|
+
# It will PUT to the service to update the resource or send
|
52
|
+
# a POST to create a new one if the resource is new.
|
53
|
+
#
|
54
|
+
# Saving a resource is a synchronous operation.
|
55
|
+
#
|
56
|
+
# @return [Boolean] True if save operation was successful,
|
57
|
+
# false otherwise.
|
58
|
+
# @see #save! See {#save!} for available options.
|
59
|
+
#
|
60
|
+
def save(**opts)
|
61
|
+
save!(**opts)
|
62
|
+
true
|
63
|
+
rescue Acfs::Error
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
# @api public
|
68
|
+
#
|
69
|
+
# Saves the resource. Raises an error if something happens.
|
70
|
+
#
|
71
|
+
# Saving a resource is a synchronous operation.
|
72
|
+
#
|
73
|
+
# @param opts [Hash] Hash with additional options.
|
74
|
+
# @option opts [Hash] :data Data to send to remote service.
|
75
|
+
# Default will be resource attributes.
|
76
|
+
#
|
77
|
+
# @raise [Acfs::InvalidResource]
|
78
|
+
# If remote services respond with 422 response. Will fill
|
79
|
+
# errors with data from response
|
80
|
+
# @raise [Acfs::ErroneousResponse]
|
81
|
+
# If remote service respond with not successful response.
|
82
|
+
#
|
83
|
+
# @see #save
|
84
|
+
#
|
85
|
+
def save!(**opts)
|
86
|
+
opts[:data] = attributes unless opts[:data]
|
87
|
+
|
88
|
+
operation((new? ? :create : :update), **opts) do |data|
|
89
|
+
update_with data
|
90
|
+
end
|
91
|
+
rescue ::Acfs::InvalidResource => e
|
92
|
+
self.remote_errors = e.errors
|
93
|
+
raise e
|
94
|
+
end
|
95
|
+
|
96
|
+
# @api public
|
97
|
+
#
|
98
|
+
# Update attributes with given data and save resource.
|
99
|
+
#
|
100
|
+
# Saving a resource is a synchronous operation.
|
101
|
+
#
|
102
|
+
# @param attrs [Hash] Hash with attributes to write.
|
103
|
+
# @param opts [Hash] Options passed to `save`.
|
104
|
+
#
|
105
|
+
# @return [Boolean]
|
106
|
+
# True if save operation was successful, false otherwise.
|
107
|
+
#
|
108
|
+
# @see #save
|
109
|
+
# @see #attributes=
|
110
|
+
# @see #update_attributes!
|
111
|
+
#
|
112
|
+
def update_attributes(attrs, **opts)
|
113
|
+
check_loaded!(**opts)
|
114
|
+
|
115
|
+
self.attributes = attrs
|
116
|
+
save(**opts)
|
117
|
+
end
|
118
|
+
|
119
|
+
# @api public
|
120
|
+
#
|
121
|
+
# Update attributes with given data and save resource.
|
122
|
+
#
|
123
|
+
# Saving a resource is a synchronous operation.
|
124
|
+
#
|
125
|
+
# @param [Hash] attrs Hash with attributes to write.
|
126
|
+
# @param [Hash] opts Options passed to `save!`.
|
127
|
+
#
|
128
|
+
# @raise [Acfs::InvalidResource]
|
129
|
+
# If remote services respond with 422 response. Will fill
|
130
|
+
# errors with data from response
|
131
|
+
#
|
132
|
+
# @raise [Acfs::ErroneousResponse]
|
133
|
+
# If remote service respond with not successful response.
|
134
|
+
#
|
135
|
+
# @see #save!
|
136
|
+
# @see #attributes=
|
137
|
+
# @see #update_attributes
|
138
|
+
#
|
139
|
+
def update_attributes!(attrs, **opts)
|
140
|
+
check_loaded! opts
|
141
|
+
|
142
|
+
self.attributes = attrs
|
143
|
+
save!(**opts)
|
144
|
+
end
|
145
|
+
|
146
|
+
# @api public
|
147
|
+
#
|
148
|
+
# Destroy resource by sending a DELETE request.
|
149
|
+
#
|
150
|
+
# Deleting a resource is a synchronous operation.
|
151
|
+
#
|
152
|
+
# @return [Boolean]
|
153
|
+
# @see #delete!
|
154
|
+
#
|
155
|
+
def delete(**opts)
|
156
|
+
delete!(**opts)
|
157
|
+
true
|
158
|
+
rescue Acfs::Error
|
159
|
+
false
|
160
|
+
end
|
161
|
+
|
162
|
+
# @api public
|
163
|
+
#
|
164
|
+
# Destroy resource by sending a DELETE request.
|
165
|
+
# Will raise an error in case something goes wrong.
|
166
|
+
#
|
167
|
+
# Deleting a resource is a synchronous operation.
|
168
|
+
|
169
|
+
# @raise [Acfs::ErroneousResponse]
|
170
|
+
# If remote service respond with not successful response.
|
171
|
+
#
|
172
|
+
# @return [undefined]
|
173
|
+
# @see #delete
|
174
|
+
#
|
175
|
+
def delete!(**opts)
|
176
|
+
opts[:params] ||= {}
|
177
|
+
opts[:params] = attributes_for_url(:delete).merge opts[:params]
|
178
|
+
|
179
|
+
operation(:delete, **opts) do |data|
|
180
|
+
update_with data
|
181
|
+
freeze
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def attributes_for_url(action)
|
188
|
+
arguments_for_url = self.class.location(action: action).arguments
|
189
|
+
attributes.slice(*arguments_for_url)
|
190
|
+
end
|
191
|
+
|
192
|
+
module ClassMethods
|
193
|
+
# @api public
|
194
|
+
#
|
195
|
+
# Create a new resource sending given data. If resource cannot be
|
196
|
+
# created an error will be thrown.
|
197
|
+
#
|
198
|
+
# Saving a resource is a synchronous operation.
|
199
|
+
#
|
200
|
+
# @param data [Hash{Symbol, String => Object}]
|
201
|
+
# Data to send in create request.
|
202
|
+
#
|
203
|
+
# @return [self] Newly resource object.
|
204
|
+
#
|
205
|
+
# @raise [Acfs::InvalidResource]
|
206
|
+
# If remote services respond with 422 response. Will fill
|
207
|
+
# errors with data from response
|
208
|
+
#
|
209
|
+
# @raise [Acfs::ErroneousResponse]
|
210
|
+
# If remote service respond with not successful response.
|
211
|
+
#
|
212
|
+
# @see Acfs::Model::Persistence#save! Available options. `:data`
|
213
|
+
# will be overridden with provided data hash.
|
214
|
+
# @see #create
|
215
|
+
#
|
216
|
+
def create!(data, _opts = {})
|
217
|
+
new(data).tap(&:save!)
|
218
|
+
end
|
219
|
+
|
220
|
+
# @api public
|
221
|
+
#
|
222
|
+
# Create a new resource sending given data. If resource cannot be
|
223
|
+
# create model will be returned and error hash contains response
|
224
|
+
# errors if available.
|
225
|
+
#
|
226
|
+
# Saving a resource is a synchronous operation.
|
227
|
+
#
|
228
|
+
# @param data [Hash{Symbol, String => Object}]
|
229
|
+
# Data to send in create request.
|
230
|
+
#
|
231
|
+
# @return [self] Newly resource object.
|
232
|
+
#
|
233
|
+
# @raise [Acfs::ErroneousResponse]
|
234
|
+
# If remote service respond with not successful response.
|
235
|
+
#
|
236
|
+
# @see Acfs::Model::Persistence#save! Available options. `:data`
|
237
|
+
# will be overridden with provided data hash.
|
238
|
+
# @see #create!
|
239
|
+
#
|
240
|
+
def create(data, _opts = {})
|
241
|
+
model = new data
|
242
|
+
model.save
|
243
|
+
model
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def update_with(data)
|
248
|
+
self.attributes = data
|
249
|
+
loaded!
|
250
|
+
end
|
251
|
+
|
252
|
+
def check_loaded!(opts = {})
|
253
|
+
return if loaded? || opts[:force]
|
254
|
+
|
255
|
+
raise ::Acfs::ResourceNotLoaded.new resource: self
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Acfs::Resource
|
4
|
+
# Methods providing the query interface for finding resouces.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class MyUser < Acfs::Resource
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# MyUser.find(5) # Find single resource
|
11
|
+
# MyUser.all # Full or partial collection of
|
12
|
+
# # resources
|
13
|
+
# Comment.where(user: user.id) # Collection with additional parameter
|
14
|
+
# # to filter resources
|
15
|
+
#
|
16
|
+
module QueryMethods
|
17
|
+
extend ActiveSupport::Concern
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
# @api public
|
21
|
+
#
|
22
|
+
# @overload find(id, opts = {})
|
23
|
+
# Find a single resource by given ID.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# user = User.find(5) # Will query `http://base.url/users/5`
|
27
|
+
#
|
28
|
+
# @param id [Fixnum] Resource IDs to fetch from remote service.
|
29
|
+
# @param params [Hash] Additional options.
|
30
|
+
# @option opts [Hash] :params Additional parameters added to
|
31
|
+
# request. `:id` will be overridden with given ID.
|
32
|
+
#
|
33
|
+
# @yield [resource] Callback block to be executed after resource
|
34
|
+
# was fetched successfully.
|
35
|
+
# @yieldparam resource [self] Fetched resources.
|
36
|
+
#
|
37
|
+
# @return [self] Resource object if only one ID was given.
|
38
|
+
#
|
39
|
+
# @overload find(ids, opts = {})
|
40
|
+
# Load collection of specified resources by given IDs.
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# User.find([1, 2, 5]) # Will return collection and will request
|
44
|
+
# # `http://base.url/users/1`,
|
45
|
+
# # `http://base.url/users/2`
|
46
|
+
# # and `http://base.url/users/5` parallel
|
47
|
+
#
|
48
|
+
# @param ids [Array<Integer>] List of resource IDs.
|
49
|
+
# @param opts [Hash] Additional options.
|
50
|
+
# @option opts [Hash] :params Additional parameters added to
|
51
|
+
# request. `:id` will be overridden with individual resource ID.
|
52
|
+
#
|
53
|
+
# @yield [collection] Callback block to be executed after collection
|
54
|
+
# was fetched successfully.
|
55
|
+
# @yieldparam resource [Collection] Collection with fetched resources.
|
56
|
+
#
|
57
|
+
# @return [Collection] Collection of requested resources.
|
58
|
+
#
|
59
|
+
def find(id_or_ids, **opts, &block)
|
60
|
+
if id_or_ids.respond_to? :each
|
61
|
+
find_multiple id_or_ids, opts, &block
|
62
|
+
else
|
63
|
+
find_single id_or_ids, opts, &block
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @api public
|
68
|
+
#
|
69
|
+
# Try to load all resources.
|
70
|
+
#
|
71
|
+
# @param params [Hash] Request parameters that will be send to
|
72
|
+
# remote service.
|
73
|
+
#
|
74
|
+
# @yield [collection] Callback block to be executed when resource
|
75
|
+
# collection was loaded successfully.
|
76
|
+
# @yieldparam collection [Collection] Collection of fetched resources.
|
77
|
+
#
|
78
|
+
# @return [Collection] Collection of requested resources.
|
79
|
+
#
|
80
|
+
def all(params = {}, opts = {}, &block)
|
81
|
+
collection = ::Acfs::Collection.new self
|
82
|
+
collection.__callbacks__ << block if block
|
83
|
+
|
84
|
+
operation(:list, **opts, params: params) do |data, response|
|
85
|
+
data.each {|obj| collection << create_resource(obj) }
|
86
|
+
collection.process_response response
|
87
|
+
collection.loaded!
|
88
|
+
collection.__invoke__
|
89
|
+
end
|
90
|
+
|
91
|
+
collection
|
92
|
+
end
|
93
|
+
alias where all
|
94
|
+
|
95
|
+
# @api public
|
96
|
+
#
|
97
|
+
# Try to load first resource. Return nil if no object can be loaded.
|
98
|
+
#
|
99
|
+
# @param params [Hash] Request parameters that will be send
|
100
|
+
# to remote service.
|
101
|
+
#
|
102
|
+
# @yield [resource] Callback block to be executed after
|
103
|
+
# resource was fetched (even if nil).
|
104
|
+
# @yieldparam resource [self] Fetched resource, nil
|
105
|
+
# if empty list is returned
|
106
|
+
#
|
107
|
+
# @return [self] Resource object, nil if empty list is returned
|
108
|
+
#
|
109
|
+
def find_by(params, &block)
|
110
|
+
Acfs::Util::ResourceDelegator.new(new).tap do |m|
|
111
|
+
m.__callbacks__ << block unless block.nil?
|
112
|
+
operation(:list, params: params) do |data|
|
113
|
+
if data.empty?
|
114
|
+
m.__setobj__ nil
|
115
|
+
else
|
116
|
+
m.__setobj__ create_resource(data.first, origin: m.__getobj__)
|
117
|
+
end
|
118
|
+
m.__invoke__
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# @api public
|
124
|
+
#
|
125
|
+
# Try to load first resource. Raise Acfs::ResourceNotFound
|
126
|
+
# exception if no object can be loaded.
|
127
|
+
#
|
128
|
+
# @param params [Hash] Request parameters that will be send
|
129
|
+
# to remote service.
|
130
|
+
#
|
131
|
+
# @yield [resource] Callback block to be executed after
|
132
|
+
# resource was fetched successfully.
|
133
|
+
# @yieldparam resource [self] Fetched resource, nil
|
134
|
+
# if empty list is returned
|
135
|
+
#
|
136
|
+
# @return [self] Resource object, nil if empty list is returned
|
137
|
+
#
|
138
|
+
def find_by!(params, &block)
|
139
|
+
find_by params do |m|
|
140
|
+
if m.nil?
|
141
|
+
raise Acfs::ResourceNotFound.new message: 'Received erroneous ' \
|
142
|
+
"response: no `#{name}` with params #{params} found"
|
143
|
+
end
|
144
|
+
block&.call m
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# @api public
|
149
|
+
#
|
150
|
+
# Iterates over all pages returned by index action.
|
151
|
+
#
|
152
|
+
# Server must return a paginated resource.
|
153
|
+
#
|
154
|
+
# @example
|
155
|
+
# User.each_page do |page|
|
156
|
+
# p page.size
|
157
|
+
# end
|
158
|
+
# Acfs.run
|
159
|
+
# # => 50
|
160
|
+
# # => 50
|
161
|
+
# # => 42
|
162
|
+
#
|
163
|
+
# @param opts [Hash] Options passed to {#where}.
|
164
|
+
#
|
165
|
+
# @yield [collection] Callback that will be invoked for each page.
|
166
|
+
# @yieldparam collection [Collection] Paginated collection.
|
167
|
+
#
|
168
|
+
# @return [Collection] First page.
|
169
|
+
#
|
170
|
+
def each_page(opts = {})
|
171
|
+
cb = proc do |collection|
|
172
|
+
yield collection
|
173
|
+
collection.next_page(&cb)
|
174
|
+
end
|
175
|
+
where opts, &cb
|
176
|
+
end
|
177
|
+
|
178
|
+
# @api public
|
179
|
+
#
|
180
|
+
# Iterates over all items of all pages returned by index action.
|
181
|
+
#
|
182
|
+
# Server must return a paginated resource.
|
183
|
+
#
|
184
|
+
# @example
|
185
|
+
# index = 0
|
186
|
+
# User.each_item do |page|
|
187
|
+
# index += 1
|
188
|
+
# end
|
189
|
+
# Acfs.run
|
190
|
+
# print index
|
191
|
+
# # => 142
|
192
|
+
#
|
193
|
+
# @param opts [Hash] Options passed to {#each_page}.
|
194
|
+
#
|
195
|
+
# @yield [item] Callback that will be invoked for each item.
|
196
|
+
# @yieldparam item [self] Resource.
|
197
|
+
# @yieldparam collection [Acfs::Collection] Collection.
|
198
|
+
#
|
199
|
+
def each_item(opts = {})
|
200
|
+
each_page(opts) do |collection|
|
201
|
+
collection.each do |item|
|
202
|
+
yield item, collection
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def find_single(id, opts, &block)
|
210
|
+
model = Acfs::Util::ResourceDelegator.new new
|
211
|
+
|
212
|
+
opts[:params] ||= {}
|
213
|
+
opts[:params].merge! id: id unless id.nil?
|
214
|
+
|
215
|
+
model.__callbacks__ << block unless block.nil?
|
216
|
+
|
217
|
+
operation(:read, **opts) do |data|
|
218
|
+
model.__setobj__ create_resource data, origin: model.__getobj__
|
219
|
+
model.__invoke__
|
220
|
+
end
|
221
|
+
|
222
|
+
model
|
223
|
+
end
|
224
|
+
|
225
|
+
def find_multiple(ids, opts, &block)
|
226
|
+
::Acfs::Collection.new(self).tap do |collection|
|
227
|
+
collection.__callbacks__ << block unless block.nil?
|
228
|
+
|
229
|
+
counter = 0
|
230
|
+
ids.each_with_index do |id, index|
|
231
|
+
find_single(id, opts) do |resource|
|
232
|
+
collection[index] = resource
|
233
|
+
if (counter += 1) == ids.size
|
234
|
+
collection.loaded!
|
235
|
+
collection.__invoke__
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def create_resource(data, **opts)
|
243
|
+
type = data.delete 'type'
|
244
|
+
klass = resource_class_lookup(type)
|
245
|
+
(opts[:origin].is_a?(klass) ? opts[:origin] : klass.new).tap do |m|
|
246
|
+
m.write_attributes(data, **opts)
|
247
|
+
m.loaded!
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def resource_class_lookup(type)
|
252
|
+
return self if type.nil?
|
253
|
+
|
254
|
+
klass = type.camelize.constantize
|
255
|
+
|
256
|
+
unless klass <= self
|
257
|
+
raise Acfs::ResourceTypeError.new type_name: type, base_class: self
|
258
|
+
end
|
259
|
+
|
260
|
+
klass
|
261
|
+
rescue NameError
|
262
|
+
raise Acfs::ResourceTypeError.new type_name: type, base_class: self
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|