acfs 1.3.2 → 1.5.1
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 +39 -0
- data/README.md +10 -23
- data/acfs.gemspec +19 -20
- data/lib/acfs.rb +2 -0
- data/lib/acfs/adapter/base.rb +6 -8
- data/lib/acfs/adapter/typhoeus.rb +25 -6
- data/lib/acfs/collection.rb +2 -1
- data/lib/acfs/collections/paginatable.rb +4 -3
- data/lib/acfs/configuration.rb +14 -7
- data/lib/acfs/errors.rb +60 -19
- data/lib/acfs/global.rb +2 -0
- data/lib/acfs/location.rb +9 -5
- data/lib/acfs/middleware/base.rb +5 -1
- data/lib/acfs/middleware/json.rb +6 -2
- data/lib/acfs/middleware/logger.rb +2 -0
- data/lib/acfs/middleware/msgpack.rb +2 -0
- data/lib/acfs/middleware/print.rb +2 -0
- data/lib/acfs/middleware/serializer.rb +2 -0
- data/lib/acfs/operation.rb +20 -3
- data/lib/acfs/request.rb +5 -1
- data/lib/acfs/request/callbacks.rb +5 -1
- data/lib/acfs/resource.rb +2 -0
- data/lib/acfs/resource/attributes.rb +7 -4
- data/lib/acfs/resource/attributes/base.rb +2 -1
- data/lib/acfs/resource/attributes/boolean.rb +2 -0
- data/lib/acfs/resource/attributes/date_time.rb +3 -2
- data/lib/acfs/resource/attributes/dict.rb +2 -0
- data/lib/acfs/resource/attributes/float.rb +5 -3
- data/lib/acfs/resource/attributes/integer.rb +2 -0
- data/lib/acfs/resource/attributes/list.rb +2 -0
- data/lib/acfs/resource/attributes/string.rb +2 -0
- data/lib/acfs/resource/attributes/uuid.rb +4 -3
- data/lib/acfs/resource/dirty.rb +2 -0
- data/lib/acfs/resource/initialization.rb +2 -0
- data/lib/acfs/resource/loadable.rb +2 -0
- data/lib/acfs/resource/locatable.rb +10 -6
- data/lib/acfs/resource/operational.rb +2 -1
- data/lib/acfs/resource/persistence.rb +7 -6
- data/lib/acfs/resource/query_methods.rb +6 -4
- data/lib/acfs/resource/service.rb +3 -1
- data/lib/acfs/resource/validation.rb +19 -7
- data/lib/acfs/response.rb +2 -0
- data/lib/acfs/response/formats.rb +2 -0
- data/lib/acfs/response/status.rb +3 -1
- data/lib/acfs/rspec.rb +2 -0
- data/lib/acfs/runner.rb +6 -1
- data/lib/acfs/service.rb +24 -13
- data/lib/acfs/service/middleware.rb +2 -0
- data/lib/acfs/service/middleware/stack.rb +5 -3
- data/lib/acfs/singleton_resource.rb +4 -2
- data/lib/acfs/stub.rb +32 -11
- data/lib/acfs/util.rb +2 -0
- data/lib/acfs/version.rb +4 -2
- data/lib/acfs/yard.rb +1 -0
- data/spec/acfs/adapter/typhoeus_spec.rb +30 -3
- data/spec/acfs/collection_spec.rb +7 -5
- data/spec/acfs/configuration_spec.rb +2 -0
- data/spec/acfs/global_spec.rb +6 -3
- data/spec/acfs/location_spec.rb +2 -0
- data/spec/acfs/middleware/json_spec.rb +17 -1
- data/spec/acfs/middleware/msgpack_spec.rb +2 -0
- data/spec/acfs/operation_spec.rb +2 -0
- data/spec/acfs/request/callbacks_spec.rb +2 -0
- data/spec/acfs/request_spec.rb +3 -1
- data/spec/acfs/resource/attributes/boolean_spec.rb +2 -0
- data/spec/acfs/resource/attributes/date_time_spec.rb +2 -0
- data/spec/acfs/resource/attributes/dict_spec.rb +4 -2
- data/spec/acfs/resource/attributes/float_spec.rb +3 -1
- data/spec/acfs/resource/attributes/integer_spec.rb +2 -0
- data/spec/acfs/resource/attributes/list_spec.rb +5 -3
- data/spec/acfs/resource/attributes/uuid_spec.rb +2 -0
- data/spec/acfs/resource/attributes_spec.rb +8 -8
- data/spec/acfs/resource/dirty_spec.rb +2 -0
- data/spec/acfs/resource/initialization_spec.rb +8 -2
- data/spec/acfs/resource/loadable_spec.rb +2 -0
- data/spec/acfs/resource/locatable_spec.rb +2 -0
- data/spec/acfs/resource/persistance_spec.rb +10 -4
- data/spec/acfs/resource/query_methods_spec.rb +24 -17
- data/spec/acfs/resource/validation_spec.rb +2 -0
- data/spec/acfs/response/formats_spec.rb +3 -1
- data/spec/acfs/response/status_spec.rb +2 -0
- data/spec/acfs/runner_spec.rb +6 -8
- data/spec/acfs/service/middleware_spec.rb +2 -0
- data/spec/acfs/service_spec.rb +3 -1
- data/spec/acfs/singleton_resource_spec.rb +2 -0
- data/spec/acfs/stub_spec.rb +2 -0
- data/spec/acfs_spec.rb +2 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/hash.rb +2 -0
- data/spec/support/response.rb +2 -0
- data/spec/support/service.rb +1 -0
- data/spec/support/shared/find_callbacks.rb +2 -0
- metadata +26 -26
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Acfs::Resource
|
2
4
|
# @api private
|
3
5
|
#
|
@@ -11,7 +13,6 @@ class Acfs::Resource
|
|
11
13
|
extend ActiveSupport::Concern
|
12
14
|
delegate :operation, to: :'self.class'
|
13
15
|
|
14
|
-
#
|
15
16
|
module ClassMethods
|
16
17
|
# Invoke CRUD operation.
|
17
18
|
def operation(action, opts = {}, &block)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Acfs::Resource
|
2
4
|
#
|
3
5
|
# Allow to track the persistence state of a model.
|
@@ -40,7 +42,7 @@ class Acfs::Resource
|
|
40
42
|
def new?
|
41
43
|
!loaded?
|
42
44
|
end
|
43
|
-
|
45
|
+
alias new_record? new?
|
44
46
|
|
45
47
|
# @api public
|
46
48
|
#
|
@@ -86,9 +88,9 @@ class Acfs::Resource
|
|
86
88
|
operation((new? ? :create : :update), opts) do |data|
|
87
89
|
update_with data
|
88
90
|
end
|
89
|
-
rescue ::Acfs::InvalidResource =>
|
90
|
-
self.remote_errors =
|
91
|
-
raise
|
91
|
+
rescue ::Acfs::InvalidResource => e
|
92
|
+
self.remote_errors = e.errors
|
93
|
+
raise e
|
92
94
|
end
|
93
95
|
|
94
96
|
# @api public
|
@@ -242,8 +244,6 @@ class Acfs::Resource
|
|
242
244
|
end
|
243
245
|
end
|
244
246
|
|
245
|
-
private
|
246
|
-
|
247
247
|
def update_with(data)
|
248
248
|
self.attributes = data
|
249
249
|
loaded!
|
@@ -251,6 +251,7 @@ class Acfs::Resource
|
|
251
251
|
|
252
252
|
def check_loaded!(opts = {})
|
253
253
|
return if loaded? || opts[:force]
|
254
|
+
|
254
255
|
raise ::Acfs::ResourceNotLoaded.new resource: self
|
255
256
|
end
|
256
257
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Acfs::Resource
|
2
4
|
# Methods providing the query interface for finding resouces.
|
3
5
|
#
|
@@ -14,7 +16,6 @@ class Acfs::Resource
|
|
14
16
|
module QueryMethods
|
15
17
|
extend ActiveSupport::Concern
|
16
18
|
|
17
|
-
#
|
18
19
|
module ClassMethods
|
19
20
|
# @api public
|
20
21
|
#
|
@@ -89,7 +90,7 @@ class Acfs::Resource
|
|
89
90
|
|
90
91
|
collection
|
91
92
|
end
|
92
|
-
|
93
|
+
alias where all
|
93
94
|
|
94
95
|
# @api public
|
95
96
|
#
|
@@ -140,7 +141,7 @@ class Acfs::Resource
|
|
140
141
|
raise Acfs::ResourceNotFound.new message: 'Received erroneous ' \
|
141
142
|
"response: no `#{name}` with params #{params} found"
|
142
143
|
end
|
143
|
-
block
|
144
|
+
block&.call m
|
144
145
|
end
|
145
146
|
end
|
146
147
|
|
@@ -249,6 +250,7 @@ class Acfs::Resource
|
|
249
250
|
|
250
251
|
def resource_class_lookup(type)
|
251
252
|
return self if type.nil?
|
253
|
+
|
252
254
|
klass = type.camelize.constantize
|
253
255
|
|
254
256
|
unless klass <= self
|
@@ -256,7 +258,7 @@ class Acfs::Resource
|
|
256
258
|
end
|
257
259
|
|
258
260
|
klass
|
259
|
-
rescue NameError
|
261
|
+
rescue NameError
|
260
262
|
raise Acfs::ResourceTypeError.new type_name: type, base_class: self
|
261
263
|
end
|
262
264
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Acfs::Resource
|
2
4
|
# Included by Acfs::Model. Allows to configure the service
|
3
5
|
# a resource belongs to.
|
@@ -5,7 +7,6 @@ class Acfs::Resource
|
|
5
7
|
module Service
|
6
8
|
extend ActiveSupport::Concern
|
7
9
|
|
8
|
-
#
|
9
10
|
module ClassMethods
|
10
11
|
# @api public
|
11
12
|
#
|
@@ -35,6 +36,7 @@ class Acfs::Resource
|
|
35
36
|
#
|
36
37
|
def service(klass = nil, options = {})
|
37
38
|
return (@service = klass.new options) if klass
|
39
|
+
|
38
40
|
@service || superclass.service
|
39
41
|
end
|
40
42
|
end
|
@@ -1,12 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Acfs::Resource
|
2
|
-
#
|
3
4
|
module Validation
|
4
|
-
def valid?(*args)
|
5
|
-
super
|
6
|
-
remote_errors.each {|f, e| errors.add f, e }
|
7
|
-
errors.empty?
|
8
|
-
end
|
9
|
-
|
10
5
|
def remote_errors
|
11
6
|
@remote_errors ||= ActiveModel::Errors.new self
|
12
7
|
end
|
@@ -31,7 +26,24 @@ class Acfs::Resource
|
|
31
26
|
unless valid?(new? ? :create : :save)
|
32
27
|
raise ::Acfs::InvalidResource.new resource: self, errors: errors.to_a
|
33
28
|
end
|
29
|
+
|
34
30
|
super
|
35
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
|
36
48
|
end
|
37
49
|
end
|
data/lib/acfs/response.rb
CHANGED
data/lib/acfs/response/status.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Acfs
|
2
4
|
class Response
|
3
5
|
# Method to fetch information about response status.
|
@@ -11,7 +13,7 @@ module Acfs
|
|
11
13
|
# return response.response_code unless response.nil?
|
12
14
|
# 0
|
13
15
|
end
|
14
|
-
|
16
|
+
alias code status_code
|
15
17
|
|
16
18
|
# Return true if response was successful indicated by
|
17
19
|
# response status code.
|
data/lib/acfs/rspec.rb
CHANGED
data/lib/acfs/runner.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'acfs/service/middleware'
|
2
4
|
|
3
5
|
module Acfs
|
@@ -59,7 +61,7 @@ module Acfs
|
|
59
61
|
|
60
62
|
enqueue_operations
|
61
63
|
start_all
|
62
|
-
rescue
|
64
|
+
rescue StandardError
|
63
65
|
queue.clear
|
64
66
|
raise
|
65
67
|
end
|
@@ -87,10 +89,13 @@ module Acfs
|
|
87
89
|
|
88
90
|
def op_request(op)
|
89
91
|
return if Acfs::Stub.enabled? && Acfs::Stub.stubbed(op)
|
92
|
+
|
90
93
|
req = op.service.prepare op.request
|
91
94
|
return unless req.is_a? Acfs::Request
|
95
|
+
|
92
96
|
req = prepare req
|
93
97
|
return unless req.is_a? Acfs::Request
|
98
|
+
|
94
99
|
yield req
|
95
100
|
end
|
96
101
|
end
|
data/lib/acfs/service.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'acfs/service/middleware'
|
2
4
|
|
3
5
|
module Acfs
|
@@ -38,21 +40,27 @@ module Acfs
|
|
38
40
|
|
39
41
|
action = opts[:action] || :list
|
40
42
|
|
41
|
-
path =
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
path = begin
|
44
|
+
if opts[:path].is_a?(Hash) && opts[:path].key?(action)
|
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
|
49
52
|
|
50
|
-
|
53
|
+
if path.blank?
|
54
|
+
path = (resource_class.name || 'class').pluralize.underscore
|
55
|
+
end
|
51
56
|
|
52
|
-
|
53
|
-
|
57
|
+
resource_class.location_default_path(action, path.strip)
|
58
|
+
end
|
59
|
+
end
|
54
60
|
|
55
|
-
|
61
|
+
if path.nil?
|
62
|
+
raise ArgumentError.new "Location for `#{action}' explicit disabled by set to nil."
|
63
|
+
end
|
56
64
|
|
57
65
|
Location.new [self.class.base_url.to_s, path.to_s].join('/')
|
58
66
|
end
|
@@ -81,7 +89,10 @@ module Acfs
|
|
81
89
|
#
|
82
90
|
def base_url
|
83
91
|
unless (base = Acfs::Configuration.current.locate identity)
|
84
|
-
raise ArgumentError.new
|
92
|
+
raise ArgumentError.new \
|
93
|
+
"#{identity} not configured. Add `locate '" \
|
94
|
+
"#{identity.to_s.underscore}', 'http://service.url/'` " \
|
95
|
+
'to your configuration.'
|
85
96
|
end
|
86
97
|
|
87
98
|
base.to_s
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Acfs
|
2
4
|
class Service
|
3
5
|
module Middleware
|
@@ -5,7 +7,7 @@ module Acfs
|
|
5
7
|
include Enumerable
|
6
8
|
|
7
9
|
MUTEX = Mutex.new
|
8
|
-
IDENTITY = ->
|
10
|
+
IDENTITY = ->(i) { i }
|
9
11
|
|
10
12
|
attr_reader :middlewares
|
11
13
|
|
@@ -41,7 +43,7 @@ module Acfs
|
|
41
43
|
next_middleware.call(klass.call(env, *args))
|
42
44
|
end
|
43
45
|
else
|
44
|
-
|
46
|
+
raise "Invalid middleware, doesn't respond to `call`: #{klass.inspect}"
|
45
47
|
end
|
46
48
|
end
|
47
49
|
end
|
@@ -51,7 +53,7 @@ module Acfs
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def each
|
54
|
-
middlewares.each {
|
56
|
+
middlewares.each {|x| yield x.first }
|
55
57
|
end
|
56
58
|
|
57
59
|
def clear
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Acfs
|
2
4
|
# Acfs SingletonResources
|
3
5
|
#
|
@@ -71,8 +73,8 @@ module Acfs
|
|
71
73
|
def all
|
72
74
|
raise ::Acfs::UnsupportedOperation.new
|
73
75
|
end
|
74
|
-
|
75
|
-
|
76
|
+
alias find_by all
|
77
|
+
alias find_by! all
|
76
78
|
|
77
79
|
# @api private
|
78
80
|
def location_default_path(_, path)
|
data/lib/acfs/stub.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
|
3
5
|
module Acfs
|
4
6
|
# Global handler for stubbing resources.
|
5
7
|
#
|
6
8
|
class Stub
|
7
|
-
ACTIONS = [
|
9
|
+
ACTIONS = %i[read create update delete list].freeze
|
8
10
|
|
9
11
|
attr_reader :opts
|
10
12
|
|
@@ -13,7 +15,10 @@ module Acfs
|
|
13
15
|
|
14
16
|
@opts[:with].stringify_keys! if @opts[:with].is_a? Hash
|
15
17
|
@opts[:return].stringify_keys! if @opts[:return].is_a? Hash
|
16
|
-
|
18
|
+
|
19
|
+
if @opts[:return].is_a?(Array) # rubocop:disable Style/GuardClause
|
20
|
+
@opts[:return].map! {|h| h.stringify_keys! if h.is_a? Hash }
|
21
|
+
end
|
17
22
|
end
|
18
23
|
|
19
24
|
def accept?(op)
|
@@ -28,8 +33,13 @@ module Acfs
|
|
28
33
|
case opts.fetch(:match, :inclusion)
|
29
34
|
when :legacy
|
30
35
|
return true if with.empty? && params.empty? && data.empty?
|
31
|
-
|
32
|
-
|
36
|
+
if with.reject {|_, v| v.nil? } == params.reject {|_, v| v.nil? }
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
if with.reject {|_, v| v.nil? } == data.reject {|_, v| v.nil? }
|
40
|
+
return true
|
41
|
+
end
|
42
|
+
|
33
43
|
false
|
34
44
|
when :inclusion
|
35
45
|
with.each_pair.all? do |k, v|
|
@@ -43,7 +53,8 @@ module Acfs
|
|
43
53
|
end
|
44
54
|
|
45
55
|
def called?(count = nil)
|
46
|
-
count = count.count if count.respond_to?
|
56
|
+
count = count.count if count.respond_to?(:count)
|
57
|
+
|
47
58
|
count.nil? ? calls.any? : calls.size == count
|
48
59
|
end
|
49
60
|
|
@@ -60,8 +71,8 @@ module Acfs
|
|
60
71
|
|
61
72
|
response = Acfs::Response.new op.request,
|
62
73
|
headers: opts[:headers] || {},
|
63
|
-
status:
|
64
|
-
data:
|
74
|
+
status: opts[:status] || 200,
|
75
|
+
data: data || {}
|
65
76
|
op.call data, response
|
66
77
|
else
|
67
78
|
raise ArgumentError.new 'Unsupported stub.'
|
@@ -72,6 +83,7 @@ module Acfs
|
|
72
83
|
|
73
84
|
def raise_error(op, name, data)
|
74
85
|
raise name if name.is_a? Class
|
86
|
+
|
75
87
|
data.stringify_keys! if data.respond_to? :stringify_keys!
|
76
88
|
|
77
89
|
op.handle_failure ::Acfs::Response.new op.request, status: Rack::Utils.status_code(name), data: data
|
@@ -83,7 +95,9 @@ module Acfs
|
|
83
95
|
#
|
84
96
|
def resource(klass, action, opts = {}, &_block)
|
85
97
|
action = action.to_sym
|
86
|
-
|
98
|
+
unless ACTIONS.include? action
|
99
|
+
raise ArgumentError.new "Unknown action `#{action}`."
|
100
|
+
end
|
87
101
|
|
88
102
|
Stub.new(opts).tap do |stub|
|
89
103
|
stubs[klass] ||= {}
|
@@ -128,7 +142,9 @@ module Acfs
|
|
128
142
|
|
129
143
|
accepted_stubs = stubs.select {|stub| stub.accept? op }
|
130
144
|
|
131
|
-
|
145
|
+
if accepted_stubs.size > 1
|
146
|
+
raise AmbiguousStubError.new stubs: accepted_stubs, operation: op
|
147
|
+
end
|
132
148
|
|
133
149
|
accepted_stubs.first
|
134
150
|
end
|
@@ -137,6 +153,7 @@ module Acfs
|
|
137
153
|
stub = stub_for op
|
138
154
|
unless stub
|
139
155
|
return false if allow_requests?
|
156
|
+
|
140
157
|
raise RealRequestsNotAllowedError.new <<-MSG.strip.gsub(/^[ ]{12}/, '')
|
141
158
|
No stub found for `#{op.action}' on `#{op.resource.name}' with params `#{op.full_params.inspect}', data `#{op.data.inspect}' and id `#{op.id}'.
|
142
159
|
|
@@ -159,8 +176,12 @@ module Acfs
|
|
159
176
|
stubs.each do |stub|
|
160
177
|
out << " #{action}"
|
161
178
|
out << " with #{stub.opts[:with].inspect}" if stub.opts[:with]
|
162
|
-
|
163
|
-
|
179
|
+
if stub.opts[:return]
|
180
|
+
out << " and return #{stub.opts[:return].inspect}"
|
181
|
+
end
|
182
|
+
if stub.opts[:raise]
|
183
|
+
out << " and raise #{stub.opts[:raise].inspect}"
|
184
|
+
end
|
164
185
|
out << "\n"
|
165
186
|
end
|
166
187
|
end
|