acfs 1.4.0 → 1.7.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/README.md +22 -39
  4. data/acfs.gemspec +8 -14
  5. data/lib/acfs/adapter/base.rb +2 -0
  6. data/lib/acfs/adapter/typhoeus.rb +16 -11
  7. data/lib/acfs/collections/paginatable.rb +1 -1
  8. data/lib/acfs/configuration.rb +13 -3
  9. data/lib/acfs/errors.rb +41 -21
  10. data/lib/acfs/global.rb +2 -2
  11. data/lib/acfs/location.rb +26 -32
  12. data/lib/acfs/middleware/base.rb +2 -2
  13. data/lib/acfs/middleware/json.rb +4 -2
  14. data/lib/acfs/middleware/logger.rb +4 -6
  15. data/lib/acfs/middleware/serializer.rb +1 -1
  16. data/lib/acfs/operation.rb +21 -8
  17. data/lib/acfs/request/callbacks.rb +4 -4
  18. data/lib/acfs/request.rb +4 -11
  19. data/lib/acfs/resource/attributes/date_time.rb +1 -1
  20. data/lib/acfs/resource/attributes/uuid.rb +1 -1
  21. data/lib/acfs/resource/attributes.rb +16 -15
  22. data/lib/acfs/resource/dirty.rb +2 -2
  23. data/lib/acfs/resource/initialization.rb +5 -5
  24. data/lib/acfs/resource/locatable.rb +11 -8
  25. data/lib/acfs/resource/operational.rb +6 -3
  26. data/lib/acfs/resource/persistence.rb +13 -15
  27. data/lib/acfs/resource/query_methods.rb +10 -10
  28. data/lib/acfs/resource/service.rb +2 -2
  29. data/lib/acfs/resource/validation.rb +17 -7
  30. data/lib/acfs/response.rb +5 -5
  31. data/lib/acfs/runner.rb +15 -15
  32. data/lib/acfs/service.rb +16 -19
  33. data/lib/acfs/singleton_resource.rb +2 -2
  34. data/lib/acfs/stub.rb +41 -31
  35. data/lib/acfs/version.rb +2 -2
  36. data/spec/acfs/adapter/typhoeus_spec.rb +2 -2
  37. data/spec/acfs/collection_spec.rb +66 -41
  38. data/spec/acfs/configuration_spec.rb +22 -12
  39. data/spec/acfs/global_spec.rb +11 -9
  40. data/spec/acfs/location_spec.rb +2 -2
  41. data/spec/acfs/middleware/json_spec.rb +22 -8
  42. data/spec/acfs/middleware/{msgpack_spec.rb → message_pack_spec.rb} +6 -6
  43. data/spec/acfs/operation_spec.rb +3 -2
  44. data/spec/acfs/request/callbacks_spec.rb +19 -10
  45. data/spec/acfs/request_spec.rb +16 -20
  46. data/spec/acfs/resource/attributes/boolean_spec.rb +32 -32
  47. data/spec/acfs/resource/attributes/date_time_spec.rb +16 -8
  48. data/spec/acfs/resource/attributes/dict_spec.rb +15 -9
  49. data/spec/acfs/resource/attributes/float_spec.rb +20 -10
  50. data/spec/acfs/resource/attributes/integer_spec.rb +10 -5
  51. data/spec/acfs/resource/attributes/list_spec.rb +13 -8
  52. data/spec/acfs/resource/attributes/uuid_spec.rb +12 -6
  53. data/spec/acfs/resource/attributes_spec.rb +37 -38
  54. data/spec/acfs/resource/dirty_spec.rb +6 -3
  55. data/spec/acfs/resource/initialization_spec.rb +4 -5
  56. data/spec/acfs/resource/loadable_spec.rb +3 -1
  57. data/spec/acfs/resource/locatable_spec.rb +24 -18
  58. data/spec/acfs/resource/{persistance_spec.rb → persistence_spec.rb} +122 -90
  59. data/spec/acfs/resource/query_methods_spec.rb +143 -110
  60. data/spec/acfs/resource/validation_spec.rb +34 -27
  61. data/spec/acfs/response/formats_spec.rb +8 -8
  62. data/spec/acfs/response/status_spec.rb +16 -9
  63. data/spec/acfs/runner_spec.rb +10 -8
  64. data/spec/acfs/service/middleware_spec.rb +3 -3
  65. data/spec/acfs/service_spec.rb +6 -5
  66. data/spec/acfs/singleton_resource_spec.rb +2 -1
  67. data/spec/acfs/stub_spec.rb +57 -53
  68. data/spec/acfs_spec.rb +111 -93
  69. data/spec/spec_helper.rb +1 -2
  70. data/spec/support/response.rb +2 -2
  71. data/spec/support/service.rb +1 -1
  72. data/spec/support/shared/find_callbacks.rb +14 -10
  73. metadata +30 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f998ff3e28801676c732fa44df93e18f651e96eb986c857c61b7f25b312a2c1
