acfs 1.3.3 → 1.3.4

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 +339 -0
  3. data/LICENSE +22 -0
  4. data/README.md +335 -0
  5. data/acfs.gemspec +46 -0
  6. data/lib/acfs.rb +51 -0
  7. data/lib/acfs/adapter/base.rb +24 -0
  8. data/lib/acfs/adapter/typhoeus.rb +69 -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 +127 -0
  13. data/lib/acfs/global.rb +101 -0
  14. data/lib/acfs/location.rb +82 -0
  15. data/lib/acfs/middleware/base.rb +24 -0
  16. data/lib/acfs/middleware/json.rb +29 -0
  17. data/lib/acfs/middleware/logger.rb +25 -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 +83 -0
  22. data/lib/acfs/request.rb +39 -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 +269 -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 +132 -0
  39. data/lib/acfs/resource/operational.rb +23 -0
  40. data/lib/acfs/resource/persistence.rb +260 -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 +39 -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 +97 -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 +194 -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 +65 -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 +181 -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 +43 -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 +136 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0acb19bd6d18e4b93032f4524ca80005cc7d8b6c4beff1bc48c7bd4cc3f96f9e
4
- data.tar.gz: f62b3bb0715a4df057784c44475cd273a64b1d2ddef61d50d9d04189b70c1d05
3
+ metadata.gz: 46140352fd22984ff4f5dd0b58e103145560e0c49483a2b478decdfce5ba03f4
4
+ data.tar.gz: 29d4c1767beb1637a84c8ddb227ce29fba8b58a4de00456eaf0c5a617968ecae
5
5
  SHA512:
