acfs 1.0.0.dev.1.b305 → 1.0.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.
Files changed (89) hide show
  1. checksums.yaml +5 -13
  2. data/CHANGELOG.md +64 -0
  3. data/README.md +2 -2
  4. data/acfs.gemspec +4 -4
  5. data/lib/acfs.rb +7 -5
  6. data/lib/acfs/adapter/base.rb +0 -2
  7. data/lib/acfs/adapter/typhoeus.rb +17 -13
  8. data/lib/acfs/collections/paginatable.rb +10 -10
  9. data/lib/acfs/configuration.rb +4 -5
  10. data/lib/acfs/errors.rb +10 -9
  11. data/lib/acfs/global.rb +16 -7
  12. data/lib/acfs/location.rb +11 -11
  13. data/lib/acfs/middleware/base.rb +1 -2
  14. data/lib/acfs/middleware/json.rb +27 -0
  15. data/lib/acfs/middleware/logger.rb +0 -2
  16. data/lib/acfs/middleware/msgpack.rb +30 -0
  17. data/lib/acfs/middleware/print.rb +0 -2
  18. data/lib/acfs/middleware/serializer.rb +39 -0
  19. data/lib/acfs/operation.rb +5 -5
  20. data/lib/acfs/request.rb +4 -4
  21. data/lib/acfs/request/callbacks.rb +2 -3
  22. data/lib/acfs/resource.rb +2 -2
  23. data/lib/acfs/resource/attributes.rb +10 -37
  24. data/lib/acfs/resource/attributes/base.rb +10 -19
  25. data/lib/acfs/resource/attributes/boolean.rb +10 -8
  26. data/lib/acfs/resource/attributes/date_time.rb +6 -9
  27. data/lib/acfs/resource/attributes/dict.rb +37 -0
  28. data/lib/acfs/resource/attributes/float.rb +11 -5
  29. data/lib/acfs/resource/attributes/integer.rb +7 -5
  30. data/lib/acfs/resource/attributes/list.rb +13 -6
  31. data/lib/acfs/resource/attributes/string.rb +3 -5
  32. data/lib/acfs/resource/attributes/uuid.rb +8 -17
  33. data/lib/acfs/resource/loadable.rb +0 -1
  34. data/lib/acfs/resource/locatable.rb +0 -4
  35. data/lib/acfs/resource/operational.rb +0 -2
  36. data/lib/acfs/resource/persistence.rb +17 -17
  37. data/lib/acfs/resource/query_methods.rb +3 -5
  38. data/lib/acfs/resource/service.rb +0 -2
  39. data/lib/acfs/resource/validation.rb +0 -2
  40. data/lib/acfs/response.rb +1 -2
  41. data/lib/acfs/response/formats.rb +1 -2
  42. data/lib/acfs/response/status.rb +3 -5
  43. data/lib/acfs/runner.rb +21 -11
  44. data/lib/acfs/service.rb +4 -6
  45. data/lib/acfs/service/middleware.rb +20 -30
  46. data/lib/acfs/service/middleware/stack.rb +63 -0
  47. data/lib/acfs/singleton_resource.rb +0 -2
  48. data/lib/acfs/stub.rb +30 -21
  49. data/lib/acfs/util.rb +1 -1
  50. data/lib/acfs/version.rb +4 -2
  51. data/spec/acfs/adapter/typhoeus_spec.rb +12 -4
  52. data/spec/acfs/collection_spec.rb +45 -33
  53. data/spec/acfs/configuration_spec.rb +9 -1
  54. data/spec/acfs/global_spec.rb +21 -3
  55. data/spec/acfs/middleware/json_spec.rb +63 -0
  56. data/spec/acfs/middleware/msgpack_spec.rb +60 -0
  57. data/spec/acfs/operation_spec.rb +10 -0
  58. data/spec/acfs/request/callbacks_spec.rb +8 -8
  59. data/spec/acfs/request_spec.rb +5 -5
  60. data/spec/acfs/resource/attributes/boolean_spec.rb +40 -9
  61. data/spec/acfs/resource/attributes/date_time_spec.rb +29 -35
  62. data/spec/acfs/resource/attributes/dict_spec.rb +75 -0
  63. data/spec/acfs/resource/attributes/float_spec.rb +48 -9
  64. data/spec/acfs/resource/attributes/integer_spec.rb +34 -0
  65. data/spec/acfs/resource/attributes/list_spec.rb +43 -19
  66. data/spec/acfs/resource/attributes/uuid_spec.rb +31 -54
  67. data/spec/acfs/resource/attributes_spec.rb +17 -31
  68. data/spec/acfs/resource/locatable_spec.rb +2 -2
  69. data/spec/acfs/resource/persistance_spec.rb +65 -34
  70. data/spec/acfs/resource/query_methods_spec.rb +87 -87
  71. data/spec/acfs/resource/validation_spec.rb +4 -5
  72. data/spec/acfs/response/formats_spec.rb +1 -1
  73. data/spec/acfs/response/status_spec.rb +1 -1
  74. data/spec/acfs/runner_spec.rb +28 -3
  75. data/spec/acfs/service/middleware_spec.rb +4 -20
  76. data/spec/acfs/service_spec.rb +3 -5
  77. data/spec/acfs/singleton_resource_spec.rb +1 -2
  78. data/spec/acfs/stub_spec.rb +137 -22
  79. data/spec/acfs_spec.rb +22 -19
  80. data/spec/spec_helper.rb +2 -1
  81. data/spec/support/service.rb +10 -6
  82. data/spec/support/shared/find_callbacks.rb +7 -7
  83. metadata +37 -30
  84. data/lib/acfs/middleware/json_decoder.rb +0 -16
  85. data/lib/acfs/middleware/json_encoder.rb +0 -20
  86. data/lib/acfs/middleware/msgpack_decoder.rb +0 -26
  87. data/lib/acfs/middleware/msgpack_encoder.rb +0 -19
  88. data/spec/acfs/middleware/json_decoder_spec.rb +0 -45
  89. data/spec/acfs/middleware/msgpack_decoder_spec.rb +0 -36