4
- data.tar.gz: a122064cb630a664489629ef658d5ecde917ced4cdc43a600b49d0e18e00bea7
3
+ metadata.gz: 3cbc57dd595fa4242fa029498d90ca5a497216d2003674296209b679b84a6df2
4
+ data.tar.gz: 31a009d88f2a028e2fc11513b68dd011f5cfa0b7f11a83238f167e807c365b35
5
5
  SHA512:
6
- metadata.gz: 7801c96d794f08ad03b9ad204f8e7c8b611300922b12714ce7864a33c5a6268bc47c42e66b86c410358f17a0439d382c358ba15283eee07ff092d19a1f86a8be
7
- data.tar.gz: 78b2c804251fb6aaee270d9c894ff36b7b8dc7bb777148de55963255847fc28adcdbff9c7132352c5448bef7b9980fd72e6a14ffceda38540d661aceaf1e5e9d
6
+ metadata.gz: ba37a7c96e65b8774879835d93ccf43a5c91bcd77ce9cf604937544f0897ebf307ffbbf914d5e7304eb90e6f62139e37eb52b2e6406db4e86d454ef45bb4423b
7
+ data.tar.gz: 976193afda7d9e6d5bbdc3eef3ec7235a90abcaf23e79f055735cb663400c02448a683e734b9f3212b2a27c101fabad4055f0a0c5dff23a517885a7bda4cea43
data/CHANGELOG.md CHANGED
@@ -6,6 +6,7 @@
6
6
  ---
7
7
 
8
8
  ### New
9
+ * Support for Ruby 3.1 and Rails 7.0
9
10
 
10
11
  ### Changes
11
12
 
@@ -14,6 +15,31 @@
14
15
  ### Breaks
15
16
 
16
17
 
18
+ ## 1.6.0 - (2021-01-07)
19
+ ---
20
+
21
+ ### New
22
+ * Support Ruby 3.0
23
+ * Use keyword arguments in parameters and when calling methods
24
+
25
+
26
+ ## 1.5.1 - (2020-12-30)
27
+ ---
28
+
29
+ ### Changes
30
+ * Revert back to using `::MultiJson`
31
+
32
+
33
+ ## 1.5.0 - (2020-06-19)
34
+ ---
35
+
36
+ ### New
37
+ * Error classes for more HTTP error responses: `400`, `401`, `403`, `500`, `502`, `503`, `504`.
38
+
39
+ ### Changes
40
+ * Replace deprecated MultiJson with core JSON module
41
+
42
+
17
43
  ## 1.4.0 - (2020-06-12)
18
44
  ---
19
45
 
data/README.md CHANGED
@@ -8,13 +8,16 @@
8
8
 
9
9
  Acfs is a library to develop API client libraries for single services within a larger service oriented application.
10
10
 
11
- Acfs covers model and service abstraction, convenient query and filter methods, full middleware stack for pre-processing requests and responses on a per service level and automatic request queuing and parallel processing. See Usage for more.
11
+ Acfs covers model and service abstraction, convenient query and filter methods, full middleware stack for pre-processing requests and responses, as well as automatic request queuing and parallel processing.
12
+
12
13
 
13
14
  ## Installation
14
15
 
15
16
  Add this line to your application's Gemfile:
16
17
 
17
- gem 'acfs', '~> 1.3'
18
+ ```ruby
19
+ gem 'acfs', '~> 1.7'
20
+ ```
18
21
 
19
22
  And then execute:
20
23
 
@@ -24,6 +27,7 @@ Or install it yourself as:
24
27
 
25
28
  > gem install acfs
