acfs 1.3.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -1
  3. data/README.md +10 -25
  4. data/acfs.gemspec +20 -15
  5. data/lib/acfs.rb +2 -0
  6. data/lib/acfs/adapter/base.rb +6 -8
  7. data/lib/acfs/adapter/typhoeus.rb +25 -6
  8. data/lib/acfs/collection.rb +2 -1
  9. data/lib/acfs/collections/paginatable.rb +4 -3
  10. data/lib/acfs/configuration.rb +14 -7
  11. data/lib/acfs/errors.rb +60 -19
  12. data/lib/acfs/global.rb +12 -2
  13. data/lib/acfs/location.rb +9 -5
  14. data/lib/acfs/middleware/base.rb +5 -1
  15. data/lib/acfs/middleware/json.rb +5 -3
  16. data/lib/acfs/middleware/logger.rb +2 -0
  17. data/lib/acfs/middleware/msgpack.rb +2 -0
  18. data/lib/acfs/middleware/print.rb +2 -0
  19. data/lib/acfs/middleware/serializer.rb +2 -0
  20. data/lib/acfs/operation.rb +20 -3
  21. data/lib/acfs/request.rb +5 -1
  22. data/lib/acfs/request/callbacks.rb +5 -1
  23. data/lib/acfs/resource.rb +2 -0
  24. data/lib/acfs/resource/attributes.rb +5 -2
  25. data/lib/acfs/resource/attributes/base.rb +2 -1
  26. data/lib/acfs/resource/attributes/boolean.rb +2 -0
  27. data/lib/acfs/resource/attributes/date_time.rb +2 -1
  28. data/lib/acfs/resource/attributes/dict.rb +2 -0
  29. data/lib/acfs/resource/attributes/float.rb +5 -3
  30. data/lib/acfs/resource/attributes/integer.rb +2 -0
  31. data/lib/acfs/resource/attributes/list.rb +2 -0
  32. data/lib/acfs/resource/attributes/string.rb +2 -0
  33. data/lib/acfs/resource/attributes/uuid.rb +4 -3
  34. data/lib/acfs/resource/dirty.rb +2 -0
  35. data/lib/acfs/resource/initialization.rb +2 -0
  36. data/lib/acfs/resource/loadable.rb +2 -0
  37. data/lib/acfs/resource/locatable.rb +10 -6
  38. data/lib/acfs/resource/operational.rb +2 -1
  39. data/lib/acfs/resource/persistence.rb +7 -6
  40. data/lib/acfs/resource/query_methods.rb +6 -4
  41. data/lib/acfs/resource/service.rb +3 -1
  42. data/lib/acfs/resource/validation.rb +3 -1
  43. data/lib/acfs/response.rb +2 -0
  44. data/lib/acfs/response/formats.rb +2 -0
  45. data/lib/acfs/response/status.rb +3 -1
  46. data/lib/acfs/rspec.rb +2 -0
  47. data/lib/acfs/runner.rb +6 -1
  48. data/lib/acfs/service.rb +24 -13
  49. data/lib/acfs/service/middleware.rb +2 -0
  50. data/lib/acfs/service/middleware/stack.rb +5 -3
  51. data/lib/acfs/singleton_resource.rb +4 -2
  52. data/lib/acfs/stub.rb +32 -11
  53. data/lib/acfs/util.rb +2 -0
  54. data/lib/acfs/version.rb +4 -2
  55. data/lib/acfs/yard.rb +1 -0
  56. data/spec/acfs/adapter/typhoeus_spec.rb +30 -3
  57. data/spec/acfs/collection_spec.rb +7 -5
  58. data/spec/acfs/configuration_spec.rb +2 -0
  59. data/spec/acfs/global_spec.rb +50 -3
  60. data/spec/acfs/location_spec.rb +2 -0
  61. data/spec/acfs/middleware/json_spec.rb +3 -1
  62. data/spec/acfs/middleware/msgpack_spec.rb +2 -0
  63. data/spec/acfs/operation_spec.rb +2 -0
  64. data/spec/acfs/request/callbacks_spec.rb +2 -0
  65. data/spec/acfs/request_spec.rb +3 -1
  66. data/spec/acfs/resource/attributes/boolean_spec.rb +2 -0
  67. data/spec/acfs/resource/attributes/date_time_spec.rb +2 -0
  68. data/spec/acfs/resource/attributes/dict_spec.rb +4 -2
  69. data/spec/acfs/resource/attributes/float_spec.rb +3 -1
  70. data/spec/acfs/resource/attributes/integer_spec.rb +2 -0
  71. data/spec/acfs/resource/attributes/list_spec.rb +5 -3
  72. data/spec/acfs/resource/attributes/uuid_spec.rb +2 -0
  73. data/spec/acfs/resource/attributes_spec.rb +8 -8
  74. data/spec/acfs/resource/dirty_spec.rb +2 -0
  75. data/spec/acfs/resource/initialization_spec.rb +8 -2
  76. data/spec/acfs/resource/loadable_spec.rb +2 -0
  77. data/spec/acfs/resource/locatable_spec.rb +2 -0
  78. data/spec/acfs/resource/persistance_spec.rb +10 -4
  79. data/spec/acfs/resource/query_methods_spec.rb +24 -17
  80. data/spec/acfs/resource/validation_spec.rb +2 -0
  81. data/spec/acfs/response/formats_spec.rb +3 -1
  82. data/spec/acfs/response/status_spec.rb +2 -0
  83. data/spec/acfs/runner_spec.rb +6 -8
  84. data/spec/acfs/service/middleware_spec.rb +2 -0
  85. data/spec/acfs/service_spec.rb +3 -1
  86. data/spec/acfs/singleton_resource_spec.rb +2 -0
  87. data/spec/acfs/stub_spec.rb +2 -0
  88. data/spec/acfs_spec.rb +2 -0
  89. data/spec/spec_helper.rb +3 -1
  90. data/spec/support/hash.rb +2 -0
  91. data/spec/support/response.rb +2 -0
  92. data/spec/support/service.rb +1 -0
  93. data/spec/support/shared/find_callbacks.rb +2 -0
  94. metadata +13 -28
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs
2
4
  #
