acfs 0.42.0 → 0.43.0

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