acfs 1.0.0.dev.1.b305 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/CHANGELOG.md +64 -0
- data/README.md +2 -2
- data/acfs.gemspec +4 -4
- data/lib/acfs.rb +7 -5
- data/lib/acfs/adapter/base.rb +0 -2
- data/lib/acfs/adapter/typhoeus.rb +17 -13
- data/lib/acfs/collections/paginatable.rb +10 -10
- data/lib/acfs/configuration.rb +4 -5
- data/lib/acfs/errors.rb +10 -9
- data/lib/acfs/global.rb +16 -7
- data/lib/acfs/location.rb +11 -11
- data/lib/acfs/middleware/base.rb +1 -2
- data/lib/acfs/middleware/json.rb +27 -0
- data/lib/acfs/middleware/logger.rb +0 -2
- data/lib/acfs/middleware/msgpack.rb +30 -0
- data/lib/acfs/middleware/print.rb +0 -2
- data/lib/acfs/middleware/serializer.rb +39 -0
- data/lib/acfs/operation.rb +5 -5
- data/lib/acfs/request.rb +4 -4
- data/lib/acfs/request/callbacks.rb +2 -3
- data/lib/acfs/resource.rb +2 -2
- data/lib/acfs/resource/attributes.rb +10 -37
- data/lib/acfs/resource/attributes/base.rb +10 -19
- data/lib/acfs/resource/attributes/boolean.rb +10 -8
- data/lib/acfs/resource/attributes/date_time.rb +6 -9
- data/lib/acfs/resource/attributes/dict.rb +37 -0
- data/lib/acfs/resource/attributes/float.rb +11 -5
- data/lib/acfs/resource/attributes/integer.rb +7 -5
- data/lib/acfs/resource/attributes/list.rb +13 -6
- data/lib/acfs/resource/attributes/string.rb +3 -5
- data/lib/acfs/resource/attributes/uuid.rb +8 -17
- data/lib/acfs/resource/loadable.rb +0 -1
- data/lib/acfs/resource/locatable.rb +0 -4
- data/lib/acfs/resource/operational.rb +0 -2
- data/lib/acfs/resource/persistence.rb +17 -17
- data/lib/acfs/resource/query_methods.rb +3 -5
- data/lib/acfs/resource/service.rb +0 -2
- data/lib/acfs/resource/validation.rb +0 -2
- data/lib/acfs/response.rb +1 -2
- data/lib/acfs/response/formats.rb +1 -2
- data/lib/acfs/response/status.rb +3 -5
- data/lib/acfs/runner.rb +21 -11
- data/lib/acfs/service.rb +4 -6
- data/lib/acfs/service/middleware.rb +20 -30
- data/lib/acfs/service/middleware/stack.rb +63 -0
- data/lib/acfs/singleton_resource.rb +0 -2
- data/lib/acfs/stub.rb +30 -21
- data/lib/acfs/util.rb +1 -1
- data/lib/acfs/version.rb +4 -2
- data/spec/acfs/adapter/typhoeus_spec.rb +12 -4
- data/spec/acfs/collection_spec.rb +45 -33
- data/spec/acfs/configuration_spec.rb +9 -1
- data/spec/acfs/global_spec.rb +21 -3
- data/spec/acfs/middleware/json_spec.rb +63 -0
- data/spec/acfs/middleware/msgpack_spec.rb +60 -0
- data/spec/acfs/operation_spec.rb +10 -0
- data/spec/acfs/request/callbacks_spec.rb +8 -8
- data/spec/acfs/request_spec.rb +5 -5
- data/spec/acfs/resource/attributes/boolean_spec.rb +40 -9
- data/spec/acfs/resource/attributes/date_time_spec.rb +29 -35
- data/spec/acfs/resource/attributes/dict_spec.rb +75 -0
- data/spec/acfs/resource/attributes/float_spec.rb +48 -9
- data/spec/acfs/resource/attributes/integer_spec.rb +34 -0
- data/spec/acfs/resource/attributes/list_spec.rb +43 -19
- data/spec/acfs/resource/attributes/uuid_spec.rb +31 -54
- data/spec/acfs/resource/attributes_spec.rb +17 -31
- data/spec/acfs/resource/locatable_spec.rb +2 -2
- data/spec/acfs/resource/persistance_spec.rb +65 -34
- data/spec/acfs/resource/query_methods_spec.rb +87 -87
- data/spec/acfs/resource/validation_spec.rb +4 -5
- data/spec/acfs/response/formats_spec.rb +1 -1
- data/spec/acfs/response/status_spec.rb +1 -1
- data/spec/acfs/runner_spec.rb +28 -3
- data/spec/acfs/service/middleware_spec.rb +4 -20
- data/spec/acfs/service_spec.rb +3 -5
- data/spec/acfs/singleton_resource_spec.rb +1 -2
- data/spec/acfs/stub_spec.rb +137 -22
- data/spec/acfs_spec.rb +22 -19
- data/spec/spec_helper.rb +2 -1
- data/spec/support/service.rb +10 -6
- data/spec/support/shared/find_callbacks.rb +7 -7
- metadata +37 -30
- data/lib/acfs/middleware/json_decoder.rb +0 -16
- data/lib/acfs/middleware/json_encoder.rb +0 -20
- data/lib/acfs/middleware/msgpack_decoder.rb +0 -26
- data/lib/acfs/middleware/msgpack_encoder.rb +0 -19
- data/spec/acfs/middleware/json_decoder_spec.rb +0 -45
- data/spec/acfs/middleware/msgpack_decoder_spec.rb +0 -36
data/lib/acfs/middleware/base.rb
CHANGED
@@ -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 {
|
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
|
@@ -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
|
@@ -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
|
data/lib/acfs/operation.rb
CHANGED
@@ -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
|
-
{
|
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,
|
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
|
-
|
62
|
-
|
61
|
+
operation: self,
|
62
|
+
response: response
|
63
63
|
|
64
64
|
handle_failure response unless response.success?
|
65
65
|
callback.call response.data, response
|
data/lib/acfs/request.rb
CHANGED
@@ -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 |
|
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
|
-
|
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 {
|
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
|
data/lib/acfs/resource.rb
CHANGED
@@ -17,8 +17,8 @@ class Acfs::Resource
|
|
17
17
|
if ActiveModel::VERSION::MAJOR >= 4
|
18
18
|
include ActiveModel::Model
|
19
19
|
else
|
20
|
-
extend
|
21
|
-
extend
|
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
|
-
|
227
|
-
|
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
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
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
|
-
|
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
|
-
|
266
|
-
|
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
|
-
|
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 :
|
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
|
13
|
-
|
6
|
+
def initialize(default: nil)
|
7
|
+
@default = default
|
14
8
|
end
|
15
9
|
|
16
|
-
def
|
17
|
-
|
10
|
+
def cast(value)
|
11
|
+
cast_value(value) unless value.nil?
|
18
12
|
end
|
19
13
|
|
20
14
|
def default_value
|
21
|
-
if
|
22
|
-
|
15
|
+
if default.respond_to? :call
|
16
|
+
default
|
23
17
|
else
|
24
|
-
cast
|
18
|
+
cast default
|
25
19
|
end
|
26
20
|
end
|
27
21
|
|
28
|
-
|
29
|
-
return nil if obj.nil? && nil_allowed? || (obj == '' && blank_allowed?)
|
30
|
-
cast_type obj
|
31
|
-
end
|
22
|
+
private
|
32
23
|
|
33
|
-
def
|
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]
|
23
|
+
# @param [Object] value Object to cast.
|
26
24
|
# @return [TrueClass, FalseClass] Casted boolean.
|
27
25
|
#
|
28
|
-
def
|
29
|
-
return true if
|
30
|
-
return false if
|
26
|
+
def cast_value(value)
|
27
|
+
return true if value == true
|
28
|
+
return false if value == false
|
31
29
|
|
32
|
-
|
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]
|
18
|
+
# @param [Object] value Object to cast.
|
20
19
|
# @return [DateTime] Casted object as DateTime.
|
21
20
|
#
|
22
|
-
def
|
23
|
-
if
|
21
|
+
def cast_value(value)
|
22
|
+
if value.blank?
|
24
23
|
nil
|
25
|
-
elsif
|
26
|
-
|
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
|
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
|