3
5
  # Global Acfs module methods.
@@ -73,7 +75,7 @@ module Acfs
73
75
  end
74
76
  return false if block.nil?
75
77
 
76
- if resource.loaded?
78
+ if resource.nil? || resource.loaded?
77
79
  block.call resource
78
80
  else
79
81
  resource.__callbacks__ << block
@@ -81,9 +83,17 @@ module Acfs
81
83
  end
82
84
 
83
85
  def on(*resources)
86
+ # If all resources have already been loaded, we run the callback immediately.
87
+ if resources.all? {|res| res.nil? || res.loaded? }
88
+ yield(*resources)
89
+ return
90
+ end
91
+
92
+ # Otherwise, we add a callback to *each* resource with a guard that ensures
93
+ # that only the very last resource being loaded executes the callback.
84
94
  resources.each do |resource|
85
95
  add_callback resource do |_|
86
- yield(*resources) unless resources.any? {|res| !res.loaded? }
96
+ yield(*resources) if resources.all? {|res| res.nil? || res.loaded? }
87
97
  end
88
98
  end
89
99
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs
2
4
  # @api private
3
5
  #
@@ -6,7 +8,7 @@ module Acfs
6
8
  class Location
7
9
  attr_reader :arguments, :raw, :struct, :args
8
10
 
9
- REGEXP = /^:([A-z][A-z0-9_]*)$/
11
+ REGEXP = /^:([A-z][A-z0-9_]*)$/.freeze
10
12
 
11
13
  def initialize(uri, args = {})
12
14
  @raw = URI.parse uri
@@ -40,13 +42,15 @@ module Acfs
40
42
  def raw_uri
41
43
  raw.to_s
42
44
  end
43
- alias_method :to_s, :raw_uri
45
+ alias to_s raw_uri
44
46
 
45
47
  private
46
48
 
47
49
  def extract_arg(key, hashes)
48
50
  hashes.each_with_index do |hash, index|
49
- return (index == 0 ? hash.delete(key) : hash.fetch(key)) if hash.key?(key)
51
+ if hash.key?(key)
52
+ return (index.zero? ? hash.delete(key) : hash.fetch(key))
53
+ end
50
54
  end
51
55
 
52
56
  nil
@@ -68,9 +72,9 @@ module Acfs
68
72
  args.fetch(sym) do
69
73
  if args[:raise].nil? || args[:raise]
70
74
  raise ArgumentError.new "URI path argument `#{sym}' missing on `#{self}'. Given: `#{args}.inspect'"
71
- else
72
- ":#{sym}"
73
75
  end
76
+
77
+ ":#{sym}"
74
78
  end
75
79
  end
76
80
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs
2
4
  module Middleware
3
5
  # A base middleware that does not modify request or response.
@@ -12,7 +14,9 @@ module Acfs
12
14
  end
13
15
 
14
16
  def call(request)
