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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +8 -9
  4. data/acfs.gemspec +3 -3
  5. data/lib/acfs.rb +0 -2
  6. data/lib/acfs/adapter/base.rb +0 -2
  7. data/lib/acfs/adapter/typhoeus.rb +6 -9
  8. data/lib/acfs/collection.rb +3 -3
  9. data/lib/acfs/collections/paginatable.rb +16 -16
  10. data/lib/acfs/configuration.rb +3 -5
  11. data/lib/acfs/errors.rb +8 -7
  12. data/lib/acfs/global.rb +1 -1
  13. data/lib/acfs/location.rb +11 -11
  14. data/lib/acfs/middleware/base.rb +1 -2
  15. data/lib/acfs/middleware/json.rb +0 -1
  16. data/lib/acfs/middleware/logger.rb +0 -2
  17. data/lib/acfs/middleware/print.rb +0 -2
  18. data/lib/acfs/middleware/serializer.rb +3 -6
  19. data/lib/acfs/operation.rb +3 -4
  20. data/lib/acfs/request.rb +2 -3
  21. data/lib/acfs/request/callbacks.rb +2 -3
  22. data/lib/acfs/resource.rb +34 -5
  23. data/lib/acfs/{model → resource}/attributes.rb +70 -46
  24. data/lib/acfs/{model → resource}/attributes/base.rb +10 -6
  25. data/lib/acfs/resource/attributes/boolean.rb +33 -0
  26. data/lib/acfs/resource/attributes/date_time.rb +32 -0
  27. data/lib/acfs/{model → resource}/attributes/dict.rb +1 -3
  28. data/lib/acfs/{model → resource}/attributes/float.rb +3 -6
  29. data/lib/acfs/{model → resource}/attributes/integer.rb +3 -6
  30. data/lib/acfs/{model → resource}/attributes/list.rb +2 -5
  31. data/lib/acfs/{model → resource}/attributes/string.rb +4 -6
  32. data/lib/acfs/{model → resource}/attributes/uuid.rb +18 -8
  33. data/lib/acfs/resource/dirty.rb +47 -0
  34. data/lib/acfs/{model → resource}/initialization.rb +8 -10
  35. data/lib/acfs/{model → resource}/loadable.rb +3 -4
  36. data/lib/acfs/{model → resource}/locatable.rb +22 -23
  37. data/lib/acfs/{model → resource}/operational.rb +2 -3
  38. data/lib/acfs/resource/persistence.rb +257 -0
  39. data/lib/acfs/{model → resource}/query_methods.rb +81 -66
  40. data/lib/acfs/{model → resource}/service.rb +10 -9
  41. data/lib/acfs/resource/validation.rb +28 -0
  42. data/lib/acfs/response.rb +1 -2
  43. data/lib/acfs/response/formats.rb +1 -2
  44. data/lib/acfs/response/status.rb +3 -5
  45. data/lib/acfs/runner.rb +4 -5
  46. data/lib/acfs/service.rb +4 -6
  47. data/lib/acfs/service/middleware.rb +1 -3
  48. data/lib/acfs/singleton_resource.rb +11 -24
  49. data/lib/acfs/stub.rb +30 -22
  50. data/lib/acfs/util.rb +1 -1
  51. data/lib/acfs/version.rb +4 -2
  52. data/spec/acfs/adapter/typhoeus_spec.rb +4 -4
  53. data/spec/acfs/collection_spec.rb +33 -33
  54. data/spec/acfs/configuration_spec.rb +0 -1
  55. data/spec/acfs/global_spec.rb +3 -3
  56. data/spec/acfs/middleware/json_spec.rb +2 -2
  57. data/spec/acfs/middleware/msgpack_spec.rb +4 -4
  58. data/spec/acfs/request/callbacks_spec.rb +8 -8
  59. data/spec/acfs/request_spec.rb +5 -5
  60. data/spec/acfs/{model → resource}/attributes/boolean_spec.rb +2 -2
  61. data/spec/acfs/{model → resource}/attributes/date_time_spec.rb +7 -7
  62. data/spec/acfs/{model → resource}/attributes/dict_spec.rb +6 -6
  63. data/spec/acfs/{model → resource}/attributes/float_spec.rb +3 -3
  64. data/spec/acfs/{model → resource}/attributes/list_spec.rb +5 -5
  65. data/spec/acfs/{model → resource}/attributes/uuid_spec.rb +6 -6
  66. data/spec/acfs/{model → resource}/attributes_spec.rb +31 -17
  67. data/spec/acfs/{model → resource}/dirty_spec.rb +7 -5
  68. data/spec/acfs/{model → resource}/initialization_spec.rb +7 -7
  69. data/spec/acfs/{model → resource}/loadable_spec.rb +4 -3
  70. data/spec/acfs/{model → resource}/locatable_spec.rb +24 -14
  71. data/spec/acfs/{model → resource}/persistance_spec.rb +34 -34
  72. data/spec/acfs/{model → resource}/query_methods_spec.rb +171 -130
  73. data/spec/acfs/{model → resource}/validation_spec.rb +5 -6
  74. data/spec/acfs/response/formats_spec.rb +1 -1
  75. data/spec/acfs/response/status_spec.rb +1 -1
  76. data/spec/acfs/runner_spec.rb +2 -3
  77. data/spec/acfs/service/middleware_spec.rb +1 -1
  78. data/spec/acfs/service_spec.rb +3 -5
  79. data/spec/acfs/singleton_resource_spec.rb +3 -3
  80. data/spec/acfs/stub_spec.rb +52 -24
  81. data/spec/acfs_spec.rb +22 -19
  82. data/spec/spec_helper.rb +1 -1
  83. data/spec/support/hash.rb +9 -0
  84. data/spec/support/service.rb +4 -7
  85. data/spec/support/shared/find_callbacks.rb +7 -7
  86. metadata +52 -52
  87. data/lib/acfs/model.rb +0 -43
  88. data/lib/acfs/model/attributes/boolean.rb +0 -38
  89. data/lib/acfs/model/attributes/date_time.rb +0 -30
  90. data/lib/acfs/model/dirty.rb +0 -49
  91. data/lib/acfs/model/persistence.rb +0 -243
  92. data/lib/acfs/model/relations.rb +0 -10
  93. data/lib/acfs/model/validation.rb +0 -30