26
29
 
30
+
27
31
  ## Usage
28
32
 
29
33
  First you need to define your service(s):
@@ -119,7 +123,7 @@ Acfs.run # This call will fire all request as parallel as possible.
119
123
  @friends[0].name # => "Miraculix"
120
124
  ```
121
125
 
122
- Use `.find_by` to get first element only. `.find_by` will call the `index`-Action and return the first resource. Optionally passed params will be sent as `GET` parameters and can be used for filtering in the service's controller.
126
+ Use `.find_by` to get first element only. `.find_by` will call the `index`-Action and return the first resource. Optionally passed parameters will be sent as `GET` parameters and can be used for filtering in the service's controller.
123
127
  ```ruby
124
128
  @user = User.find_by age: 24
125
129
 
@@ -145,6 +149,7 @@ Acfs has basic update support using `PUT` requests:
145
149
  @user.persisted? # => true
146
150
  ```
147
151
 
152
+
148
153
  ## Singleton resources
149
154
 
150
155
  Singletons can be used in Acfs by creating a new resource which inherits from `SingletonResource`:
@@ -176,18 +181,17 @@ my_single.save # sends PUT request to /single
176
181
  my_single.delete # sends DELETE request to /single
177
182
  ```
178
183
 
179
- You also can pass parameters to the find call, these will sent as GET params to the index action:
184
+ You also can pass parameters to the find call. They will be sent as query parameters to the index action:
180
185
 
181
186
  ```ruby
182
187
  my_single = Single.find name: 'Max'
183
188
  Acfs.run # sends GET request with param to /single?name=Max
184
189
  ```
185
190
 
191
+
186
192
  ## Resource Inheritance
187
193
 
188
- Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a
189
- `type` attribute exists and is a valid subclass of your resource they will be converted
190
- to you subclassed resources:
194
+ Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a `type` attribute exists and is a valid subclass of your resource they will be converted to you subclassed resources:
191
195
 
192
196
  ```ruby
193
197
  class Computer < Acfs::Resource
@@ -198,8 +202,7 @@ class Pc < Computer end
198
202
  class Mac < Computer end
199
203
  ```
200
204
 
201
- With the following response on `GET /computers` the collection will contain the appropriate
202
- subclass resources:
205
+ With the following response on `GET /computers` the collection will contain the appropriate subclass resources:
203
206
 
204
207
  ```json
205
208
  [
@@ -219,6 +222,7 @@ Acfs.run
219
222
  @computer[2].class # => Pc
220
223
  ```
221
224
 
225
+
222
226
  ## Stubbing
223
227
 
224
228
  You can stub resources in applications using an Acfs service client:
@@ -243,9 +247,9 @@ it 'should find user number one' do
243
247
  user = MyUser.find 1
244
248
  Acfs.run
245
249
 
246
- expect(user.id).to be == 1
247
- expect(user.name).to be == 'John Smith'
248
- expect(user.age).to be == 32
250
+ expect(user.id).to eq 1
251
+ expect(user.name).to eq 'John Smith'
252
+ expect(user.age).to eq 32
249
253
 
250
254
  expect(@stub).to be_called
251
255
  expect(@stub).to_not be_called 5.times
@@ -260,8 +264,8 @@ end
260
264
  it 'should allow stub resource creation' do
261
265
  session = Session.create! ident: 'john@exmaple.org', password: 's3cr3t'
262
266
 
263
- expect(session.id).to be == 'longhash'
264
- expect(session.user).to be == 1
267
+ expect(session.id).to eq 'longhash'
268
+ expect(session.user).to eq 1
265
269
  end
266
270
  ```
267
271
 
@@ -279,11 +283,10 @@ it 'should find user number one' do
279
283
  end
280
284
  ```
281
285
 
282
- ## Instrumentation
283
286
 
284
- Acfs supports [instrumentation via active support][1].
287
+ ## Instrumentation
285
288
 
286
- Acfs expose to following events
289
+ Acfs supports [instrumentation via active support][1] and exposes the following events:
287
290
 
288
291
  * `acfs.operation.complete(operation, response)`: Acfs operation completed
289
292
  * `acfs.runner.sync_run(operation)`: Run operation right now skipping queue.
