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
@@ -0,0 +1,257 @@
1
+ class Acfs::Resource
2
+ #
3
+ # Allow to track the persistence state of a model.
4
+ #
5
+ module Persistence
6
+ extend ActiveSupport::Concern
7
+
8
+ # @api public
9
+ #
10
+ # Check if the model is persisted. A model is persisted if
11
+ # it is saved after being created
12
+ #
13
+ # @example Newly created resource:
14
+ # user = User.new name: "John"
15
+ # user.persisted? # => false
16
+ # user.save
17
+ # user.persisted? # => true
18
+ #
19
+ # @example Modified resource:
20
+ # user2 = User.find 5
21
+ # user2.persisted? # => true
22
+ # user2.name = 'Amy'
23
+ # user2.persisted? # => true
24
+ # user2.save
25
+ # user2.persisted? # => true
26
+ #
27
+ # @return [Boolean] True if resource has been saved
28
+ #
29
+ def persisted?
30
+ !new?
31
+ end
32
+
33
+ # @api public
34
+ #
35
+ # Return true if model is a new record and was not saved yet.
36
+ #
37
+ # @return [Boolean] True if resource is newly created,
38
+ # false otherwise.
39
+ #
40
+ def new?
41
+ !loaded?
42
+ end
43
+ alias_method :new_record?, :new?
44
+
45
+ # @api public
46
+ #
47
+ # Saves the resource.
48
+ #
49
+ # It will PUT to the service to update the resource or send
50
+ # a POST to create a new one if the resource is new.
51
+ #
52
+ # Saving a resource is a synchronous operation.
53
+ #
54
+ # @return [Boolean] True if save operation was successful,
55
+ # false otherwise.
56
+ # @see #save! See {#save!} for available options.
57
+ #
58
+ def save(*args)
59
+ save!(*args)
60
+ true
61
+ rescue Acfs::Error
62
+ false
63
+ end
64
+
65
+ # @api public
66
+ #
67
+ # Saves the resource. Raises an error if something happens.
68
+ #
69
+ # Saving a resource is a synchronous operation.
70
+ #
71
+ # @param opts [Hash] Hash with additional options.
72
+ # @option opts [Hash] :data Data to send to remote service.
73
+ # Default will be resource attributes.
74
+ #
75
+ # @raise [Acfs::InvalidResource]
76
+ # If remote services respond with 422 response. Will fill
77
+ # errors with data from response
78
+ # @raise [Acfs::ErroneousResponse]
79
+ # If remote service respond with not successful response.
80
+ #
81
+ # @see #save
82
+ #
83
+ def save!(opts = {})
84
+ opts[:data] = attributes unless opts[:data]
85
+
86
+ operation((new? ? :create : :update), opts) do |data|
87
+ update_with data
88
+ end
89
+ rescue ::Acfs::InvalidResource => err
90
+ self.remote_errors = err.errors
91
+ raise err
92
+ end
93
+
94
+ # @api public
95
+ #
96
+ # Update attributes with given data and save resource.
97
+ #
98
+ # Saving a resource is a synchronous operation.
99
+ #
100
+ # @param attrs [Hash] Hash with attributes to write.
101
+ # @param opts [Hash] Options passed to `save`.
102
+ #
103
+ # @return [Boolean]
104
+ # True if save operation was successful, false otherwise.
105
+ #
106
+ # @see #save
107
+ # @see #attributes=
108
+ # @see #update_attributes!
109
+ #
110
+ def update_attributes(attrs, opts = {})
111
+ check_loaded! opts
112
+
113
+ self.attributes = attrs
114
+ save opts
115
+ end
116
+
117
+ # @api public
118
+ #
119
+ # Update attributes with given data and save resource.
120
+ #
121
+ # Saving a resource is a synchronous operation.
122
+ #
123
+ # @param [Hash] attrs Hash with attributes to write.
124
+ # @param [Hash] opts Options passed to `save!`.
125
+ #
126
+ # @raise [Acfs::InvalidResource]
127
+ # If remote services respond with 422 response. Will fill
128
+ # errors with data from response
129
+ #
130
+ # @raise [Acfs::ErroneousResponse]
131
+ # If remote service respond with not successful response.
132
+ #
133
+ # @see #save!
134
+ # @see #attributes=
135
+ # @see #update_attributes
136
+ #
137
+ def update_attributes!(attrs, opts = {})
138
+ check_loaded! opts
139
+
140
+ self.attributes = attrs
141
+ save! opts
142
+ end
143
+
144
+ # @api public
145
+ #
146
+ # Destroy resource by sending a DELETE request.
147
+ #
148
+ # Deleting a resource is a synchronous operation.
149
+ #
150
+ # @return [Boolean]
151
+ # @see #delete!
152
+ #
153
+ def delete(opts = {})
154
+ delete! opts
155
+ true
156
+ rescue Acfs::Error
157
+ false
158
+ end
159
+
160
+ # @api public
161
+ #
162
+ # Destroy resource by sending a DELETE request.
163
+ # Will raise an error in case something goes wrong.
164
+ #
165
+ # Deleting a resource is a synchronous operation.
166
+
167
+ # @raise [Acfs::ErroneousResponse]
168
+ # If remote service respond with not successful response.
169
+ #
170
+ # @return [undefined]
171
+ # @see #delete
172
+ #
173
+ def delete!(opts = {})
174
+ opts[:params] ||= {}
175
+ opts[:params] = attributes_for_url(:delete).merge opts[:params]
176
+
177
+ operation :delete, opts do |data|
178
+ update_with data
179
+ freeze
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ def attributes_for_url(action)
186
+ arguments_for_url = self.class.location(action: action).arguments
187
+ attributes.slice(*arguments_for_url)
188
+ end
189
+
190
+ module ClassMethods
191
+ # @api public
192
+ #
193
+ # Create a new resource sending given data. If resource cannot be
194
+ # created an error will be thrown.
195
+ #
196
+ # Saving a resource is a synchronous operation.
197
+ #
198
+ # @param data [Hash{Symbol, String => Object}]
199
+ # Data to send in create request.
200
+ #
201
+ # @return [self] Newly resource object.
202
+ #
203
+ # @raise [Acfs::InvalidResource]
204
+ # If remote services respond with 422 response. Will fill
205
+ # errors with data from response
206
+ #
207
+ # @raise [Acfs::ErroneousResponse]
208
+ # If remote service respond with not successful response.
209
+ #
210
+ # @see Acfs::Model::Persistence#save! Available options. `:data`
211
+ # will be overridden with provided data hash.
212
+ # @see #create
213
+ #
214
+ def create!(data, _opts = {})
215
+ new(data).tap(&:save!)
216
+ end
217
+
218
+ # @api public
219
+ #
220
+ # Create a new resource sending given data. If resource cannot be
221
+ # create model will be returned and error hash contains response
222
+ # errors if available.
223
+ #
224
+ # Saving a resource is a synchronous operation.
225
+ #
226
+ # @param data [Hash{Symbol, String => Object}]
227
+ # Data to send in create request.
228
+ #
229
+ # @return [self] Newly resource object.
230
+ #
231
+ # @raise [Acfs::ErroneousResponse]
232
+ # If remote service respond with not successful response.
233
+ #
234
+ # @see Acfs::Model::Persistence#save! Available options. `:data`
235
+ # will be overridden with provided data hash.
236
+ # @see #create!
237
+ #
238
+ def create(data, _opts = {})
239
+ model = new data
240
+ model.save
241
+ model
242
+ end
243
+ end
244
+
245
+ private
246
+
247
+ def update_with(data)
248
+ self.attributes = data
249
+ loaded!
250
+ end
251
+
252
+ def check_loaded!(opts = {})
253
+ return if loaded? || opts[:force]
254
+ raise ::Acfs::ResourceNotLoaded.new resource: self
255
+ end
256
+ end
257
+ end
@@ -1,10 +1,8 @@
1
- module Acfs::Model
2
-
1
+ class Acfs::Resource
3
2
  # Methods providing the query interface for finding resouces.