@@ -1,6 +1,5 @@
1
1
  module Acfs
2
2
  module Middleware
3
-
4
3
  # A base middleware that does not modify request or response.
5
4
  # Can be used as super class for custom middleware implementations.
6
5
  #
@@ -13,7 +12,7 @@ module Acfs
13
12
  end
14
13
 
15
14
  def call(request)
16
- request.on_complete { |res, nxt| response(res, nxt) } if respond_to? :response
15
+ request.on_complete {|res, nxt| response(res, nxt) } if respond_to? :response
17
16
  app.call(request)
18
17
  end
19
18
  end
@@ -0,0 +1,27 @@
1
+ require 'multi_json'
2
+
3
+ module Acfs
4
+ module Middleware
5
+ # A middleware to encore request data using JSON.
6
+ #
7
+ class JSON < Serializer
8
+ def mime
9
+ ::Mime::JSON
10
+ end
11
+
12
+ def encode(data)
13
+ ::MultiJson.dump data
14
+ end
15
+
16
+ def decode(body)
17
+ ::MultiJson.load body
18
+ end
19
+ end
20
+
21
+ # @deprecated
22
+ JsonDecoder = JSON
23
+
24
+ # @deprecated
25
+ JsonEncoder = JSON
26
+ end
27
+ end
@@ -2,11 +2,9 @@ require 'logger'
2
2
 
3
3
  module Acfs
4
4
  module Middleware
5
-
6
5
  # Log requests and responses.
7
6
  #
8
7
  class Logger < Base
9
-
10
8
  def initialize(app, options = {})
11
9
  super
12
10
  @logger = options[:logger] if options[:logger]
@@ -0,0 +1,30 @@
1
+ require 'msgpack'
2
+ require 'action_dispatch'
3
+
4
+ module Acfs
5
+ module Middleware
6
+ class MessagePack < Serializer
7
+ unless defined?(::Mime::MSGPACK)
8
+ ::Mime::Type.register 'application/x-msgpack', :msgpack
9
+ end
10
+
11
+ def mime
12
+ ::Mime::MSGPACK
13
+ end
14
+
15
+ def encode(data)
16
+ ::MessagePack.pack data
17
+ end
18
+
19
+ def decode(body)
20
+ ::MessagePack.unpack body
21
+ end
22
+ end
23
+
24
+ # @deprecated
25
+ MessagePackEncoder = MessagePack
26
+
27
+ # @deprecated
28
+ MessagePackDecoder = MessagePack
29
+ end
30
+ end
@@ -1,10 +1,8 @@
1
1
  module Acfs
