acfs 1.5.1 → 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 +8 -0
- data/acfs.gemspec +2 -0
- data/lib/acfs/adapter/base.rb +2 -0
- data/lib/acfs/adapter/typhoeus.rb +6 -3
- data/lib/acfs/collections/paginatable.rb +3 -3
- data/lib/acfs/errors.rb +9 -9
- data/lib/acfs/location.rb +24 -30
- data/lib/acfs/middleware/base.rb +2 -2
- data/lib/acfs/middleware/logger.rb +4 -6
- data/lib/acfs/middleware/serializer.rb +1 -1
- data/lib/acfs/operation.rb +5 -7
- data/lib/acfs/request.rb +1 -9
- data/lib/acfs/resource/attributes.rb +12 -13
- data/lib/acfs/resource/dirty.rb +2 -2
- data/lib/acfs/resource/initialization.rb +5 -5
- data/lib/acfs/resource/locatable.rb +10 -7
- data/lib/acfs/resource/operational.rb +6 -3
- data/lib/acfs/resource/persistence.rb +13 -13
- data/lib/acfs/resource/query_methods.rb +7 -7
- data/lib/acfs/resource/service.rb +2 -2
- data/lib/acfs/resource/validation.rb +1 -1
- data/lib/acfs/response.rb +5 -5
- data/lib/acfs/service.rb +11 -19
- data/lib/acfs/singleton_resource.rb +2 -2
- data/lib/acfs/stub.rb +12 -6
- data/lib/acfs/version.rb +2 -2
- data/spec/acfs/adapter/typhoeus_spec.rb +1 -1
- data/spec/acfs/location_spec.rb +2 -2
- data/spec/acfs/request_spec.rb +1 -1
- data/spec/acfs/resource/attributes_spec.rb +3 -3
- data/spec/acfs/resource/persistance_spec.rb +11 -11
- data/spec/acfs/service_spec.rb +1 -1
- data/spec/acfs_spec.rb +1 -1
- data/spec/spec_helper.rb +0 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46dc13b234cd5b704b67a731292b06ad8b32ee3fafb0a7a0af2a1003824e6df8
|
4
|
+
data.tar.gz: a35249f8e2a7a6cc49351b62c75b3fe641b26691132f4fb70b0a183ab041a380
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07132ad9797084fccd7fdeb31cc9f51572ddda1eb9dc88aaf875cd244000d15823223d3c6c503f0af7ac4cc1f59cb0b748eea75d8592323b807001e82c93d781
|
7
|
+
data.tar.gz: 6d6d15b6841820ae2ee672eec742b417113523ce613f1a7c7d493eeed50232b0915d7edcbf3df1b7060c5ced484bb82f59904e34fe6e17f70babe3b5ddf0a4e7
|
data/CHANGELOG.md
CHANGED
data/acfs.gemspec
CHANGED
@@ -25,6 +25,8 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
26
26
|
spec.require_paths = %w[lib]
|
27
27
|
|
28
|
+
spec.required_ruby_version = '>= 2.5.0'
|
29
|
+
|
28
30
|
spec.add_runtime_dependency 'actionpack', '>= 5.2'
|
29
31
|
spec.add_runtime_dependency 'activemodel', '>= 5.2'
|
30
32
|
spec.add_runtime_dependency 'activesupport', '>= 5.2'
|
data/lib/acfs/adapter/base.rb
CHANGED
@@ -13,8 +13,11 @@ module Acfs
|
|
13
13
|
# Adapter for Typhoeus.
|
14
14
|
#
|
15
15
|
class Typhoeus < Base
|
16
|
-
def initialize(
|
17
|
-
|
16
|
+
def initialize(**kwargs)
|
17
|
+
super
|
18
|
+
|
19
|
+
@opts = DEFAULT_OPTIONS
|
20
|
+
@opts = @opts.merge(opts) if (opts = kwargs.delete(:opts))
|
18
21
|
@kwargs = kwargs
|
19
22
|
end
|
20
23
|
|
@@ -52,7 +55,7 @@ module Acfs
|
|
52
55
|
body: req.body
|
53
56
|
}
|
54
57
|
|
55
|
-
request = ::Typhoeus::Request.new(req.url, **@opts
|
58
|
+
request = ::Typhoeus::Request.new(req.url, **@opts, **opts)
|
56
59
|
|
57
60
|
request.on_complete do |response|
|
58
61
|
raise ::Acfs::TimeoutError.new(req) if response.timed_out?
|
@@ -5,7 +5,7 @@ module Acfs::Collections
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
def self.operation(_action, opts
|
8
|
+
def self.operation(_action, **opts, &_block)
|
9
9
|
opts[:url]
|
10
10
|
end
|
11
11
|
|
@@ -68,8 +68,8 @@ module Acfs::Collections
|
|
68
68
|
def setup_params(params)
|
69
69
|
@current_page = begin
|
70
70
|
Integer params.fetch(:page, 1)
|
71
|
-
|
72
|
-
|
71
|
+
rescue ArgumentError
|
72
|
+
params[:page]
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
data/lib/acfs/errors.rb
CHANGED
@@ -39,7 +39,7 @@ module Acfs
|
|
39
39
|
@response = opts[:response]
|
40
40
|
|
41
41
|
message = if response
|
42
|
-
(opts[:message] ? opts[:message]
|
42
|
+
(opts[:message] ? "#{opts[:message]}:" : 'Received') +
|
43
43
|
" #{response.code} for #{response.request.method.upcase}" \
|
44
44
|
" #{response.request.url} #{response.request.format}"
|
45
45
|
else
|
@@ -87,12 +87,13 @@ module Acfs
|
|
87
87
|
@errors = opts.delete :errors
|
88
88
|
@resource = opts.delete :resource
|
89
89
|
|
90
|
-
|
91
|
-
|
92
|
-
@errors.
|
93
|
-
|
94
|
-
|
95
|
-
|
90
|
+
case @errors
|
91
|
+
when Hash
|
92
|
+
opts[:message] ||= @errors.each_pair.map do |k, v|
|
93
|
+
@errors.is_a?(Array) ? "#{k}: #{v.join(', ')}" : "#{k}: #{v}"
|
94
|
+
end.join ', '
|
95
|
+
when Array
|
96
|
+
opts[:message] ||= @errors.join ', '
|
96
97
|
end
|
97
98
|
|
98
99
|
super
|
@@ -131,8 +132,7 @@ module Acfs
|
|
131
132
|
# parent resource. Check if the type is set to the correct
|
132
133
|
# Acfs::Resource Name
|
133
134
|
class ResourceTypeError < Error
|
134
|
-
attr_reader :base_class
|
135
|
-
attr_reader :type_name
|
135
|
+
attr_reader :base_class, :type_name
|
136
136
|
|
137
137
|
def initialize(opts = {})
|
138
138
|
@base_class = opts.delete :base_class
|
data/lib/acfs/location.rb
CHANGED
@@ -6,36 +6,31 @@ module Acfs
|
|
6
6
|
# Describes a URL with placeholders.
|
7
7
|
#
|
8
8
|
class Location
|
9
|
-
attr_reader :arguments, :raw, :struct, :
|
9
|
+
attr_reader :arguments, :raw, :struct, :vars
|
10
10
|
|
11
11
|
REGEXP = /^:([A-z][A-z0-9_]*)$/.freeze
|
12
12
|
|
13
|
-
def initialize(uri,
|
13
|
+
def initialize(uri, vars = {})
|
14
14
|
@raw = URI.parse uri
|
15
|
-
@
|
15
|
+
@vars = vars
|
16
16
|
@struct = raw.path.split('/').reject(&:empty?).map {|s| s =~ REGEXP ? Regexp.last_match[1].to_sym : s }
|
17
17
|
@arguments = struct.select {|s| s.is_a?(Symbol) }
|
18
18
|
end
|
19
19
|
|
20
|
-
def build(
|
21
|
-
|
22
|
-
raise ArgumentError.new "URI path arguments must be a hash, `#{args.inspect}' given."
|
23
|
-
end
|
24
|
-
|
25
|
-
self.class.new raw.to_s, args.merge(self.args)
|
20
|
+
def build(vars)
|
21
|
+
self.class.new raw.to_s, vars.stringify_keys.merge(self.vars)
|
26
22
|
end
|
27
23
|
|
28
24
|
def extract_from(*args)
|
29
|
-
|
30
|
-
|
31
|
-
end
|
25
|
+
vars = {}
|
26
|
+
arguments.each {|key| vars[key.to_s] = extract_arg(key, args) }
|
32
27
|
|
33
|
-
build
|
28
|
+
build(vars)
|
34
29
|
end
|
35
30
|
|
36
31
|
def str
|
37
32
|
uri = raw.dup
|
38
|
-
uri.path =
|
33
|
+
uri.path = "/#{struct.map(&method(:lookup_variable)).join('/')}"
|
39
34
|
uri.to_s
|
40
35
|
end
|
41
36
|
|
@@ -56,27 +51,26 @@ module Acfs
|
|
56
51
|
nil
|
57
52
|
end
|
58
53
|
|
59
|
-
def
|
60
|
-
|
61
|
-
end
|
54
|
+
def lookup_variable(name)
|
55
|
+
return name unless name.is_a?(Symbol)
|
62
56
|
|
63
|
-
|
64
|
-
|
65
|
-
|
57
|
+
value = vars.fetch(name.to_s) do
|
58
|
+
if @raise.nil? || @raise
|
59
|
+
raise ArgumentError.new <<~ERROR.strip
|
60
|
+
URI path argument `#{name}' missing on `#{self}'. Given: `#{vars}.inspect'
|
61
|
+
ERROR
|
62
|
+
end
|
66
63
|
|
67
|
-
|
68
|
-
|
64
|
+
":#{name}"
|
65
|
+
end
|
69
66
|
|
70
|
-
|
71
|
-
args.fetch(sym.to_s) do
|
72
|
-
args.fetch(sym) do
|
73
|
-
if args[:raise].nil? || args[:raise]
|
74
|
-
raise ArgumentError.new "URI path argument `#{sym}' missing on `#{self}'. Given: `#{args}.inspect'"
|
75
|
-
end
|
67
|
+
value = value.to_s.strip
|
76
68
|
|
77
|
-
|
78
|
-
|
69
|
+
if value.empty?
|
70
|
+
raise ArgumentError.new "Cannot replace path argument `#{name}' with empty string."
|
79
71
|
end
|
72
|
+
|
73
|
+
::URI.encode_www_form_component(value)
|
80
74
|
end
|
81
75
|
end
|
82
76
|
end
|
data/lib/acfs/middleware/base.rb
CHANGED
@@ -7,19 +7,17 @@ module Acfs
|
|
7
7
|
# Log requests and responses.
|
8
8
|
#
|
9
9
|
class Logger < Base
|
10
|
-
|
10
|
+
attr_reader :logger
|
11
|
+
|
12
|
+
def initialize(app, **opts)
|
11
13
|
super
|
12
|
-
@logger = options[:logger]
|
14
|
+
@logger = options[:logger] || ::Logger.new($stdout)
|
13
15
|
end
|
14
16
|
|
15
17
|
def response(res, nxt)
|
16
18
|
logger.info "[ACFS] #{res.request.method.to_s.upcase} #{res.request.url} -> #{res.status}"
|
17
19
|
nxt.call res
|
18
20
|
end
|
19
|
-
|
20
|
-
def logger
|
21
|
-
@logger ||= ::Logger.new STDOUT
|
22
|
-
end
|
23
21
|
end
|
24
22
|
end
|
25
23
|
end
|
@@ -29,7 +29,7 @@ module Acfs
|
|
29
29
|
request.headers['Accept'] = accept.join(',')
|
30
30
|
|
31
31
|
request.on_complete do |response, nxt|
|
32
|
-
response.data = decode
|
32
|
+
response.data = decode(response.body) if mime == response.content_type
|
33
33
|
|
34
34
|
nxt.call response
|
35
35
|
end
|
data/lib/acfs/operation.rb
CHANGED
@@ -12,7 +12,7 @@ module Acfs
|
|
12
12
|
delegate :service, to: :resource
|
13
13
|
delegate :call, to: :callback
|
14
14
|
|
15
|
-
def initialize(resource, action, opts
|
15
|
+
def initialize(resource, action, **opts, &block)
|
16
16
|
@resource = resource
|
17
17
|
@action = action.to_sym
|
18
18
|
|
@@ -21,9 +21,7 @@ module Acfs
|
|
21
21
|
@params = (opts[:params] || {}).dup
|
22
22
|
@data = (opts[:data] || {}).dup
|
23
23
|
|
24
|
-
|
25
|
-
@url = opts[:url]
|
26
|
-
else
|
24
|
+
unless (@url = opts[:url])
|
27
25
|
@location = resource.location(action: @action).extract_from(@params, @data)
|
28
26
|
@url = location.str
|
29
27
|
end
|
@@ -45,11 +43,11 @@ module Acfs
|
|
45
43
|
end
|
46
44
|
|
47
45
|
def full_params
|
48
|
-
(id ? params.merge(id: id) : params).merge
|
46
|
+
(id ? params.merge(id: id) : params).merge(location_vars)
|
49
47
|
end
|
50
48
|
|
51
|
-
def
|
52
|
-
location ? location.
|
49
|
+
def location_vars
|
50
|
+
location ? location.vars : {}
|
53
51
|
end
|
54
52
|
|
55
53
|
def method
|
data/lib/acfs/request.rb
CHANGED
@@ -11,7 +11,7 @@ module Acfs
|
|
11
11
|
attr_reader :url, :headers, :params, :data, :method, :operation
|
12
12
|
|
13
13
|
include Request::Callbacks
|
14
|
-
def initialize(url, options
|
14
|
+
def initialize(url, **options, &block)
|
15
15
|
@url = URI.parse(url.to_s).tap do |_url|
|
16
16
|
@data = options.delete(:data) || nil
|
17
17
|
@format = options.delete(:format) || :json
|
@@ -28,13 +28,5 @@ module Acfs
|
|
28
28
|
def data?
|
29
29
|
!data.nil?
|
30
30
|
end
|
31
|
-
|
32
|
-
class << self
|
33
|
-
def new(*attrs)
|
34
|
-
return attrs[0] if attrs[0].is_a? self
|
35
|
-
|
36
|
-
super
|
37
|
-
end
|
38
|
-
end
|
39
31
|
end
|
40
32
|
end
|
@@ -68,7 +68,7 @@ class Acfs::Resource
|
|
68
68
|
# @see #write_attributes Delegates attributes hash to {#write_attributes}.
|
69
69
|
#
|
70
70
|
def attributes=(attributes)
|
71
|
-
write_attributes
|
71
|
+
write_attributes(attributes)
|
72
72
|
end
|
73
73
|
|
74
74
|
# @api public
|
@@ -103,17 +103,16 @@ class Acfs::Resource
|
|
103
103
|
#
|
104
104
|
# @see #write_attribute Delegates attribute values to `#write_attribute`.
|
105
105
|
#
|
106
|
-
def write_attributes(attributes, opts
|
106
|
+
def write_attributes(attributes, **opts)
|
107
107
|
unless attributes.respond_to?(:each) && attributes.respond_to?(:keys)
|
108
108
|
return false
|
109
109
|
end
|
110
110
|
|
111
|
-
if opts.fetch(:unknown, :ignore) == :raise
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
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(', ')}"
|
117
116
|
end
|
118
117
|
|
119
118
|
procs = {}
|
@@ -122,12 +121,12 @@ class Acfs::Resource
|
|
122
121
|
if attributes[key].is_a? Proc
|
123
122
|
procs[key] = attributes[key]
|
124
123
|
else
|
125
|
-
write_local_attribute
|
124
|
+
write_local_attribute(key, attributes[key], **opts)
|
126
125
|
end
|
127
126
|
end
|
128
127
|
|
129
128
|
procs.each do |key, proc|
|
130
|
-
write_local_attribute
|
129
|
+
write_local_attribute(key, instance_exec(&proc), **opts)
|
131
130
|
end
|
132
131
|
|
133
132
|
true
|
@@ -201,12 +200,12 @@ class Acfs::Resource
|
|
201
200
|
# @param [Symbol, String, Class] type Attribute
|
202
201
|
# type identifier or type class.
|
203
202
|
#
|
204
|
-
def attribute(name, type, opts
|
203
|
+
def attribute(name, type, **opts)
|
205
204
|
if type.is_a?(Symbol) || type.is_a?(String)
|
206
205
|
type = "#{ATTR_CLASS_BASE}::#{type.to_s.classify}".constantize
|
207
206
|
end
|
208
207
|
|
209
|
-
define_attribute
|
208
|
+
define_attribute(name.to_sym, type, **opts)
|
210
209
|
end
|
211
210
|
|
212
211
|
# @api public
|
@@ -265,7 +264,7 @@ end
|
|
265
264
|
|
266
265
|
# Load attribute type classes.
|
267
266
|
#
|
268
|
-
Dir[File.
|
267
|
+
Dir[File.join(__dir__, 'attributes/*.rb')].sort.each do |path|
|
269
268
|
filename = File.basename(path)
|
270
269
|
require "acfs/resource/attributes/#{filename}"
|
271
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,11 +112,11 @@ 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
|
-
.location(self.class, opts
|
119
|
+
.location(self.class, **opts, action: :read)
|
117
120
|
.build(attributes).str
|
118
121
|
end
|
119
122
|
|
@@ -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
|
@@ -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
|
@@ -214,7 +214,7 @@ class Acfs::Resource
|
|
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
|
@@ -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
|
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/service.rb
CHANGED
@@ -28,34 +28,26 @@ module Acfs
|
|
28
28
|
|
29
29
|
# @api private
|
30
30
|
#
|
31
|
-
def initialize(options
|
31
|
+
def initialize(**options)
|
32
32
|
@options = options
|
33
33
|
end
|
34
34
|
|
35
35
|
# @api private
|
36
36
|
# @return [Location]
|
37
37
|
#
|
38
|
-
def location(resource_class,
|
39
|
-
|
38
|
+
def location(resource_class, path: nil, action: :list, **)
|
39
|
+
path ||= options[:path]
|
40
40
|
|
41
|
-
|
41
|
+
if path.is_a?(Hash) && path.key?(action)
|
42
|
+
path = path.fetch(action)
|
43
|
+
else
|
44
|
+
path = path.is_a?(Hash) ? path[:all].to_s : path.to_s
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
opts[:path].fetch(action)
|
46
|
-
else
|
47
|
-
path = if opts[:path].is_a?(Hash)
|
48
|
-
opts[:path][:all].to_s
|
49
|
-
else
|
50
|
-
opts[:path].to_s
|
51
|
-
end
|
52
|
-
|
53
|
-
if path.blank?
|
54
|
-
path = (resource_class.name || 'class').pluralize.underscore
|
55
|
-
end
|
56
|
-
|
57
|
-
resource_class.location_default_path(action, path.strip)
|
46
|
+
if path.blank?
|
47
|
+
path = (resource_class.name || 'class').pluralize.underscore
|
58
48
|
end
|
49
|
+
|
50
|
+
path = resource_class.location_default_path(action, path.strip)
|
59
51
|
end
|
60
52
|
|
61
53
|
if path.nil?
|
@@ -27,10 +27,10 @@ module Acfs
|
|
27
27
|
# @return [undefined]
|
28
28
|
# @see #delete
|
29
29
|
#
|
30
|
-
def delete!(opts
|
30
|
+
def delete!(**opts)
|
31
31
|
opts[:params] ||= {}
|
32
32
|
|
33
|
-
operation
|
33
|
+
operation(:delete, **opts) do |data|
|
34
34
|
update_with data
|
35
35
|
freeze
|
36
36
|
end
|
data/lib/acfs/stub.rb
CHANGED
@@ -22,7 +22,7 @@ module Acfs
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def accept?(op)
|
25
|
-
return opts[:with].call
|
25
|
+
return opts[:with].call(op) if opts[:with].respond_to?(:call)
|
26
26
|
|
27
27
|
params = op.full_params.stringify_keys
|
28
28
|
data = op.data.stringify_keys
|
@@ -84,9 +84,13 @@ module Acfs
|
|
84
84
|
def raise_error(op, name, data)
|
85
85
|
raise name if name.is_a? Class
|
86
86
|
|
87
|
-
data.stringify_keys! if data.respond_to?
|
87
|
+
data.stringify_keys! if data.respond_to?(:stringify_keys!)
|
88
88
|
|
89
|
-
op.handle_failure ::Acfs::Response.new
|
89
|
+
op.handle_failure ::Acfs::Response.new(
|
90
|
+
op.request,
|
91
|
+
status: Rack::Utils.status_code(name),
|
92
|
+
data: data
|
93
|
+
)
|
90
94
|
end
|
91
95
|
|
92
96
|
class << self
|
@@ -154,12 +158,14 @@ module Acfs
|
|
154
158
|
unless stub
|
155
159
|
return false if allow_requests?
|
156
160
|
|
157
|
-
raise RealRequestsNotAllowedError.new
|
158
|
-
No stub found for `#{op.action}' on `#{op.resource.name}'
|
161
|
+
raise RealRequestsNotAllowedError.new <<~ERROR
|
162
|
+
No stub found for `#{op.action}' on `#{op.resource.name}' \
|
163
|
+
with params `#{op.full_params.inspect}', data `#{op.data.inspect}' \
|
164
|
+
and id `#{op.id}'.
|
159
165
|
|
160
166
|
Available stubs:
|
161
167
|
#{pretty_print}
|
162
|
-
|
168
|
+
ERROR
|
163
169
|
end
|
164
170
|
|
165
171
|
stub.call op
|
data/lib/acfs/version.rb
CHANGED
data/spec/acfs/location_spec.rb
CHANGED
@@ -5,7 +5,7 @@ require 'spec_helper'
|
|
5
5
|
describe ::Acfs::Location do
|
6
6
|
let(:location) { described_class.new(uri, args) }
|
7
7
|
let(:uri) { 'http://localhost/users/:id' }
|
8
|
-
let(:args) { {id
|
8
|
+
let(:args) { {'id' => 4} }
|
9
9
|
|
10
10
|
describe '#str' do
|
11
11
|
subject(:str) { location.str }
|
@@ -15,7 +15,7 @@ describe ::Acfs::Location do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
context 'with special characters' do
|
18
|
-
let(:args) { {id
|
18
|
+
let(:args) { {'id' => '4 [@(\/!^$'} }
|
19
19
|
|
20
20
|
it 'escapes special characters' do
|
21
21
|
expect(str).to eq 'http://localhost/users/4+%5B%40%28%5C%2F%21%5E%24'
|
data/spec/acfs/request_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe Acfs::Request do
|
|
9
9
|
let(:data) { nil }
|
10
10
|
let(:method) { :get }
|
11
11
|
let(:options) { {method: method, headers: headers, params: params, data: data} }
|
12
|
-
let(:request) { Acfs::Request.new(url, options) }
|
12
|
+
let(:request) { Acfs::Request.new(url, **options) }
|
13
13
|
|
14
14
|
describe '#url' do
|
15
15
|
it 'should return request URL' do
|
@@ -48,10 +48,10 @@ describe Acfs::Resource::Attributes do
|
|
48
48
|
write_attribute :name, "The Great #{name}"
|
49
49
|
end
|
50
50
|
end
|
51
|
-
let(:args) { [params] }
|
52
51
|
let(:params) { {name: 'James'} }
|
52
|
+
let(:opts) { {} }
|
53
53
|
let(:m) { model.new }
|
54
|
-
let(:action) { -> { m.write_attributes(
|
54
|
+
let(:action) { -> { m.write_attributes(params, **opts) } }
|
55
55
|
subject { action }
|
56
56
|
|
57
57
|
it 'should update attributes' do
|
@@ -79,7 +79,7 @@ describe Acfs::Resource::Attributes do
|
|
79
79
|
end
|
80
80
|
|
81
81
|
context 'with unknown: :raise option' do
|
82
|
-
let(:
|
82
|
+
let(:opts) { {unknown: :raise} }
|
83
83
|
|
84
84
|
it { should raise_error(ArgumentError, /unknown attribute/i) }
|
85
85
|
|
@@ -101,12 +101,12 @@ describe Acfs::Resource::Persistence do
|
|
101
101
|
let!(:model) { model_class.find 1 }
|
102
102
|
|
103
103
|
describe '#update_attributes' do
|
104
|
-
subject { -> { model.update_attributes
|
104
|
+
subject { -> { model.update_attributes({name: 'John'}) } }
|
105
105
|
it { expect { subject.call }.to raise_error Acfs::ResourceNotLoaded }
|
106
106
|
end
|
107
107
|
|
108
108
|
describe '#update_attributes!' do
|
109
|
-
subject { -> { model.update_attributes!
|
109
|
+
subject { -> { model.update_attributes!({name: 'John'}) } }
|
110
110
|
it { expect { subject.call }.to raise_error Acfs::ResourceNotLoaded }
|
111
111
|
end
|
112
112
|
end
|
@@ -179,18 +179,18 @@ describe Acfs::Resource::Persistence do
|
|
179
179
|
before { model; Acfs.run }
|
180
180
|
|
181
181
|
it 'should set attributes' do
|
182
|
-
model.update_attributes
|
182
|
+
model.update_attributes({name: 'Idefix'})
|
183
183
|
expect(model.attributes.symbolize_keys).to eq id: 1, name: 'Idefix', age: 12
|
184
184
|
end
|
185
185
|
|
186
186
|
it 'should save resource' do
|
187
|
-
expect(model.__getobj__).to receive(:save)
|
188
|
-
model.update_attributes
|
187
|
+
expect(model.__getobj__).to receive(:save)
|
188
|
+
model.update_attributes({name: 'Idefix'})
|
189
189
|
end
|
190
190
|
|
191
|
-
it 'should
|
191
|
+
it 'should kwargs to save' do
|
192
192
|
expect(model.__getobj__).to receive(:save).with(bla: 'blub')
|
193
|
-
model.update_attributes({name: 'Idefix'},
|
193
|
+
model.update_attributes({name: 'Idefix'}, bla: 'blub')
|
194
194
|
end
|
195
195
|
end
|
196
196
|
|
@@ -199,18 +199,18 @@ describe Acfs::Resource::Persistence do
|
|
199
199
|
before { model; Acfs.run }
|
200
200
|
|
201
201
|
it 'should set attributes' do
|
202
|
-
model.update_attributes!
|
202
|
+
model.update_attributes!({name: 'Idefix'})
|
203
203
|
expect(model.attributes.symbolize_keys).to eq id: 1, name: 'Idefix', age: 12
|
204
204
|
end
|
205
205
|
|
206
206
|
it 'should save resource' do
|
207
|
-
expect(model.__getobj__).to receive(:save!)
|
208
|
-
model.update_attributes!
|
207
|
+
expect(model.__getobj__).to receive(:save!)
|
208
|
+
model.update_attributes!({name: 'Idefix'})
|
209
209
|
end
|
210
210
|
|
211
211
|
it 'should pass second hash to save' do
|
212
212
|
expect(model.__getobj__).to receive(:save!).with(bla: 'blub')
|
213
|
-
model.update_attributes!({name: 'Idefix'},
|
213
|
+
model.update_attributes!({name: 'Idefix'}, bla: 'blub')
|
214
214
|
end
|
215
215
|
end
|
216
216
|
end
|
data/spec/acfs/service_spec.rb
CHANGED
@@ -5,7 +5,7 @@ require 'spec_helper'
|
|
5
5
|
describe Acfs::Service do
|
6
6
|
let(:srv_class) { Class.new(Acfs::Service) { identity :test } }
|
7
7
|
let(:options) { {} }
|
8
|
-
let(:service) { srv_class.new
|
8
|
+
let(:service) { srv_class.new(**options) }
|
9
9
|
|
10
10
|
before do
|
11
11
|
Acfs::Configuration.current.locate :test, ''
|
data/spec/acfs_spec.rb
CHANGED
@@ -191,7 +191,7 @@ describe 'Acfs' do
|
|
191
191
|
it 'should load associated resources from different service' do
|
192
192
|
@user = MyUser.find 2 do |user|
|
193
193
|
expect(user.id).to be == 2
|
194
|
-
@comments = Comment.where
|
194
|
+
@comments = Comment.where({user: user.id})
|
195
195
|
end
|
196
196
|
|
197
197
|
Acfs.run
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acfs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Graichen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -221,14 +221,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
221
221
|
requirements:
|
222
222
|
- - ">="
|
223
223
|
- !ruby/object:Gem::Version
|
224
|
-
version:
|
224
|
+
version: 2.5.0
|
225
225
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
226
226
|
requirements:
|
227
227
|
- - ">="
|
228
228
|
- !ruby/object:Gem::Version
|
229
229
|
version: '0'
|
230
230
|
requirements: []
|
231
|
-
rubygems_version: 3.2.
|
231
|
+
rubygems_version: 3.2.4
|
232
232
|
signing_key:
|
233
233
|
specification_version: 4
|
234
234
|
summary: An abstract API base client for service oriented application.
|