acfs 0.50.0 → 1.0.0.dev.1.b297
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 +13 -5
- data/CHANGELOG.md +0 -80
- data/README.md +9 -8
- data/acfs.gemspec +7 -7
- data/lib/acfs.rb +5 -7
- data/lib/acfs/adapter/base.rb +2 -0
- data/lib/acfs/adapter/typhoeus.rb +13 -17
- data/lib/acfs/collections/paginatable.rb +10 -10
- data/lib/acfs/configuration.rb +5 -4
- data/lib/acfs/errors.rb +9 -10
- data/lib/acfs/global.rb +7 -16
- data/lib/acfs/location.rb +11 -11
- data/lib/acfs/middleware/base.rb +2 -1
- data/lib/acfs/middleware/json_decoder.rb +16 -0
- data/lib/acfs/middleware/json_encoder.rb +20 -0
- data/lib/acfs/middleware/logger.rb +2 -0
- data/lib/acfs/middleware/msgpack_decoder.rb +26 -0
- data/lib/acfs/middleware/msgpack_encoder.rb +19 -0
- data/lib/acfs/middleware/print.rb +2 -0
- data/lib/acfs/operation.rb +5 -5
- data/lib/acfs/request.rb +4 -4
- data/lib/acfs/request/callbacks.rb +3 -2
- data/lib/acfs/resource.rb +2 -2
- data/lib/acfs/resource/attributes.rb +38 -11
- data/lib/acfs/resource/attributes/base.rb +19 -10
- data/lib/acfs/resource/attributes/boolean.rb +8 -10
- data/lib/acfs/resource/attributes/date_time.rb +9 -6
- data/lib/acfs/resource/attributes/float.rb +5 -11
- data/lib/acfs/resource/attributes/integer.rb +5 -7
- data/lib/acfs/resource/attributes/list.rb +6 -13
- data/lib/acfs/resource/attributes/string.rb +5 -3
- data/lib/acfs/resource/attributes/uuid.rb +17 -8
- data/lib/acfs/resource/dirty.rb +15 -3
- data/lib/acfs/resource/loadable.rb +1 -0
- data/lib/acfs/resource/locatable.rb +4 -0
- data/lib/acfs/resource/operational.rb +2 -0
- data/lib/acfs/resource/persistence.rb +17 -17
- data/lib/acfs/resource/query_methods.rb +7 -8
- data/lib/acfs/resource/service.rb +2 -0
- data/lib/acfs/resource/validation.rb +4 -4
- data/lib/acfs/response.rb +2 -1
- data/lib/acfs/response/formats.rb +3 -2
- data/lib/acfs/response/status.rb +5 -3
- data/lib/acfs/runner.rb +11 -21
- data/lib/acfs/service.rb +6 -4
- data/lib/acfs/service/middleware.rb +30 -20
- data/lib/acfs/singleton_resource.rb +2 -0
- data/lib/acfs/stub.rb +21 -30
- data/lib/acfs/util.rb +1 -1
- data/lib/acfs/version.rb +4 -6
- data/spec/acfs/adapter/typhoeus_spec.rb +4 -12
- data/spec/acfs/collection_spec.rb +33 -45
- data/spec/acfs/configuration_spec.rb +1 -9
- data/spec/acfs/global_spec.rb +3 -21
- data/spec/acfs/middleware/json_decoder_spec.rb +45 -0
- data/spec/acfs/middleware/msgpack_decoder_spec.rb +36 -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 +9 -40
- data/spec/acfs/resource/attributes/date_time_spec.rb +35 -29
- data/spec/acfs/resource/attributes/float_spec.rb +9 -48
- data/spec/acfs/resource/attributes/list_spec.rb +19 -43
- data/spec/acfs/resource/attributes/uuid_spec.rb +54 -31
- data/spec/acfs/resource/attributes_spec.rb +31 -17
- data/spec/acfs/resource/locatable_spec.rb +2 -2
- data/spec/acfs/resource/persistance_spec.rb +34 -65
- data/spec/acfs/resource/query_methods_spec.rb +90 -97
- data/spec/acfs/resource/validation_spec.rb +5 -4
- data/spec/acfs/response/formats_spec.rb +4 -4
- data/spec/acfs/response/status_spec.rb +1 -1
- data/spec/acfs/runner_spec.rb +3 -28
- data/spec/acfs/service/middleware_spec.rb +20 -4
- data/spec/acfs/service_spec.rb +5 -3
- data/spec/acfs/singleton_resource_spec.rb +2 -1
- data/spec/acfs/stub_spec.rb +22 -137
- data/spec/acfs_spec.rb +19 -22
- data/spec/spec_helper.rb +2 -3
- data/spec/support/service.rb +6 -10
- data/spec/support/shared/find_callbacks.rb +7 -7
- metadata +36 -43
- data/lib/acfs/middleware/json.rb +0 -27
- data/lib/acfs/middleware/msgpack.rb +0 -30
- data/lib/acfs/middleware/serializer.rb +0 -39
- data/lib/acfs/resource/attributes/dict.rb +0 -37
- data/lib/acfs/service/middleware/stack.rb +0 -63
- data/spec/acfs/middleware/json_spec.rb +0 -63
- data/spec/acfs/middleware/msgpack_spec.rb +0 -60
- data/spec/acfs/operation_spec.rb +0 -10
- data/spec/acfs/resource/attributes/dict_spec.rb +0 -75
- data/spec/acfs/resource/attributes/integer_spec.rb +0 -34
data/lib/acfs/location.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Acfs
|
2
|
+
|
2
3
|
# @api private
|
3
4
|
#
|
4
5
|
# Describes a URL with placeholders.
|
@@ -6,17 +7,17 @@ module Acfs
|
|
6
7
|
class Location
|
7
8
|
attr_reader :arguments, :raw, :struct, :args
|
8
9
|
|
9
|
-
REGEXP
|
10
|
+
REGEXP= /^:([A-z][A-z0-9_]*)$/
|
10
11
|
|
11
12
|
def initialize(uri, args = {})
|
12
13
|
@raw = URI.parse uri
|
13
14
|
@args = args
|
14
|
-
@struct = raw.path.split('/').reject(&:empty?).map
|
15
|
-
@arguments = struct.select
|
15
|
+
@struct = raw.path.split('/').reject(&:empty?).map{|s| s =~ REGEXP ? $1.to_sym : s }
|
16
|
+
@arguments = struct.select{|s| Symbol === s }
|
16
17
|
end
|
17
18
|
|
18
19
|
def build(args = {})
|
19
|
-
unless args
|
20
|
+
unless Hash === args
|
20
21
|
raise ArgumentError.new "URI path arguments must be a hash, `#{args.inspect}' given."
|
21
22
|
end
|
22
23
|
|
@@ -24,8 +25,8 @@ module Acfs
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def extract_from(*args)
|
27
|
-
args =
|
28
|
-
arguments.each
|
28
|
+
args = Hash.new.tap do |collect|
|
29
|
+
arguments.each{|key| collect[key] = extract_arg key, args }
|
29
30
|
end
|
30
31
|
|
31
32
|
build args
|
@@ -33,7 +34,7 @@ module Acfs
|
|
33
34
|
|
34
35
|
def str
|
35
36
|
uri = raw.dup
|
36
|
-
uri.path = URI.escape '/' + struct.map
|
37
|
+
uri.path = URI.escape '/' + struct.map{|s| lookup_arg(s, args) }.join('/')
|
37
38
|
uri.to_s
|
38
39
|
end
|
39
40
|
|
@@ -43,17 +44,16 @@ module Acfs
|
|
43
44
|
alias_method :to_s, :raw_uri
|
44
45
|
|
45
46
|
private
|
46
|
-
|
47
47
|
def extract_arg(key, hashes)
|
48
48
|
hashes.each_with_index do |hash, index|
|
49
|
-
return (index == 0 ? hash.delete(key) : hash.fetch(key)) if hash.
|
49
|
+
return (index == 0 ? hash.delete(key) : hash.fetch(key)) if hash.has_key?(key)
|
50
50
|
end
|
51
51
|
|
52
52
|
nil
|
53
53
|
end
|
54
54
|
|
55
55
|
def lookup_arg(arg, args)
|
56
|
-
|
56
|
+
Symbol === arg ? lookup_replacement(arg, args) : arg
|
57
57
|
end
|
58
58
|
|
59
59
|
def lookup_replacement(sym, args)
|
@@ -67,7 +67,7 @@ module Acfs
|
|
67
67
|
args.fetch(sym.to_s) do
|
68
68
|
args.fetch(sym) do
|
69
69
|
if args[:raise].nil? || args[:raise]
|
70
|
-
raise ArgumentError.new "URI path argument `#{sym}' missing on `#{
|
70
|
+
raise ArgumentError.new "URI path argument `#{sym}' missing on `#{to_s}'. Given: `#{args}.inspect'"
|
71
71
|
else
|
72
72
|
":#{sym}"
|
73
73
|
end
|
data/lib/acfs/middleware/base.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Acfs
|
2
2
|
module Middleware
|
3
|
+
|
3
4
|
# A base middleware that does not modify request or response.
|
4
5
|
# Can be used as super class for custom middleware implementations.
|
5
6
|
#
|
@@ -12,7 +13,7 @@ module Acfs
|
|
12
13
|
end
|
13
14
|
|
14
15
|
def call(request)
|
15
|
-
request.on_complete {|res, nxt| response(res, nxt) } if respond_to? :response
|
16
|
+
request.on_complete { |res, nxt| response(res, nxt) } if respond_to? :response
|
16
17
|
app.call(request)
|
17
18
|
end
|
18
19
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module Acfs
|
4
|
+
module Middleware
|
5
|
+
|
6
|
+
# A middleware to automatically decode JSON responses.
|
7
|
+
#
|
8
|
+
class JsonDecoder < Base
|
9
|
+
|
10
|
+
def response(response, nxt)
|
11
|
+
response.data = ::MultiJson.load(response.body) if response.json?
|
12
|
+
nxt.call response
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module Acfs
|
4
|
+
module Middleware
|
5
|
+
|
6
|
+
# A middleware to encore request data using JSON.
|
7
|
+
#
|
8
|
+
class JsonEncoder < Base
|
9
|
+
|
10
|
+
def call(request)
|
11
|
+
unless request.method == :get or request.data.nil?
|
12
|
+
request.body = ::MultiJson.dump(request.data)
|
13
|
+
request.headers['Content-Type'] = 'application/json'
|
14
|
+
end
|
15
|
+
|
16
|
+
app.call request
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
require 'action_dispatch'
|
3
|
+
|
4
|
+
module Acfs
|
5
|
+
module Middleware
|
6
|
+
|
7
|
+
# Register msgpack mime type
|
8
|
+
::Mime::Type.register 'application/x-msgpack', :msgpack
|
9
|
+
|
10
|
+
# A middleware to decode Message Pack responses.
|
11
|
+
#
|
12
|
+
class MessagePackDecoder < Base
|
13
|
+
|
14
|
+
CONTENT_TYPES = %w(application/x-msgpack)
|
15
|
+
|
16
|
+
def response(response, nxt)
|
17
|
+
response.data = ::MessagePack.unpack(response.body) if message_pack?(response)
|
18
|
+
nxt.call response
|
19
|
+
end
|
20
|
+
|
21
|
+
def message_pack?(response)
|
22
|
+
CONTENT_TYPES.include? response.content_type
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
require 'action_dispatch'
|
3
|
+
|
4
|
+
module Acfs
|
5
|
+
module Middleware
|
6
|
+
|
7
|
+
# A middleware to encode request data with Message Pack.
|
8
|
+
#
|
9
|
+
class MessagePackEncoder < Base
|
10
|
+
|
11
|
+
def call(request)
|
12
|
+
request.body = ::MessagePack.dump(request.data)
|
13
|
+
request.headers['Content-Type'] = 'application/x-msgpack'
|
14
|
+
|
15
|
+
app.call request
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/acfs/operation.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Acfs
|
2
|
+
|
2
3
|
# @api private
|
3
4
|
#
|
4
5
|
# Describes a CRUD operation. Handle request creation and response
|
@@ -50,16 +51,15 @@ module Acfs
|
|
50
51
|
end
|
51
52
|
|
52
53
|
def method
|
53
|
-
{read: :get, list: :get, update: :put, create: :post, delete: :delete}[action]
|
54
|
+
{ read: :get, list: :get, update: :put, create: :post, delete: :delete }[action]
|
54
55
|
end
|
55
56
|
|
56
57
|
def request
|
57
|
-
request = ::Acfs::Request.new url, method: method, params: params,
|
58
|
-
data: data, operation: self
|
58
|
+
request = ::Acfs::Request.new url, method: method, params: params, data: data
|
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
|
+
|
4
5
|
# Encapsulate all data required to make up a request to the
|
5
6
|
# underlaying http library.
|
6
7
|
#
|
7
8
|
class Request
|
8
9
|
attr_accessor :body, :format
|
9
|
-
attr_reader :url, :headers, :params, :data, :method
|
10
|
+
attr_reader :url, :headers, :params, :data, :method
|
10
11
|
|
11
12
|
include Request::Callbacks
|
12
13
|
|
13
14
|
def initialize(url, options = {}, &block)
|
14
|
-
@url = URI.parse(url.to_s).tap do |
|
15
|
+
@url = URI.parse(url.to_s).tap do |url|
|
15
16
|
@data = options.delete(:data) || nil
|
16
17
|
@format = options.delete(:format) || :json
|
17
18
|
@headers = options.delete(:headers) || {}
|
18
19
|
@params = options.delete(:params) || {}
|
19
20
|
@method = options.delete(:method) || :get
|
20
21
|
end.to_s
|
21
|
-
|
22
|
-
on_complete(&block) if block_given?
|
22
|
+
on_complete &block if block_given?
|
23
23
|
end
|
24
24
|
|
25
25
|
def data?
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Acfs
|
2
2
|
class Request
|
3
|
+
|
3
4
|
# Module containing callback handling for Requests.
|
4
5
|
# Current the only callback type is `on_complete`:
|
5
6
|
#
|
@@ -7,6 +8,7 @@ module Acfs
|
|
7
8
|
# request.on_complete { |response| ... }
|
8
9
|
#
|
9
10
|
module Callbacks
|
11
|
+
|
10
12
|
# Add a new `on_complete` callback for this request.
|
11
13
|
#
|
12
14
|
# @example Set on_complete.
|
@@ -41,9 +43,8 @@ module Acfs
|
|
41
43
|
end
|
42
44
|
|
43
45
|
private
|
44
|
-
|
45
46
|
def call_callback(res, index)
|
46
|
-
callbacks[index].call res, proc {|
|
47
|
+
callbacks[index].call res, proc { |res| call_callback res, index + 1 } if index < callbacks.size
|
47
48
|
end
|
48
49
|
end
|
49
50
|
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
|
@@ -47,7 +47,7 @@ class Acfs::Resource
|
|
47
47
|
# Attributes and their values.
|
48
48
|
#
|
49
49
|
def attributes
|
50
|
-
@
|
50
|
+
@attributes ||= HashWithIndifferentAccess.new
|
51
51
|
end
|
52
52
|
|
53
53
|
# @api public
|
@@ -178,6 +178,7 @@ class Acfs::Resource
|
|
178
178
|
|
179
179
|
#
|
180
180
|
module ClassMethods
|
181
|
+
|
181
182
|
ATTR_CLASS_BASE = '::Acfs::Resource::Attributes'.freeze
|
182
183
|
|
183
184
|
#
|
@@ -222,30 +223,56 @@ class Acfs::Resource
|
|
222
223
|
# Attributes with default values.
|
223
224
|
#
|
224
225
|
def attributes
|
225
|
-
|
226
|
-
|
226
|
+
Hash.new.tap do |attrs|
|
227
|
+
defined_attributes.each do |key, attr|
|
228
|
+
attrs[key] = attr.default_value
|
229
|
+
end
|
227
230
|
end
|
228
231
|
end
|
229
232
|
|
230
233
|
def defined_attributes
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
234
|
+
@attributes ||= begin
|
235
|
+
attributes = {}
|
236
|
+
if superclass.respond_to?(:defined_attributes)
|
237
|
+
attributes.merge superclass.defined_attributes
|
238
|
+
end
|
239
|
+
|
240
|
+
attributes
|
235
241
|
end
|
236
242
|
end
|
237
243
|
|
238
|
-
|
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
|
239
264
|
|
240
|
-
|
241
|
-
|
265
|
+
attribute_types
|
266
|
+
end
|
242
267
|
end
|
243
268
|
|
269
|
+
private
|
270
|
+
|
244
271
|
def define_attribute(name, type, opts = {})
|
245
272
|
name = name.to_s
|
246
273
|
attribute = type.new opts
|
247
274
|
|
248
|
-
|
275
|
+
defined_attributes[name] = attribute
|
249
276
|
define_attribute_method name
|
250
277
|
|
251
278
|
send :define_method, name do
|
@@ -1,27 +1,36 @@
|
|
1
1
|
module Acfs::Resource::Attributes
|
2
|
+
|
2
3
|
#
|
3
4
|
class Base
|
4
|
-
attr_reader :
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
@options = opts
|
9
|
+
@options.reverse_merge! allow_nil: true
|
10
|
+
end
|
5
11
|
|
6
|
-
def
|
7
|
-
|
12
|
+
def nil_allowed?
|
13
|
+
options[:allow_nil]
|
8
14
|
end
|
9
15
|
|
10
|
-
def
|
11
|
-
|
16
|
+
def blank_allowed?
|
17
|
+
options[:allow_blank]
|
12
18
|
end
|
13
19
|
|
14
20
|
def default_value
|
15
|
-
if default.
|
16
|
-
default
|
21
|
+
if options[:default].is_a? Proc
|
22
|
+
options[:default]
|
17
23
|
else
|
18
|
-
cast default
|
24
|
+
cast options[:default]
|
19
25
|
end
|
20
26
|
end
|
21
27
|
|
22
|
-
|
28
|
+
def cast(obj)
|
29
|
+
return nil if obj.nil? && nil_allowed? || (obj == '' && blank_allowed?)
|
30
|
+
cast_type obj
|
31
|
+
end
|
23
32
|
|
24
|
-
def
|
33
|
+
def cast_type(obj)
|
25
34
|
raise NotImplementedError
|
26
35
|
end
|
27
36
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module Acfs::Resource::Attributes
|
2
|
+
|
2
3
|
# @api public
|
3
4
|
#
|
4
5
|
# Boolean attribute type. Use it in your model as an attribute type:
|
@@ -14,24 +15,21 @@ module Acfs::Resource::Attributes
|
|
14
15
|
# true, on, yes
|
15
16
|
#
|
16
17
|
class Boolean < Base
|
17
|
-
|
18
|
+
|
19
|
+
TRUE_VALUES = %w(true on yes 1)
|
18
20
|
|
19
21
|
# @api public
|
20
22
|
#
|
21
23
|
# Cast given object to boolean.
|
22
24
|
#
|
23
|
-
# @param [Object]
|
25
|
+
# @param [Object] obj Object to cast.
|
24
26
|
# @return [TrueClass, FalseClass] Casted boolean.
|
25
27
|
#
|
26
|
-
def
|
27
|
-
return true if
|
28
|
-
return false if
|
28
|
+
def cast_type(obj)
|
29
|
+
return true if obj.is_a? TrueClass
|
30
|
+
return false if obj.is_a? FalseClass
|
29
31
|
|
30
|
-
|
31
|
-
nil
|
32
|
-
else
|
33
|
-
!FALSE_VALUES.include?(value)
|
34
|
-
end
|
32
|
+
TRUE_VALUES.include? obj.to_s
|
35
33
|
end
|
36
34
|
end
|
37
35
|
end
|