2
2
  module Middleware
3
-
4
3
  # Print resquests and response on terminal
5
4
  #
6
5
  class Print < Base
7
-
8
6
  def call(req)
9
7
  puts '-' * 80
10
8
  puts req.inspect
@@ -0,0 +1,39 @@
1
+ module Acfs
2
+ module Middleware
3
+ # A base middleware that does not modify request or response.
4
+ # Can be used as super class for custom middleware implementations.
5
+ #
6
+ class Serializer < Base
7
+ def encode(_data)
8
+ raise NotImplementedError
9
+ end
10
+
11
+ def decode(_data)
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def mime
16
+ raise NotImplementedError
17
+ end
18
+
19
+ def call(request)
20
+ unless request.headers['Content-Type']
21
+ request.body = encode request.data
22
+ request.headers['Content-Type'] = mime
23
+ end
24
+
25
+ accept = request.headers['Accept'].to_s.split(',')
26
+ accept << "#{mime};q=#{options.fetch(:q, 1)}"
27
+ request.headers['Accept'] = accept.join(',')
28
+
29
+ request.on_complete do |response, nxt|
30
+ response.data = decode response.body if mime == response.content_type
31
+
32
+ nxt.call response
33
+ end
34
+
35
+ app.call(request)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,5 +1,4 @@
1
1
  module Acfs
2
-
3
2
  # @api private
4
3
  #
5
4
  # Describes a CRUD operation. Handle request creation and response
@@ -51,15 +50,16 @@ module Acfs
51
50
  end
52
51
 
53
52
  def method
54
- { read: :get, list: :get, update: :put, create: :post, delete: :delete }[action]
53
+ {read: :get, list: :get, update: :put, create: :post, delete: :delete}[action]
55
54
  end
56
55
 
57
56
  def request
58
- request = ::Acfs::Request.new url, method: method, params: params, data: data
57
+ request = ::Acfs::Request.new url, method: method, params: params,
58
+ data: data, operation: self
59
59
  request.on_complete do |response|
60
60
  ::ActiveSupport::Notifications.instrument 'acfs.operation.complete',
61
- operation: self,
62
- response: response
61
+ operation: self,
62
+ response: response
63
63
 
64
64
  handle_failure response unless response.success?
65
65
  callback.call response.data, response
@@ -1,25 +1,25 @@
1
1
  require 'acfs/request/callbacks'
2
2
 
3
3
  module Acfs
4
-
5
4
  # Encapsulate all data required to make up a request to the
6
5
  # underlaying http library.
7
6
  #
8
7
  class Request
9
8
  attr_accessor :body, :format
10
- attr_reader :url, :headers, :params, :data, :method
9
+ attr_reader :url, :headers, :params, :data, :method, :operation
11
10
 
12
11
  include Request::Callbacks
13
12
 
14
13
  def initialize(url, options = {}, &block)
15
- @url = URI.parse(url.to_s).tap do |url|
14
+ @url = URI.parse(url.to_s).tap do |_url|
16
15
  @data = options.delete(:data) || nil
17
16
  @format = options.delete(:format) || :json
18
17
  @headers = options.delete(:headers) || {}
19
18
  @params = options.delete(:params) || {}
20
19
  @method = options.delete(:method) || :get
21
20
  end.to_s
22
- on_complete &block if block_given?
21
+ @operation = options.delete(:operation) || nil
22
+ on_complete(&block) if block_given?
23
23
  end
24
24
 
25
25
  def data?
@@ -1,6 +1,5 @@
1
1
  module Acfs
2
2
  class Request
3
-
4
3
  # Module containing callback handling for Requests.
5
4
  # Current the only callback type is `on_complete`:
6
5
  #
@@ -8,7 +7,6 @@ module Acfs
8
7
  # request.on_complete { |response| ... }