6
- metadata.gz: 1ca766572e15eb68818f22f0ef02d3cb3c4ad7177f2eb8b7a0da01b12052adfc118054308790fb0826ebd417022f59e6a64baee1a484f446bc74a1340cb2dca3
7
- data.tar.gz: d3df90c063afa8ad98672c10beacf350e24bf34c725cc83e0c15fd931080e27fcd0e7726d815aad059ad5de29f5b041f6ccf14733f2a5e4c7ca2c7934a9c1373
6
+ metadata.gz: 9e62d17adf91634bbf1ee4799c7915674135812c53e31e447a0a5edece1853973399e69eecafeb87f8c6a510f0a3e593da7f7b075aae63d6c10b74e7dcfc36c9
7
+ data.tar.gz: 43631bee9b0ecee7d4132cc624464066bec35ec762a0531801d4ab1dfb97f2299b598bd45835c0bce1a9bbf94bae9ad38f88519c6efdb9ff8c5f295bfed8e4e5
@@ -0,0 +1,339 @@
1
+ # Changelog
2
+
3
+
4
+
5
+ ## Unreleased
6
+ ---
7
+
8
+ ### New
9
+
10
+ ### Changes
11
+
12
+ ### Fixes
13
+
14
+ ### Breaks
15
+
16
+
17
+ ## 1.3.4 - (2020-03-22)
18
+ ---
19
+
20
+ ### Fixes
21
+ * Empty package build for Gem release 1.3.3
22
+
23
+
24
+ ## 1.3.3 - (2020-03-22)
25
+ ---
26
+
27
+ ### Changes
28
+ * Improved handling of low-level connection errors and timeouts
29
+
30
+
31
+ ## 1.3.2 - (2019-09-24)
32
+
33
+
34
+ ### Fixes
35
+ * Fix Acfs.on callbacks for empty find_by results (#42)
36
+
37
+
38
+ ---
39
+
40
+ ## 1.3.1 - (2019-07-02)
41
+
42
+ ### Fixes
43
+ * Improve URL argument encoding when building resource requests
44
+
45
+ ## 1.3.0
46
+
47
+ * Change default error messages to a more compact representation to ease integration with error reporting services.
48
+
49
+ ## 1.2.1
50
+
51
+ * Fix issues with resources errors if response payload differs from the expected `field => [messages]`, such as `field => message` or `[messages]`.
52
+
53
+ ## 1.2.0
54
+
55
+ * Add Rails 5.2 compatibility
56
+
57
+ ## 1.1.1
58
+
59
+ * `each_item`: Pass collection to provided block (#40)
60
+
61
+ ## 1.1.0
62
+
63
+ * Add support for Rails 5.1
64
+
65
+ ## 1.0.1
66
+
67
+ * Fix deprecation warnings when using ::Mime
68
+
69
+ ## 1.0.0
70
+
71
+ * Switch to first non-development major as it's long time used in production.
72
+ * Fix NewRelic RPM inference with middleware stack inherited from `ActionDispatch::MiddlewareStack`.
73
+
74
+ ## 0.48.0
75
+
76
+ * Remove #attribute_types broke since f7e4109 (Sep 2013, v0.23)
77
+ * Fix attribute inheritance on subclassing broken since commit 7cf1d11 (Apr 2014, v0.43)
78
+
79
+ ## 0.47.0
80
+
81
+ * Change blank value handling of dict and list type (0a12ef1)
82
+
83
+ ## 0.46.0
84
+
85
+ * Rework types system (#39)
86
+
87
+ ## 0.45.0
88
+
89
+ * Fetching multiple records (`find(ary)`) is stable now, but untested (#38)
90
+ * Middleware stack is build on ActionDispatch::MiddlewareStack now
91
+ * Deprecate legacy middleware names (xyEncoder, xyDecoder)
92
+
93
+ ## 0.44.0
94
+
95
+ * Add option to configure adapter creation and pass option to typhoeus adapter e.g.
96
+ limiting concurrency.
97
+
98
+ ## 0.43.2
99
+
100
+ * add `total_count` for paginated collections
101
+
102
+ ## 0.43.1
103
+
104
+ * Fix `:with` condition matching on stubs
105
+
106
+ ## 0.43.0
107
+
108
+ * Remove `Acfs::Model` (inherit from `Acfs::Resource`)
109
+ * Stub does only a partial match of `:with` attributes now
110
+ * Allow blocks as stub `:return`s
111
+
112
+ ## 0.42.0
113
+
114
+ * Add simple dict attribute type
115
+
116
+ ## 0.40.0
117
+
118
+ * Change `Resource#persisted?` to return true if it is not new
119
+
120
+ ## 0.39.1
121
+
122
+ * Fix automatic path parameter handling for #destroy
123
+
124
+ ## 0.39.0
125
+
126
+ * Add new event acfs.operation.before_process
127
+
128
+ ## 0.38.0
129
+
130
+ * Allow middlewares to abort request processing
131
+ * Allow middlewares to receive the request operation object (via the request)
132
+
133
+ ## 0.37.0
134
+
135
+ * Add Acfs.on
136
+
137
+ ## 0.36.0
138
+
139
+ * Add #each_page and #each_item query methods
140
+
141
+ ## 0.35.0
142
+
143
+ * Add instrumentation support
144
+
145
+ ## 0.34.1
146
+
147
+ * Fix leaking failed requests in request queues
148
+
149
+ ## 0.34.0
150
+
151
+ * Add support for will_paginate view helper used with `Acfs::Collection`s
152
+ * Add support for pagination header added by [paginate-responder](https://github.com/jgraichen/paginate-responder)
153
+ * Improve `Resource#new?` detection by using `loaded?` instead of presence of `:id` attribute
154
+
155
+ ## 0.33.0
156
+
157
+ * Do not raise errors on unknown attributes by default, add :unknown option.
158
+ * Add support to store unknown attributes
159
+
160
+ ## 0.32.1
161
+
162
+ * Fix multiple callbacks on `QueryMethods#all`
163
+
164
+ ## 0.32.0
165
+
166
+ * Add new attribute type `UUID`
167
+
168
+ ## 0.31.0
169
+
170
+ * Add experimental support for multiple and chained paths with placeholders
171
+
172
+ ## 0.30.0
173
+
174
+ * Add experimental support for multiple operation callbacks (Acfs.add_callback)
175
+
176
+ ## 0.29.1
177
+
178
+ * Fix: rescue NameError and NoMethodError on invalid type
179
+
180
+ ## 0.29.0
181
+
182
+ * Add find_by!
183
+
184
+ ## 0.28.0
185
+
186
+ * Add find_by
187
+
188
+ ## 0.27.0
189
+
190
+ * Reset method to clear stubs, request queues, internal state
191
+ * Add RSpec helper to enable stubs and clear state after each spec
192
+
193
+ ## 0.26.0
194
+
195
+ * Add support for singleton resources
196
+
197
+ ## 0.25.0
198
+
199
+ * Add option to allow blank attribute values (Johannes Jasper)
200
+ * Internal changes
201
+
202
+ ## 0.24.0
203
+
204
+ * Fix issues with stubs using type inheritance
205
+ * Allow '1' as true value for bool attributes (Tino Junge)
206
+
207
+ ## 0.23.2
208
+
209
+ * Fix regression in delegator usage by #find due to resource type inheritance.
210
+
211
+ ## 0.23.1
212
+
213
+ * Fix error class name typo
214
+
215
+ ## 0.23.0
216
+
217
+ * Add Resource Type Inheritance
218
+
219
+ ## 0.22.2
220
+
221
+ * Preserve errors received from service on revalidation (2f1fc178)
222
+ * Fix parameter ordering bug on stubs (1dc78dc8)
223
+
224
+ ## 0.22.1
225
+
226
+ * Fix hash modification on iteration bug on ActiveModel::Errors due to string keys in error hash
227
+
228
+ ## 0.22.0
229
+
230
+ * Fill local resource errors hash also on 422 responses when saving resources
231
+
232
+ ## 0.21.1
233
+
234
+ * Fix wrong validation context
235
+
236
+ ## 0.21.0
237
+
238
+ * Add update_attributes
239
+ * Add validation check to `save` method
240
+ * Inherit attributes to subclasses
241
+
242
+ ## 0.20.0
243
+
244
+ * Remove messaging
245
+ * Introduce `Acfs::Resource`
246
+
247
+ ## 0.19.0
248
+
249
+ * Add support for DateTime and Float attribute types
250
+ * Add experimental list attribute type
251
+ * Allow block usage in stub `with` option
252
+ * Allow to test if operation stubs were called and how often
253
+ * Fix bug on operation stubs
254
+
255
+ ## 0.18.0
256
+
257
+ * Basic DELETE operations
258
+
259
+ ## 0.17.0
260
+
261
+ * Basic messaging
262
+ * Extensible YARD documentation
263
+
264
+ ## 0.16.0
265
+
266
+ * Add YAML configuration
267
+ * Add external configuration for services
268
+ * Add Rubinius support
269
+
270
+ ## 0.15.0
271
+
272
+ * Add stubbing capabilities for resources
273
+
274
+ ## 0.14.0 & 0.13.0
275
+
276
+ * Fix response attributes
277
+
278
+ ## 0.12.0
279
+
280
+ * Add JRuby support
281
+ * Improve handling of error respones (422)
282
+
283
+ ## 0.11.0
284
+
285
+ * Add Logger Middleware
286
+ * Add handling of error responses
287
+
288
+ ## 0.10.0
289
+
290
+ * Return hash with indifferent access for resource attributes
291
+
292
+ ## 0.9.0
293
+
294
+ * Add create operation
295
+
296
+ ## 0.8.0
297
+
298
+ * Add save operation (PUT and POST)
299
+ * Add JSON and MessagePack encoder middlewares for encoding request data
300
+ * ActiveModel::Dirty
301
+ * Add persistant state methods
302
+
303
+ ## 0.7.0
304
+
305
+ * Per-service middleware stack
306
+
307
+ ## 0.6.0
308
+
309
+ * Add support for multiple ids for .find
310
+ * Add MessagePack support
311
+
312
+ ## 0.5.1
313
+
314
+ * Fix mime type parsing for mime types with aditional parameters (ActionPack < 4.0)
315
+
316
+ ## 0.5.0
317
+
318
+ * Add mime type support for respones
319
+
320
+ ## 0.4.0
321
+
322
+ * Improve JSON response detection
323
+ * Add bool attribute type
324
+
325
+ ## 0.3.0
326
+
327
+ * Add tracking for loading state (if resource is loaded or queued)
328
+ * Add JSON middleware to decode respones
329
+ * Add middleware support
330
+ * Add method to fetch single resources or list of resources
331
+ * Use typhoeus as http library for parallel request processing
332
+
333
+ ## 0.2.0
334
+
335
+ * Allow to define resources and attributes
336
+
337
+ ## 0.1.0
338
+
339
+ * 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,335 @@
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
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'acfs', '~> 1.3'
18
+
19
+ And then execute:
20
+
21
+ > bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ > gem install acfs
26
+
27
+ ## Usage
28
+
29
+ First you need to define your service(s):
30
+
31
+ ```ruby
32
+ class UserService < Acfs::Service
33
+ self.base_url = 'http://users.myapp.org'
34
+
35
+ # You can configure middlewares you want to use for the service here.
36
+ # Each service has it own middleware stack.
37
+ #
38
+ use Acfs::Middleware::JsonDecoder
39
+ use Acfs::Middleware::MessagePackDecoder
40
+ end
41
+ ```
42
+
43
+ This specifies where the `UserService` is located. You can now create some models representing resources served by the `UserService`.
44
+
45
+ ```ruby
46
+ class User < Acfs::Resource
47
+ service UserService # Associate `User` model with `UserService`.
48
+
49
+ # Define model attributes and types
50
+ # Types are needed to parse and generate request and response payload.
51
+
52
+ attribute :id, :uuid # Types can be classes or symbols.
53
+ # Symbols will be used to load a class from `Acfs::Model::Attributes` namespace.
54
+ # Eg. `:uuid` will load class `Acfs::Model::Attributes::Uuid`.
55
+
56
+ attribute :name, :string, default: 'Anonymous'
57
+ attribute :age, ::Acfs::Model::Attributes::Integer # Or use :integer
58
+
59
+ end
60
+ ```
61
+
62
+ The service and model classes can be shipped as a gem or git submodule to be included by the frontend application(s).
63
+
64
+ You can use the model there:
65
+
66
+ ```ruby
67
+ @user = User.find 14
68
+
69
+ @user.loaded? #=> false
70
+
71
+ Acfs.run # This will run all queued request as parallel as possible.
72
+ # For @user the following URL will be requested:
73
+ # `http://users.myapp.org/users/14`
74
+
75
+ @model.name # => "..."
76
+
77
+ @users = User.all
78
+ @users.loaded? #=> false
79
+
80
+ Acfs.run # Will request `http://users.myapp.org/users`
81
+
82
+ @users #=> [<User>, ...]
83
+ ```
84
+
85
+ If you need multiple resources or dependent resources first define a "plan" how they can be loaded:
86
+
87
+ ```ruby
88
+ @user = User.find(5) do |user|
89
+ # Block will be executed right after user with id 5 is loaded
90
+
91
+ # You can load additional resources also from other services
92
+ # Eg. fetch comments from `CommentSerivce`. The line below will
93
+ # load comments from `http://comments.myapp.org/comments?user=5`
94
+ @comments = Comment.where user: user.id
95
+
96
+ # You can load multiple resources in parallel if you have multiple
97
+ # ids.
98
+ @friends = User.find 1, 4, 10 do |friends|
99
+ # This block will be executed when all friends are loaded.
100
+ # [ ... ]
101
+ end
102
+ end
103
+
104
+ Acfs.run # This call will fire all request as parallel as possible.
105
+ # The sequence above would look similar to:
106
+ #
107
+ # Start Fin
108
+ # |===================| `Acfs.run`
109
+ # |====| /users/5
110
+ # | |==============| /comments?user=5
111
+ # | |======| /users/1
112
+ # | |=======| /users/4
113
+ # | |======| /users/10
114
+
115
+ # Now we can access all resources:
116
+
117
+ @user.name # => "John
118
+ @comments.size # => 25
119
+ @friends[0].name # => "Miraculix"
120
+ ```
121
+
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.
123
+ ```ruby
124
+ @user = User.find_by age: 24
125
+
126
+ Acfs.run # Will request `http://users.myapp.org/users?age=24`
127
+
128
+ @user # Contains the first user object returned by the index action
129
+ ```
130
+ 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.
131
+
132
+ Acfs has basic update support using `PUT` requests:
133
+
134
+ ```ruby
135
+ @user = User.find 5
136
+ @user.name = "Bob"
137
+
138
+ @user.changed? # => true
139
+ @user.persisted? # => false
140
+
141
+ @user.save # Or .save!
142
+ # Will PUT new resource to service synchronously.
143
+
144
+ @user.changed? # => false
145
+ @user.persisted? # => true
146
+ ```
147
+
148
+ ## Singleton resources
149
+
150
+ Singletons can be used in Acfs by creating a new resource which inherits from `SingletonResource`:
151
+
152
+ ```ruby
153
+ class Single < Acfs::SingletonResource
154
+ service UserService # Associate `Single` model with `UserService`.
155
+
156
+ # Define model attributes and types as with regular resources
157
+
158
+ attribute :name, :string, default: 'Anonymous'
159
+ attribute :age, :integer
160
+
161
+ end
162
+ ```
163
+
164
+ The following code explains the routing for singleton resource requests:
165
+
166
+ ```ruby
167
+ my_single = Single.new
168
+ mysingle.save # sends POST request to /single
169
+
170
+ my_single = Single.find
171
+ Acfs.run # sends GET request to /single
172
+
173
+ my_single.age = 28
174
+ my_single.save # sends PUT request to /single
175
+
176
+ my_single.delete # sends DELETE request to /single
177
+ ```
178
+
179
+ You also can pass parameters to the find call, these will sent as GET params to the index action:
180
+
181
+ ```ruby
182
+ my_single = Single.find name: 'Max'
183
+ Acfs.run # sends GET request with param to /single?name=Max
184
+ ```
185
+
186
+ ## Resource Inheritance
187
+
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:
191
+
192
+ ```ruby
193
+ class Computer < Acfs::Resource
194
+ ...
195
+ end
196
+
197
+ class Pc < Computer end
198
+ class Mac < Computer end
199
+ ```
200
+
201
+ With the following response on `GET /computers` the collection will contain the appropriate
202
+ subclass resources:
203
+
204
+ ```json
205
+ [
206
+ { "id": 5, "type": "Computer"},
207
+ { "id": 6, "type": "Mac"},
208
+ { "id": 8, "type": "Pc"}
209
+ ]
210
+ ```
211
+
212
+ ```ruby
213
+ @computers = Computer.all
214
+
215
+ Acfs.run
216
+
217
+ @computer[0].class # => Computer
218
+ @computer[1].class # => Mac
219
+ @computer[2].class # => Pc
220
+ ```
221
+
222
+ ## Stubbing
223
+
224
+ You can stub resources in applications using an Acfs service client:
225
+
226
+ ```ruby
227
+ # spec_helper.rb
228
+
229
+ # This will enable stabs before each spec and clear internal state
230
+ # after each spec.
231
+ require 'acfs/rspec'
232
+ ```
233
+
234
+ ```ruby
235
+ before do
236
+ @stub = Acfs::Stub.resource MyUser, :read, with: { id: 1 }, return: { id: 1, name: 'John Smith', age: 32 }
237
+ Acfs::Stub.resource MyUser, :read, with: { id: 2 }, raise: :not_found
238
+ Acfs::Stub.resource Session, :create, with: { ident: 'john@exmaple.org', password: 's3cr3t' }, return: { id: 'longhash', user: 1 }
239
+ Acfs::Stub.resource MyUser, :update, with: lambda { |op| op.data.include? :my_var }, raise: 400
240
+ end
241
+
242
+ it 'should find user number one' do
243
+ user = MyUser.find 1
244
+ Acfs.run
245
+
246
+ expect(user.id).to be == 1
247
+ expect(user.name).to be == 'John Smith'
248
+ expect(user.age).to be == 32
249
+
250
+ expect(@stub).to be_called
251
+ expect(@stub).to_not be_called 5.times
252
+ end
253
+
254
+ it 'should not find user number two' do
255
+ MyUser.find 3
256
+
257
+ expect { Acfs.run }.to raise_error(Acfs::ResourceNotFound)
258
+ end
259
+
260
+ it 'should allow stub resource creation' do
261
+ session = Session.create! ident: 'john@exmaple.org', password: 's3cr3t'
262
+
263
+ expect(session.id).to be == 'longhash'
264
+ expect(session.user).to be == 1
265
+ end
266
+ ```
267
+
268
+ By default Acfs raises an error when a non stubbed resource should be requested. You can switch of the behavior:
269
+
270
+ ```ruby
271
+ before do
272
+ Acfs::Stub.allow_requests = true
273
+ end
274
+
275
+ it 'should find user number one' do
276
+ user = MyUser.find 1
277
+ Acfs.run # Would have raised Acfs::RealRequestNotAllowedError
278
+ # Will run real request to user service instead.
279
+ end
280
+ ```
281
+
282
+ ## Instrumentation
283
+
284
+ Acfs supports [instrumentation via active support][1].
285
+
286
+ Acfs expose to following events
287
+
288
+ * `acfs.operation.complete(operation, response)`: Acfs operation completed
289
+ * `acfs.runner.sync_run(operation)`: Run operation right now skipping queue.
290
+ * `acfs.runner.enqueue(operation)`: Enqueue operation to be run later.
291
+ * `acfs.before_run`: directly before `acfs.run`
292
+ * `acfs.run`: Run all queued operations.
293
+
294
+ Read [official guide][2] to see to to subscribe.
295
+
296
+ [1]: http://guides.rubyonrails.org/active_support_instrumentation.html
297
+ [2]: http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event
298
+
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
+
315
+ ## Contributing
316
+
317
+ 1. Fork it
318
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
319
+ 4. Add specs for your feature
320
+ 5. Implement your feature
321
+ 6. Commit your changes (`git commit -am 'Add some feature'`)
322
+ 7. Push to the branch (`git push origin my-new-feature`)
323
+ 8. Create new Pull Request
324
+
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
+
331
+ ## License
332
+
333
+ MIT License
334
+
335
+ Copyright (c) 2013 Jan Graichen. MIT license, see LICENSE for more details.