acfs 0.42.0 → 0.43.0
Sign up to get free protection for your applications and to get access to all the features.
- 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