acfs 1.4.0 → 1.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f998ff3e28801676c732fa44df93e18f651e96eb986c857c61b7f25b312a2c1
4
- data.tar.gz: a122064cb630a664489629ef658d5ecde917ced4cdc43a600b49d0e18e00bea7
3
+ metadata.gz: 53acc88297d3217a5d01db2ce713f935835b05438471fd2b69918029b5480954
4
+ data.tar.gz: d61a2f4e5014dab37d4e1a857d548df8dfb42afcaba219005556f203ea68da65
5
5
  SHA512:
6
- metadata.gz: 7801c96d794f08ad03b9ad204f8e7c8b611300922b12714ce7864a33c5a6268bc47c42e66b86c410358f17a0439d382c358ba15283eee07ff092d19a1f86a8be
7
- data.tar.gz: 78b2c804251fb6aaee270d9c894ff36b7b8dc7bb777148de55963255847fc28adcdbff9c7132352c5448bef7b9980fd72e6a14ffceda38540d661aceaf1e5e9d
6
+ metadata.gz: 35f66d3569256b183cb7c78fed7c41f0ce916f41ec081e60696693af8b869d47e797a53474942c4847cefb621e58d3d52ddf629168c63b7b2d558d1db23cf74a
7
+ data.tar.gz: 5e03be0b16cb469061b55a3c4467a4fa71496925caa086ed5d024ed79aaefdcc568afd970d526c284b08b8db06593f51886d8642f035f78f2ed7f89bd1b8f311
@@ -14,6 +14,16 @@
14
14
  ### Breaks
15
15
 
16
16
 
17
+ ## 1.5.0 - (2020-06-19)
18
+ ---
19
+
20
+ ### New
21
+ * Error classes for more HTTP error responses: `400`, `401`, `403`, `500`, `502`, `503`, `504`.
22
+
23
+ ### Changes
24
+ * Replace deprecated MultiJson with core JSON module
25
+
26
+
17
27
  ## 1.4.0 - (2020-06-12)
18
28
  ---
19
29
 
data/README.md CHANGED
@@ -10,6 +10,7 @@ Acfs is a library to develop API client libraries for single services within a l
10
10
 
11
11
  Acfs covers model and service abstraction, convenient query and filter methods, full middleware stack for pre-processing requests and responses on a per service level and automatic request queuing and parallel processing. See Usage for more.
12
12
 
13
+
13
14
  ## Installation
14
15
 
15
16
  Add this line to your application's Gemfile:
@@ -24,6 +25,7 @@ Or install it yourself as:
24
25
 
25
26
  > gem install acfs
26
27
 
28
+
27
29
  ## Usage
28
30
 
29
31
  First you need to define your service(s):
@@ -145,6 +147,7 @@ Acfs has basic update support using `PUT` requests:
145
147
  @user.persisted? # => true
146
148
  ```
147
149
 
150
+
148
151
  ## Singleton resources
149
152
 
150
153
  Singletons can be used in Acfs by creating a new resource which inherits from `SingletonResource`:
@@ -183,6 +186,7 @@ my_single = Single.find name: 'Max'
183
186
  Acfs.run # sends GET request with param to /single?name=Max
184
187
  ```
185
188
 
189
+
186
190
  ## Resource Inheritance
187
191
 
188
192
  Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a
@@ -219,6 +223,7 @@ Acfs.run
219
223
  @computer[2].class # => Pc
220
224
  ```
221
225
 
226
+
222
227
  ## Stubbing
223
228
 
224
229
  You can stub resources in applications using an Acfs service client:
@@ -279,6 +284,7 @@ it 'should find user number one' do
279
284
  end
280
285
  ```
281
286
 
287
+
282
288
  ## Instrumentation
283
289
 
284
290
  Acfs supports [instrumentation via active support][1].
@@ -296,21 +302,6 @@ Read [official guide][2] to see to to subscribe.
296
302
  [1]: http://guides.rubyonrails.org/active_support_instrumentation.html
