acfs 1.5.1 → 1.6.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 +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.
|