acfs 1.4.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +22 -39
- data/acfs.gemspec +8 -14
- data/lib/acfs/adapter/base.rb +2 -0
- data/lib/acfs/adapter/typhoeus.rb +16 -11
- data/lib/acfs/collections/paginatable.rb +1 -1
- data/lib/acfs/configuration.rb +13 -3
- data/lib/acfs/errors.rb +41 -21
- data/lib/acfs/global.rb +2 -2
- data/lib/acfs/location.rb +26 -32
- data/lib/acfs/middleware/base.rb +2 -2
- data/lib/acfs/middleware/json.rb +4 -2
- data/lib/acfs/middleware/logger.rb +4 -6
- data/lib/acfs/middleware/serializer.rb +1 -1
- data/lib/acfs/operation.rb +21 -8
- data/lib/acfs/request/callbacks.rb +4 -4
- data/lib/acfs/request.rb +4 -11
- data/lib/acfs/resource/attributes/date_time.rb +1 -1
- data/lib/acfs/resource/attributes/uuid.rb +1 -1
- data/lib/acfs/resource/attributes.rb +16 -15
- data/lib/acfs/resource/dirty.rb +2 -2
- data/lib/acfs/resource/initialization.rb +5 -5
- data/lib/acfs/resource/locatable.rb +11 -8
- data/lib/acfs/resource/operational.rb +6 -3
- data/lib/acfs/resource/persistence.rb +13 -15
- data/lib/acfs/resource/query_methods.rb +10 -10
- data/lib/acfs/resource/service.rb +2 -2
- data/lib/acfs/resource/validation.rb +17 -7
- data/lib/acfs/response.rb +5 -5
- data/lib/acfs/runner.rb +15 -15
- data/lib/acfs/service.rb +16 -19
- data/lib/acfs/singleton_resource.rb +2 -2
- data/lib/acfs/stub.rb +41 -31
- data/lib/acfs/version.rb +2 -2
- data/spec/acfs/adapter/typhoeus_spec.rb +2 -2
- data/spec/acfs/collection_spec.rb +66 -41
- data/spec/acfs/configuration_spec.rb +22 -12
- data/spec/acfs/global_spec.rb +11 -9
- data/spec/acfs/location_spec.rb +2 -2
- data/spec/acfs/middleware/json_spec.rb +22 -8
- data/spec/acfs/middleware/{msgpack_spec.rb → message_pack_spec.rb} +6 -6
- data/spec/acfs/operation_spec.rb +3 -2
- data/spec/acfs/request/callbacks_spec.rb +19 -10
- data/spec/acfs/request_spec.rb +16 -20
- data/spec/acfs/resource/attributes/boolean_spec.rb +32 -32
- data/spec/acfs/resource/attributes/date_time_spec.rb +16 -8
- data/spec/acfs/resource/attributes/dict_spec.rb +15 -9
- data/spec/acfs/resource/attributes/float_spec.rb +20 -10
- data/spec/acfs/resource/attributes/integer_spec.rb +10 -5
- data/spec/acfs/resource/attributes/list_spec.rb +13 -8
- data/spec/acfs/resource/attributes/uuid_spec.rb +12 -6
- data/spec/acfs/resource/attributes_spec.rb +37 -38
- data/spec/acfs/resource/dirty_spec.rb +6 -3
- data/spec/acfs/resource/initialization_spec.rb +4 -5
- data/spec/acfs/resource/loadable_spec.rb +3 -1
- data/spec/acfs/resource/locatable_spec.rb +24 -18
- data/spec/acfs/resource/{persistance_spec.rb → persistence_spec.rb} +122 -90
- data/spec/acfs/resource/query_methods_spec.rb +143 -110
- data/spec/acfs/resource/validation_spec.rb +34 -27
- data/spec/acfs/response/formats_spec.rb +8 -8
- data/spec/acfs/response/status_spec.rb +16 -9
- data/spec/acfs/runner_spec.rb +10 -8
- data/spec/acfs/service/middleware_spec.rb +3 -3
- data/spec/acfs/service_spec.rb +6 -5
- data/spec/acfs/singleton_resource_spec.rb +2 -1
- data/spec/acfs/stub_spec.rb +57 -53
- data/spec/acfs_spec.rb +111 -93
- data/spec/spec_helper.rb +1 -2
- data/spec/support/response.rb +2 -2
- data/spec/support/service.rb +1 -1
- data/spec/support/shared/find_callbacks.rb +14 -10
- metadata +30 -29
data/lib/acfs/operation.rb
CHANGED
@@ -8,10 +8,11 @@ module Acfs
|
|
8
8
|
#
|
9
9
|
class Operation
|
10
10
|
attr_reader :action, :params, :resource, :data, :callback, :location, :url
|
11
|
+
|
11
12
|
delegate :service, to: :resource
|
12
13
|
delegate :call, to: :callback
|
13
14
|
|
14
|
-
def initialize(resource, action, opts
|
15
|
+
def initialize(resource, action, **opts, &block)
|
15
16
|
@resource = resource
|
16
17
|
@action = action.to_sym
|
17
18
|
|
@@ -20,9 +21,7 @@ module Acfs
|
|
20
21
|
@params = (opts[:params] || {}).dup
|
21
22
|
@data = (opts[:data] || {}).dup
|
22
23
|
|
23
|
-
|
24
|
-
@url = opts[:url]
|
25
|
-
else
|
24
|
+
unless (@url = opts[:url])
|
26
25
|
@location = resource.location(action: @action).extract_from(@params, @data)
|
27
26
|
@url = location.str
|
28
27
|
end
|
@@ -44,11 +43,11 @@ module Acfs
|
|
44
43
|
end
|
45
44
|
|
46
45
|
def full_params
|
47
|
-
(id ? params.merge(id: id) : params).merge
|
46
|
+
(id ? params.merge(id: id) : params).merge(location_vars)
|
48
47
|
end
|
49
48
|
|
50
|
-
def
|
51
|
-
location ? location.
|
49
|
+
def location_vars
|
50
|
+
location ? location.vars : {}
|
52
51
|
end
|
53
52
|
|
54
53
|
def method
|
@@ -57,7 +56,7 @@ module Acfs
|
|
57
56
|
|
58
57
|
def request
|
59
58
|
request = ::Acfs::Request.new url, method: method, params: params,
|
60
|
-
|
59
|
+
data: data, operation: self
|
61
60
|
request.on_complete do |response|
|
62
61
|
::ActiveSupport::Notifications.instrument 'acfs.operation.complete',
|
63
62
|
operation: self,
|
@@ -71,10 +70,24 @@ module Acfs
|
|
71
70
|
|
72
71
|
def handle_failure(response)
|
73
72
|
case response.code
|
73
|
+
when 400
|
74
|
+
raise ::Acfs::BadRequest.new response: response
|
75
|
+
when 401
|
76
|
+
raise ::Acfs::Unauthorized.new response: response
|
77
|
+
when 403
|
78
|
+
raise ::Acfs::Forbidden.new response: response
|
74
79
|
when 404
|
75
80
|
raise ::Acfs::ResourceNotFound.new response: response
|
76
81
|
when 422
|
77
82
|
raise ::Acfs::InvalidResource.new response: response, errors: response.data.try(:[], 'errors')
|
83
|
+
when 500
|
84
|
+
raise ::Acfs::ServerError.new response: response
|
85
|
+
when 502
|
86
|
+
raise ::Acfs::BadGateway.new response: response
|
87
|
+
when 503
|
88
|
+
raise ::Acfs::ServiceUnavailable.new response: response
|
89
|
+
when 504
|
90
|
+
raise ::Acfs::GatewayTimeout.new response: response
|
78
91
|
else
|
79
92
|
raise ::Acfs::ErroneousResponse.new response: response
|
80
93
|
end
|
@@ -21,7 +21,7 @@ module Acfs
|
|
21
21
|
# @return [ Acfs::Request ] The request itself.
|
22
22
|
#
|
23
23
|
def on_complete(&block)
|
24
|
-
callbacks.insert 0, block if
|
24
|
+
callbacks.insert 0, block if block
|
25
25
|
self
|
26
26
|
end
|
27
27
|
|
@@ -45,9 +45,9 @@ module Acfs
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def call_callback(res, index)
|
48
|
-
if index
|
49
|
-
|
50
|
-
|
48
|
+
return if index >= callbacks.size
|
49
|
+
|
50
|
+
callbacks[index].call(res, proc {|bres| call_callback bres, index + 1 })
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
data/lib/acfs/request.rb
CHANGED
@@ -11,8 +11,7 @@ module Acfs
|
|
11
11
|
attr_reader :url, :headers, :params, :data, :method, :operation
|
12
12
|
|
13
13
|
include Request::Callbacks
|
14
|
-
|
15
|
-
def initialize(url, options = {}, &block)
|
14
|
+
def initialize(url, **options, &block)
|
16
15
|
@url = URI.parse(url.to_s).tap do |_url|
|
17
16
|
@data = options.delete(:data) || nil
|
18
17
|
@format = options.delete(:format) || :json
|
@@ -20,20 +19,14 @@ module Acfs
|
|
20
19
|
@params = options.delete(:params) || {}
|
21
20
|
@method = options.delete(:method) || :get
|
22
21
|
end.to_s
|
22
|
+
|
23
23
|
@operation = options.delete(:operation) || nil
|
24
|
-
|
24
|
+
|
25
|
+
on_complete(&block) if block
|
25
26
|
end
|
26
27
|
|
27
28
|
def data?
|
28
29
|
!data.nil?
|
29
30
|
end
|
30
|
-
|
31
|
-
class << self
|
32
|
-
def new(*attrs)
|
33
|
-
return attrs[0] if attrs[0].is_a? self
|
34
|
-
|
35
|
-
super
|
36
|
-
end
|
37
|
-
end
|
38
31
|
end
|
39
32
|
end
|
@@ -22,7 +22,7 @@ module Acfs::Resource::Attributes
|
|
22
22
|
def cast_value(value)
|
23
23
|
if value.blank?
|
24
24
|
nil
|
25
|
-
elsif value.
|
25
|
+
elsif !value.is_a?(::String) && value.respond_to?(:to_datetime)
|
26
26
|
value.to_datetime
|
27
27
|
else
|
28
28
|
::DateTime.iso8601 value
|
@@ -48,9 +48,11 @@ class Acfs::Resource
|
|
48
48
|
# @return [HashWithIndifferentAccess{Symbol => Object}]
|
49
49
|
# Attributes and their values.
|
50
50
|
#
|
51
|
+
# rubocop:disable Naming/MemoizedInstanceVariableName
|
51
52
|
def attributes
|
52
53
|
@_attrs ||= HashWithIndifferentAccess.new
|
53
54
|
end
|
55
|
+
# rubocop:enable Naming/MemoizedInstanceVariableName
|
54
56
|
|
55
57
|
# @api public
|
56
58
|
#
|
@@ -66,7 +68,7 @@ class Acfs::Resource
|
|
66
68
|
# @see #write_attributes Delegates attributes hash to {#write_attributes}.
|
67
69
|
#
|
68
70
|
def attributes=(attributes)
|
69
|
-
write_attributes
|
71
|
+
write_attributes(attributes)
|
70
72
|
end
|
71
73
|
|
72
74
|
# @api public
|
@@ -101,17 +103,16 @@ class Acfs::Resource
|
|
101
103
|
#
|
102
104
|
# @see #write_attribute Delegates attribute values to `#write_attribute`.
|
103
105
|
#
|
104
|
-
def write_attributes(attributes, opts
|
106
|
+
def write_attributes(attributes, **opts)
|
105
107
|
unless attributes.respond_to?(:each) && attributes.respond_to?(:keys)
|
106
108
|
return false
|
107
109
|
end
|
108
110
|
|
109
|
-
if opts.fetch(:unknown, :ignore) == :raise
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
end
|
111
|
+
if opts.fetch(:unknown, :ignore) == :raise &&
|
112
|
+
(attributes.keys.map(&:to_s) - self.class.attributes.keys).any?
|
113
|
+
missing = attributes.keys - self.class.attributes.keys
|
114
|
+
missing.map!(&:inspect)
|
115
|
+
raise ArgumentError.new "Unknown attributes: #{missing.join(', ')}"
|
115
116
|
end
|
116
117
|
|
117
118
|
procs = {}
|
@@ -120,12 +121,12 @@ class Acfs::Resource
|
|
120
121
|
if attributes[key].is_a? Proc
|
121
122
|
procs[key] = attributes[key]
|
122
123
|
else
|
123
|
-
write_local_attribute
|
124
|
+
write_local_attribute(key, attributes[key], **opts)
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
127
128
|
procs.each do |key, proc|
|
128
|
-
write_local_attribute
|
129
|
+
write_local_attribute(key, instance_exec(&proc), **opts)
|
129
130
|
end
|
130
131
|
|
131
132
|
true
|
@@ -199,12 +200,12 @@ class Acfs::Resource
|
|
199
200
|
# @param [Symbol, String, Class] type Attribute
|
200
201
|
# type identifier or type class.
|
201
202
|
#
|
202
|
-
def attribute(name, type, opts
|
203
|
+
def attribute(name, type, **opts)
|
203
204
|
if type.is_a?(Symbol) || type.is_a?(String)
|
204
205
|
type = "#{ATTR_CLASS_BASE}::#{type.to_s.classify}".constantize
|
205
206
|
end
|
206
207
|
|
207
|
-
define_attribute
|
208
|
+
define_attribute(name.to_sym, type, **opts)
|
208
209
|
end
|
209
210
|
|
210
211
|
# @api public
|
@@ -242,9 +243,9 @@ class Acfs::Resource
|
|
242
243
|
@local_attributes ||= {}
|
243
244
|
end
|
244
245
|
|
245
|
-
def define_attribute(name, type, opts
|
246
|
+
def define_attribute(name, type, **opts)
|
246
247
|
name = name.to_s
|
247
|
-
attribute = type.new
|
248
|
+
attribute = type.new(**opts)
|
248
249
|
|
249
250
|
local_attributes[name] = attribute
|
250
251
|
define_attribute_method name
|
@@ -263,7 +264,7 @@ end
|
|
263
264
|
|
264
265
|
# Load attribute type classes.
|
265
266
|
#
|
266
|
-
Dir[File.
|
267
|
+
Dir[File.join(__dir__, 'attributes/*.rb')].sort.each do |path|
|
267
268
|
filename = File.basename(path)
|
268
269
|
require "acfs/resource/attributes/#{filename}"
|
269
270
|
end
|
data/lib/acfs/resource/dirty.rb
CHANGED
@@ -8,7 +8,7 @@ class Acfs::Resource
|
|
8
8
|
#
|
9
9
|
# @api public
|
10
10
|
#
|
11
|
-
# Initializes a new model with the given `params`.
|
11
|
+
# Initializes a new model with the given `params`.
|
12
12
|
#
|
13
13
|
# @example
|
14
14
|
# class User < Acfs::Resource
|
@@ -17,15 +17,15 @@ class Acfs::Resource
|
|
17
17
|
# attribute :age, :integer, default: 18
|
18
18
|
# end
|
19
19
|
#
|
20
|
-
# user = User.new(name: 'bob')
|
20
|
+
# user = User.new({name: 'bob'})
|
21
21
|
# user.name # => "bob"
|
22
22
|
# user.email # => "bob@dom.tld"
|
23
23
|
# user.age # => 18
|
24
24
|
#
|
25
|
-
# @param
|
25
|
+
# @param attributes [Hash{Symbol => Object}] Attributes to set on resource.
|
26
26
|
#
|
27
|
-
def initialize(
|
28
|
-
write_attributes
|
27
|
+
def initialize(attributes = {})
|
28
|
+
write_attributes(attributes) if attributes
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -40,15 +40,18 @@ class Acfs::Resource
|
|
40
40
|
# usually `:list`, `:create`, `:read`, `:update` or`:delete`.
|
41
41
|
# @return [String] Generated URL.
|
42
42
|
#
|
43
|
-
def url(suffix = nil, opts
|
43
|
+
def url(suffix = nil, **opts)
|
44
44
|
if suffix.is_a? Hash
|
45
45
|
opts = suffix
|
46
46
|
suffix = nil
|
47
47
|
end
|
48
48
|
|
49
|
-
|
49
|
+
kwargs = {}
|
50
|
+
kwargs[:path] = opts[:path] if opts.key?(:path)
|
51
|
+
kwargs[:action] = opts.delete(:action) if opts.key?(:action)
|
52
|
+
kwargs[:action] = :list if suffix
|
50
53
|
|
51
|
-
url = location(
|
54
|
+
url = location(**kwargs).build(opts.stringify_keys).str
|
52
55
|
url += "/#{suffix}" if suffix.to_s.present?
|
53
56
|
url
|
54
57
|
end
|
@@ -81,8 +84,8 @@ class Acfs::Resource
|
|
81
84
|
#
|
82
85
|
# @return [Location] Location object.
|
83
86
|
#
|
84
|
-
def location(opts
|
85
|
-
service.location(self, opts)
|
87
|
+
def location(**opts)
|
88
|
+
service.location(self, **opts)
|
86
89
|
end
|
87
90
|
|
88
91
|
# @api private
|
@@ -109,12 +112,12 @@ class Acfs::Resource
|
|
109
112
|
# @return [ String ] Generated URL.
|
110
113
|
# @see ClassMethods#url
|
111
114
|
#
|
112
|
-
def url(opts
|
115
|
+
def url(**opts)
|
113
116
|
return nil if need_primary_key? && !primary_key?
|
114
117
|
|
115
118
|
self.class.service
|
116
|
-
|
117
|
-
|
119
|
+
.location(self.class, **opts, action: :read)
|
120
|
+
.build(attributes).str
|
118
121
|
end
|
119
122
|
|
120
123
|
# @api private
|
@@ -11,12 +11,15 @@ class Acfs::Resource
|
|
11
11
|
#
|
12
12
|
module Operational
|
13
13
|
extend ActiveSupport::Concern
|
14
|
-
|
14
|
+
|
15
|
+
def operation(*args, **kwargs, &block)
|
16
|
+
self.class.operation(*args, **kwargs, &block)
|
17
|
+
end
|
15
18
|
|
16
19
|
module ClassMethods
|
17
20
|
# Invoke CRUD operation.
|
18
|
-
def operation(action, opts
|
19
|
-
Acfs.runner.process ::Acfs::Operation.new
|
21
|
+
def operation(action, **opts, &block)
|
22
|
+
Acfs.runner.process ::Acfs::Operation.new(self, action, **opts, &block)
|
20
23
|
end
|
21
24
|
end
|
22
25
|
end
|
@@ -57,8 +57,8 @@ class Acfs::Resource
|
|
57
57
|
# false otherwise.
|
58
58
|
# @see #save! See {#save!} for available options.
|
59
59
|
#
|
60
|
-
def save(
|
61
|
-
save!(
|
60
|
+
def save(**opts)
|
61
|
+
save!(**opts)
|
62
62
|
true
|
63
63
|
rescue Acfs::Error
|
64
64
|
false
|
@@ -82,10 +82,10 @@ class Acfs::Resource
|
|
82
82
|
#
|
83
83
|
# @see #save
|
84
84
|
#
|
85
|
-
def save!(opts
|
85
|
+
def save!(**opts)
|
86
86
|
opts[:data] = attributes unless opts[:data]
|
87
87
|
|
88
|
-
operation((new? ? :create : :update), opts) do |data|
|
88
|
+
operation((new? ? :create : :update), **opts) do |data|
|
89
89
|
update_with data
|
90
90
|
end
|
91
91
|
rescue ::Acfs::InvalidResource => e
|
@@ -109,11 +109,11 @@ class Acfs::Resource
|
|
109
109
|
# @see #attributes=
|
110
110
|
# @see #update_attributes!
|
111
111
|
#
|
112
|
-
def update_attributes(attrs, opts
|
113
|
-
check_loaded!
|
112
|
+
def update_attributes(attrs, **opts)
|
113
|
+
check_loaded!(**opts)
|
114
114
|
|
115
115
|
self.attributes = attrs
|
116
|
-
save
|
116
|
+
save(**opts)
|
117
117
|
end
|
118
118
|
|
119
119
|
# @api public
|
@@ -136,11 +136,11 @@ class Acfs::Resource
|
|
136
136
|
# @see #attributes=
|
137
137
|
# @see #update_attributes
|
138
138
|
#
|
139
|
-
def update_attributes!(attrs, opts
|
139
|
+
def update_attributes!(attrs, **opts)
|
140
140
|
check_loaded! opts
|
141
141
|
|
142
142
|
self.attributes = attrs
|
143
|
-
save!
|
143
|
+
save!(**opts)
|
144
144
|
end
|
145
145
|
|
146
146
|
# @api public
|
@@ -152,8 +152,8 @@ class Acfs::Resource
|
|
152
152
|
# @return [Boolean]
|
153
153
|
# @see #delete!
|
154
154
|
#
|
155
|
-
def delete(opts
|
156
|
-
delete!
|
155
|
+
def delete(**opts)
|
156
|
+
delete!(**opts)
|
157
157
|
true
|
158
158
|
rescue Acfs::Error
|
159
159
|
false
|
@@ -172,11 +172,11 @@ class Acfs::Resource
|
|
172
172
|
# @return [undefined]
|
173
173
|
# @see #delete
|
174
174
|
#
|
175
|
-
def delete!(opts
|
175
|
+
def delete!(**opts)
|
176
176
|
opts[:params] ||= {}
|
177
177
|
opts[:params] = attributes_for_url(:delete).merge opts[:params]
|
178
178
|
|
179
|
-
operation
|
179
|
+
operation(:delete, **opts) do |data|
|
180
180
|
update_with data
|
181
181
|
freeze
|
182
182
|
end
|
@@ -244,8 +244,6 @@ class Acfs::Resource
|
|
244
244
|
end
|
245
245
|
end
|
246
246
|
|
247
|
-
private
|
248
|
-
|
249
247
|
def update_with(data)
|
250
248
|
self.attributes = data
|
251
249
|
loaded!
|
@@ -56,7 +56,7 @@ class Acfs::Resource
|
|
56
56
|
#
|
57
57
|
# @return [Collection] Collection of requested resources.
|
58
58
|
#
|
59
|
-
def find(id_or_ids, opts
|
59
|
+
def find(id_or_ids, **opts, &block)
|
60
60
|
if id_or_ids.respond_to? :each
|
61
61
|
find_multiple id_or_ids, opts, &block
|
62
62
|
else
|
@@ -81,7 +81,7 @@ class Acfs::Resource
|
|
81
81
|
collection = ::Acfs::Collection.new self
|
82
82
|
collection.__callbacks__ << block if block
|
83
83
|
|
84
|
-
operation
|
84
|
+
operation(:list, **opts, params: params) do |data, response|
|
85
85
|
data.each {|obj| collection << create_resource(obj) }
|
86
86
|
collection.process_response response
|
87
87
|
collection.loaded!
|
@@ -109,7 +109,7 @@ class Acfs::Resource
|
|
109
109
|
def find_by(params, &block)
|
110
110
|
Acfs::Util::ResourceDelegator.new(new).tap do |m|
|
111
111
|
m.__callbacks__ << block unless block.nil?
|
112
|
-
operation
|
112
|
+
operation(:list, params: params) do |data|
|
113
113
|
if data.empty?
|
114
114
|
m.__setobj__ nil
|
115
115
|
else
|
@@ -139,7 +139,7 @@ class Acfs::Resource
|
|
139
139
|
find_by params do |m|
|
140
140
|
if m.nil?
|
141
141
|
raise Acfs::ResourceNotFound.new message: 'Received erroneous ' \
|
142
|
-
|
142
|
+
"response: no `#{name}` with params #{params} found"
|
143
143
|
end
|
144
144
|
block&.call m
|
145
145
|
end
|
@@ -210,11 +210,11 @@ class Acfs::Resource
|
|
210
210
|
model = Acfs::Util::ResourceDelegator.new new
|
211
211
|
|
212
212
|
opts[:params] ||= {}
|
213
|
-
opts[:params]
|
213
|
+
opts[:params][:id] = id unless id.nil?
|
214
214
|
|
215
215
|
model.__callbacks__ << block unless block.nil?
|
216
216
|
|
217
|
-
operation
|
217
|
+
operation(:read, **opts) do |data|
|
218
218
|
model.__setobj__ create_resource data, origin: model.__getobj__
|
219
219
|
model.__invoke__
|
220
220
|
end
|
@@ -228,7 +228,7 @@ class Acfs::Resource
|
|
228
228
|
|
229
229
|
counter = 0
|
230
230
|
ids.each_with_index do |id, index|
|
231
|
-
find_single
|
231
|
+
find_single(id, opts) do |resource|
|
232
232
|
collection[index] = resource
|
233
233
|
if (counter += 1) == ids.size
|
234
234
|
collection.loaded!
|
@@ -239,11 +239,11 @@ class Acfs::Resource
|
|
239
239
|
end
|
240
240
|
end
|
241
241
|
|
242
|
-
def create_resource(data, opts
|
242
|
+
def create_resource(data, **opts)
|
243
243
|
type = data.delete 'type'
|
244
244
|
klass = resource_class_lookup(type)
|
245
245
|
(opts[:origin].is_a?(klass) ? opts[:origin] : klass.new).tap do |m|
|
246
|
-
m.write_attributes
|
246
|
+
m.write_attributes(data, **opts)
|
247
247
|
m.loaded!
|
248
248
|
end
|
249
249
|
end
|
@@ -258,7 +258,7 @@ class Acfs::Resource
|
|
258
258
|
end
|
259
259
|
|
260
260
|
klass
|
261
|
-
rescue NameError
|
261
|
+
rescue NameError
|
262
262
|
raise Acfs::ResourceTypeError.new type_name: type, base_class: self
|
263
263
|
end
|
264
264
|
end
|
@@ -34,8 +34,8 @@ class Acfs::Resource
|
|
34
34
|
# @param options [Object] Option delegated to
|
35
35
|
# service class initializer.
|
36
36
|
#
|
37
|
-
def service(klass = nil, options
|
38
|
-
return (@service = klass.new
|
37
|
+
def service(klass = nil, **options)
|
38
|
+
return (@service = klass.new(**options)) if klass
|
39
39
|
|
40
40
|
@service || superclass.service
|
41
41
|
end
|
@@ -2,12 +2,6 @@
|
|
2
2
|
|
3
3
|
class Acfs::Resource
|
4
4
|
module Validation
|
5
|
-
def valid?(*args)
|
6
|
-
super
|
7
|
-
remote_errors.each {|f, e| errors.add f, e }
|
8
|
-
errors.empty?
|
9
|
-
end
|
10
|
-
|
11
5
|
def remote_errors
|
12
6
|
@remote_errors ||= ActiveModel::Errors.new self
|
13
7
|
end
|
@@ -28,12 +22,28 @@ class Acfs::Resource
|
|
28
22
|
end
|
29
23
|
end
|
30
24
|
|
31
|
-
def save!(
|
25
|
+
def save!(**kwargs)
|
32
26
|
unless valid?(new? ? :create : :save)
|
33
27
|
raise ::Acfs::InvalidResource.new resource: self, errors: errors.to_a
|
34
28
|
end
|
35
29
|
|
36
30
|
super
|
37
31
|
end
|
32
|
+
|
33
|
+
if ::ActiveModel.version >= Gem::Version.new('6.1')
|
34
|
+
def valid?(*args)
|
35
|
+
super
|
36
|
+
|
37
|
+
remote_errors.each {|e| errors.add(e.attribute, e.message) }
|
38
|
+
errors.empty?
|
39
|
+
end
|
40
|
+
else
|
41
|
+
def valid?(*args)
|
42
|
+
super
|
43
|
+
|
44
|
+
remote_errors.each {|f, e| errors.add(f, e) }
|
45
|
+
errors.empty?
|
46
|
+
end
|
47
|
+
end
|
38
48
|
end
|
39
49
|
end
|
data/lib/acfs/response.rb
CHANGED
@@ -19,12 +19,12 @@ module Acfs
|
|
19
19
|
# :response_body, :response_headers, :response_code, :headers,
|
20
20
|
# to: :response
|
21
21
|
|
22
|
-
def initialize(request,
|
22
|
+
def initialize(request, **opts)
|
23
23
|
@request = request
|
24
|
-
@status =
|
25
|
-
@headers =
|
26
|
-
@body =
|
27
|
-
@data =
|
24
|
+
@status = opts[:status] || 0
|
25
|
+
@headers = opts[:headers] || {}
|
26
|
+
@body = opts[:body] || ''
|
27
|
+
@data = opts[:data] || nil
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
data/lib/acfs/runner.rb
CHANGED
@@ -17,16 +17,16 @@ module Acfs
|
|
17
17
|
# Process an operation. Synchronous operations will be run
|
18
18
|
# and parallel operations will be queued.
|
19
19
|
#
|
20
|
-
def process(
|
21
|
-
::ActiveSupport::Notifications.instrument
|
22
|
-
|
20
|
+
def process(operation)
|
21
|
+
::ActiveSupport::Notifications.instrument('acfs.operation.before_process', operation: operation)
|
22
|
+
operation.synchronous? ? run(operation) : enqueue(operation)
|
23
23
|
end
|
24
24
|
|
25
25
|
# Run operation right now skipping queue.
|
26
26
|
#
|
27
|
-
def run(
|
28
|
-
::ActiveSupport::Notifications.instrument
|
29
|
-
|
27
|
+
def run(operation)
|
28
|
+
::ActiveSupport::Notifications.instrument('acfs.runner.sync_run', operation: operation) do
|
29
|
+
operation_request(operation) {|req| adapter.run req }
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -38,12 +38,12 @@ module Acfs
|
|
38
38
|
|
39
39
|
# Enqueue operation to be run later.
|
40
40
|
#
|
41
|
-
def enqueue(
|
42
|
-
::ActiveSupport::Notifications.instrument
|
41
|
+
def enqueue(operation)
|
42
|
+
::ActiveSupport::Notifications.instrument('acfs.runner.enqueue', operation: operation) do
|
43
43
|
if running?
|
44
|
-
|
44
|
+
operation_request(operation) {|req| adapter.queue req }
|
45
45
|
else
|
46
|
-
queue <<
|
46
|
+
queue << operation
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
@@ -82,15 +82,15 @@ module Acfs
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def enqueue_operations
|
85
|
-
while (
|
86
|
-
|
85
|
+
while (operation = queue.shift)
|
86
|
+
operation_request(operation) {|req| adapter.queue req }
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
def
|
91
|
-
return if Acfs::Stub.enabled? && Acfs::Stub.stubbed(
|
90
|
+
def operation_request(operation)
|
91
|
+
return if Acfs::Stub.enabled? && Acfs::Stub.stubbed(operation)
|
92
92
|
|
93
|
-
req =
|
93
|
+
req = operation.service.prepare(operation.request)
|
94
94
|
return unless req.is_a? Acfs::Request
|
95
95
|
|
96
96
|
req = prepare req
|