acfs 0.42.0 → 0.43.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +8 -9
- data/acfs.gemspec +3 -3
- data/lib/acfs.rb +0 -2
- data/lib/acfs/adapter/base.rb +0 -2
- data/lib/acfs/adapter/typhoeus.rb +6 -9
- data/lib/acfs/collection.rb +3 -3
- data/lib/acfs/collections/paginatable.rb +16 -16
- data/lib/acfs/configuration.rb +3 -5
- data/lib/acfs/errors.rb +8 -7
- data/lib/acfs/global.rb +1 -1
- data/lib/acfs/location.rb +11 -11
- data/lib/acfs/middleware/base.rb +1 -2
- data/lib/acfs/middleware/json.rb +0 -1
- data/lib/acfs/middleware/logger.rb +0 -2
- data/lib/acfs/middleware/print.rb +0 -2
- data/lib/acfs/middleware/serializer.rb +3 -6
- data/lib/acfs/operation.rb +3 -4
- data/lib/acfs/request.rb +2 -3
- data/lib/acfs/request/callbacks.rb +2 -3
- data/lib/acfs/resource.rb +34 -5
- data/lib/acfs/{model → resource}/attributes.rb +70 -46
- data/lib/acfs/{model → resource}/attributes/base.rb +10 -6
- data/lib/acfs/resource/attributes/boolean.rb +33 -0
- data/lib/acfs/resource/attributes/date_time.rb +32 -0
- data/lib/acfs/{model → resource}/attributes/dict.rb +1 -3
- data/lib/acfs/{model → resource}/attributes/float.rb +3 -6
- data/lib/acfs/{model → resource}/attributes/integer.rb +3 -6
- data/lib/acfs/{model → resource}/attributes/list.rb +2 -5
- data/lib/acfs/{model → resource}/attributes/string.rb +4 -6
- data/lib/acfs/{model → resource}/attributes/uuid.rb +18 -8
- data/lib/acfs/resource/dirty.rb +47 -0
- data/lib/acfs/{model → resource}/initialization.rb +8 -10
- data/lib/acfs/{model → resource}/loadable.rb +3 -4
- data/lib/acfs/{model → resource}/locatable.rb +22 -23
- data/lib/acfs/{model → resource}/operational.rb +2 -3
- data/lib/acfs/resource/persistence.rb +257 -0
- data/lib/acfs/{model → resource}/query_methods.rb +81 -66
- data/lib/acfs/{model → resource}/service.rb +10 -9
- data/lib/acfs/resource/validation.rb +28 -0
- 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 +4 -5
- data/lib/acfs/service.rb +4 -6
- data/lib/acfs/service/middleware.rb +1 -3
- data/lib/acfs/singleton_resource.rb +11 -24
- data/lib/acfs/stub.rb +30 -22
- data/lib/acfs/util.rb +1 -1
- data/lib/acfs/version.rb +4 -2
- data/spec/acfs/adapter/typhoeus_spec.rb +4 -4
- data/spec/acfs/collection_spec.rb +33 -33
- data/spec/acfs/configuration_spec.rb +0 -1
- data/spec/acfs/global_spec.rb +3 -3
- data/spec/acfs/middleware/json_spec.rb +2 -2
- data/spec/acfs/middleware/msgpack_spec.rb +4 -4
- data/spec/acfs/request/callbacks_spec.rb +8 -8
- data/spec/acfs/request_spec.rb +5 -5
- data/spec/acfs/{model → resource}/attributes/boolean_spec.rb +2 -2
- data/spec/acfs/{model → resource}/attributes/date_time_spec.rb +7 -7
- data/spec/acfs/{model → resource}/attributes/dict_spec.rb +6 -6
- data/spec/acfs/{model → resource}/attributes/float_spec.rb +3 -3
- data/spec/acfs/{model → resource}/attributes/list_spec.rb +5 -5
- data/spec/acfs/{model → resource}/attributes/uuid_spec.rb +6 -6
- data/spec/acfs/{model → resource}/attributes_spec.rb +31 -17
- data/spec/acfs/{model → resource}/dirty_spec.rb +7 -5
- data/spec/acfs/{model → resource}/initialization_spec.rb +7 -7
- data/spec/acfs/{model → resource}/loadable_spec.rb +4 -3
- data/spec/acfs/{model → resource}/locatable_spec.rb +24 -14
- data/spec/acfs/{model → resource}/persistance_spec.rb +34 -34
- data/spec/acfs/{model → resource}/query_methods_spec.rb +171 -130
- data/spec/acfs/{model → resource}/validation_spec.rb +5 -6
- data/spec/acfs/response/formats_spec.rb +1 -1
- data/spec/acfs/response/status_spec.rb +1 -1
- data/spec/acfs/runner_spec.rb +2 -3
- data/spec/acfs/service/middleware_spec.rb +1 -1
- data/spec/acfs/service_spec.rb +3 -5
- data/spec/acfs/singleton_resource_spec.rb +3 -3
- data/spec/acfs/stub_spec.rb +52 -24
- data/spec/acfs_spec.rb +22 -19
- data/spec/spec_helper.rb +1 -1
- data/spec/support/hash.rb +9 -0
- data/spec/support/service.rb +4 -7
- data/spec/support/shared/find_callbacks.rb +7 -7
- metadata +52 -52
- data/lib/acfs/model.rb +0 -43
- data/lib/acfs/model/attributes/boolean.rb +0 -38
- data/lib/acfs/model/attributes/date_time.rb +0 -30
- data/lib/acfs/model/dirty.rb +0 -49
- data/lib/acfs/model/persistence.rb +0 -243
- data/lib/acfs/model/relations.rb +0 -10
- data/lib/acfs/model/validation.rb +0 -30
@@ -1,36 +1,37 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
class Acfs::Resource
|
3
2
|
# Included by Acfs::Model. Allows to configure the service
|
4
3
|
# a resource belongs to.
|
5
4
|
#
|
6
5
|
module Service
|
7
6
|
extend ActiveSupport::Concern
|
8
7
|
|
8
|
+
#
|
9
9
|
module ClassMethods
|
10
|
-
|
11
10
|
# @api public
|
12
11
|
#
|
13
12
|
# @overload service()
|
14
13
|
# Return service instance.
|
15
14
|
#
|
16
|
-
# @return [
|
15
|
+
# @return [Service] Service class instance.
|
17
16
|
#
|
18
17
|
# @overload service(klass, options = {})
|
19
|
-
# Link to service this model belongs to. Connection
|
20
|
-
# are fetched from service.
|
18
|
+
# Link to service this model belongs to. Connection
|
19
|
+
# settings like base URL are fetched from service.
|
20
|
+
# Return assigned service if no arguments are given.
|
21
21
|
#
|
22
22
|
# @example
|
23
23
|
# class AccountService < Acfs::Client
|
24
24
|
# self.base_url = 'http://acc.serv.org'
|
25
25
|
# end
|
26
26
|
#
|
27
|
-
# class MyUser
|
27
|
+
# class MyUser < Acfs::Resource
|
28
28
|
# service AccountService
|
29
29
|
# end
|
30
30
|
# MyUser.find 5 # Will fetch `http://acc.serv.org/users/5`
|
31
31
|
#
|
32
|
-
# @param [
|
33
|
-
# @param [
|
32
|
+
# @param klass [Class] Service class derived from {Acfs::Service}.
|
33
|
+
# @param options [Object] Option delegated to
|
34
|
+
# service class initializer.
|
34
35
|
#
|
35
36
|
def service(klass = nil, options = {})
|
36
37
|
return (@service = klass.new options) if klass
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Acfs::Resource
|
2
|
+
#
|
3
|
+
module Validation
|
4
|
+
def valid?(*args)
|
5
|
+
super
|
6
|
+
remote_errors.each {|f, e| errors.add f, e }
|
7
|
+
errors.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
def remote_errors
|
11
|
+
@remote_errors ||= ActiveModel::Errors.new self
|
12
|
+
end
|
13
|
+
|
14
|
+
def remote_errors=(errors)
|
15
|
+
(errors || []).each do |field, errs|
|
16
|
+
self.errors.set field.to_sym, errs
|
17
|
+
remote_errors.set field.to_sym, errs
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def save!(*_)
|
22
|
+
unless valid?(new? ? :create : :save)
|
23
|
+
raise ::Acfs::InvalidResource.new resource: self, errors: errors.to_a
|
24
|
+
end
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/acfs/response.rb
CHANGED
@@ -3,7 +3,6 @@ require 'acfs/response/status'
|
|
3
3
|
require 'active_support/core_ext/module/delegation'
|
4
4
|
|
5
5
|
module Acfs
|
6
|
-
|
7
6
|
# This represents a response. In addition to an standard HTTP
|
8
7
|
# it has a field `data` for storing the encoded body.
|
9
8
|
#
|
@@ -14,7 +13,7 @@ module Acfs
|
|
14
13
|
include Response::Formats
|
15
14
|
include Response::Status
|
16
15
|
|
17
|
-
#delegate :status, :status_message, :success?, :modified?, :timed_out?,
|
16
|
+
# delegate :status, :status_message, :success?, :modified?, :timed_out?,
|
18
17
|
# :response_body, :response_headers, :response_code, :headers,
|
19
18
|
# to: :response
|
20
19
|
|
@@ -2,10 +2,8 @@ require 'action_dispatch'
|
|
2
2
|
|
3
3
|
module Acfs
|
4
4
|
class Response
|
5
|
-
|
6
5
|
# Quick accessors for format handling.
|
7
6
|
module Formats
|
8
|
-
|
9
7
|
def content_type
|
10
8
|
@content_type ||= read_content_type
|
11
9
|
end
|
@@ -15,6 +13,7 @@ module Acfs
|
|
15
13
|
end
|
16
14
|
|
17
15
|
private
|
16
|
+
|
18
17
|
def read_content_type
|
19
18
|
return 'text/plain' unless headers && headers['Content-Type']
|
20
19
|
|
data/lib/acfs/response/status.rb
CHANGED
@@ -1,19 +1,17 @@
|
|
1
1
|
module Acfs
|
2
2
|
class Response
|
3
|
-
|
4
3
|
# Method to fetch information about response status.
|
5
4
|
#
|
6
5
|
module Status
|
7
|
-
|
8
6
|
# Return response status code. Will return zero if
|
9
7
|
# request was not executed or failed on client side.
|
10
8
|
#
|
11
9
|
def status_code
|
12
10
|
return @status.to_i if defined? :@status
|
13
|
-
#return response.response_code unless response.nil?
|
14
|
-
#0
|
11
|
+
# return response.response_code unless response.nil?
|
12
|
+
# 0
|
15
13
|
end
|
16
|
-
|
14
|
+
alias_method :code, :status_code
|
17
15
|
|
18
16
|
# Return true if response was successful indicated by
|
19
17
|
# response status code.
|
data/lib/acfs/runner.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'acfs/service/middleware'
|
2
2
|
|
3
3
|
module Acfs
|
4
|
-
|
5
4
|
# @api private
|
6
5
|
#
|
7
6
|
class Runner
|
@@ -25,7 +24,7 @@ module Acfs
|
|
25
24
|
#
|
26
25
|
def run(op)
|
27
26
|
::ActiveSupport::Notifications.instrument 'acfs.runner.sync_run', operation: op do
|
28
|
-
op_request(op) {
|
27
|
+
op_request(op) {|req| adapter.run req }
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
@@ -40,7 +39,7 @@ module Acfs
|
|
40
39
|
def enqueue(op)
|
41
40
|
::ActiveSupport::Notifications.instrument 'acfs.runner.enqueue', operation: op do
|
42
41
|
if running?
|
43
|
-
op_request(op) {
|
42
|
+
op_request(op) {|req| adapter.queue req }
|
44
43
|
else
|
45
44
|
queue << op
|
46
45
|
end
|
@@ -82,12 +81,12 @@ module Acfs
|
|
82
81
|
|
83
82
|
def enqueue_operations
|
84
83
|
while (op = queue.shift)
|
85
|
-
op_request(op) {
|
84
|
+
op_request(op) {|req| adapter.queue req }
|
86
85
|
end
|
87
86
|
end
|
88
87
|
|
89
88
|
def op_request(op)
|
90
|
-
return if Acfs::Stub.enabled?
|
89
|
+
return if Acfs::Stub.enabled? && Acfs::Stub.stubbed(op)
|
91
90
|
req = op.service.prepare op.request
|
92
91
|
return unless req.is_a? Acfs::Request
|
93
92
|
req = prepare req
|
data/lib/acfs/service.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'acfs/service/middleware'
|
2
2
|
|
3
3
|
module Acfs
|
4
|
-
|
5
4
|
# User {Acfs::Service} to define your services. That includes
|
6
5
|
# an identity used to identify the service in configuration files
|
7
6
|
# and middlewares the service uses.
|
@@ -35,14 +34,14 @@ module Acfs
|
|
35
34
|
# @return [Location]
|
36
35
|
#
|
37
36
|
def location(resource_class, opts = {})
|
38
|
-
opts.reverse_merge!
|
37
|
+
opts.reverse_merge! options
|
39
38
|
|
40
39
|
action = opts[:action] || :list
|
41
40
|
|
42
|
-
path = if
|
41
|
+
path = if opts[:path].is_a?(Hash) && opts[:path].key?(action)
|
43
42
|
opts[:path].fetch(action)
|
44
43
|
else
|
45
|
-
path = if
|
44
|
+
path = if opts[:path].is_a?(Hash)
|
46
45
|
opts[:path][:all].to_s
|
47
46
|
else
|
48
47
|
opts[:path].to_s
|
@@ -59,7 +58,6 @@ module Acfs
|
|
59
58
|
end
|
60
59
|
|
61
60
|
class << self
|
62
|
-
|
63
61
|
# @api public
|
64
62
|
#
|
65
63
|
# @overload identity()
|
@@ -83,7 +81,7 @@ module Acfs
|
|
83
81
|
#
|
84
82
|
def base_url
|
85
83
|
unless (base = Acfs::Configuration.current.locate identity)
|
86
|
-
raise ArgumentError
|
84
|
+
raise ArgumentError.new "#{identity} not configured. Add `locate '#{identity.to_s.underscore}', 'http://service.url/'` to your configuration."
|
87
85
|
end
|
88
86
|
|
89
87
|
base.to_s
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Acfs
|
2
2
|
class Service
|
3
|
-
|
4
3
|
# Module providing all function to register middlewares
|
5
4
|
# on services and process queued request through the
|
6
5
|
# middleware stack.
|
@@ -16,7 +15,6 @@ module Acfs
|
|
16
15
|
end
|
17
16
|
|
18
17
|
module ClassMethods
|
19
|
-
|
20
18
|
# @api public
|
21
19
|
#
|
22
20
|
# Register a new middleware to be used for this service.
|
@@ -47,7 +45,7 @@ module Acfs
|
|
47
45
|
# @return [#call]
|
48
46
|
#
|
49
47
|
def middleware
|
50
|
-
@middleware ||= proc {
|
48
|
+
@middleware ||= proc {|request| request }
|
51
49
|
end
|
52
50
|
|
53
51
|
# @api public
|
@@ -1,33 +1,18 @@
|
|
1
1
|
module Acfs
|
2
|
-
|
3
2
|
# Acfs SingletonResources
|
4
3
|
#
|
5
4
|
# Usage explanation:
|
6
5
|
# Single.find => sends GET request to http://service:port/single
|
7
|
-
# my_single.save => sends POST request to http://service:port/single
|
8
|
-
#
|
6
|
+
# my_single.save => sends POST request to http://service:port/single
|
7
|
+
# if my_single is a new object
|
8
|
+
# or sends PUT request to http://service:port/single
|
9
|
+
# if my_single has been requested before
|
9
10
|
# my_single.delete => sends DELETE request to http://service:port/single
|
10
11
|
#
|
11
12
|
# SingletonResources do not support the Resource method :all, since
|
12
13
|
# always only a single instance of the resource is being returned
|
13
14
|
#
|
14
15
|
class SingletonResource < Acfs::Resource
|
15
|
-
|
16
|
-
# @api public
|
17
|
-
#
|
18
|
-
# Return true if model is a new record and was not saved yet.
|
19
|
-
#
|
20
|
-
# Checks weather object is loaded via delegator or not, since
|
21
|
-
# the id-attribute is not required for singletons this check
|
22
|
-
# cannot check for existence of value in id-attribute
|
23
|
-
#
|
24
|
-
# @return [Boolean] True if resource is newly created, false otherwise.
|
25
|
-
#
|
26
|
-
def new?
|
27
|
-
!loaded?
|
28
|
-
end
|
29
|
-
alias :new_record? :new?
|
30
|
-
|
31
16
|
# @api public
|
32
17
|
#
|
33
18
|
# Destroy resource by sending a DELETE request.
|
@@ -66,7 +51,8 @@ module Acfs
|
|
66
51
|
# @param [ Hash ] opts Additional options.
|
67
52
|
# @option opts [ Hash ] :params Additional parameters added to request.
|
68
53
|
#
|
69
|
-
# @yield [ resource ] Callback block to be executed after
|
54
|
+
# @yield [ resource ] Callback block to be executed after
|
55
|
+
# resource was fetched successfully.
|
70
56
|
# @yieldparam resource [ self ] Fetched resources.
|
71
57
|
#
|
72
58
|
# @return [ self ] Resource object.
|
@@ -82,10 +68,11 @@ module Acfs
|
|
82
68
|
# methods :all and :where are not defined.
|
83
69
|
# :find_by is not defined on singletons, use :find instead
|
84
70
|
#
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
71
|
+
def all
|
72
|
+
raise ::Acfs::UnsupportedOperation.new
|
73
|
+
end
|
74
|
+
alias_method :find_by, :all
|
75
|
+
alias_method :find_by!, :all
|
89
76
|
|
90
77
|
# @api private
|
91
78
|
def location_default_path(_, path)
|
data/lib/acfs/stub.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'rack/utils'
|
2
2
|
|
3
3
|
module Acfs
|
4
|
-
|
5
4
|
# Global handler for stubbing resources.
|
6
5
|
#
|
7
6
|
class Stub
|
@@ -14,7 +13,7 @@ module Acfs
|
|
14
13
|
|
15
14
|
@opts[:with].stringify_keys! if @opts[:with].is_a? Hash
|
16
15
|
@opts[:return].stringify_keys! if @opts[:return].is_a? Hash
|
17
|
-
@opts[:return].map! {
|
16
|
+
@opts[:return].map! {|h| h.stringify_keys! if h.is_a? Hash } if @opts[:return].is_a? Array
|
18
17
|
end
|
19
18
|
|
20
19
|
def accept?(op)
|
@@ -22,15 +21,21 @@ module Acfs
|
|
22
21
|
|
23
22
|
params = op.full_params.stringify_keys
|
24
23
|
data = op.data.stringify_keys
|
25
|
-
|
26
|
-
|
27
|
-
return true if
|
28
|
-
|
29
|
-
opts
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
24
|
+
with = opts[:with]
|
25
|
+
|
26
|
+
return true if with.nil?
|
27
|
+
|
28
|
+
case opts.fetch(:match, :inclusion)
|
29
|
+
when :legacy
|
30
|
+
return true if with.empty? && params.empty? && data.empty?
|
31
|
+
return true if with.reject {|_, v| v.nil? } == params.reject {|_, v| v.nil? }
|
32
|
+
return true if with.reject {|_, v| v.nil? } == data.reject {|_, v| v.nil? }
|
33
|
+
false
|
34
|
+
when :inclusion
|
35
|
+
!with.each_pair.any? do |k, v|
|
36
|
+
(params.key?(k) && params[k] != v) || (data.key?(k) && data[k] != v)
|
37
|
+
end
|
38
|
+
end
|
34
39
|
end
|
35
40
|
|
36
41
|
def calls
|
@@ -51,17 +56,20 @@ module Acfs
|
|
51
56
|
if err
|
52
57
|
raise_error op, err, opts[:return]
|
53
58
|
elsif data
|
59
|
+
data = data.call(op) if data.respond_to?(:call)
|
60
|
+
|
54
61
|
response = Acfs::Response.new op.request,
|
55
|
-
|
56
|
-
|
57
|
-
|
62
|
+
headers: opts[:headers] || {},
|
63
|
+
status: opts[:status] || 200,
|
64
|
+
data: data || {}
|
58
65
|
op.call data, response
|
59
66
|
else
|
60
|
-
raise ArgumentError
|
67
|
+
raise ArgumentError.new 'Unsupported stub.'
|
61
68
|
end
|
62
69
|
end
|
63
70
|
|
64
71
|
private
|
72
|
+
|
65
73
|
def raise_error(op, name, data)
|
66
74
|
raise name if name.is_a? Class
|
67
75
|
data.stringify_keys! if data.respond_to? :stringify_keys!
|
@@ -70,13 +78,12 @@ module Acfs
|
|
70
78
|
end
|
71
79
|
|
72
80
|
class << self
|
73
|
-
|
74
81
|
# Stub a resource with given handler block. An already created handler
|
75
82
|
# for same resource class will be overridden.
|
76
83
|
#
|
77
|
-
def resource(klass, action, opts = {}, &
|
84
|
+
def resource(klass, action, opts = {}, &_block)
|
78
85
|
action = action.to_sym
|
79
|
-
raise ArgumentError
|
86
|
+
raise ArgumentError.new "Unknown action `#{action}`." unless ACTIONS.include? action
|
80
87
|
|
81
88
|
Stub.new(opts).tap do |stub|
|
82
89
|
stubs[klass] ||= {}
|
@@ -97,11 +104,11 @@ module Acfs
|
|
97
104
|
@enabled ||= false
|
98
105
|
end
|
99
106
|
|
100
|
-
def enable
|
107
|
+
def enable
|
101
108
|
@enabled = true
|
102
109
|
end
|
103
110
|
|
104
|
-
def disable
|
111
|
+
def disable
|
105
112
|
@enabled = false
|
106
113
|
end
|
107
114
|
|
@@ -119,7 +126,7 @@ module Acfs
|
|
119
126
|
return false unless (classes = stubs[op.resource])
|
120
127
|
return false unless (stubs = classes[op.action])
|
121
128
|
|
122
|
-
accepted_stubs = stubs.select {
|
129
|
+
accepted_stubs = stubs.select {|stub| stub.accept? op }
|
123
130
|
|
124
131
|
raise AmbiguousStubError.new stubs: accepted_stubs, operation: op if accepted_stubs.size > 1
|
125
132
|
|
@@ -143,8 +150,9 @@ module Acfs
|
|
143
150
|
end
|
144
151
|
|
145
152
|
private
|
153
|
+
|
146
154
|
def pretty_print
|
147
|
-
out =
|
155
|
+
out = ''
|
148
156
|
stubs.each do |klass, actions|
|
149
157
|
out << ' ' << klass.name << ":\n"
|
150
158
|
actions.each do |action, stubs|
|
data/lib/acfs/util.rb
CHANGED
data/lib/acfs/version.rb
CHANGED