acfs 1.3.3 → 1.3.4
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 +339 -0
- data/LICENSE +22 -0
- data/README.md +335 -0
- data/acfs.gemspec +46 -0
- data/lib/acfs.rb +51 -0
- data/lib/acfs/adapter/base.rb +24 -0
- data/lib/acfs/adapter/typhoeus.rb +69 -0
- data/lib/acfs/collection.rb +28 -0
- data/lib/acfs/collections/paginatable.rb +76 -0
- data/lib/acfs/configuration.rb +120 -0
- data/lib/acfs/errors.rb +127 -0
- data/lib/acfs/global.rb +101 -0
- data/lib/acfs/location.rb +82 -0
- data/lib/acfs/middleware/base.rb +24 -0
- data/lib/acfs/middleware/json.rb +29 -0
- data/lib/acfs/middleware/logger.rb +25 -0
- data/lib/acfs/middleware/msgpack.rb +32 -0
- data/lib/acfs/middleware/print.rb +23 -0
- data/lib/acfs/middleware/serializer.rb +41 -0
- data/lib/acfs/operation.rb +83 -0
- data/lib/acfs/request.rb +39 -0
- data/lib/acfs/request/callbacks.rb +54 -0
- data/lib/acfs/resource.rb +39 -0
- data/lib/acfs/resource/attributes.rb +269 -0
- data/lib/acfs/resource/attributes/base.rb +29 -0
- data/lib/acfs/resource/attributes/boolean.rb +39 -0
- data/lib/acfs/resource/attributes/date_time.rb +32 -0
- data/lib/acfs/resource/attributes/dict.rb +39 -0
- data/lib/acfs/resource/attributes/float.rb +33 -0
- data/lib/acfs/resource/attributes/integer.rb +29 -0
- data/lib/acfs/resource/attributes/list.rb +36 -0
- data/lib/acfs/resource/attributes/string.rb +26 -0
- data/lib/acfs/resource/attributes/uuid.rb +48 -0
- data/lib/acfs/resource/dirty.rb +37 -0
- data/lib/acfs/resource/initialization.rb +31 -0
- data/lib/acfs/resource/loadable.rb +35 -0
- data/lib/acfs/resource/locatable.rb +132 -0
- data/lib/acfs/resource/operational.rb +23 -0
- data/lib/acfs/resource/persistence.rb +260 -0
- data/lib/acfs/resource/query_methods.rb +266 -0
- data/lib/acfs/resource/service.rb +44 -0
- data/lib/acfs/resource/validation.rb +39 -0
- data/lib/acfs/response.rb +30 -0
- data/lib/acfs/response/formats.rb +27 -0
- data/lib/acfs/response/status.rb +33 -0
- data/lib/acfs/rspec.rb +13 -0
- data/lib/acfs/runner.rb +102 -0
- data/lib/acfs/service.rb +97 -0
- data/lib/acfs/service/middleware.rb +58 -0
- data/lib/acfs/service/middleware/stack.rb +65 -0
- data/lib/acfs/singleton_resource.rb +85 -0
- data/lib/acfs/stub.rb +194 -0
- data/lib/acfs/util.rb +22 -0
- data/lib/acfs/version.rb +16 -0
- data/lib/acfs/yard.rb +6 -0
- data/spec/acfs/adapter/typhoeus_spec.rb +55 -0
- data/spec/acfs/collection_spec.rb +157 -0
- data/spec/acfs/configuration_spec.rb +53 -0
- data/spec/acfs/global_spec.rb +140 -0
- data/spec/acfs/location_spec.rb +25 -0
- data/spec/acfs/middleware/json_spec.rb +65 -0
- data/spec/acfs/middleware/msgpack_spec.rb +62 -0
- data/spec/acfs/operation_spec.rb +12 -0
- data/spec/acfs/request/callbacks_spec.rb +48 -0
- data/spec/acfs/request_spec.rb +79 -0
- data/spec/acfs/resource/attributes/boolean_spec.rb +58 -0
- data/spec/acfs/resource/attributes/date_time_spec.rb +51 -0
- data/spec/acfs/resource/attributes/dict_spec.rb +77 -0
- data/spec/acfs/resource/attributes/float_spec.rb +61 -0
- data/spec/acfs/resource/attributes/integer_spec.rb +36 -0
- data/spec/acfs/resource/attributes/list_spec.rb +60 -0
- data/spec/acfs/resource/attributes/uuid_spec.rb +42 -0
- data/spec/acfs/resource/attributes_spec.rb +181 -0
- data/spec/acfs/resource/dirty_spec.rb +49 -0
- data/spec/acfs/resource/initialization_spec.rb +36 -0
- data/spec/acfs/resource/loadable_spec.rb +22 -0
- data/spec/acfs/resource/locatable_spec.rb +118 -0
- data/spec/acfs/resource/persistance_spec.rb +322 -0
- data/spec/acfs/resource/query_methods_spec.rb +548 -0
- data/spec/acfs/resource/validation_spec.rb +129 -0
- data/spec/acfs/response/formats_spec.rb +52 -0
- data/spec/acfs/response/status_spec.rb +71 -0
- data/spec/acfs/runner_spec.rb +95 -0
- data/spec/acfs/service/middleware_spec.rb +35 -0
- data/spec/acfs/service_spec.rb +48 -0
- data/spec/acfs/singleton_resource_spec.rb +17 -0
- data/spec/acfs/stub_spec.rb +345 -0
- data/spec/acfs_spec.rb +205 -0
- data/spec/fixtures/config.yml +14 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/hash.rb +11 -0
- data/spec/support/response.rb +12 -0
- data/spec/support/service.rb +92 -0
- data/spec/support/shared/find_callbacks.rb +50 -0
- metadata +136 -3
data/acfs.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'acfs/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'acfs'
|
9
|
+
spec.version = Acfs::VERSION
|
10
|
+
spec.authors = ['Jan Graichen']
|
11
|
+
spec.email = %w[jgraichen@altimos.de]
|
12
|
+
spec.homepage = 'https://github.com/jgraichen/acfs'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
spec.description = 'API Client For Services'
|
15
|
+
spec.summary = <<~SUMMARY.strip
|
16
|
+
An abstract API base client for service oriented application.
|
17
|
+
SUMMARY
|
18
|
+
|
19
|
+
spec.files = Dir['**/*'].grep(%r{
|
20
|
+
^((bin|lib|test|spec|features)/|
|
21
|
+
.*\.gemspec|.*LICENSE.*|.*README.*|.*CHANGELOG.*)
|
22
|
+
}xi)
|
23
|
+
|
24
|
+
spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f) }
|
25
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
26
|
+
spec.require_paths = %w[lib]
|
27
|
+
|
28
|
+
spec.add_runtime_dependency 'actionpack', '>= 4.2'
|
29
|
+
spec.add_runtime_dependency 'activemodel', '>= 4.2'
|
30
|
+
spec.add_runtime_dependency 'activesupport', '>= 4.2'
|
31
|
+
spec.add_runtime_dependency 'multi_json'
|
32
|
+
|
33
|
+
# Bundle update w/o version resolves to 0.3.3 ...
|
34
|
+
spec.add_runtime_dependency 'typhoeus', '~> 1.0'
|
35
|
+
|
36
|
+
spec.add_runtime_dependency 'rack'
|
37
|
+
|
38
|
+
spec.add_development_dependency 'bundler'
|
39
|
+
|
40
|
+
if ENV['TRAVIS_BUILD_NUMBER'] && !ENV['TRAVIS_TAG']
|
41
|
+
# Append travis build number for auto-releases
|
42
|
+
# rubocop:disable Gemspec/DuplicatedAssignment
|
43
|
+
spec.version = "#{spec.version}.1.b#{ENV['TRAVIS_BUILD_NUMBER']}"
|
44
|
+
# rubocop:enable Gemspec/DuplicatedAssignment
|
45
|
+
end
|
46
|
+
end
|
data/lib/acfs.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/hash'
|
5
|
+
require 'active_support/core_ext/class'
|
6
|
+
require 'active_support/core_ext/string'
|
7
|
+
require 'active_support/core_ext/module'
|
8
|
+
require 'active_support/notifications'
|
9
|
+
|
10
|
+
module Acfs
|
11
|
+
extend ActiveSupport::Autoload
|
12
|
+
require 'acfs/version'
|
13
|
+
require 'acfs/errors'
|
14
|
+
require 'acfs/global'
|
15
|
+
require 'acfs/util'
|
16
|
+
|
17
|
+
require 'acfs/collection'
|
18
|
+
require 'acfs/configuration'
|
19
|
+
require 'acfs/location'
|
20
|
+
require 'acfs/operation'
|
21
|
+
require 'acfs/request'
|
22
|
+
require 'acfs/resource'
|
23
|
+
require 'acfs/response'
|
24
|
+
require 'acfs/runner'
|
25
|
+
require 'acfs/service'
|
26
|
+
require 'acfs/singleton_resource'
|
27
|
+
|
28
|
+
extend Global
|
29
|
+
|
30
|
+
autoload :Stub
|
31
|
+
|
32
|
+
module Middleware
|
33
|
+
extend ActiveSupport::Autoload
|
34
|
+
require 'acfs/middleware/base'
|
35
|
+
require 'acfs/middleware/serializer'
|
36
|
+
|
37
|
+
autoload :Print
|
38
|
+
autoload :Logger
|
39
|
+
autoload :JSON
|
40
|
+
autoload :JsonDecoder, 'acfs/middleware/json'
|
41
|
+
autoload :JsonEncoder, 'acfs/middleware/json'
|
42
|
+
autoload :MessagePack, 'acfs/middleware/msgpack'
|
43
|
+
autoload :MessagePackDecoder, 'acfs/middleware/msgpack'
|
44
|
+
autoload :MessagePackEncoder, 'acfs/middleware/msgpack'
|
45
|
+
end
|
46
|
+
|
47
|
+
module Adapter
|
48
|
+
require 'acfs/adapter/base'
|
49
|
+
require 'acfs/adapter/typhoeus'
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Adapter
|
4
|
+
# Base adapter handling operation queuing
|
5
|
+
# and processing.
|
6
|
+
#
|
7
|
+
class Base
|
8
|
+
# Start processing queued requests.
|
9
|
+
#
|
10
|
+
def start; end
|
11
|
+
|
12
|
+
# Abort running and queued requests.
|
13
|
+
#
|
14
|
+
def abort; end
|
15
|
+
|
16
|
+
# Run request right now skipping queue.
|
17
|
+
#
|
18
|
+
def run(_); end
|
19
|
+
|
20
|
+
# Enqueue request to be run later.
|
21
|
+
#
|
22
|
+
def queue(_); end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'typhoeus'
|
4
|
+
|
5
|
+
module Acfs
|
6
|
+
module Adapter
|
7
|
+
# Adapter for Typhoeus.
|
8
|
+
#
|
9
|
+
class Typhoeus < Base
|
10
|
+
def initialize(**kwargs)
|
11
|
+
@options = kwargs
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
hydra.run
|
16
|
+
rescue StandardError
|
17
|
+
@hydra = nil
|
18
|
+
raise
|
19
|
+
end
|
20
|
+
|
21
|
+
delegate :abort, to: :hydra
|
22
|
+
|
23
|
+
def run(request)
|
24
|
+
convert_request(request).run
|
25
|
+
end
|
26
|
+
|
27
|
+
def queue(request)
|
28
|
+
hydra.queue convert_request request
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def hydra
|
34
|
+
@hydra ||= ::Typhoeus::Hydra.new(**@options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def convert_request(req)
|
38
|
+
request = ::Typhoeus::Request.new req.url,
|
39
|
+
method: req.method,
|
40
|
+
params: req.params,
|
41
|
+
headers: req.headers.merge(
|
42
|
+
'Expect' => '',
|
43
|
+
'Transfer-Encoding' => ''
|
44
|
+
),
|
45
|
+
body: req.body
|
46
|
+
|
47
|
+
request.on_complete do |response|
|
48
|
+
if response.timed_out?
|
49
|
+
raise ::Acfs::TimeoutError.new(req)
|
50
|
+
elsif response.code.zero?
|
51
|
+
# Failed to get HTTP response
|
52
|
+
raise ::Acfs::RequestError.new(req, response.return_message)
|
53
|
+
else
|
54
|
+
req.complete! convert_response(req, response)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
request
|
59
|
+
end
|
60
|
+
|
61
|
+
def convert_response(request, response)
|
62
|
+
Acfs::Response.new request,
|
63
|
+
status: response.code,
|
64
|
+
headers: response.headers,
|
65
|
+
body: response.body
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
require 'acfs/resource/loadable'
|
6
|
+
require 'acfs/collections/paginatable'
|
7
|
+
|
8
|
+
module Acfs
|
9
|
+
class Collection < ::Delegator
|
10
|
+
include Resource::Loadable
|
11
|
+
include Acfs::Util::Callbacks
|
12
|
+
include Collections::Paginatable
|
13
|
+
|
14
|
+
def initialize(resource_class)
|
15
|
+
super([])
|
16
|
+
|
17
|
+
@resource_class = resource_class
|
18
|
+
end
|
19
|
+
|
20
|
+
def __getobj__
|
21
|
+
@models
|
22
|
+
end
|
23
|
+
|
24
|
+
def __setobj__(obj)
|
25
|
+
@models = obj
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Collections
|
4
|
+
module Paginatable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
def self.operation(_action, opts = {}, &_block)
|
9
|
+
opts[:url]
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :total_pages, :current_page, :total_count
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_response(response)
|
16
|
+
setup_params response.request.params if response.request
|
17
|
+
setup_headers response.headers
|
18
|
+
end
|
19
|
+
|
20
|
+
def next_page(&block)
|
21
|
+
page 'next', &block
|
22
|
+
end
|
23
|
+
|
24
|
+
def prev_page(&block)
|
25
|
+
page 'prev', &block
|
26
|
+
end
|
27
|
+
|
28
|
+
def first_page(&block)
|
29
|
+
page 'first', &block
|
30
|
+
end
|
31
|
+
|
32
|
+
def last_page(&block)
|
33
|
+
page 'last', &block
|
34
|
+
end
|
35
|
+
|
36
|
+
def page(rel, &block)
|
37
|
+
return unless relations[rel]
|
38
|
+
|
39
|
+
@resource_class.all nil, url: relations[rel], &block
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def relations
|
45
|
+
@relations ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def setup_headers(headers)
|
49
|
+
if headers['X-Total-Pages']
|
50
|
+
@total_pages = Integer(headers['X-Total-Pages'])
|
51
|
+
end
|
52
|
+
|
53
|
+
if headers['X-Total-Count']
|
54
|
+
@total_count = Integer(headers['X-Total-Count'])
|
55
|
+
end
|
56
|
+
|
57
|
+
setup_links headers['Link'] if headers['Link']
|
58
|
+
end
|
59
|
+
|
60
|
+
def setup_links(links)
|
61
|
+
links.split(/,\s+/).each do |link|
|
62
|
+
if link =~ /^\s*<([^>]+)>.*\s+rel="([\w_-]+)".*$/
|
63
|
+
relations[Regexp.last_match[2]] = Regexp.last_match[1]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def setup_params(params)
|
69
|
+
@current_page = begin
|
70
|
+
Integer params.fetch(:page, 1)
|
71
|
+
rescue ArgumentError
|
72
|
+
params[:page]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Acfs
|
7
|
+
# Acfs configuration is used to locate services and get their base URLs.
|
8
|
+
#
|
9
|
+
class Configuration
|
10
|
+
attr_reader :locations
|
11
|
+
attr_accessor :adapter
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
def initialize
|
15
|
+
@locations = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api public
|
19
|
+
#
|
20
|
+
# Configure using given block. If block accepts zero arguments
|
21
|
+
# bock will be evaluated in context of the configuration instance
|
22
|
+
# otherwise the configuration instance will be given as first arguments.
|
23
|
+
#
|
24
|
+
# @yield [configuration] Give configuration as arguments or evaluate block
|
25
|
+
# in context of configuration object.
|
26
|
+
# @yieldparam configuration [Configuration] Configuration object.
|
27
|
+
# @return [undefined]
|
28
|
+
#
|
29
|
+
def configure(&block)
|
30
|
+
if block.arity.positive?
|
31
|
+
block.call self
|
32
|
+
else
|
33
|
+
instance_eval(&block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api public
|
38
|
+
#
|
39
|
+
# @overload locate(service, uri)
|
40
|
+
# Configures URL where a service can be reached.
|
41
|
+
#
|
42
|
+
# @param [Symbol] service
|
43
|
+
# Service identity key for service that is reachable under given URL.
|
44
|
+
#
|
45
|
+
# @param [String] uri
|
46
|
+
# URL where service is reachable. Will be passed to {URI.parse}.
|
47
|
+
#
|
48
|
+
# @return [undefined]
|
49
|
+
#
|
50
|
+
# @overload locate(service)
|
51
|
+
# Return configured base URL for given service identity key.
|
52
|
+
#
|
53
|
+
# @param [Symbol] service Service identity key to lookup.
|
54
|
+
# @return [URI, NilClass] Configured base URL or nil.
|
55
|
+
#
|
56
|
+
def locate(service, uri = nil)
|
57
|
+
service = service.to_s.underscore.to_sym
|
58
|
+
if uri.nil?
|
59
|
+
locations[service]
|
60
|
+
else
|
61
|
+
locations[service] = URI.parse uri
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @api public
|
66
|
+
#
|
67
|
+
# Load configuration from given YAML file.
|
68
|
+
#
|
69
|
+
# @param [String] filename Path to YAML configuration file.
|
70
|
+
# @return [undefined]
|
71
|
+
#
|
72
|
+
def load(filename)
|
73
|
+
config = YAML.safe_load(File.read(filename), [], [], true)
|
74
|
+
env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
|
75
|
+
|
76
|
+
config = config[env] if config.key? env
|
77
|
+
config.each do |key, value|
|
78
|
+
case key
|
79
|
+
when 'services' then load_services value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# @api private
|
85
|
+
#
|
86
|
+
# Load services from configuration YAML.
|
87
|
+
#
|
88
|
+
def load_services(services)
|
89
|
+
services.each do |service, data|
|
90
|
+
if (val = data).is_a?(String) || (val = data['locate'])
|
91
|
+
locate service.to_sym, val
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class << self
|
97
|
+
# @api private
|
98
|
+
#
|
99
|
+
# Return current configuration object.
|
100
|
+
#
|
101
|
+
# @return [Configuration]
|
102
|
+
#
|
103
|
+
def current
|
104
|
+
@current ||= new
|
105
|
+
end
|
106
|
+
|
107
|
+
# @api private
|
108
|
+
#
|
109
|
+
# Swap configuration object with given new one. Must be
|
110
|
+
# a {Configuration} object.
|
111
|
+
#
|
112
|
+
# @param [Configuration] configuration
|
113
|
+
# @return [undefined]
|
114
|
+
#
|
115
|
+
def set(configuration)
|
116
|
+
@current = configuration if configuration.is_a? Configuration
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/acfs/errors.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs
|
4
|
+
# Acfs base error.
|
5
|
+
#
|
6
|
+
class Error < StandardError
|
7
|
+
def initialize(opts = {}, message = nil)
|
8
|
+
opts.merge! message: message if message
|
9
|
+
super opts[:message]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class UnsupportedOperation < StandardError; end
|
14
|
+
|
15
|
+
class RequestError < Error
|
16
|
+
attr_reader :request
|
17
|
+
|
18
|
+
def initialize(request, message)
|
19
|
+
@request = request
|
20
|
+
|
21
|
+
message = "#{message}: #{request.method.upcase} #{request.url}"
|
22
|
+
|
23
|
+
super message: message
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class TimeoutError < RequestError
|
28
|
+
def initialize(request)
|
29
|
+
super(request, "Timeout reached")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Response error containing the responsible response object.
|
34
|
+
#
|
35
|
+
class ErroneousResponse < Error
|
36
|
+
attr_reader :response
|
37
|
+
|
38
|
+
def initialize(opts = {})
|
39
|
+
@response = opts[:response]
|
40
|
+
|
41
|
+
if response
|
42
|
+
message = (opts[:message] ? opts[:message] + ':' : 'Received') +
|
43
|
+
" #{response.code} for #{response.request.method.upcase}" \
|
44
|
+
" #{response.request.url} #{response.request.format}"
|
45
|
+
else
|
46
|
+
message = opts[:message] || 'Received erroneous response'
|
47
|
+
end
|
48
|
+
|
49
|
+
super opts, message
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class AmbiguousStubError < Error
|
54
|
+
attr_reader :stubs, :operation
|
55
|
+
|
56
|
+
def initialize(opts = {})
|
57
|
+
require 'pp'
|
58
|
+
|
59
|
+
@stubs = opts.delete :stubs
|
60
|
+
@operation = opts.delete :operation
|
61
|
+
|
62
|
+
message = "Ambiguous stubs for #{operation.action} " \
|
63
|
+
"on #{operation.resource}.\n" +
|
64
|
+
stubs.map {|s| " #{s.opts.pretty_inspect}" }.join
|
65
|
+
|
66
|
+
super opts, message
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Resource not found error raised on a 404 response
|
71
|
+
#
|
72
|
+
class ResourceNotFound < ErroneousResponse
|
73
|
+
end
|
74
|
+
|
75
|
+
class InvalidResource < ErroneousResponse
|
76
|
+
attr_reader :errors, :resource
|
77
|
+
|
78
|
+
def initialize(opts = {})
|
79
|
+
@errors = opts.delete :errors
|
80
|
+
@resource = opts.delete :resource
|
81
|
+
|
82
|
+
if @errors.is_a?(Hash)
|
83
|
+
opts[:message] ||= @errors.each_pair.map do |k, v|
|
84
|
+
@errors.is_a?(Array) ? "#{k}: #{v.join(', ')}" : "#{k}: #{v}"
|
85
|
+
end.join ', '
|
86
|
+
elsif @errors.is_a?(Array)
|
87
|
+
opts[:message] ||= @errors.join ', '
|
88
|
+
end
|
89
|
+
|
90
|
+
super
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# A ResourceNotLoaded error will be thrown when calling some
|
95
|
+
# modifing methods on not loaded resources as it is usally
|
96
|
+
# unwanted to call e.g. `update_attributes` on a not loaded
|
97
|
+
# resource.
|
98
|
+
# Correct solution is to first run `Acfs.run` to fetch the
|
99
|
+
# resource and then update the resource.
|
100
|
+
#
|
101
|
+
class ResourceNotLoaded < Error
|
102
|
+
attr_reader :resource
|
103
|
+
|
104
|
+
def initialize(opts = {})
|
105
|
+
@resource = opts.delete :resource
|
106
|
+
super
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Gets raised if ressource type is no valid subclass of
|
111
|
+
# parent resource. Check if the type is set to the correct
|
112
|
+
# Acfs::Resource Name
|
113
|
+
class ResourceTypeError < Error
|
114
|
+
attr_reader :base_class
|
115
|
+
attr_reader :type_name
|
116
|
+
|
117
|
+
def initialize(opts = {})
|
118
|
+
@base_class = opts.delete :base_class
|
119
|
+
@type_name = opts.delete :type_name
|
120
|
+
opts[:message] = "Received resource type `#{type_name}` " \
|
121
|
+
"is no subclass of #{base_class}"
|
122
|
+
super
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class RealRequestsNotAllowedError < StandardError; end
|
127
|
+
end
|