9
8
  #
10
9
  module Callbacks
11
-
12
10
  # Add a new `on_complete` callback for this request.
13
11
  #
14
12
  # @example Set on_complete.
@@ -43,8 +41,9 @@ module Acfs
43
41
  end
44
42
 
45
43
  private
44
+
46
45
  def call_callback(res, index)
47
- callbacks[index].call res, proc { |res| call_callback res, index + 1 } if index < callbacks.size
46
+ callbacks[index].call res, proc {|bres| call_callback bres, index + 1 } if index < callbacks.size
48
47
  end
49
48
  end
50
49
  end
@@ -17,8 +17,8 @@ class Acfs::Resource
17
17
  if ActiveModel::VERSION::MAJOR >= 4
18
18
  include ActiveModel::Model
19
19
  else
20
- extend ActiveModel::Naming
21
- extend ActiveModel::Translation
20
+ extend ActiveModel::Naming
21
+ extend ActiveModel::Translation
22
22
  include ActiveModel::Conversion
23
23
  include ActiveModel::Validations
24
24
  end
@@ -178,7 +178,6 @@ class Acfs::Resource
178
178
 
179
179
  #
180
180
  module ClassMethods
181
-
182
181
  ATTR_CLASS_BASE = '::Acfs::Resource::Attributes'.freeze
183
182
 
184
183
  #
@@ -223,56 +222,30 @@ class Acfs::Resource
223
222
  # Attributes with default values.
224
223
  #
225
224
  def attributes
226
- Hash.new.tap do |attrs|
227
- defined_attributes.each do |key, attr|
228
- attrs[key] = attr.default_value
229
- end
225
+ defined_attributes.each_with_object({}) do |(key, attr), hash|
226
+ hash[key] = attr.default_value
230
227
  end
231
228
  end
232
229
 
233
230
  def defined_attributes
234
- @attributes ||= begin
235
- attributes = {}
236
- if superclass.respond_to?(:defined_attributes)
237
- attributes.merge superclass.defined_attributes
238
- end
239
-
240
- attributes
231
+ if superclass.respond_to?(:defined_attributes)
232
+ superclass.defined_attributes.merge(local_attributes)
233
+ else
234
+ local_attributes
241
235
  end
242
236
  end
243
237
 
244
- # @api public
245
- #
246
- # Return hash of attributes and there types.
247
- #
248
- # @example
249
- # class User < Acfs::Resource
250
- # attribute :name, :string
251
- # attribute :age, :integer, default: 25
252
- # end
253
- # User.attributes # => {"name": Acfs::Model::Attributes::String,
254
- # # "age": Acfs::Model::Attributes::Integer}
255
- #
256
- # @return [Hash{Symbol => Class}] Attributes and their types.
257
- #
258
- def attribute_types
259
- @attribute_types ||= begin
260
- attribute_types = {}
261
- if superclass.respond_to?(:attribute_types)
262
- attribute_types.merge superclass.attribute_types
263
- end
238
+ private
264
239
 
265
- attribute_types
266
- end
240
+ def local_attributes
241
+ @local_attributes ||= {}
267
242
  end
268
243
 
269
- private
270
-
271
244
  def define_attribute(name, type, opts = {})
272
245
  name = name.to_s
273
246
  attribute = type.new opts
274
247
 
275
- defined_attributes[name] = attribute
248
+ local_attributes[name] = attribute
276
249
  define_attribute_method name
277
250
 
278
251
  send :define_method, name do
@@ -1,36 +1,27 @@
1
1
  module Acfs::Resource::Attributes
2
-
3
2
  #
4
3
  class Base
5
- attr_reader :options
6
-
7
- def initialize(opts = {})
8
- @options = opts
9
- @options.reverse_merge! allow_nil: true
10
- end
4
+ attr_reader :default
11
5
 
12
- def nil_allowed?
13
- options[:allow_nil]
6
+ def initialize(default: nil)
7
+ @default = default
14
8
  end
15
9
 
16
- def blank_allowed?
17
- options[:allow_blank]
10
+ def cast(value)
11
+ cast_value(value) unless value.nil?
18
12
  end
19
13
 
20
14
  def default_value
