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
@@ -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