4
3
  #
5
4
  # @example
6
- # class MyUser
7
- # include Acfs::Model
5
+ # class MyUser < Acfs::Resource
8
6
  # end
9
7
  #
10
8
  # MyUser.find(5) # Find single resource
@@ -16,8 +14,8 @@ module Acfs::Model
16
14
  module QueryMethods
17
15
  extend ActiveSupport::Concern
18
16
 
17
+ #
19
18
  module ClassMethods
20
-
21
19
  # @api public
22
20
  #
23
21
  # @overload find(id, opts = {})
@@ -26,59 +24,64 @@ module Acfs::Model
26
24
  # @example
27
25
  # user = User.find(5) # Will query `http://base.url/users/5`
28
26
  #
29
- # @param [ Fixnum ] id Resource IDs to fetch from remote service.
30
- # @param [ Hash ] opts Additional options.
31
- # @option opts [ Hash ] :params Additional parameters added to request. `:id` will be overridden
32
- # with given ID.
27
+ # @param id [Fixnum] Resource IDs to fetch from remote service.
28
+ # @param params [Hash] Additional options.
29
+ # @option opts [Hash] :params Additional parameters added to
30
+ # request. `:id` will be overridden with given ID.
33
31
  #
34
- # @yield [ resource ] Callback block to be executed after resource was fetched successfully.
35
- # @yieldparam resource [ self ] Fetched resources.
32
+ # @yield [resource] Callback block to be executed after resource
33
+ # was fetched successfully.
34
+ # @yieldparam resource [self] Fetched resources.
36
35
  #