297
303
  [2]: http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event
298
304
 
299
- ## Roadmap
300
-
301
- * Update
302
- * Better new? detection eg. storing ETag from request resources.
303
- * Use PATCH for with only changed attributes and `If-Unmodifed-Since`
304
- and `If-Match` header fields if resource was surly loaded from service
305
- and not created with an id (e.g `User.new id: 5, name: "john"`).
306
- * Conflict detection (ETag / If-Unmodified-Since)
307
- * High level features
308
- * Support for custom mime types on client and server side. (`application/vnd.myservice.user.v2+msgpack`)
309
- * Server side components
310
- * Reusing model definitions for generating responses?
311
- * Rails responders providing REST operations with integrated ETag,
312
- Modified Headers, conflict detection, ...
313
- * Documentation
314
305
 
315
306
  ## Contributing
316
307
 
@@ -322,14 +313,9 @@ Read [official guide][2] to see to to subscribe.
322
313
  7. Push to the branch (`git push origin my-new-feature`)
323
314
  8. Create new Pull Request
324
315
 
325
- ## Contributors
326
-
327
- * [Nicolas Fricke](https://github.com/nicolas-fricke)
328
- * [Tino Junge](https://github.com/tino-junge)
329
- * [Malte Swart](https://github.com/mswart)
330
316
 
331
317
  ## License
332
318
 
333
319
  MIT License
334
320
 
335
- Copyright (c) 2013 Jan Graichen. MIT license, see LICENSE for more details.
321
+ Copyright (c) 2013-2020 Jan Graichen. MIT license, see LICENSE for more details.
@@ -25,15 +25,11 @@ 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.add_runtime_dependency 'actionpack', '>= 4.2'
29
- spec.add_runtime_dependency 'activemodel', '>= 4.2'
30
- spec.add_runtime_dependency 'activesupport', '>= 4.2'
31
- spec.add_runtime_dependency 'multi_json'
32
-
33
- # Bundle update w/o version resolves to 0.3.3 ...
34
- spec.add_runtime_dependency 'typhoeus', '~> 1.0'
35
-
28
+ spec.add_runtime_dependency 'actionpack', '>= 5.2'
29
+ spec.add_runtime_dependency 'activemodel', '>= 5.2'
30
+ spec.add_runtime_dependency 'activesupport', '>= 5.2'
36
31
  spec.add_runtime_dependency 'rack'
32
+ spec.add_runtime_dependency 'typhoeus', '~> 1.0'
37
33
 
38
34
  spec.add_development_dependency 'bundler'
39
35
 
@@ -55,14 +55,14 @@ module Acfs
55
55
  request = ::Typhoeus::Request.new(req.url, **@opts.merge(opts))
56
56
 
57
57
  request.on_complete do |response|
58
- if response.timed_out?
59
- raise ::Acfs::TimeoutError.new(req)
60
- elsif response.code.zero?
58
+ raise ::Acfs::TimeoutError.new(req) if response.timed_out?
59
+
60
+ if response.code.zero?
61
61
  # Failed to get HTTP response
62
62
  raise ::Acfs::RequestError.new(req, response.return_message)
63
- else
64
- req.complete! convert_response(req, response)
65
63
  end
64
+
65
+ req.complete! convert_response(req, response)
66
66
  end
67
67
 
68
68
  request
@@ -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
- rescue ArgumentError
72
- params[:page]
71
+ rescue ArgumentError
72
+ params[:page]
73
73
  end
74
74
  end
75
75
  end
@@ -26,7 +26,7 @@ module Acfs
26
26
 
27
27
  class TimeoutError < RequestError
28
28
  def initialize(request)
29
- super(request, "Timeout reached")
29
+ super(request, 'Timeout reached')
30
30
  end
31
31
  end
32
32
 
@@ -38,13 +38,13 @@ module Acfs
38
38
  def initialize(opts = {})
39
39
  @response = opts[:response]
40
40
 
41
- if response
42
- message = (opts[:message] ? opts[:message] + ':' : 'Received') +
43
- " #{response.code} for #{response.request.method.upcase}" \
44
- " #{response.request.url} #{response.request.format}"
45
- else
46
- message = opts[:message] || 'Received erroneous response'
47
- end
41
+ message = if response
42
+ (opts[:message] ? opts[:message] + ':' : 'Received') +
43
+ " #{response.code} for #{response.request.method.upcase}" \
44
+ " #{response.request.url} #{response.request.format}"
45
+ else
46
+ opts[:message] || 'Received erroneous response'
47
+ end
48
48
 
49
49
  super opts, message
50
50
  end
@@ -67,11 +67,19 @@ module Acfs
67
67
  end
68
68
  end
69
69
 
70
- # Resource not found error raised on a 404 response
71
- #
72
- class ResourceNotFound < ErroneousResponse
73
- end
70
+ # 400
71
+ class BadRequest < ErroneousResponse; end
72
+
73
+ # 401
74
+ class Unauthorized < ErroneousResponse; end
75
+
76
+ # 403
77
+ class Forbidden < ErroneousResponse; end
74
78
 
79
+ # 404
80
+ class ResourceNotFound < ErroneousResponse; end
81
+
82
+ # 422
75
83
  class InvalidResource < ErroneousResponse
76
84
  attr_reader :errors, :resource
77
85
 
@@ -91,6 +99,18 @@ module Acfs
91
99
  end
92
100
  end
93
101
 
102
+ # 500
103
+ class ServerError < ErroneousResponse; end
104
+
105
+ # 502
106
+ class BadGateway < ErroneousResponse; end
107
+
108
+ # 503
109
+ class ServiceUnavailable < ErroneousResponse; end
110
+
111
+ # 504
112
+ class GatewayTimeout < ErroneousResponse; end
113
+
94
114
  # A ResourceNotLoaded error will be thrown when calling some
95
115
  # modifing methods on not loaded resources as it is usally
96
116
  # unwanted to call e.g. `update_attributes` on a not loaded
@@ -49,7 +49,7 @@ module Acfs
49
49
  def extract_arg(key, hashes)
50
50
  hashes.each_with_index do |hash, index|
51
51
  if hash.key?(key)
52
- return (index == 0 ? hash.delete(key) : hash.fetch(key))
52
+ return (index.zero? ? hash.delete(key) : hash.fetch(key))
53
53
  end
54
54
  end
55
55
 
@@ -72,9 +72,9 @@ module Acfs
72
72
  args.fetch(sym) do
73
73
  if args[:raise].nil? || args[:raise]
74
74
  raise ArgumentError.new "URI path argument `#{sym}' missing on `#{self}'. Given: `#{args}.inspect'"
75
- else
76
- ":#{sym}"
77
75
  end
76
+
77
+ ":#{sym}"
78
78
  end
79
79
  end
80
80
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'multi_json'
3
+ require 'json'
4
4
 
5
5
  module Acfs
6
6
  module Middleware
@@ -12,11 +12,11 @@ module Acfs
12
12
  end
13
13
 
14
14
  def encode(data)
15
- ::MultiJson.dump data
15
+ ::JSON.dump data
16
16
  end
17
17
 
18
18
  def decode(body)
19
- ::MultiJson.load body
19
+ ::JSON.load body
20
20
  end
21
21
  end
22
22
 
@@ -8,6 +8,7 @@ module Acfs
8
8
  #
9
9
  class Operation
10
10
  attr_reader :action, :params, :resource, :data, :callback, :location, :url
11
+
11
12
  delegate :service, to: :resource
12
13
  delegate :call, to: :callback
13
14
 
@@ -71,10 +72,24 @@ module Acfs
71
72
 
72
73
  def handle_failure(response)
73
74
  case response.code
75
+ when 400
76
+ raise ::Acfs::BadRequest.new response: response
77
+ when 401
78
+ raise ::Acfs::Unauthorized.new response: response
79
+ when 403
80
+ raise ::Acfs::Forbidden.new response: response
74
81
  when 404
75
82
  raise ::Acfs::ResourceNotFound.new response: response
76
83
  when 422
77
84
  raise ::Acfs::InvalidResource.new response: response, errors: response.data.try(:[], 'errors')
85
+ when 500
86
+ raise ::Acfs::ServerError.new response: response
87
+ when 502
88
+ raise ::Acfs::BadGateway.new response: response
89
+ when 503
90
+ raise ::Acfs::ServiceUnavailable.new response: response
91
+ when 504
92
+ raise ::Acfs::GatewayTimeout.new response: response
78
93
  else
79
94
  raise ::Acfs::ErroneousResponse.new response: response
80
95
  end
@@ -11,7 +11,6 @@ module Acfs
11
11
  attr_reader :url, :headers, :params, :data, :method, :operation
12
12
 
13
13
  include Request::Callbacks
14
-
15
14
  def initialize(url, options = {}, &block)
16
15
  @url = URI.parse(url.to_s).tap do |_url|
17
16
  @data = options.delete(:data) || nil
@@ -20,7 +19,9 @@ module Acfs
20
19
  @params = options.delete(:params) || {}
21
20
  @method = options.delete(:method) || :get
22
21
  end.to_s
22
+
23
23
  @operation = options.delete(:operation) || nil
24
+
24
25
  on_complete(&block) if block_given?
25
26
  end
26
27
 
@@ -45,9 +45,9 @@ module Acfs
45
45
  private
46
46
 
47
47
  def call_callback(res, index)
48
- if index < callbacks.size
49
- callbacks[index].call res, proc {|bres| call_callback bres, index + 1 }
50
- end
48
+ return if index >= callbacks.size
49
+
50
+ callbacks[index].call(res, proc {|bres| call_callback bres, index + 1 })
51
51
  end
52
52
  end
53
53
  end
@@ -48,9 +48,11 @@ class Acfs::Resource
48
48
  # @return [HashWithIndifferentAccess{Symbol => Object}]
49
49
  # Attributes and their values.
50
50
  #
51
+ # rubocop:disable Naming/MemoizedInstanceVariableName
51
52
  def attributes
52
53
  @_attrs ||= HashWithIndifferentAccess.new
53
54
  end
55
+ # rubocop:enable Naming/MemoizedInstanceVariableName
54
56
 
55
57
  # @api public
56
58
  #
@@ -244,8 +244,6 @@ class Acfs::Resource
244
244
  end
245
245
  end
246
246
 
247
- private
248
-
249
247
  def update_with(data)
250
248
  self.attributes = data
251
249
  loaded!
@@ -258,7 +258,7 @@ class Acfs::Resource
258
258
  end
259
259
 
260
260
  klass
261
- rescue NameError, NoMethodError
261
+ rescue NameError
262
262
  raise Acfs::ResourceTypeError.new type_name: type, base_class: self
263
263
  end
264
264
  end
@@ -40,21 +40,23 @@ module Acfs
40
40
 
41
41
  action = opts[:action] || :list
42
42
 
43
- path = if opts[:path].is_a?(Hash) && opts[:path].key?(action)
44
- opts[:path].fetch(action)
45
- else
46
- path = if opts[:path].is_a?(Hash)
47
- opts[:path][:all].to_s
48
- else
49
- opts[:path].to_s
50
- end
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
51
52
 
52
- if path.blank?
53
- path = (resource_class.name || 'class').pluralize.underscore
54
- end
53
+ if path.blank?
54
+ path = (resource_class.name || 'class').pluralize.underscore
55
+ end
55
56
 
56
- resource_class.location_default_path(action, path.strip)
57
- end
57
+ resource_class.location_default_path(action, path.strip)
58
+ end
59
+ end
58
60
 
59
61
  if path.nil?
60
62
  raise ArgumentError.new "Location for `#{action}' explicit disabled by set to nil."
@@ -87,7 +89,10 @@ module Acfs
87
89
  #
88
90
  def base_url
89
91
  unless (base = Acfs::Configuration.current.locate identity)
90
- raise ArgumentError.new "#{identity} not configured. Add `locate '#{identity.to_s.underscore}', 'http://service.url/'` to your configuration."
92
+ raise ArgumentError.new \
93
+ "#{identity} not configured. Add `locate '" \
94
+ "#{identity.to_s.underscore}', 'http://service.url/'` " \
95
+ 'to your configuration.'
91
96
  end
92
97
 
93
98
  base.to_s
@@ -16,7 +16,7 @@ module Acfs
16
16
  @opts[:with].stringify_keys! if @opts[:with].is_a? Hash
17
17
  @opts[:return].stringify_keys! if @opts[:return].is_a? Hash
18
18
 
19
- if @opts[:return].is_a? Array
19
+ if @opts[:return].is_a?(Array) # rubocop:disable Style/GuardClause
20
20
  @opts[:return].map! {|h| h.stringify_keys! if h.is_a? Hash }
21
21
  end
22
22
  end
@@ -53,9 +53,8 @@ module Acfs
53
53
  end
54
54
 
55
55
  def called?(count = nil)
56
- if count.respond_to? :count
57
- count = count.count
58
- end # For `5.times` Enumerators
56
+ count = count.count if count.respond_to?(:count)
57
+
59
58
  count.nil? ? calls.any? : calls.size == count
60
59
  end
61
60
 
@@ -3,7 +3,7 @@
3
3
  module Acfs
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 4
6
+ MINOR = 5
7
7
  PATCH = 0
8
8
  STAGE = nil
9
9
 
@@ -122,8 +122,8 @@ describe ::Acfs::Global do
122
122
 
123
123
  describe '#runner' do
124
124
  it 'returns per-thread runner' do
125
- runner1 = Thread.new { acfs.runner } .value
126
- runner2 = Thread.new { acfs.runner } .value
125
+ runner1 = Thread.new { acfs.runner }.value
126
+ runner2 = Thread.new { acfs.runner }.value
127
127
 
128
128
  expect(runner1).to_not equal runner2
129
129
  end
@@ -47,7 +47,7 @@ describe Acfs::Middleware::JSON do
47
47
  let(:body) { data.to_json[4..-4] }
48
48
 
49
49
  it 'should raise an error' do
50
- expect { request.complete! response }.to raise_error(MultiJson::LoadError)
50
+ expect { request.complete! response }.to raise_error(::JSON::ParserError)
51
51
  end
52
52
  end
53
53
 
@@ -35,7 +35,7 @@ describe Acfs::Resource::Attributes::Float do
35
35
 
36
36
  context 'with -Infinity' do
37
37
  let(:value) { '-Infinity' }
38
- it { expect(subject.call).to eq -::Float::INFINITY }
38
+ it { expect(subject.call).to eq(-::Float::INFINITY) }
39
39
  end
40
40
 
41
41
  context 'with NaN' do
@@ -85,11 +85,9 @@ describe Acfs::Resource::Attributes do
85
85
 
86
86
  it do
87
87
  expect do
88
- begin
89
- subject.call
90
- rescue StandardError
91
- true
92
- end
88
+ subject.call
89
+ rescue StandardError
90
+ true
93
91
  end.to_not change(m, :attributes)
94
92
  end
95
93
  end
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.0
4
+ version: 1.5.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: 2020-06-12 00:00:00.000000000 Z
11
+ date: 2020-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -16,44 +16,44 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.2'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4.2'
26
+ version: '5.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '4.2'
33
+ version: '5.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '4.2'
40
+ version: '5.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: activesupport
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '4.2'
47
+ version: '5.2'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '4.2'
54
+ version: '5.2'
55
55
  - !ruby/object:Gem::Dependency
56
- name: multi_json
56
+ name: rack
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.0'
83
- - !ruby/object:Gem::Dependency
84
- name: rack
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :runtime
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: bundler
99
85
  requirement: !ruby/object:Gem::Requirement