acfs 1.3.3 → 1.6.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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +372 -0
  3. data/LICENSE +22 -0
  4. data/README.md +321 -0
  5. data/acfs.gemspec +38 -0
  6. data/lib/acfs.rb +51 -0
  7. data/lib/acfs/adapter/base.rb +26 -0
  8. data/lib/acfs/adapter/typhoeus.rb +82 -0
  9. data/lib/acfs/collection.rb +28 -0
  10. data/lib/acfs/collections/paginatable.rb +76 -0
  11. data/lib/acfs/configuration.rb +120 -0
  12. data/lib/acfs/errors.rb +147 -0
  13. data/lib/acfs/global.rb +101 -0
  14. data/lib/acfs/location.rb +76 -0
  15. data/lib/acfs/middleware/base.rb +24 -0
  16. data/lib/acfs/middleware/json.rb +31 -0
  17. data/lib/acfs/middleware/logger.rb +23 -0
  18. data/lib/acfs/middleware/msgpack.rb +32 -0
  19. data/lib/acfs/middleware/print.rb +23 -0
  20. data/lib/acfs/middleware/serializer.rb +41 -0
  21. data/lib/acfs/operation.rb +96 -0
  22. data/lib/acfs/request.rb +32 -0
  23. data/lib/acfs/request/callbacks.rb +54 -0
  24. data/lib/acfs/resource.rb +39 -0
  25. data/lib/acfs/resource/attributes.rb +270 -0
  26. data/lib/acfs/resource/attributes/base.rb +29 -0
  27. data/lib/acfs/resource/attributes/boolean.rb +39 -0
  28. data/lib/acfs/resource/attributes/date_time.rb +32 -0
  29. data/lib/acfs/resource/attributes/dict.rb +39 -0
  30. data/lib/acfs/resource/attributes/float.rb +33 -0
  31. data/lib/acfs/resource/attributes/integer.rb +29 -0
  32. data/lib/acfs/resource/attributes/list.rb +36 -0
  33. data/lib/acfs/resource/attributes/string.rb +26 -0
  34. data/lib/acfs/resource/attributes/uuid.rb +48 -0
  35. data/lib/acfs/resource/dirty.rb +37 -0
  36. data/lib/acfs/resource/initialization.rb +31 -0
  37. data/lib/acfs/resource/loadable.rb +35 -0
  38. data/lib/acfs/resource/locatable.rb +135 -0
  39. data/lib/acfs/resource/operational.rb +26 -0
  40. data/lib/acfs/resource/persistence.rb +258 -0
  41. data/lib/acfs/resource/query_methods.rb +266 -0
  42. data/lib/acfs/resource/service.rb +44 -0
  43. data/lib/acfs/resource/validation.rb +49 -0
  44. data/lib/acfs/response.rb +30 -0
  45. data/lib/acfs/response/formats.rb +27 -0
  46. data/lib/acfs/response/status.rb +33 -0
  47. data/lib/acfs/rspec.rb +13 -0
  48. data/lib/acfs/runner.rb +102 -0
  49. data/lib/acfs/service.rb +94 -0
  50. data/lib/acfs/service/middleware.rb +58 -0
  51. data/lib/acfs/service/middleware/stack.rb +65 -0
  52. data/lib/acfs/singleton_resource.rb +85 -0
  53. data/lib/acfs/stub.rb +199 -0
  54. data/lib/acfs/util.rb +22 -0
  55. data/lib/acfs/version.rb +16 -0
  56. data/lib/acfs/yard.rb +6 -0
  57. data/spec/acfs/adapter/typhoeus_spec.rb +55 -0
  58. data/spec/acfs/collection_spec.rb +157 -0
  59. data/spec/acfs/configuration_spec.rb +53 -0
  60. data/spec/acfs/global_spec.rb +140 -0
  61. data/spec/acfs/location_spec.rb +25 -0
  62. data/spec/acfs/middleware/json_spec.rb +79 -0
  63. data/spec/acfs/middleware/msgpack_spec.rb +62 -0
  64. data/spec/acfs/operation_spec.rb +12 -0
  65. data/spec/acfs/request/callbacks_spec.rb +48 -0
  66. data/spec/acfs/request_spec.rb +79 -0
  67. data/spec/acfs/resource/attributes/boolean_spec.rb +58 -0
  68. data/spec/acfs/resource/attributes/date_time_spec.rb +51 -0
  69. data/spec/acfs/resource/attributes/dict_spec.rb +77 -0
  70. data/spec/acfs/resource/attributes/float_spec.rb +61 -0
  71. data/spec/acfs/resource/attributes/integer_spec.rb +36 -0
  72. data/spec/acfs/resource/attributes/list_spec.rb +60 -0
  73. data/spec/acfs/resource/attributes/uuid_spec.rb +42 -0
  74. data/spec/acfs/resource/attributes_spec.rb +179 -0
  75. data/spec/acfs/resource/dirty_spec.rb +49 -0
  76. data/spec/acfs/resource/initialization_spec.rb +36 -0
  77. data/spec/acfs/resource/loadable_spec.rb +22 -0
  78. data/spec/acfs/resource/locatable_spec.rb +118 -0
  79. data/spec/acfs/resource/persistance_spec.rb +322 -0
  80. data/spec/acfs/resource/query_methods_spec.rb +548 -0
  81. data/spec/acfs/resource/validation_spec.rb +129 -0
  82. data/spec/acfs/response/formats_spec.rb +52 -0
  83. data/spec/acfs/response/status_spec.rb +71 -0
  84. data/spec/acfs/runner_spec.rb +95 -0
  85. data/spec/acfs/service/middleware_spec.rb +35 -0
  86. data/spec/acfs/service_spec.rb +48 -0
  87. data/spec/acfs/singleton_resource_spec.rb +17 -0
  88. data/spec/acfs/stub_spec.rb +345 -0
  89. data/spec/acfs_spec.rb +205 -0
  90. data/spec/fixtures/config.yml +14 -0
  91. data/spec/spec_helper.rb +42 -0
  92. data/spec/support/hash.rb +11 -0
  93. data/spec/support/response.rb +12 -0
  94. data/spec/support/service.rb +92 -0
  95. data/spec/support/shared/find_callbacks.rb +50 -0
  96. metadata +159 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0acb19bd6d18e4b93032f4524ca80005cc7d8b6c4beff1bc48c7bd4cc3f96f9e