15
- request.on_complete {|res, nxt| response(res, nxt) } if respond_to? :response
17
+ if respond_to? :response
18
+ request.on_complete {|res, nxt| response(res, nxt) }
19
+ end
16
20
  app.call(request)
17
21
  end
18
22
  end
@@ -1,4 +1,6 @@
1
- require 'multi_json'
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
2
4
 
3
5
  module Acfs
4
6
  module Middleware
@@ -10,11 +12,11 @@ module Acfs
10
12
  end
11
13
 
12
14
  def encode(data)
13
- ::MultiJson.dump data
15
+ ::JSON.dump data
14
16
  end
15
17
 
16
18
  def decode(body)
17
- ::MultiJson.load body
19
+ ::JSON.load body
18
20
  end
19
21
  end
20
22
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
 
3
5
  module Acfs
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'msgpack'
2
4
  require 'action_dispatch'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs
2
4
  module Middleware
3
5
  # Print resquests and response on terminal
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs
2
4
  module Middleware
3
5
  # A base middleware that does not modify request or response.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs
2
4
  # @api private
3
5
  #
@@ -6,6 +8,7 @@ module Acfs
6
8
  #
7
9
  class Operation
8
10
  attr_reader :action, :params, :resource, :data, :callback, :location, :url
11
+
9
12
  delegate :service, to: :resource
10
13
  delegate :call, to: :callback
11
14
 
@@ -19,7 +22,7 @@ module Acfs
19
22
  @data = (opts[:data] || {}).dup
20
23
 
21
24
  if opts[:url]
22
- @url = opts[:url]
25
+ @url = opts[:url]
23
26
  else
24
27
  @location = resource.location(action: @action).extract_from(@params, @data)
25
28
  @url = location.str
@@ -29,11 +32,11 @@ module Acfs
29
32
  end
30
33
 
31
34
  def single?
32
- [:read, :update, :delete].include? action
35
+ %i[read update delete].include? action
33
36
  end
34
37
 
35
38
  def synchronous?
36
- [:update, :delete, :create].include? action
39
+ %i[update delete create].include? action
37
40
  end
38
41
 
39
42
  def id
@@ -69,10 +72,24 @@ module Acfs
69
72
 
70
73
  def handle_failure(response)
71
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
72
81
  when 404
73
82
  raise ::Acfs::ResourceNotFound.new response: response
74
83
  when 422
75
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
76
93
  else
77
94
  raise ::Acfs::ErroneousResponse.new response: response
78
95
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'acfs/request/callbacks'
2
4
 
3
5
  module Acfs
@@ -9,7 +11,6 @@ module Acfs
9
11
  attr_reader :url, :headers, :params, :data, :method, :operation
10
12
 
11
13
  include Request::Callbacks
12
-
13
14
  def initialize(url, options = {}, &block)
14
15
  @url = URI.parse(url.to_s).tap do |_url|
15
16
  @data = options.delete(:data) || nil
@@ -18,7 +19,9 @@ module Acfs
18
19
  @params = options.delete(:params) || {}
19
20
  @method = options.delete(:method) || :get
20
21
  end.to_s
22
+
21
23
  @operation = options.delete(:operation) || nil
24
+
22
25
  on_complete(&block) if block_given?
23
26
  end
24
27
 
@@ -29,6 +32,7 @@ module Acfs
29
32
  class << self
30
33
  def new(*attrs)
31
34
  return attrs[0] if attrs[0].is_a? self
35
+
32
36
  super
33
37
  end
34
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs
2
4
  class Request
3
5
  # Module containing callback handling for Requests.
@@ -43,7 +45,9 @@ module Acfs
43
45
  private
44
46
 
45
47
  def call_callback(res, index)
46
- callbacks[index].call res, proc {|bres| call_callback bres, index + 1 } if index < callbacks.size
48
+ return if index >= callbacks.size
49
+
50
+ callbacks[index].call(res, proc {|bres| call_callback bres, index + 1 })
47
51
  end
48
52
  end
49
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_model'
2
4
 
3
5
  # @api public
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Acfs::Resource
2
4
  #
3
5
  # = Acfs Attributes
@@ -46,9 +48,11 @@ class Acfs::Resource
46
48
  # @return [HashWithIndifferentAccess{Symbol => Object}]
47
49
  # Attributes and their values.
48
50
  #
51
+ # rubocop:disable Naming/MemoizedInstanceVariableName
49
52
  def attributes
50
53
  @_attrs ||= HashWithIndifferentAccess.new
51
54
  end
55
+ # rubocop:enable Naming/MemoizedInstanceVariableName
52
56
 