21
- if options[:default].is_a? Proc
22
- options[:default]
15
+ if default.respond_to? :call
16
+ default
23
17
  else
24
- cast options[:default]
18
+ cast default
25
19
  end
26
20
  end
27
21
 
28
- def cast(obj)
29
- return nil if obj.nil? && nil_allowed? || (obj == '' && blank_allowed?)
30
- cast_type obj
31
- end
22
+ private
32
23
 
33
- def cast_type(obj)
24
+ def cast_value(_value)
34
25
  raise NotImplementedError
35
26
  end
36
27
  end
@@ -1,5 +1,4 @@
1
1
  module Acfs::Resource::Attributes
2
-
3
2
  # @api public
4
3
  #
5
4
  # Boolean attribute type. Use it in your model as an attribute type:
@@ -15,21 +14,24 @@ module Acfs::Resource::Attributes
15
14
  # true, on, yes
16
15
  #
17
16
  class Boolean < Base
18
-
19
- TRUE_VALUES = %w(true on yes 1)
17
+ FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF', 'no', 'NO'].to_set
20
18
 
21
19
  # @api public
22
20
  #
23
21
  # Cast given object to boolean.
24
22
  #
25
- # @param [Object] obj Object to cast.
23
+ # @param [Object] value Object to cast.
26
24
  # @return [TrueClass, FalseClass] Casted boolean.
27
25
  #
28
- def cast_type(obj)
29
- return true if obj.is_a? TrueClass
30
- return false if obj.is_a? FalseClass
26
+ def cast_value(value)
27
+ return true if value == true
28
+ return false if value == false
31
29
 
32
- TRUE_VALUES.include? obj.to_s
30
+ if value.blank?
31
+ nil
32
+ else
33
+ !FALSE_VALUES.include?(value)
34
+ end
33
35
  end
34
36
  end
35
37
  end
@@ -1,5 +1,4 @@
1
1
  module Acfs::Resource::Attributes
2
-
3
2
  # @api public
4
3
  #
5
4
  # DateTime attribute type. Use it in your model as
@@ -16,18 +15,16 @@ module Acfs::Resource::Attributes
16
15
  #
17
16
  # Cast given object to DateTime.
18
17
  #
19
- # @param [Object] obj Object to cast.
18
+ # @param [Object] value Object to cast.
20
19
  # @return [DateTime] Casted object as DateTime.
21
20
  #
22
- def cast_type(obj)
23
- if nil_allowed? && obj.blank?
21
+ def cast_value(value)
22
+ if value.blank?
24
23
  nil
25
- elsif obj.is_a? ::DateTime
26
- obj
27
- elsif obj.is_a?(Time) || obj.is_a?(Date)
28
- ::DateTime.iso8601 obj.iso8601
24
+ elsif value.acts_like?(:time) || value.acts_like?(:date)
25
+ value.to_datetime
29
26
  else
30
- ::DateTime.iso8601 obj
27
+ ::DateTime.iso8601 value
31
28
  end
32
29
  end
33
30
  end
@@ -0,0 +1,37 @@
1
+ module Acfs::Resource::Attributes
2
+ # @api public
3
+ #
4
+ # Dict attribute type. Use it in your model as an attribute type:
5
+ #
6
+ # @example
7
+ # class User
8
+ # include Acfs::Model
9
+ # attribute :opts, :dict
10
+ # end
11
+ #
12
+ class Dict < Base
13
+ # @api public
14
+ #
15
+ # Cast given object to a dict/hash.
16
+ #
17
+ # @param [Object] value Object to cast.
18
+ # @return [Hash] Casted object as hash.
19
+ # @raise [TypeError] If object cannot be casted to a hash.
20
+ #
21
+ def cast_value(value)
22
+ return {} if value.blank?
23
+
24
+ if value.is_a?(Hash)
25
+ value
26
+ elsif value.respond_to?(:serializable_hash)
27
+ value.serializable_hash
28
+ elsif value.respond_to?(:to_hash)
29
+ value.to_hash
30
+ elsif value.respond_to?(:to_h)
31
+ value.to_h
32
+ else
33
+ Hash(value)
34
+ end
35
+ end
36
+ end
37
+ end