@@ -1,36 +1,37 @@
1
- module Acfs::Model
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 [ Service ] Service class instance.
15
+ # @return [Service] Service class instance.
17
16
  #
18
17
  # @overload service(klass, options = {})
19
- # Link to service this model belongs to. Connection settings like base URL
20
- # are fetched from service. Return assigned service if no arguments are given.
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 [ Class ] klass Service class derived from {Acfs::Service}.
33
- # @param [ Object ] options Option delegated to service class initializer.
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
@@ -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
 
@@ -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
- alias :code :status_code
14
+ alias_method :code, :status_code
17
15
 
18
16
  # Return true if response was successful indicated by
19
17
  # response status code.
@@ -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) { |req| adapter.run req }
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) { |req| adapter.queue req }
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) { |req| adapter.queue req }
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? and Acfs::Stub.stubbed(op)
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
@@ -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! self.options
37
+ opts.reverse_merge! options
39
38
 
40
39
  action = opts[:action] || :list
41
40
 
42
- path = if Hash === opts[:path] && opts[:path].has_key?(action)
41
+ path = if opts[:path].is_a?(Hash) && opts[:path].key?(action)
43
42
  opts[:path].fetch(action)
44
43
  else
45
- path = if Hash === opts[:path]
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, "#{identity} not configured. Add `locate '#{identity.to_s.underscore}', 'http://service.url/'` to your configuration."
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 { |request| request}
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 if my_single is a new object
8
- # or sends PUT request to http://service:port/single if my_single has been requested before
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 resource was fetched successfully.
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
- undef_method :all
86
- undef_method :where
87
- undef_method :find_by
88
- undef_method :find_by!
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)
@@ -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! { |h| h.stringify_keys! if h.is_a? Hash } if @opts[:return].is_a? Array
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
- return true if opts[:with] == params || data == opts[:with]
27
- return true if opts[:with].nil? && params.empty? && data.empty?
28
-
29
- opts[:with] ||= {}
30
- return true if opts[:with].reject{|_, v| v.nil? } == params.reject {|_, v| v.nil? }
31
- return true if opts[:with].reject{|_, v| v.nil? } == data.reject {|_, v| v.nil? }
32
-
33
- false
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
- headers: opts[:headers] || {},
56
- status: opts[:status] || 200,
57
- data: data || {}
62
+ headers: opts[:headers] || {},
63
+ status: opts[:status] || 200,
64
+ data: data || {}
58
65
  op.call data, response
59
66
  else
60
- raise ArgumentError, 'Unsupported stub.'
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 = {}, &block)
84
+ def resource(klass, action, opts = {}, &_block)
78
85
  action = action.to_sym
79
- raise ArgumentError, "Unknown action `#{action}`." unless ACTIONS.include? action
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 { |stub| stub.accept? op }
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 = String.new
155
+ out = ''
148
156
  stubs.each do |klass, actions|
149
157
  out << ' ' << klass.name << ":\n"
150
158
  actions.each do |action, stubs|
@@ -7,7 +7,7 @@ module Acfs
7
7
  end
8
8
 
9
9
  def __invoke__
10
- __callbacks__.each{|c| c.call self}
10
+ __callbacks__.each {|c| c.call self }
11
11
  end
12
12
  end
13
13
 
@@ -1,12 +1,14 @@
1
1
  module Acfs
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 42
4
+ MINOR = 43
5
5
  PATCH = 0
6
6
  STAGE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.')
9
9
 
10
- def self.to_s; STRING end
10
+ def self.to_s
11
+ STRING
12
+ end
11
13
  end
12
14
  end