53
57
  # @api public
54
58
  #
@@ -176,9 +180,8 @@ class Acfs::Resource
176
180
  attributes[name.to_s] = value
177
181
  end
178
182
 
179
- #
180
183
  module ClassMethods
181
- ATTR_CLASS_BASE = '::Acfs::Resource::Attributes'.freeze
184
+ ATTR_CLASS_BASE = '::Acfs::Resource::Attributes'
182
185
 
183
186
  #
184
187
  # @api public
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs::Resource::Attributes
2
- #
3
4
  class Base
4
5
  attr_reader :default
5
6
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs::Resource::Attributes
2
4
  # @api public
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs::Resource::Attributes
2
4
  # @api public
3
5
  #
@@ -10,7 +12,6 @@ module Acfs::Resource::Attributes
10
12
  # end
11
13
  #
12
14
  class DateTime < Base
13
-
14
15
  # @api public
15
16
  #
16
17
  # Cast given object to DateTime.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs::Resource::Attributes
2
4
  # @api public
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs::Resource::Attributes
2
4
  # @api public
3
5
  #
@@ -21,9 +23,9 @@ module Acfs::Resource::Attributes
21
23
 
22
24
  case value
23
25
  when ::Float then value
24
- when "Infinity" then ::Float::INFINITY
25
- when "-Infinity" then -::Float::INFINITY
26
- when "NaN" then ::Float::NAN
26
+ when 'Infinity' then ::Float::INFINITY
27
+ when '-Infinity' then -::Float::INFINITY
28
+ when 'NaN' then ::Float::NAN
27
29
  else Float(value)
28
30
  end
29
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs::Resource::Attributes
2
4
  # @api public
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs::Resource::Attributes
2
4
  # @api public
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs::Resource::Attributes
2
4
  # @api public
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Acfs::Resource::Attributes
2
4
  # @api public
3
5
  #
@@ -9,8 +11,7 @@ module Acfs::Resource::Attributes
9
11
  # end
10
12
  #
11
13
  class UUID < Base
12
- #
13
- UUID_REGEXP = /[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}/i
14
+ UUID_REGEXP = /[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}/i.freeze
14
15
 
15
16
  # @api public
16
17
  #
@@ -37,7 +38,7 @@ module Acfs::Resource::Attributes
37
38
  elsif value.to_s =~ UUID_REGEXP
38
39
  value
39
40
  else
40
- raise TypeError.new "Invalid UUID: `#{value.to_s}'"
41
+ raise TypeError.new "Invalid UUID: `#{value}'"
41
42
  end
42
43
  end
43
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Acfs::Resource
2
4
  #
3
5
  # Thin wrapper around ActiveModel::Dirty
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Acfs::Resource
2
4
  #
3
5
  # Initialization drop-in for pre-4.0 ActiveModel.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Acfs::Resource
2
4
  # Provides method to check for loading state of resources.
3
5
  # A resource that is created but not yet fetched will be loaded
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Acfs::Resource
2
4
  # Provide methods for generation URLs for resources.
3
5
  #
@@ -11,7 +13,6 @@ class Acfs::Resource
11
13
  module Locatable
12
14
  extend ActiveSupport::Concern
13
15
 
14
- #
15
16
  module ClassMethods
16
17
  # @overload url(suffix)
17
18
  # @deprecated
@@ -40,7 +41,10 @@ class Acfs::Resource
40
41
  # @return [String] Generated URL.
41
42
  #
42
43
  def url(suffix = nil, opts = {})
43
- opts, suffix = suffix, nil if suffix.is_a? Hash
44
+ if suffix.is_a? Hash
45
+ opts = suffix
46
+ suffix = nil
47
+ end
44
48
 
45
49
  opts[:action] = :list if suffix
46
50
 
@@ -85,9 +89,9 @@ class Acfs::Resource
85
89
  def location_default_path(action, path)
86
90
  case action
87
91
  when :list, :create
88
- return path
92
+ path
89
93
  when :read, :update, :delete
90
- return "#{path}/:id"
94
+ "#{path}/:id"
91
95
  end
92
96
  end
93
97
  end
@@ -109,8 +113,8 @@ class Acfs::Resource
109
113
  return nil if need_primary_key? && !primary_key?
110
114
 
111
115
  self.class.service
112
- .location(self.class, opts.reverse_merge(action: :read))
113
- .build(attributes).str
116
+ .location(self.class, opts.reverse_merge(action: :read))
117
+ .build(attributes).str
114
118
  end
115
119
 
116
120
  # @api private