@@ -291,26 +294,11 @@ Acfs expose to following events
291
294
  * `acfs.before_run`: directly before `acfs.run`
292
295
  * `acfs.run`: Run all queued operations.
293
296
 
294
- Read [official guide][2] to see to to subscribe.
297
+ Read the [official guide][2] on how to subscribe to these events.
295
298
 
296
299
  [1]: http://guides.rubyonrails.org/active_support_instrumentation.html
297
300
  [2]: http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event
298
301
 
299
- ## Roadmap
300
-
301
- * Update
302
- * Better new? detection eg. storing ETag from request resources.
303
- * Use PATCH for with only changed attributes and `If-Unmodifed-Since`
304
- and `If-Match` header fields if resource was surly loaded from service
305
- and not created with an id (e.g `User.new id: 5, name: "john"`).
306
- * Conflict detection (ETag / If-Unmodified-Since)
307
- * High level features
308
- * Support for custom mime types on client and server side. (`application/vnd.myservice.user.v2+msgpack`)
309
- * Server side components
310
- * Reusing model definitions for generating responses?
311
- * Rails responders providing REST operations with integrated ETag,
312
- Modified Headers, conflict detection, ...
313
- * Documentation
314
302
 
315
303
  ## Contributing
316
304
 
@@ -322,14 +310,9 @@ Read [official guide][2] to see to to subscribe.
322
310
  7. Push to the branch (`git push origin my-new-feature`)
323
311
  8. Create new Pull Request
324
312
 
