acfs 1.6.0 → 1.7.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 +1 -0
- data/README.md +16 -19
- data/acfs.gemspec +2 -0
- data/lib/acfs/adapter/typhoeus.rb +6 -4
- data/lib/acfs/configuration.rb +13 -3
- data/lib/acfs/errors.rb +1 -1
- data/lib/acfs/global.rb +2 -2
- data/lib/acfs/location.rb +1 -1
- data/lib/acfs/operation.rb +1 -1
- data/lib/acfs/request/callbacks.rb +1 -1
- data/lib/acfs/request.rb +1 -1
- data/lib/acfs/resource/attributes/uuid.rb +1 -1
- data/lib/acfs/resource/locatable.rb +2 -2
- data/lib/acfs/resource/query_methods.rb +2 -2
- data/lib/acfs/runner.rb +15 -15
- data/lib/acfs/stub.rb +34 -29
- data/lib/acfs/version.rb +2 -2
- data/spec/acfs/adapter/typhoeus_spec.rb +1 -1
- data/spec/acfs/collection_spec.rb +66 -41
- data/spec/acfs/configuration_spec.rb +22 -12
- data/spec/acfs/global_spec.rb +9 -7
- data/spec/acfs/middleware/json_spec.rb +8 -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 +15 -19
- 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 +33 -32
- 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} +114 -82
- 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 +5 -4
- 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 -1
- data/spec/support/response.rb +2 -2
- data/spec/support/service.rb +1 -1
- data/spec/support/shared/find_callbacks.rb +14 -10
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3cbc57dd595fa4242fa029498d90ca5a497216d2003674296209b679b84a6df2
|
4
|
+
data.tar.gz: 31a009d88f2a028e2fc11513b68dd011f5cfa0b7f11a83238f167e807c365b35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba37a7c96e65b8774879835d93ccf43a5c91bcd77ce9cf604937544f0897ebf307ffbbf914d5e7304eb90e6f62139e37eb52b2e6406db4e86d454ef45bb4423b
|
7
|
+
data.tar.gz: 976193afda7d9e6d5bbdc3eef3ec7235a90abcaf23e79f055735cb663400c02448a683e734b9f3212b2a27c101fabad4055f0a0c5dff23a517885a7bda4cea43
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -8,14 +8,16 @@
|
|
8
8
|
|
9
9
|
Acfs is a library to develop API client libraries for single services within a larger service oriented application.
|
10
10
|
|
11
|
-
Acfs covers model and service abstraction, convenient query and filter methods, full middleware stack for pre-processing requests and responses
|
11
|
+
Acfs covers model and service abstraction, convenient query and filter methods, full middleware stack for pre-processing requests and responses, as well as automatic request queuing and parallel processing.
|
12
12
|
|
13
13
|
|
14
14
|
## Installation
|
15
15
|
|
16
16
|
Add this line to your application's Gemfile:
|
17
17
|
|
18
|
-
|
18
|
+
```ruby
|
19
|
+
gem 'acfs', '~> 1.7'
|
20
|
+
```
|
19
21
|
|
20
22
|
And then execute:
|
21
23
|
|
@@ -121,7 +123,7 @@ Acfs.run # This call will fire all request as parallel as possible.
|
|
121
123
|
@friends[0].name # => "Miraculix"
|
122
124
|
```
|
123
125
|
|
124
|
-
Use `.find_by` to get first element only. `.find_by` will call the `index`-Action and return the first resource. Optionally passed
|
126
|
+
Use `.find_by` to get first element only. `.find_by` will call the `index`-Action and return the first resource. Optionally passed parameters will be sent as `GET` parameters and can be used for filtering in the service's controller.
|
125
127
|
```ruby
|
126
128
|
@user = User.find_by age: 24
|
127
129
|
|
@@ -179,7 +181,7 @@ my_single.save # sends PUT request to /single
|
|
179
181
|
my_single.delete # sends DELETE request to /single
|
180
182
|
```
|
181
183
|
|
182
|
-
You also can pass parameters to the find call
|
184
|
+
You also can pass parameters to the find call. They will be sent as query parameters to the index action:
|
183
185
|
|
184
186
|
```ruby
|
185
187
|
my_single = Single.find name: 'Max'
|
@@ -189,9 +191,7 @@ Acfs.run # sends GET request with param to /single?name=Max
|
|
189
191
|
|
190
192
|
## Resource Inheritance
|
191
193
|
|
192
|
-
Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a
|
193
|
-
`type` attribute exists and is a valid subclass of your resource they will be converted
|
194
|
-
to you subclassed resources:
|
194
|
+
Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a `type` attribute exists and is a valid subclass of your resource they will be converted to you subclassed resources:
|
195
195
|
|
196
196
|
```ruby
|
197
197
|
class Computer < Acfs::Resource
|
@@ -202,8 +202,7 @@ class Pc < Computer end
|
|
202
202
|
class Mac < Computer end
|
203
203
|
```
|
204
204
|
|
205
|
-
With the following response on `GET /computers` the collection will contain the appropriate
|
206
|
-
subclass resources:
|
205
|
+
With the following response on `GET /computers` the collection will contain the appropriate subclass resources:
|
207
206
|
|
208
207
|
```json
|
209
208
|
[
|
@@ -248,9 +247,9 @@ it 'should find user number one' do
|
|
248
247
|
user = MyUser.find 1
|
249
248
|
Acfs.run
|
250
249
|
|
251
|
-
expect(user.id).to
|
252
|
-
expect(user.name).to
|
253
|
-
expect(user.age).to
|
250
|
+
expect(user.id).to eq 1
|
251
|
+
expect(user.name).to eq 'John Smith'
|
252
|
+
expect(user.age).to eq 32
|
254
253
|
|
255
254
|
expect(@stub).to be_called
|
256
255
|
expect(@stub).to_not be_called 5.times
|
@@ -265,8 +264,8 @@ end
|
|
265
264
|
it 'should allow stub resource creation' do
|
266
265
|
session = Session.create! ident: 'john@exmaple.org', password: 's3cr3t'
|
267
266
|
|
268
|
-
expect(session.id).to
|
269
|
-
expect(session.user).to
|
267
|
+
expect(session.id).to eq 'longhash'
|
268
|
+
expect(session.user).to eq 1
|
270
269
|
end
|
271
270
|
```
|
272
271
|
|
@@ -287,9 +286,7 @@ end
|
|
287
286
|
|
288
287
|
## Instrumentation
|
289
288
|
|
290
|
-
Acfs supports [instrumentation via active support][1]
|
291
|
-
|
292
|
-
Acfs expose to following events
|
289
|
+
Acfs supports [instrumentation via active support][1] and exposes the following events:
|
293
290
|
|
294
291
|
* `acfs.operation.complete(operation, response)`: Acfs operation completed
|
295
292
|
* `acfs.runner.sync_run(operation)`: Run operation right now skipping queue.
|
@@ -297,7 +294,7 @@ Acfs expose to following events
|
|
297
294
|
* `acfs.before_run`: directly before `acfs.run`
|
298
295
|
* `acfs.run`: Run all queued operations.
|
299
296
|
|
300
|
-
Read [official guide][2]
|
297
|
+
Read the [official guide][2] on how to subscribe to these events.
|
301
298
|
|
302
299
|
[1]: http://guides.rubyonrails.org/active_support_instrumentation.html
|
303
300
|
[2]: http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event
|
@@ -318,4 +315,4 @@ Read [official guide][2] to see to to subscribe.
|
|
318
315
|
|
319
316
|
MIT License
|
320
317
|
|
321
|
-
Copyright (c) 2013-
|
318
|
+
Copyright (c) 2013-2022 Jan Graichen. MIT license, see LICENSE for more details.
|
data/acfs.gemspec
CHANGED
@@ -16,6 +16,8 @@ Gem::Specification.new do |spec|
|
|
16
16
|
An abstract API base client for service oriented application.
|
17
17
|
SUMMARY
|
18
18
|
|
19
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
20
|
+
|
19
21
|
spec.files = Dir['**/*'].grep(%r{
|
20
22
|
^((bin|lib|test|spec|features)/|
|
21
23
|
.*\.gemspec|.*LICENSE.*|.*README.*|.*CHANGELOG.*)
|
@@ -7,7 +7,7 @@ module Acfs
|
|
7
7
|
DEFAULT_OPTIONS = {
|
8
8
|
tcp_keepalive: true,
|
9
9
|
tcp_keepidle: 5,
|
10
|
-
tcp_keepintvl: 5
|
10
|
+
tcp_keepintvl: 5,
|
11
11
|
}.freeze
|
12
12
|
|
13
13
|
# Adapter for Typhoeus.
|
@@ -16,8 +16,10 @@ module Acfs
|
|
16
16
|
def initialize(**kwargs)
|
17
17
|
super
|
18
18
|
|
19
|
+
extra_opts = kwargs.delete(:opts)
|
20
|
+
|
19
21
|
@opts = DEFAULT_OPTIONS
|
20
|
-
@opts = @opts.merge(
|
22
|
+
@opts = @opts.merge(extra_opts) if extra_opts
|
21
23
|
@kwargs = kwargs
|
22
24
|
end
|
23
25
|
|
@@ -50,9 +52,9 @@ module Acfs
|
|
50
52
|
params: req.params,
|
51
53
|
headers: req.headers.merge(
|
52
54
|
'Expect' => '',
|
53
|
-
'Transfer-Encoding' => ''
|
55
|
+
'Transfer-Encoding' => '',
|
54
56
|
),
|
55
|
-
body: req.body
|
57
|
+
body: req.body,
|
56
58
|
}
|
57
59
|
|
58
60
|
request = ::Typhoeus::Request.new(req.url, **@opts, **opts)
|
data/lib/acfs/configuration.rb
CHANGED
@@ -28,7 +28,7 @@ module Acfs
|
|
28
28
|
#
|
29
29
|
def configure(&block)
|
30
30
|
if block.arity.positive?
|
31
|
-
|
31
|
+
yield self
|
32
32
|
else
|
33
33
|
instance_eval(&block)
|
34
34
|
end
|
@@ -70,8 +70,8 @@ module Acfs
|
|
70
70
|
# @return [undefined]
|
71
71
|
#
|
72
72
|
def load(filename)
|
73
|
-
config =
|
74
|
-
env
|
73
|
+
config = load_yaml_file(filename)
|
74
|
+
env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
|
75
75
|
|
76
76
|
config = config[env] if config.key? env
|
77
77
|
config.each do |key, value|
|
@@ -116,5 +116,15 @@ module Acfs
|
|
116
116
|
@current = configuration if configuration.is_a? Configuration
|
117
117
|
end
|
118
118
|
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def load_yaml_file(path)
|
123
|
+
if YAML.respond_to?(:safe_load_file)
|
124
|
+
YAML.safe_load_file(path, aliases: true)
|
125
|
+
else
|
126
|
+
YAML.safe_load(File.read(path), [], [], true)
|
127
|
+
end
|
128
|
+
end
|
119
129
|
end
|
120
130
|
end
|
data/lib/acfs/errors.rb
CHANGED
data/lib/acfs/global.rb
CHANGED
@@ -71,12 +71,12 @@ module Acfs
|
|
71
71
|
def add_callback(resource, &block)
|
72
72
|
unless resource.respond_to?(:__callbacks__)
|
73
73
|
raise ArgumentError.new 'Given resource is not an Acfs resource ' \
|
74
|
-
|
74
|
+
"delegator but a: #{resource.class.name}"
|
75
75
|
end
|
76
76
|
return false if block.nil?
|
77
77
|
|
78
78
|
if resource.nil? || resource.loaded?
|
79
|
-
|
79
|
+
yield resource
|
80
80
|
else
|
81
81
|
resource.__callbacks__ << block
|
82
82
|
end
|
data/lib/acfs/location.rb
CHANGED
data/lib/acfs/operation.rb
CHANGED
@@ -56,7 +56,7 @@ module Acfs
|
|
56
56
|
|
57
57
|
def request
|
58
58
|
request = ::Acfs::Request.new url, method: method, params: params,
|
59
|
-
|
59
|
+
data: data, operation: self
|
60
60
|
request.on_complete do |response|
|
61
61
|
::ActiveSupport::Notifications.instrument 'acfs.operation.complete',
|
62
62
|
operation: self,
|
data/lib/acfs/request.rb
CHANGED
@@ -116,8 +116,8 @@ class Acfs::Resource
|
|
116
116
|
return nil if need_primary_key? && !primary_key?
|
117
117
|
|
118
118
|
self.class.service
|
119
|
-
|
120
|
-
|
119
|
+
.location(self.class, **opts, action: :read)
|
120
|
+
.build(attributes).str
|
121
121
|
end
|
122
122
|
|
123
123
|
# @api private
|
@@ -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,7 +210,7 @@ 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
|
|
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
|
data/lib/acfs/stub.rb
CHANGED
@@ -21,11 +21,11 @@ module Acfs
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def accept?(
|
25
|
-
return opts[:with].call(
|
24
|
+
def accept?(operation)
|
25
|
+
return opts[:with].call(operation) if opts[:with].respond_to?(:call)
|
26
26
|
|
27
|
-
params =
|
28
|
-
data =
|
27
|
+
params = operation.full_params.stringify_keys
|
28
|
+
data = operation.data.stringify_keys
|
29
29
|
with = opts[:with]
|
30
30
|
|
31
31
|
return true if with.nil?
|
@@ -33,10 +33,10 @@ module Acfs
|
|
33
33
|
case opts.fetch(:match, :inclusion)
|
34
34
|
when :legacy
|
35
35
|
return true if with.empty? && params.empty? && data.empty?
|
36
|
-
if with.
|
36
|
+
if with.compact == params.compact
|
37
37
|
return true
|
38
38
|
end
|
39
|
-
if with.
|
39
|
+
if with.compact == data.compact
|
40
40
|
return true
|
41
41
|
end
|
42
42
|
|
@@ -58,22 +58,25 @@ module Acfs
|
|
58
58
|
count.nil? ? calls.any? : calls.size == count
|
59
59
|
end
|
60
60
|
|
61
|
-
def call(
|
62
|
-
calls <<
|
61
|
+
def call(operation)
|
62
|
+
calls << operation
|
63
63
|
|
64
64
|
err = opts[:raise]
|
65
65
|
data = opts[:return]
|
66
66
|
|
67
67
|
if err
|
68
|
-
raise_error
|
68
|
+
raise_error(operation, err, opts[:return])
|
69
69
|
elsif data
|
70
|
-
data = data.call(
|
70
|
+
data = data.call(operation) if data.respond_to?(:call)
|
71
71
|
|
72
|
-
response = Acfs::Response.new
|
72
|
+
response = Acfs::Response.new(
|
73
|
+
operation.request,
|
73
74
|
headers: opts[:headers] || {},
|
74
75
|
status: opts[:status] || 200,
|
75
|
-
data: data || {}
|
76
|
-
|
76
|
+
data: data || {},
|
77
|
+
)
|
78
|
+
|
79
|
+
operation.call(data, response)
|
77
80
|
else
|
78
81
|
raise ArgumentError.new 'Unsupported stub.'
|
79
82
|
end
|
@@ -81,15 +84,17 @@ module Acfs
|
|
81
84
|
|
82
85
|
private
|
83
86
|
|
84
|
-
def raise_error(
|
87
|
+
def raise_error(operation, name, data)
|
85
88
|
raise name if name.is_a? Class
|
86
89
|
|
87
90
|
data.stringify_keys! if data.respond_to?(:stringify_keys!)
|
88
91
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
92
|
+
operation.handle_failure(
|
93
|
+
::Acfs::Response.new(
|
94
|
+
operation.request,
|
95
|
+
status: Rack::Utils.status_code(name),
|
96
|
+
data: data,
|
97
|
+
),
|
93
98
|
)
|
94
99
|
end
|
95
100
|
|
@@ -140,35 +145,35 @@ module Acfs
|
|
140
145
|
@stubs ||= {}
|
141
146
|
end
|
142
147
|
|
143
|
-
def stub_for(
|
144
|
-
return false unless (classes = stubs[
|
145
|
-
return false unless (stubs = classes[
|
148
|
+
def stub_for(operation)
|
149
|
+
return false unless (classes = stubs[operation.resource])
|
150
|
+
return false unless (stubs = classes[operation.action])
|
146
151
|
|
147
|
-
accepted_stubs = stubs.select {|stub| stub.accept?
|
152
|
+
accepted_stubs = stubs.select {|stub| stub.accept?(operation) }
|
148
153
|
|
149
154
|
if accepted_stubs.size > 1
|
150
|
-
raise AmbiguousStubError.new
|
155
|
+
raise AmbiguousStubError.new(stubs: accepted_stubs, operation: operation)
|
151
156
|
end
|
152
157
|
|
153
158
|
accepted_stubs.first
|
154
159
|
end
|
155
160
|
|
156
|
-
def stubbed(
|
157
|
-
stub = stub_for
|
161
|
+
def stubbed(operation)
|
162
|
+
stub = stub_for(operation)
|
158
163
|
unless stub
|
159
164
|
return false if allow_requests?
|
160
165
|
|
161
166
|
raise RealRequestsNotAllowedError.new <<~ERROR
|
162
|
-
No stub found for `#{
|
163
|
-
with params `#{
|
164
|
-
and id `#{
|
167
|
+
No stub found for `#{operation.action}' on `#{operation.resource.name}' \
|
168
|
+
with params `#{operation.full_params.inspect}', data `#{operation.data.inspect}' \
|
169
|
+
and id `#{operation.id}'.
|
165
170
|
|
166
171
|
Available stubs:
|
167
172
|
#{pretty_print}
|
168
173
|
ERROR
|
169
174
|
end
|
170
175
|
|
171
|
-
stub.call
|
176
|
+
stub.call(operation)
|
172
177
|
true
|
173
178
|
end
|
174
179
|
|
data/lib/acfs/version.rb
CHANGED