37
- # @return [ self ] Resource object if only one ID was given.
36
+ # @return [self] Resource object if only one ID was given.
38
37
  #
39
- # @overload find(*ids, opts = {})
38
+ # @overload find(ids, opts = {})
40
39
  # Load collection of specified resources by given IDs.
41
40
  #
42
41
  # @example
43
- # User.find(1, 2, 5) # Will return collection and will request
44
- # # `http://base.url/users/1`, `http://base.url/users/2`
45
- # # and `http://base.url/users/5` parallel
46
- #
47
- # @param [ Fixnum, ... ] ids One or more resource IDs to fetch from remote service.
48
- # @param [ Hash ] opts Additional options.
49
- # @option opts [ Hash ] :params Additional parameters added to request. `:id` will be overridden
50
- # with individual resource ID.
51
- #
52
- # @yield [ collection ] Callback block to be executed after collection was fetched successfully.
53
- # @yieldparam resource [ Collection ] Collection with fetched resources.
54
- #
55
- # @return [ Collection ] Collection of requested resources if multiple IDs were given.
56
- #
57
- def find(*attrs, &block)
58
- opts = attrs.extract_options!
59
-
60
- attrs.size > 1 ? find_multiple(attrs, opts, &block) : find_single(attrs[0], opts, &block)
42
+ # User.find([1, 2, 5]) # Will return collection and will request
43
+ # # `http://base.url/users/1`,
44
+ # # `http://base.url/users/2`
45
+ # # and `http://base.url/users/5` parallel
46
+ #
47
+ # @param ids [Array<Integer>] List of resource IDs.
48
+ # @param opts [Hash] Additional options.
49
+ # @option opts [Hash] :params Additional parameters added to
50
+ # request. `:id` will be overridden with individual resource ID.
51
+ #
52
+ # @yield [collection] Callback block to be executed after collection
53
+ # was fetched successfully.
54
+ # @yieldparam resource [Collection] Collection with fetched resources.
55
+ #
56
+ # @return [Collection] Collection of requested resources.
57
+ #
58
+ def find(id_or_ids, opts = {}, &block)
59
+ if id_or_ids.respond_to? :each
60
+ find_multiple id_or_ids, opts, &block
61
+ else
62
+ find_single id_or_ids, opts, &block
63
+ end
61
64
  end
62
65
 
63
66
  # @api public
64
67
  #
65
68
  # Try to load all resources.
66
69
  #
67
- # @param [ Hash ] params Request parameters that will be send to remote service.
70
+ # @param params [Hash] Request parameters that will be send to
71
+ # remote service.
68
72
  #
69
- # @yield [ collection ] Callback block to be executed when resource collection was loaded successfully.
70
- # @yieldparam collection [ Collection ] Collection of fetched resources.
73
+ # @yield [collection] Callback block to be executed when resource
74
+ # collection was loaded successfully.
75
+ # @yieldparam collection [Collection] Collection of fetched resources.
71
76
  #
72
- # @return [ Collection ] Collection of requested resources.
77
+ # @return [Collection] Collection of requested resources.
73
78
  #
74
79
  def all(params = {}, opts = {}, &block)
75
80
  collection = ::Acfs::Collection.new self
76
81
  collection.__callbacks__ << block if block
77
82
 
78
83
  operation :list, opts.merge(params: params) do |data, response|
79
- data.each do |obj|
80
- collection << create_resource(obj)
81
- end
84
+ data.each {|obj| collection << create_resource(obj) }
82
85
  collection.process_response response
83
86
  collection.loaded!
84
87
  collection.__invoke__
@@ -86,49 +89,57 @@ module Acfs::Model
86
89
 
87
90
  collection
88
91
  end
89
- alias :where :all
92
+ alias_method :where, :all
90
93
 
91
94
  # @api public
92
95
  #
93
96
  # Try to load first resource. Return nil if no object can be loaded.
94
97
  #
95
- # @param [ Hash ] params Request parameters that will be send to remote service.
98
+ # @param params [Hash] Request parameters that will be send
99
+ # to remote service.
96
100
  #