325
- ## Contributors
326
-
327
- * [Nicolas Fricke](https://github.com/nicolas-fricke)
328
- * [Tino Junge](https://github.com/tino-junge)
329
- * [Malte Swart](https://github.com/mswart)
330
313
 
331
314
  ## License
332
315
 
333
316
  MIT License
334
317
 
335
- Copyright (c) 2013 Jan Graichen. MIT license, see LICENSE for more details.
318
+ Copyright (c) 2013-2022 Jan Graichen. MIT license, see LICENSE for more details.
data/acfs.gemspec CHANGED
@@ -16,6 +16,8 @@ Gem::Specification.new do |spec|
16
16
  An abstract API base client for service oriented application.
17
17
  SUMMARY
18
18
 
19
+ spec.metadata['rubygems_mfa_required'] = 'true'
20
+
19
21
  spec.files = Dir['**/*'].grep(%r{
20
22
  ^((bin|lib|test|spec|features)/|
21
23
  .*\.gemspec|.*LICENSE.*|.*README.*|.*CHANGELOG.*)
@@ -25,22 +27,14 @@ Gem::Specification.new do |spec|
25
27
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
26
28
  spec.require_paths = %w[lib]
27
29
 
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'
30
+ spec.required_ruby_version = '>= 2.5.0'
35
31
 
32
+ spec.add_runtime_dependency 'actionpack', '>= 5.2'
33
+ spec.add_runtime_dependency 'activemodel', '>= 5.2'
34
+ spec.add_runtime_dependency 'activesupport', '>= 5.2'
35
+ spec.add_runtime_dependency 'multi_json', '~> 1.0'
36
36
  spec.add_runtime_dependency 'rack'
37
+ spec.add_runtime_dependency 'typhoeus', '~> 1.0'
37
38
 
38
39
  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
40
  end
@@ -5,6 +5,8 @@ module Acfs::Adapter
5
5
  # and processing.
6
6
  #
7
7
  class Base
8
+ def initialize(*); end
9
+
8
10
  # Start processing queued requests.
9
11
  #
10
12
  def start; end
@@ -7,14 +7,19 @@ module Acfs
7
7
  DEFAULT_OPTIONS = {
8
8
  tcp_keepalive: true,
9
9
  tcp_keepidle: 5,
10
- tcp_keepintvl: 5
10
+ tcp_keepintvl: 5,
11
11
  }.freeze
12
12
 
13
13
  # Adapter for Typhoeus.
14
14
  #
15
15
  class Typhoeus < Base
16
- def initialize(opts: {}, **kwargs)
17
- @opts = DEFAULT_OPTIONS.merge(opts)
16
+ def initialize(**kwargs)
17
+ super
18
+
19
+ extra_opts = kwargs.delete(:opts)
20
+
21
+ @opts = DEFAULT_OPTIONS
22
+ @opts = @opts.merge(extra_opts) if extra_opts
18
23
  @kwargs = kwargs
19
24
  end
20
25
 
@@ -47,22 +52,22 @@ module Acfs
47
52
  params: req.params,
48
53
  headers: req.headers.merge(
49
54
  'Expect' => '',
50
- 'Transfer-Encoding' => ''
55
+ 'Transfer-Encoding' => '',
51
56
  ),
52
- body: req.body
57
+ body: req.body,
53
58
  }
54
59
 
55
- request = ::Typhoeus::Request.new(req.url, **@opts.merge(opts))
60
+ request = ::Typhoeus::Request.new(req.url, **@opts, **opts)
56
61
 
57
62
  request.on_complete do |response|
58
- if response.timed_out?
59
- raise ::Acfs::TimeoutError.new(req)
60
- elsif response.code.zero?
63
+ raise ::Acfs::TimeoutError.new(req) if response.timed_out?
64
+
65
+ if response.code.zero?
61
66
  # Failed to get HTTP response
62
67
  raise ::Acfs::RequestError.new(req, response.return_message)
63
- else
64
- req.complete! convert_response(req, response)
65
68
  end
69
+
70
+ req.complete! convert_response(req, response)
66
71
  end
67
72
 
68
73
  request
@@ -5,7 +5,7 @@ module Acfs::Collections
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- def self.operation(_action, opts = {}, &_block)
8
+ def self.operation(_action, **opts, &_block)
9
9
  opts[:url]
10
10
  end
11
11
 
@@ -28,7 +28,7 @@ module Acfs
28
28
  #
29
29
  def configure(&block)
30
30
  if block.arity.positive?
31
- block.call self
31
+ yield self
32
32
  else
33
33
  instance_eval(&block)
34
34
  end
@@ -70,8 +70,8 @@ module Acfs
70
70
  # @return [undefined]
71
71
  #
72
72
  def load(filename)
73
- config = YAML.safe_load(File.read(filename), [], [], true)
74
- env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
73
+ config = load_yaml_file(filename)
74
+ env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
75
75
 
76
76
  config = config[env] if config.key? env
77
77
  config.each do |key, value|
@@ -116,5 +116,15 @@ module Acfs
116
116
  @current = configuration if configuration.is_a? Configuration
117
117
  end
118
118
  end
119
+
120
+ private
121
+
122
+ def load_yaml_file(path)
123
+ if YAML.respond_to?(:safe_load_file)
124
+ YAML.safe_load_file(path, aliases: true)
125
+ else
126
+ YAML.safe_load(File.read(path), [], [], true)
127
+ end
128
+ end
119
129
  end
120
130
  end
data/lib/acfs/errors.rb CHANGED
@@ -5,7 +5,7 @@ module Acfs
5
5
  #
6
6
  class Error < StandardError
7
7
  def initialize(opts = {}, message = nil)
8
- opts.merge! message: message if message
8
+ opts[:message] = message if message
9
9
  super opts[:message]
10
10
  end
11
11
  end
@@ -26,7 +26,7 @@ module Acfs
26
26
 
27
27
  class TimeoutError < RequestError
28
28
  def initialize(request)
29
- super(request, "Timeout reached")
29
+ super(request, 'Timeout reached')
30
30
  end
31
31
  end
32
32
 
@@ -38,13 +38,13 @@ module Acfs
38
38
  def initialize(opts = {})
39
39
  @response = opts[:response]
40
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
41
+ message = if response
42
+ (opts[:message] ? "#{opts[:message]}:" : 'Received') +
43
+ " #{response.code} for #{response.request.method.upcase}" \
44
+ " #{response.request.url} #{response.request.format}"
45
+ else
46
+ opts[:message] || 'Received erroneous response'
47
+ end
48
48
 
49
49
  super opts, message
50
50
  end
@@ -67,11 +67,19 @@ module Acfs
67
67
  end
68
68
  end
69
69
 
70
- # Resource not found error raised on a 404 response
71
- #
72
- class ResourceNotFound < ErroneousResponse
73
- end
70
+ # 400
71
+ class BadRequest < ErroneousResponse; end
72
+
73
+ # 401
74
+ class Unauthorized < ErroneousResponse; end
75
+
76
+ # 403
77
+ class Forbidden < ErroneousResponse; end
74
78
 
79
+ # 404
80
+ class ResourceNotFound < ErroneousResponse; end
81
+
82
+ # 422
75
83
  class InvalidResource < ErroneousResponse
76
84
  attr_reader :errors, :resource
77
85
 
@@ -79,18 +87,31 @@ module Acfs
79
87
  @errors = opts.delete :errors
80
88
  @resource = opts.delete :resource
81
89
 
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 ', '
90
+ case @errors
91
+ when Hash
92
+ opts[:message] ||= @errors.each_pair.map do |k, v|
93
+ @errors.is_a?(Array) ? "#{k}: #{v.join(', ')}" : "#{k}: #{v}"
94
+ end.join ', '
95
+ when Array
96
+ opts[:message] ||= @errors.join ', '
88
97
  end
89
98
 
90
99
  super
91
100
  end
92
101
  end
93
102
 
103
+ # 500
104
+ class ServerError < ErroneousResponse; end
105
+
106
+ # 502
107
+ class BadGateway < ErroneousResponse; end
108
+
109
+ # 503
110
+ class ServiceUnavailable < ErroneousResponse; end
111
+
112
+ # 504
113
+ class GatewayTimeout < ErroneousResponse; end
114
+
94
115
  # A ResourceNotLoaded error will be thrown when calling some
95
116
  # modifing methods on not loaded resources as it is usally
96
117
  # unwanted to call e.g. `update_attributes` on a not loaded
@@ -111,8 +132,7 @@ module Acfs
111
132
  # parent resource. Check if the type is set to the correct
112
133
  # Acfs::Resource Name
113
134
  class ResourceTypeError < Error
114
- attr_reader :base_class
115
- attr_reader :type_name
135
+ attr_reader :base_class, :type_name
116
136
 
117
137
  def initialize(opts = {})
118
138
  @base_class = opts.delete :base_class
data/lib/acfs/global.rb CHANGED
@@ -71,12 +71,12 @@ module Acfs
71
71
  def add_callback(resource, &block)
72
72
  unless resource.respond_to?(:__callbacks__)
73
73
  raise ArgumentError.new 'Given resource is not an Acfs resource ' \
74
- "delegator but a: #{resource.class.name}"
74
+ "delegator but a: #{resource.class.name}"
75
75
  end
76
76
  return false if block.nil?
77
77
 
78
78
  if resource.nil? || resource.loaded?
79
- block.call resource
79
+ yield resource
80
80
  else
81
81
  resource.__callbacks__ << block
82
82
  end
data/lib/acfs/location.rb CHANGED
@@ -6,36 +6,31 @@ module Acfs
6
6
  # Describes a URL with placeholders.
7
7
  #
8
8
  class Location
9
- attr_reader :arguments, :raw, :struct, :args
9
+ attr_reader :arguments, :raw, :struct, :vars
10
10
 
11
11
  REGEXP = /^:([A-z][A-z0-9_]*)$/.freeze
12
12
 
13
- def initialize(uri, args = {})
13
+ def initialize(uri, vars = {})
14
14
  @raw = URI.parse uri
15
- @args = args
15
+ @vars = vars
16
16
  @struct = raw.path.split('/').reject(&:empty?).map {|s| s =~ REGEXP ? Regexp.last_match[1].to_sym : s }
17
17
  @arguments = struct.select {|s| s.is_a?(Symbol) }
18
18
  end
19
19
 
20
- def build(args = {})
21
- unless args.is_a?(Hash)
22
- raise ArgumentError.new "URI path arguments must be a hash, `#{args.inspect}' given."
23
- end
24
-
25
- self.class.new raw.to_s, args.merge(self.args)
20
+ def build(vars)
21
+ self.class.new raw.to_s, vars.stringify_keys.merge(self.vars)
26
22
  end
27
23
 
28
24
  def extract_from(*args)
29
- args = {}.tap do |collect|
30
- arguments.each {|key| collect[key] = extract_arg key, args }
31
- end
25
+ vars = {}
26
+ arguments.each {|key| vars[key.to_s] = extract_arg(key, args) }
32
27
 
33
- build args
28
+ build(vars)
34
29
  end
35
30
 
36
31
  def str
37
32
  uri = raw.dup
38
- uri.path = '/' + struct.map {|s| lookup_arg(s, args) }.join('/')
33
+ uri.path = "/#{struct.map {|s| lookup_variable(s) }.join('/')}"
39
34
  uri.to_s
40
35
  end
41
36
 
@@ -49,34 +44,33 @@ module Acfs
49
44
  def extract_arg(key, hashes)
50
45
  hashes.each_with_index do |hash, index|
51
46
  if hash.key?(key)
52
- return (index == 0 ? hash.delete(key) : hash.fetch(key))
47
+ return (index.zero? ? hash.delete(key) : hash.fetch(key))
53
48
  end
54
49
  end
55
50
 
56
51
  nil
57
52
  end
58
53
 
59
- def lookup_arg(arg, args)
60
- arg.is_a?(Symbol) ? lookup_replacement(arg, args) : arg
61
- end
54
+ def lookup_variable(name)
55
+ return name unless name.is_a?(Symbol)
56
+
57
+ value = vars.fetch(name.to_s) do
58
+ if @raise.nil? || @raise
59
+ raise ArgumentError.new <<~ERROR.strip
60
+ URI path argument `#{name}' missing on `#{self}'. Given: `#{vars}.inspect'
61
+ ERROR
62
+ end
62
63
 
63
- def lookup_replacement(sym, args)
64
- value = get_replacement(sym, args).to_s
65
- return ::URI.encode_www_form_component(value) unless value.empty?
64
+ ":#{name}"
65
+ end
66
66
 
67
- raise ArgumentError.new "Cannot replace path argument `#{sym}' with empty string."
68
- end
67
+ value = value.to_s.strip
69
68
 
70
- def get_replacement(sym, args)
71
- args.fetch(sym.to_s) do
72
- args.fetch(sym) do
73
- if args[:raise].nil? || args[:raise]
74
- raise ArgumentError.new "URI path argument `#{sym}' missing on `#{self}'. Given: `#{args}.inspect'"
75
- else
76
- ":#{sym}"
77
- end
78
- end
69
+ if value.empty?
70
+ raise ArgumentError.new "Cannot replace path argument `#{name}' with empty string."
79
71
  end
72
+
73
+ ::URI.encode_www_form_component(value)
80
74
  end
81
75
  end
82
76
  end
@@ -8,9 +8,9 @@ module Acfs
8
8
  class Base
9
9
  attr_reader :app, :options
10
10
 
11
- def initialize(app, options = {})
11
+ def initialize(app, **opts)
12
12
  @app = app
13
- @options = options
13
+ @options = opts
14
14
  end
15
15
 
16
16
  def call(request)
@@ -12,11 +12,13 @@ module Acfs
12
12
  end
13
13
 
14
14
  def encode(data)
15
- ::MultiJson.dump data
15
+ ::MultiJson.dump(data)
16
16
  end
17
17
 
18
18
  def decode(body)
19
- ::MultiJson.load body
19
+ ::MultiJson.load(body)
20
+ rescue ::MultiJson::ParseError => e
21
+ raise ::JSON::ParserError.new(e)
20
22
  end
21
23
  end
22
24
 
@@ -7,19 +7,17 @@ module Acfs
7
7
  # Log requests and responses.
8
8
  #
9
9
  class Logger < Base
10
- def initialize(app, options = {})
10
+ attr_reader :logger
11
+
12
+ def initialize(app, **opts)
11
13
  super
12
- @logger = options[:logger] if options[:logger]
14
+ @logger = options[:logger] || ::Logger.new($stdout)
13
15
  end
14
16
 
15
17
  def response(res, nxt)
16
18
  logger.info "[ACFS] #{res.request.method.to_s.upcase} #{res.request.url} -> #{res.status}"
17
19
  nxt.call res
18
20
  end
19
-
20
- def logger
21
- @logger ||= ::Logger.new STDOUT
22
- end
23
21
  end
24
22
  end
25
23
  end
@@ -29,7 +29,7 @@ module Acfs
29
29
  request.headers['Accept'] = accept.join(',')
30
30
 
31
31
  request.on_complete do |response, nxt|
32
- response.data = decode response.body if mime == response.content_type
32
+ response.data = decode(response.body) if mime == response.content_type
33
33
 
34
34
  nxt.call response
35
35
  end