4
- data.tar.gz: f62b3bb0715a4df057784c44475cd273a64b1d2ddef61d50d9d04189b70c1d05
3
+ metadata.gz: 46dc13b234cd5b704b67a731292b06ad8b32ee3fafb0a7a0af2a1003824e6df8
4
+ data.tar.gz: a35249f8e2a7a6cc49351b62c75b3fe641b26691132f4fb70b0a183ab041a380
5
5
  SHA512:
6
- metadata.gz: 1ca766572e15eb68818f22f0ef02d3cb3c4ad7177f2eb8b7a0da01b12052adfc118054308790fb0826ebd417022f59e6a64baee1a484f446bc74a1340cb2dca3
7
- data.tar.gz: d3df90c063afa8ad98672c10beacf350e24bf34c725cc83e0c15fd931080e27fcd0e7726d815aad059ad5de29f5b041f6ccf14733f2a5e4c7ca2c7934a9c1373
6
+ metadata.gz: 07132ad9797084fccd7fdeb31cc9f51572ddda1eb9dc88aaf875cd244000d15823223d3c6c503f0af7ac4cc1f59cb0b748eea75d8592323b807001e82c93d781
7
+ data.tar.gz: 6d6d15b6841820ae2ee672eec742b417113523ce613f1a7c7d493eeed50232b0915d7edcbf3df1b7060c5ced484bb82f59904e34fe6e17f70babe3b5ddf0a4e7
@@ -0,0 +1,372 @@
1
+ # Changelog
2
+
3
+
4
+
5
+ ## Unreleased
6
+ ---
7
+
8
+ ### New
9
+
10
+ ### Changes
11
+
12
+ ### Fixes
13
+
14
+ ### Breaks
15
+
16
+
17
+ ## 1.6.0 - (2021-01-07)
18
+ ---
19
+
20
+ ### New
21
+ * Support Ruby 3.0
22
+ * Use keyword arguments in parameters and when calling methods
23
+
24
+
25
+ ## 1.5.1 - (2020-12-30)
26
+ ---
27
+
28
+ ### Changes
29
+ * Revert back to using `::MultiJson`
30
+
31
+
32
+ ## 1.5.0 - (2020-06-19)
33
+ ---
34
+
35
+ ### New
36
+ * Error classes for more HTTP error responses: `400`, `401`, `403`, `500`, `502`, `503`, `504`.
37
+
38
+ ### Changes
39
+ * Replace deprecated MultiJson with core JSON module
40
+
41
+
42
+ ## 1.4.0 - (2020-06-12)
43
+ ---
44
+
45
+ ### New
46
+ * Use strict TCP keepalive probing by default (5s/5s)
47
+ * Adapter accepts curl request opts
48
+
49
+
50
+ ## 1.3.4 - (2020-03-22)
51
+ ---
52
+
53
+ ### Fixes
54
+ * Empty package build for Gem release 1.3.3
55
+
56
+
57
+ ## 1.3.3 - (2020-03-22)
58
+ ---
59
+
60
+ ### Changes
61
+ * Improved handling of low-level connection errors and timeouts
62
+
63
+
64
+ ## 1.3.2 - (2019-09-24)
65
+
66
+
67
+ ### Fixes
68
+ * Fix Acfs.on callbacks for empty find_by results (#42)
69
+
70
+
71
+ ---
72
+
73
+ ## 1.3.1 - (2019-07-02)
74
+
75
+ ### Fixes
76
+ * Improve URL argument encoding when building resource requests
77
+
78
+ ## 1.3.0
79
+
80
+ * Change default error messages to a more compact representation to ease integration with error reporting services.
81
+
82
+ ## 1.2.1
83
+
84
+ * Fix issues with resources errors if response payload differs from the expected `field => [messages]`, such as `field => message` or `[messages]`.
85
+
86
+ ## 1.2.0
87
+
88
+ * Add Rails 5.2 compatibility
89
+
90
+ ## 1.1.1
91
+
92
+ * `each_item`: Pass collection to provided block (#40)
93
+
94
+ ## 1.1.0
95
+
96
+ * Add support for Rails 5.1
97
+
98
+ ## 1.0.1
99
+
100
+ * Fix deprecation warnings when using ::Mime
101
+
102
+ ## 1.0.0
103
+
104
+ * Switch to first non-development major as it's long time used in production.
105
+ * Fix NewRelic RPM inference with middleware stack inherited from `ActionDispatch::MiddlewareStack`.
106
+
107
+ ## 0.48.0
108
+
109
+ * Remove #attribute_types broke since f7e4109 (Sep 2013, v0.23)
110
+ * Fix attribute inheritance on subclassing broken since commit 7cf1d11 (Apr 2014, v0.43)
111
+
112
+ ## 0.47.0
113
+
114
+ * Change blank value handling of dict and list type (0a12ef1)
115
+
116
+ ## 0.46.0
117
+
118
+ * Rework types system (#39)
119
+
120
+ ## 0.45.0
121
+
122
+ * Fetching multiple records (`find(ary)`) is stable now, but untested (#38)
123
+ * Middleware stack is build on ActionDispatch::MiddlewareStack now
124
+ * Deprecate legacy middleware names (xyEncoder, xyDecoder)
125
+
126
+ ## 0.44.0
127
+
128
+ * Add option to configure adapter creation and pass option to typhoeus adapter e.g.
129
+ limiting concurrency.
130
+
131
+ ## 0.43.2
132
+
133
+ * add `total_count` for paginated collections
134
+
135
+ ## 0.43.1
136
+
137
+ * Fix `:with` condition matching on stubs
138
+
139
+ ## 0.43.0
140
+
141
+ * Remove `Acfs::Model` (inherit from `Acfs::Resource`)
142
+ * Stub does only a partial match of `:with` attributes now
143
+ * Allow blocks as stub `:return`s
144
+
145
+ ## 0.42.0
146
+
147
+ * Add simple dict attribute type
148
+
149
+ ## 0.40.0
150
+
151
+ * Change `Resource#persisted?` to return true if it is not new
152
+
153
+ ## 0.39.1
154
+
155
+ * Fix automatic path parameter handling for #destroy
156
+
157
+ ## 0.39.0
158
+
159
+ * Add new event acfs.operation.before_process
160
+
161
+ ## 0.38.0
162
+
163
+ * Allow middlewares to abort request processing
164
+ * Allow middlewares to receive the request operation object (via the request)
165
+
166
+ ## 0.37.0
167
+
168
+ * Add Acfs.on
169
+
170
+ ## 0.36.0
171
+
172
+ * Add #each_page and #each_item query methods
173
+
174
+ ## 0.35.0
175
+
176
+ * Add instrumentation support
177
+
178
+ ## 0.34.1
179
+
180
+ * Fix leaking failed requests in request queues
181
+
182
+ ## 0.34.0
183
+
184
+ * Add support for will_paginate view helper used with `Acfs::Collection`s
185
+ * Add support for pagination header added by [paginate-responder](https://github.com/jgraichen/paginate-responder)
186
+ * Improve `Resource#new?` detection by using `loaded?` instead of presence of `:id` attribute
187
+
188
+ ## 0.33.0
189
+
190
+ * Do not raise errors on unknown attributes by default, add :unknown option.
191
+ * Add support to store unknown attributes
192
+
193
+ ## 0.32.1
194
+
195
+ * Fix multiple callbacks on `QueryMethods#all`
196
+
197
+ ## 0.32.0
198
+
199
+ * Add new attribute type `UUID`
200
+
201
+ ## 0.31.0
202
+
203
+ * Add experimental support for multiple and chained paths with placeholders
204
+
205
+ ## 0.30.0
206
+
207
+ * Add experimental support for multiple operation callbacks (Acfs.add_callback)
208
+
209
+ ## 0.29.1
210
+
211
+ * Fix: rescue NameError and NoMethodError on invalid type
212
+
213
+ ## 0.29.0
214
+
215
+ * Add find_by!
216
+
217
+ ## 0.28.0
218
+
219
+ * Add find_by
220
+
221
+ ## 0.27.0
222
+
223
+ * Reset method to clear stubs, request queues, internal state
224
+ * Add RSpec helper to enable stubs and clear state after each spec
225
+
226
+ ## 0.26.0
227
+
228
+ * Add support for singleton resources
229
+
230
+ ## 0.25.0
231
+
232
+ * Add option to allow blank attribute values (Johannes Jasper)
233
+ * Internal changes
234
+
235
+ ## 0.24.0
236
+
237
+ * Fix issues with stubs using type inheritance
238
+ * Allow '1' as true value for bool attributes (Tino Junge)
239
+
240
+ ## 0.23.2
241
+
242
+ * Fix regression in delegator usage by #find due to resource type inheritance.
243
+
244
+ ## 0.23.1
245
+
246
+ * Fix error class name typo
247
+
248
+ ## 0.23.0
249
+
250
+ * Add Resource Type Inheritance
251
+
252
+ ## 0.22.2
253
+
254
+ * Preserve errors received from service on revalidation (2f1fc178)
255
+ * Fix parameter ordering bug on stubs (1dc78dc8)
256
+
257
+ ## 0.22.1
258
+
259
+ * Fix hash modification on iteration bug on ActiveModel::Errors due to string keys in error hash
260
+
261
+ ## 0.22.0
262
+
263
+ * Fill local resource errors hash also on 422 responses when saving resources
264
+
265
+ ## 0.21.1
266
+
267
+ * Fix wrong validation context
268
+
269
+ ## 0.21.0
270
+
271
+ * Add update_attributes
272
+ * Add validation check to `save` method
273
+ * Inherit attributes to subclasses
274
+
275
+ ## 0.20.0
276
+
277
+ * Remove messaging
278
+ * Introduce `Acfs::Resource`
279
+
280
+ ## 0.19.0
281
+
282
+ * Add support for DateTime and Float attribute types
283
+ * Add experimental list attribute type
284
+ * Allow block usage in stub `with` option
285
+ * Allow to test if operation stubs were called and how often
286
+ * Fix bug on operation stubs
287
+
288
+ ## 0.18.0
289
+
290
+ * Basic DELETE operations
291
+
292
+ ## 0.17.0
293
+
294
+ * Basic messaging
295
+ * Extensible YARD documentation
296
+
297
+ ## 0.16.0
298
+
299
+ * Add YAML configuration
300
+ * Add external configuration for services
301
+ * Add Rubinius support
302
+
303
+ ## 0.15.0
304
+
305
+ * Add stubbing capabilities for resources
306
+
307
+ ## 0.14.0 & 0.13.0
308
+
309
+ * Fix response attributes
310
+
311
+ ## 0.12.0
312
+
313
+ * Add JRuby support
314
+ * Improve handling of error respones (422)
315
+
316
+ ## 0.11.0
317
+
318
+ * Add Logger Middleware
319
+ * Add handling of error responses
320
+
321
+ ## 0.10.0
322
+
323
+ * Return hash with indifferent access for resource attributes
324
+
325
+ ## 0.9.0
326
+
327
+ * Add create operation
328
+
329
+ ## 0.8.0
330
+
331
+ * Add save operation (PUT and POST)
332
+ * Add JSON and MessagePack encoder middlewares for encoding request data
333
+ * ActiveModel::Dirty
334
+ * Add persistant state methods
335
+
336
+ ## 0.7.0
337
+
338
+ * Per-service middleware stack
339
+
340
+ ## 0.6.0
341
+
342
+ * Add support for multiple ids for .find
343
+ * Add MessagePack support
344
+
345
+ ## 0.5.1
346
+
347
+ * Fix mime type parsing for mime types with aditional parameters (ActionPack < 4.0)
348
+
349
+ ## 0.5.0
350
+
351
+ * Add mime type support for respones
352
+
353
+ ## 0.4.0
354
+
355
+ * Improve JSON response detection
356
+ * Add bool attribute type
357
+
358
+ ## 0.3.0
359
+
360
+ * Add tracking for loading state (if resource is loaded or queued)
361
+ * Add JSON middleware to decode respones
362
+ * Add middleware support
363
+ * Add method to fetch single resources or list of resources
364
+ * Use typhoeus as http library for parallel request processing
365
+
366
+ ## 0.2.0
367
+
368
+ * Allow to define resources and attributes
369
+
370
+ ## 0.1.0
371
+
372
+ * Project start
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jan Graichen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,321 @@
1
+ # Acfs - *API client for services*
2
+
3
+ [![Gem Version](https://img.shields.io/gem/v/acfs?logo=ruby)](https://rubygems.org/gems/acfs)
4
+ [![Build Status](https://img.shields.io/travis/jgraichen/acfs/master?logo=travis)](https://travis-ci.org/jgraichen/acfs)
5
+ [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/jgraichen/acfs/Test/master?logo=github)](https://github.com/jgraichen/acfs/actions?query=branch%3Amaster)
6
+ [![Coverage Status](http://img.shields.io/coveralls/jgraichen/acfs/master.svg)](https://coveralls.io/r/jgraichen/acfs)
7
+ [![RubyDoc Documentation](http://img.shields.io/badge/rubydoc-here-blue.svg)](http://rubydoc.info/github/jgraichen/acfs/master/frames)
8
+
9
+ Acfs is a library to develop API client libraries for single services within a larger service oriented application.
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.
12
+
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ gem 'acfs', '~> 1.3'
19
+
20
+ And then execute:
21
+
22
+ > bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ > gem install acfs
27
+
28
+
29
+ ## Usage
30
+
31
+ First you need to define your service(s):
32
+
33
+ ```ruby
34
+ class UserService < Acfs::Service
35
+ self.base_url = 'http://users.myapp.org'
36
+
37
+ # You can configure middlewares you want to use for the service here.
38
+ # Each service has it own middleware stack.
39
+ #
40
+ use Acfs::Middleware::JsonDecoder
41
+ use Acfs::Middleware::MessagePackDecoder
42
+ end
43
+ ```
44
+
45
+ This specifies where the `UserService` is located. You can now create some models representing resources served by the `UserService`.
46
+
47
+ ```ruby
48
+ class User < Acfs::Resource
49
+ service UserService # Associate `User` model with `UserService`.
50
+
51
+ # Define model attributes and types
52
+ # Types are needed to parse and generate request and response payload.
53
+
54
+ attribute :id, :uuid # Types can be classes or symbols.
55
+ # Symbols will be used to load a class from `Acfs::Model::Attributes` namespace.
56
+ # Eg. `:uuid` will load class `Acfs::Model::Attributes::Uuid`.
57
+
58
+ attribute :name, :string, default: 'Anonymous'
59
+ attribute :age, ::Acfs::Model::Attributes::Integer # Or use :integer
60
+
61
+ end
62
+ ```
63
+
64
+ The service and model classes can be shipped as a gem or git submodule to be included by the frontend application(s).
65
+
66
+ You can use the model there:
67
+
68
+ ```ruby
69
+ @user = User.find 14
70
+
71
+ @user.loaded? #=> false
72
+
73
+ Acfs.run # This will run all queued request as parallel as possible.
74
+ # For @user the following URL will be requested:
75
+ # `http://users.myapp.org/users/14`
76
+
77
+ @model.name # => "..."
78
+
79
+ @users = User.all
80
+ @users.loaded? #=> false
81
+
82
+ Acfs.run # Will request `http://users.myapp.org/users`
83
+
84
+ @users #=> [<User>, ...]
85
+ ```
86
+
87
+ If you need multiple resources or dependent resources first define a "plan" how they can be loaded:
88
+
89
+ ```ruby
90
+ @user = User.find(5) do |user|
91
+ # Block will be executed right after user with id 5 is loaded
92
+
93
+ # You can load additional resources also from other services
94
+ # Eg. fetch comments from `CommentSerivce`. The line below will
95
+ # load comments from `http://comments.myapp.org/comments?user=5`
96
+ @comments = Comment.where user: user.id
97
+
98
+ # You can load multiple resources in parallel if you have multiple
99
+ # ids.
100
+ @friends = User.find 1, 4, 10 do |friends|
101
+ # This block will be executed when all friends are loaded.
102
+ # [ ... ]
103
+ end
104
+ end
105
+
106
+ Acfs.run # This call will fire all request as parallel as possible.
107
+ # The sequence above would look similar to:
108
+ #
109
+ # Start Fin
110
+ # |===================| `Acfs.run`
111
+ # |====| /users/5
112
+ # | |==============| /comments?user=5
113
+ # | |======| /users/1
114
+ # | |=======| /users/4
115
+ # | |======| /users/10
116
+
117
+ # Now we can access all resources:
118
+
119
+ @user.name # => "John
120
+ @comments.size # => 25
121
+ @friends[0].name # => "Miraculix"
122
+ ```
123
+
124
+ 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.
125
+ ```ruby
126
+ @user = User.find_by age: 24
127
+
128
+ Acfs.run # Will request `http://users.myapp.org/users?age=24`
129
+
130
+ @user # Contains the first user object returned by the index action
131
+ ```
132
+ If no object can be found, `.find_by` will return `nil`. The optional callback will then be called with `nil` as parameter. Use `.find_by!` to raise an `Acfs::ResourceNotFound` exception if no object can be found. `.find_by!` will only invoke the optional callback if an object was successfully loaded.
133
+
134
+ Acfs has basic update support using `PUT` requests:
135
+
136
+ ```ruby
137
+ @user = User.find 5
138
+ @user.name = "Bob"
139
+
140
+ @user.changed? # => true
141
+ @user.persisted? # => false
142
+
143
+ @user.save # Or .save!
144
+ # Will PUT new resource to service synchronously.
145
+
146
+ @user.changed? # => false
147
+ @user.persisted? # => true
148
+ ```
149
+
150
+
151
+ ## Singleton resources
152
+
153
+ Singletons can be used in Acfs by creating a new resource which inherits from `SingletonResource`:
154
+
155
+ ```ruby
156
+ class Single < Acfs::SingletonResource
157
+ service UserService # Associate `Single` model with `UserService`.
158
+
159
+ # Define model attributes and types as with regular resources
160
+
161
+ attribute :name, :string, default: 'Anonymous'
162
+ attribute :age, :integer
163
+
164
+ end
165
+ ```
166
+
167
+ The following code explains the routing for singleton resource requests:
168
+
169
+ ```ruby
170
+ my_single = Single.new
171
+ mysingle.save # sends POST request to /single
172
+
173
+ my_single = Single.find
174
+ Acfs.run # sends GET request to /single
175
+
176
+ my_single.age = 28
177
+ my_single.save # sends PUT request to /single
178
+
179
+ my_single.delete # sends DELETE request to /single
180
+ ```
181
+
182
+ You also can pass parameters to the find call, these will sent as GET params to the index action:
183
+
184
+ ```ruby
185
+ my_single = Single.find name: 'Max'
186
+ Acfs.run # sends GET request with param to /single?name=Max
187
+ ```
188
+
189
+
190
+ ## Resource Inheritance
191
+
192
+ Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a
193
+ `type` attribute exists and is a valid subclass of your resource they will be converted
194
+ to you subclassed resources:
195
+
196
+ ```ruby
197
+ class Computer < Acfs::Resource
198
+ ...
199
+ end
200
+
201
+ class Pc < Computer end
202
+ class Mac < Computer end
203
+ ```
204
+
205
+ With the following response on `GET /computers` the collection will contain the appropriate
206
+ subclass resources:
207
+
208
+ ```json
209
+ [
210
+ { "id": 5, "type": "Computer"},
211
+ { "id": 6, "type": "Mac"},
212
+ { "id": 8, "type": "Pc"}
213
+ ]
214
+ ```
215
+
216
+ ```ruby
217
+ @computers = Computer.all
218
+
219
+ Acfs.run
220
+
221
+ @computer[0].class # => Computer
222
+ @computer[1].class # => Mac
223
+ @computer[2].class # => Pc
224
+ ```
225
+
226
+
227
+ ## Stubbing
228
+
229
+ You can stub resources in applications using an Acfs service client:
230
+
231
+ ```ruby
232
+ # spec_helper.rb
233
+
234
+ # This will enable stabs before each spec and clear internal state
235
+ # after each spec.
236
+ require 'acfs/rspec'
237
+ ```
238
+
239
+ ```ruby
240
+ before do
241
+ @stub = Acfs::Stub.resource MyUser, :read, with: { id: 1 }, return: { id: 1, name: 'John Smith', age: 32 }
242
+ Acfs::Stub.resource MyUser, :read, with: { id: 2 }, raise: :not_found
243
+ Acfs::Stub.resource Session, :create, with: { ident: 'john@exmaple.org', password: 's3cr3t' }, return: { id: 'longhash', user: 1 }
244
+ Acfs::Stub.resource MyUser, :update, with: lambda { |op| op.data.include? :my_var }, raise: 400
245
+ end
246
+
247
+ it 'should find user number one' do
248
+ user = MyUser.find 1
249
+ Acfs.run
250
+
251
+ expect(user.id).to be == 1
252
+ expect(user.name).to be == 'John Smith'
253
+ expect(user.age).to be == 32
254
+
255
+ expect(@stub).to be_called
256
+ expect(@stub).to_not be_called 5.times
257
+ end
258
+
259
+ it 'should not find user number two' do
260
+ MyUser.find 3
261
+
262
+ expect { Acfs.run }.to raise_error(Acfs::ResourceNotFound)
263
+ end
264
+
265
+ it 'should allow stub resource creation' do
266
+ session = Session.create! ident: 'john@exmaple.org', password: 's3cr3t'
267
+
268
+ expect(session.id).to be == 'longhash'
269
+ expect(session.user).to be == 1
270
+ end
271
+ ```
272
+
273
+ By default Acfs raises an error when a non stubbed resource should be requested. You can switch of the behavior:
274
+
275
+ ```ruby
276
+ before do
277
+ Acfs::Stub.allow_requests = true
278
+ end
279
+
280
+ it 'should find user number one' do
281
+ user = MyUser.find 1
282
+ Acfs.run # Would have raised Acfs::RealRequestNotAllowedError
283
+ # Will run real request to user service instead.
284
+ end
285
+ ```
286
+
287
+
288
+ ## Instrumentation
289
+
290
+ Acfs supports [instrumentation via active support][1].
291
+
292
+ Acfs expose to following events
293
+
294
+ * `acfs.operation.complete(operation, response)`: Acfs operation completed
295
+ * `acfs.runner.sync_run(operation)`: Run operation right now skipping queue.
296
+ * `acfs.runner.enqueue(operation)`: Enqueue operation to be run later.
297
+ * `acfs.before_run`: directly before `acfs.run`
298
+ * `acfs.run`: Run all queued operations.
299
+
300
+ Read [official guide][2] to see to to subscribe.
301
+
302
+ [1]: http://guides.rubyonrails.org/active_support_instrumentation.html
303
+ [2]: http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event
304
+
305
+
306
+ ## Contributing
307
+
308
+ 1. Fork it
309
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
310
+ 4. Add specs for your feature
311
+ 5. Implement your feature
312
+ 6. Commit your changes (`git commit -am 'Add some feature'`)
313
+ 7. Push to the branch (`git push origin my-new-feature`)
314
+ 8. Create new Pull Request
315
+
316
+
317
+ ## License
318
+
319
+ MIT License
320
+
321
+ Copyright (c) 2013-2020 Jan Graichen. MIT license, see LICENSE for more details.