97
- # @yield [ resource ] Callback block to be executed after resource was fetched (even if nil).
98
- # @yieldparam resource [ self ] Fetched resource, nil if empty list is returned
101
+ # @yield [resource] Callback block to be executed after
102
+ # resource was fetched (even if nil).
103
+ # @yieldparam resource [self] Fetched resource, nil
104
+ # if empty list is returned
99
105
  #
100
- # @return [ self ] Resource object, nil if empty list is returned
106
+ # @return [self] Resource object, nil if empty list is returned
101
107
  #
102
108
  def find_by(params, &block)
103
- model = Acfs::Util::ResourceDelegator.new self.new
104
- model.__callbacks__ << block unless block.nil?
105
-
106
- operation :list, params: params do |data|
107
- if data.empty?
108
- model.__setobj__ nil
109
- else
110
- model.__setobj__ create_resource data.first, origin: model.__getobj__
109
+ Acfs::Util::ResourceDelegator.new(new).tap do |m|
110
+ m.__callbacks__ << block unless block.nil?
111
+ operation :list, params: params do |data|
112
+ if data.empty?
113
+ m.__setobj__ nil
114
+ else
115
+ m.__setobj__ create_resource(data.first, origin: m.__getobj__)
116
+ end
117
+ m.__invoke__
111
118
  end
112
- model.__invoke__
113
119
  end
114
-
115
- model
116
120
  end
117
121
 
118
122
  # @api public
119
123
  #
120
- # Try to load first resource. Raise Acfs::ResourceNotFound exception if no object can be loaded.
124
+ # Try to load first resource. Raise Acfs::ResourceNotFound
125
+ # exception if no object can be loaded.
121
126
  #
122
- # @param [ Hash ] params Request parameters that will be send to remote service.
127
+ # @param params [Hash] Request parameters that will be send
128
+ # to remote service.
123
129
  #
124
- # @yield [ resource ] Callback block to be executed after resource was fetched successfully.
125
- # @yieldparam resource [ self ] Fetched resource, nil if empty list is returned
130
+ # @yield [resource] Callback block to be executed after
131
+ # resource was fetched successfully.
132
+ # @yieldparam resource [self] Fetched resource, nil
133
+ # if empty list is returned
126
134
  #
127
- # @return [ self ] Resource object, nil if empty list is returned
135
+ # @return [self] Resource object, nil if empty list is returned
128
136
  #
129
137
  def find_by!(params, &block)
130
138
  find_by params do |m|
131
- raise Acfs::ResourceNotFound.new message: "Recieved erronious response: no `#{self.name}` with params #{params.to_s} found" if m.nil?
139
+ if m.nil?
140
+ raise Acfs::ResourceNotFound.new message: 'Recieved erronious ' \
141
+ "response: no `#{name}` with params #{params} found"
142
+ end
132
143
  block.call m unless block.nil?
133
144
  end
134
145
  end
@@ -158,7 +169,7 @@ module Acfs::Model
158
169
  def each_page(opts = {})
159
170
  cb = proc do |collection|
160
171
  yield collection
161
- collection.next_page(&cb) rescue ArgumentError
172
+ collection.next_page(&cb)
162
173
  end
163
174
  where opts, &cb
164
175
  end
@@ -185,16 +196,17 @@ module Acfs::Model
185
196
  #
186
197
  def each_item(opts = {}, &block)
187
198
  each_page(opts) do |collection|
188
- collection.each &block
199
+ collection.each(&block)
189
200
  end
190
201
  end
191
202
 
192
203
  private
204
+
193
205
  def find_single(id, opts, &block)
194
- model = Acfs::Util::ResourceDelegator.new self.new
206
+ model = Acfs::Util::ResourceDelegator.new new
195
207
 
196
208
  opts[:params] ||= {}
197
- opts[:params].merge!({ id: id }) unless id.nil?
209
+ opts[:params].merge! id: id unless id.nil?
198
210
 
199
211
  model.__callbacks__ << block unless block.nil?
200
212
 
@@ -235,12 +247,15 @@ module Acfs::Model
235
247
  def resource_class_lookup(type)
236
248
  return self if type.nil?
237
249
  klass = type.camelize.constantize
238
- raise Acfs::ResourceTypeError.new type_name: type, base_class: self unless klass <= self
250
+
251
+ unless klass <= self
252
+ raise Acfs::ResourceTypeError.new type_name: type, base_class: self
253
+ end
254
+
239
255
  klass
240
256
  rescue NameError, NoMethodError
241
257
  raise Acfs::ResourceTypeError.new type_name: type, base_class: self
242
258
  end
243
-
244
259
  end
245
260
  end
246
261
  end