jsonapi-resources 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cb9861ed5743d9c77ca4ce7e502002c46016ae82
4
- data.tar.gz: e0239d8a635a3d7667759721653d698f2aa3aac8
3
+ metadata.gz: 0c2c5b5766663832a646cbfe1437433e50781972
4
+ data.tar.gz: 2b656ca460394882492d159ba4b4bc342fd9b009
5
5
  SHA512:
6
- metadata.gz: c263872e74e45cb4d8c88b389c7e50b628206162a71709848260a6a52442b81fe9f4088ef9b9d30a5edca46093aee996f18792b9e27cc1a461ef65ba167c79f6
7
- data.tar.gz: 7e2a98858d1aa50679dd04fc7870790b5ea9ec40c44bf2c4f7578f414a9bca68bd0bca5262470e223bb0aa6bf88c2d9d04d8637a81d1de711ca5cba6d3e1a613
6
+ metadata.gz: 402ee848c09cae27b94b8358be89c24c70aa2eb79ed82e5627786ad0eeba29054af6197b67270afd41425d0117b8506908bb1a3d1d60acaf492964505500a20c
7
+ data.tar.gz: 5b857dc06609c8cf054fc09f6c04c268041366dc4b43bd9414e1d43134f0a30ce7bdf6ca718b529133613dd571ee908bb5eb975df7f87a2232fa1200bd1f41d8
data/README.md CHANGED
@@ -2,58 +2,18 @@
2
2
 
3
3
  [![Join the chat at https://gitter.im/cerebris/jsonapi-resources](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cerebris/jsonapi-resources?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4
4
 
5
- **NOTE:** This README is the documentation for `JSONAPI::Resources`. If you are viewing this at the
6
- [project page on Github](https://github.com/cerebris/jsonapi-resources) you are viewing the documentation for the `master`
7
- branch. This may contain information that is not relevant to the release you are using. Please see the README for the
8
- [version](https://github.com/cerebris/jsonapi-resources/releases) you are using.
5
+ `JSONAPI::Resources`, or "JR", provides a framework for developing an API server that complies with the
6
+ [JSON:API](http://jsonapi.org/) specification.
9
7
 
10
- ---
11
-
12
- `JSONAPI::Resources`, or "JR", provides a framework for developing a server that complies with the
13
- [JSON API](http://jsonapi.org/) specification.
14
-
15
- Like JSON API itself, JR's design is focused on the resources served by an API. JR needs little more than a definition
8
+ Like JSON:API itself, JR's design is focused on the resources served by an API. JR needs little more than a definition
16
9
  of your resources, including their attributes and relationships, to make your server compliant with JSON API.
17
10
 
18
11
  JR is designed to work with Rails 4.0+, and provides custom routes, controllers, and serializers. JR's resources may be
19
12
  backed by ActiveRecord models or by custom objects.
20
13
 
21
- ## Table of Contents
14
+ ## Documentation
22
15
 
23
- * [Demo App] (#demo-app)
24
- * [Client Libraries] (#client-libraries)
25
- * [Installation] (#installation)
26
- * [Usage] (#usage)
27
- * [Resources] (#resources)
28
- * [JSONAPI::Resource] (#jsonapiresource)
29
- * [Context] (#context)
30
- * [Attributes] (#attributes)
31
- * [Primary Key] (#primary-key)
32
- * [Model Name] (#model-name)
33
- * [Model Hints] (#model-hints)
34
- * [Relationships] (#relationships)
35
- * [Filters] (#filters)
36
- * [Pagination] (#pagination)
37
- * [Included relationships (side-loading resources)] (#included-relationships-side-loading-resources)
38
- * [Resource meta] (#resource-meta)
39
- * [Custom Links] (#custom-links)
40
- * [Callbacks] (#callbacks)
41
- * [Controllers] (#controllers)
42
- * [Namespaces] (#namespaces)
43
- * [Error Codes] (#error-codes)
44
- * [Handling Exceptions] (#handling-exceptions)
45
- * [Action Callbacks] (#action-callbacks)
46
- * [Operation Processors] (#operation-processors)
47
- * [Serializer] (#serializer)
48
- * [Serializer options] (#serializer-options)
49
- * [Formatting] (#formatting)
50
- * [Key Format] (#key-format)
51
- * [Routing] (#routing)
52
- * [Nested Routes] (#nested-routes)
53
- * [Authorization](#authorization)
54
- * [Configuration] (#configuration)
55
- * [Contributing] (#contributing)
56
- * [License] (#license)
16
+ Full documentation can be found at [http://jsonapi-resources.com](http://jsonapi-resources.com), including the [v0.8 Guide](http://jsonapi-resources.com/v0.8/guide/) specific to this version.
57
17
 
58
18
  ## Demo App
59
19
 
@@ -61,8 +21,8 @@ We have a simple demo app, called [Peeps](https://github.com/cerebris/peeps), av
61
21
 
62
22
  ## Client Libraries
63
23
 
64
- JSON API maintains a (non-verified) listing of [client libraries](http://jsonapi.org/implementations/#client-libraries)
65
- which *should* be compatible with JSON API compliant server implementations such as JR.
24
+ JSON:API maintains a (non-verified) listing of [client libraries](http://jsonapi.org/implementations/#client-libraries)
25
+ which *should* be compatible with JSON:API compliant server implementations such as JR.
66
26
 
67
27
  ## Installation
68
28
 
@@ -78,1914 +38,7 @@ Or install it yourself as:
78
38
 
79
39
  $ gem install jsonapi-resources
80
40
 
81
- ## Usage
82
-
83
- ### Resources
84
-
85
- Resources define the public interface to your API. A resource defines which attributes are exposed, as well as
86
- relationships to other resources.
87
-
88
- Resource definitions should by convention be placed in a directory under app named resources, `app/resources`. The file name should be the single underscored name of the model that backs the resource with `_resource.rb` appended. For example,
89
- a `Contact` model's resource should have a class named `ContactResource` defined in a file named `contact_resource.rb`.
90
-
91
- #### JSONAPI::Resource
92
-
93
- Resources must be derived from `JSONAPI::Resource`, or a class that is itself derived from `JSONAPI::Resource`.
94
-
95
- For example:
96
-
97
- ```ruby
98
- class ContactResource < JSONAPI::Resource
99
- end
100
- ```
101
-
102
- A jsonapi-resource generator is available
103
- ```
104
- rails generate jsonapi:resource contact
105
- ```
106
-
107
- ##### Abstract Resources
108
-
109
- Resources that are not backed by a model (purely used as base classes for other resources) should be declared as
110
- abstract.
111
-
112
- Because abstract resources do not expect to be backed by a model, they won't attempt to discover the model class
113
- or any of its relationships.
114
-
115
- ```ruby
116
- class BaseResource < JSONAPI::Resource
117
- abstract
118
-
119
- has_one :creator
120
- end
121
-
122
- class ContactResource < BaseResource
123
- end
124
- ```
125
-
126
- ##### Immutable Resources
127
-
128
- Resources that are immutable should be declared as such with the `immutable` method. Immutable resources will only
129
- generate routes for `index`, `show` and `show_relationship`.
130
-
131
- ###### Immutable for Readonly
132
-
133
- Some resources are read-only and are not to be modified through the API. Declaring a resource as immutable prevents
134
- creation of routes that allow modification of the resource.
135
-
136
- ###### Immutable Heterogeneous Collections
137
-
138
- Immutable resources can be used as the basis for a heterogeneous collection. Resources in heterogeneous collections can
139
- still be mutated through their own type-specific endpoints.
140
-
141
- ```ruby
142
- class VehicleResource < JSONAPI::Resource
143
- immutable
144
-
145
- has_one :owner
146
- attributes :make, :model, :serial_number
147
- end
148
-
149
- class CarResource < VehicleResource
150
- attributes :drive_layout
151
- has_one :driver
152
- end
153
-
154
- class BoatResource < VehicleResource
155
- attributes :length_at_water_line
156
- has_one :captain
157
- end
158
-
159
- # routes
160
- jsonapi_resources :vehicles
161
- jsonapi_resources :cars
162
- jsonapi_resources :boats
163
-
164
- ```
165
-
166
- In the above example vehicles are immutable. A call to `/vehicles` or `/vehicles/1` will return vehicles with types
167
- of either `car` or `boat`. But calls to PUT or POST a `car` must be made to `/cars`. The rails models backing the above
168
- code use Single Table Inheritance.
169
-
170
- #### Context
171
-
172
- Sometimes you will want to access things such as the current logged in user (and other state only available within your controllers) from within your resource classes. To make this state available to a resource class you need to put it into the context hash - this can be done via a `context` method on one of your controllers or across all controllers using ApplicationController.
173
-
174
- For example:
175
-
176
- ```ruby
177
- class ApplicationController < JSONAPI::ResourceController
178
- def context
179
- {current_user: current_user}
180
- end
181
- end
182
-
183
- # Specific resource controllers derive from ApplicationController
184
- # and share its context
185
- class PeopleController < ApplicationController
186
-
187
- end
188
-
189
- # Assuming you don't permit user_id (so the client won't assign a wrong user to own the object)
190
- # you can ensure the current user is assigned the record by using the controller's context hash.
191
- class PeopleResource < JSONAPI::Resource
192
- before_save do
193
- @model.user_id = context[:current_user].id if @model.new_record?
194
- end
195
- end
196
- ```
197
-
198
- You can put things that affect serialization and resource configuration into the context.
199
-
200
- #### Attributes
201
-
202
- Any of a resource's attributes that are accessible must be explicitly declared. Single attributes can be declared using
203
- the `attribute` method, and multiple attributes can be declared with the `attributes` method on the resource class.
204
-
205
- For example:
206
-
207
- ```ruby
208
- class ContactResource < JSONAPI::Resource
209
- attribute :name_first
210
- attributes :name_last, :email, :twitter
211
- end
212
- ```
213
-
214
- This resource has 4 defined attributes: `name_first`, `name_last`, `email`, `twitter`, as well as the automatically
215
- defined attributes `id` and `type`. By default these attributes must exist on the model that is handled by the resource.
216
-
217
- A resource object wraps a Ruby object, usually an `ActiveModel` record, which is available as the `@model` variable.
218
- This allows a resource's methods to access the underlying model.
219
-
220
- For example, a computed attribute for `full_name` could be defined as such:
221
-
222
- ```ruby
223
- class ContactResource < JSONAPI::Resource
224
- attributes :name_first, :name_last, :email, :twitter
225
- attribute :full_name
226
-
227
- def full_name
228
- "#{@model.name_first}, #{@model.name_last}"
229
- end
230
- end
231
- ```
232
-
233
- ##### Attribute Delegation
234
-
235
- Normally resource attributes map to an attribute on the model of the same name. Using the `delegate` option allows a resource
236
- attribute to map to a differently named model attribute. For example:
237
-
238
- ```ruby
239
- class ContactResource < JSONAPI::Resource
240
- attribute :name_first, delegate: :first_name
241
- attribute :name_last, delegate: :last_name
242
- end
243
- ```
244
-
245
- ##### Fetchable Attributes
246
-
247
- By default all attributes are assumed to be fetchable. The list of fetchable attributes can be filtered by overriding
248
- the `fetchable_fields` method.
249
-
250
- Here's an example that prevents guest users from seeing the `email` field:
251
-
252
- ```ruby
253
- class AuthorResource < JSONAPI::Resource
254
- attributes :name, :email
255
- model_name 'Person'
256
- has_many :posts
257
-
258
- def fetchable_fields
259
- if (context[:current_user].guest)
260
- super - [:email]
261
- else
262
- super
263
- end
264
- end
265
- end
266
- ```
267
-
268
- Context flows through from the controller to the resource and can be used to control the attributes based on the
269
- current user (or other value).
270
-
271
- ##### Creatable and Updatable Attributes
272
-
273
- By default all attributes are assumed to be updatable and creatable. To prevent some attributes from being accepted by
274
- the `update` or `create` methods, override the `self.updatable_fields` and `self.creatable_fields` methods on a resource.
275
-
276
- This example prevents `full_name` from being set:
277
-
278
- ```ruby
279
- class ContactResource < JSONAPI::Resource
280
- attributes :name_first, :name_last, :full_name
281
-
282
- def full_name
283
- "#{@model.name_first}, #{@model.name_last}"
284
- end
285
-
286
- def self.updatable_fields(context)
287
- super - [:full_name]
288
- end
289
-
290
- def self.creatable_fields(context)
291
- super - [:full_name]
292
- end
293
- end
294
- ```
295
-
296
- The `context` is not by default used by the `ResourceController`, but may be used if you override the controller methods.
297
- By using the context you have the option to determine the creatable and updatable fields based on the user.
298
-
299
- ##### Sortable Attributes
300
-
301
- JR supports [sorting primary resources by multiple sort criteria](http://jsonapi.org/format/#fetching-sorting).
302
-
303
- By default all attributes are assumed to be sortable. To prevent some attributes from being sortable, override the
304
- `self.sortable_fields` method on a resource.
305
-
306
- Here's an example that prevents sorting by post's `body`:
307
-
308
- ```ruby
309
- class PostResource < JSONAPI::Resource
310
- attributes :title, :body
311
-
312
- def self.sortable_fields(context)
313
- super(context) - [:body]
314
- end
315
- end
316
- ```
317
-
318
- JR also supports sorting primary resources by fields on relationships.
319
-
320
- Here's an example of sorting books by the author name:
321
-
322
- ```ruby
323
- class Book < ActiveRecord::Base
324
- belongs_to :author
325
- end
326
-
327
- class Author < ActiveRecord::Base
328
- has_many :books
329
- end
330
-
331
- class BookResource < JSONAPI::Resource
332
- attributes :title, :body
333
-
334
- def self.sortable_fields(context)
335
- super(context) << :"author.name"
336
- end
337
- end
338
- ```
339
- The request will look something like:
340
- ```
341
- GET /books?include=author&sort=author.name
342
- ```
343
-
344
- ###### Default sorting
345
-
346
- By default JR sorts ascending on the `id` of the primary resource, unless the request specifies an alternate sort order.
347
- To override this you may override the `self.default_sort` on a `resource`. `default_sort` should return an array of
348
- `sort_param` hashes. A `sort_param` hash contains a `field` and a `direction`, with `direction` being either `:asc` or
349
- `:desc`.
350
-
351
- For example:
352
-
353
- ```ruby
354
- def self.default_sort
355
- [{field: 'name_last', direction: :desc}, {field: 'name_first', direction: :desc}]
356
- end
357
- ```
358
-
359
- ##### Attribute Formatting
360
-
361
- Attributes can have a `Format`. By default all attributes use the default formatter. If an attribute has the `format`
362
- option set the system will attempt to find a formatter based on this name. In the following example the `last_login_time`
363
- will be returned formatted to a certain time zone:
364
-
365
- ```ruby
366
- class PersonResource < JSONAPI::Resource
367
- attributes :name, :email
368
- attribute :last_login_time, format: :date_with_timezone
369
- end
370
- ```
371
-
372
- The system will lookup a value formatter named `DateWithTimezoneValueFormatter` and will use this when serializing and
373
- updating the attribute. See the [Value Formatters](#value-formatters) section for more details.
374
-
375
- ##### Flattening a Rails relationship
376
-
377
- It is possible to flatten Rails relationships into attributes by using getters and setters. This can become handy if a relation needs to be created alongside the creation of the main object which can be the case if there is a bi-directional presence validation. For example:
378
-
379
- ```ruby
380
- # Given Models
381
- class Person < ActiveRecord::Base
382
- has_many :spoken_languages
383
- validates :name, :email, :spoken_languages, presence: true
384
- end
385
-
386
- class SpokenLanguage < ActiveRecord::Base
387
- belongs_to :person, inverse_of: :spoken_languages
388
- validates :person, :language_code, presence: true
389
- end
390
-
391
- # Resource with getters and setter
392
- class PersonResource < JSONAPI::Resource
393
- attributes :name, :email, :spoken_languages
394
-
395
- # Getter
396
- def spoken_languages
397
- @model.spoken_languages.pluck(:language_code)
398
- end
399
-
400
- # Setter (because spoken_languages needed for creation)
401
- def spoken_languages=(new_spoken_language_codes)
402
- @model.spoken_languages.destroy_all
403
- new_spoken_language_codes.each do |new_lang_code|
404
- @model.spoken_languages.build(language_code: new_lang_code)
405
- end
406
- end
407
- end
408
- ```
409
-
410
- #### Primary Key
411
-
412
- Resources are always represented using a key of `id`. The resource will interrogate the model to find the primary key.
413
- If the underlying model does not use `id` as the primary key _and_ does not support the `primary_key` method you
414
- must use the `primary_key` method to tell the resource which field on the model to use as the primary key. **Note:**
415
- this _must_ be the actual primary key of the model.
416
-
417
- By default only integer values are allowed for primary key. To change this behavior you can set the `resource_key_type`
418
- configuration option:
419
-
420
- ```ruby
421
- JSONAPI.configure do |config|
422
- # Allowed values are :integer(default), :uuid, :string, or a proc
423
- config.resource_key_type = :uuid
424
- end
425
- ```
426
-
427
- ##### Override key type on a resource
428
-
429
- You can override the default resource key type on a per-resource basis by calling `key_type` in the resource class,
430
- with the same allowed values as the `resource_key_type` configuration option.
431
-
432
- ```ruby
433
- class ContactResource < JSONAPI::Resource
434
- attribute :id
435
- attributes :name_first, :name_last, :email, :twitter
436
- key_type :uuid
437
- end
438
- ```
439
-
440
- ##### Custom resource key validators
441
-
442
- If you need more control over the key, you can override the #verify_key method on your resource, or set a lambda that
443
- accepts key and context arguments in `config/initializers/jsonapi_resources.rb`:
444
-
445
- ```ruby
446
- JSONAPI.configure do |config|
447
- config.resource_key_type = -> (key, context) { key && String(key) }
448
- end
449
- ```
450
-
451
- #### Model Name
452
-
453
- The name of the underlying model is inferred from the Resource name. It can be overridden by use of the `model_name`
454
- method. For example:
455
-
456
- ```ruby
457
- class AuthorResource < JSONAPI::Resource
458
- attribute :name
459
- model_name 'Person'
460
- has_many :posts
461
- end
462
- ```
463
-
464
- #### Model Hints
465
-
466
- Resource instances are created from model records. The determination of the correct resource type is performed using a
467
- simple rule based on the model's name. The name is used to find a resource in the same module (as the originating
468
- resource) that matches the name. This usually works quite well, however it can fail when model names do not match
469
- resource names. It can also fail when using namespaced models. In this case a `model_hint` can be created to map model
470
- names to resources. For example:
471
-
472
- ```ruby
473
- class AuthorResource < JSONAPI::Resource
474
- attribute :name
475
- model_name 'Person'
476
- model_hint model: Commenter, resource: :special_person
477
-
478
- has_many :posts
479
- has_many :commenters
480
- end
481
- ```
482
-
483
- Note that when `model_name` is set a corresponding `model_hint` is also added. This can be skipped by using the
484
- `add_model_hint` option set to false. For example:
485
-
486
- ```ruby
487
- class AuthorResource < JSONAPI::Resource
488
- model_name 'Legacy::Person', add_model_hint: false
489
- end
490
- ```
491
-
492
- Model hints inherit from parent resources, but are not global in scope. The `model_hint` method accepts `model` and
493
- `resource` named parameters. `model` takes an ActiveRecord class or class name (defaults to the model name), and
494
- `resource` takes a resource type or a resource class (defaults to the current resource's type).
495
-
496
- #### Relationships
497
-
498
- Related resources need to be specified in the resource. These may be declared with the `relationship` or the `has_one`
499
- and the `has_many` methods.
500
-
501
- Here's a simple example using the `relationship` method where a post has a single author and an author can have many
502
- posts:
503
-
504
- ```ruby
505
- class PostResource < JSONAPI::Resource
506
- attributes :title, :body
507
-
508
- relationship :author, to: :one
509
- end
510
- ```
511
-
512
- And the corresponding author:
513
-
514
- ```ruby
515
- class AuthorResource < JSONAPI::Resource
516
- attribute :name
517
-
518
- relationship :posts, to: :many
519
- end
520
- ```
521
-
522
- And here's the equivalent resources using the `has_one` and `has_many` methods:
523
-
524
- ```ruby
525
- class PostResource < JSONAPI::Resource
526
- attributes :title, :body
527
-
528
- has_one :author
529
- end
530
- ```
531
-
532
- And the corresponding author:
533
-
534
- ```ruby
535
- class AuthorResource < JSONAPI::Resource
536
- attribute :name
537
-
538
- has_many :posts
539
- end
540
- ```
541
-
542
- ##### Options
543
-
544
- The relationship methods (`relationship`, `has_one`, and `has_many`) support the following options:
545
-
546
- * `class_name` - a string specifying the underlying class for the related resource. Defaults to the `class_name` property on the underlying model.
547
- * `foreign_key` - the method on the resource used to fetch the related resource. Defaults to `<resource_name>_id` for has_one and `<resource_name>_ids` for has_many relationships.
548
- * `acts_as_set` - allows the entire set of related records to be replaced in one operation. Defaults to false if not set.
549
- * `polymorphic` - set to true to identify relationships that are polymorphic.
550
- * `relation_name` - the name of the relation to use on the model. A lambda may be provided which allows conditional selection of the relation based on the context.
551
- * `always_include_linkage_data` - if set to true, the relationship includes linkage data. Defaults to false if not set.
552
- * `eager_load_on_include` - if set to false, will not include this relationship in join SQL when requested via an include. You usually want to leave this on, but it will break 'relationships' which are not active record, for example if you want to expose a tree using the `ancestry` gem or similar, or the SQL query becomes too large to handle. Defaults to true if not set.
553
-
554
- `to_one` relationships support the additional option:
555
- * `foreign_key_on` - defaults to `:self`. To indicate that the foreign key is on the related resource specify `:related`.
556
-
557
- `to_many` relationships support the additional option:
558
- * `reflect` - defaults to `true`. To indicate that updates to the relationship are performed on the related resource, if relationship reflection is turned on. See [Configuration] (#configuration)
559
-
560
- Examples:
561
-
562
- ```ruby
563
- class CommentResource < JSONAPI::Resource
564
- attributes :body
565
- has_one :post
566
- has_one :author, class_name: 'Person'
567
- has_many :tags, acts_as_set: true
568
- end
569
-
570
- class ExpenseEntryResource < JSONAPI::Resource
571
- attributes :cost, :transaction_date
572
-
573
- has_one :currency, class_name: 'Currency', foreign_key: 'currency_code'
574
- has_one :employee
575
- end
576
-
577
- class TagResource < JSONAPI::Resource
578
- attributes :name
579
- has_one :taggable, polymorphic: true
580
- end
581
- ```
582
-
583
- ```ruby
584
- class BookResource < JSONAPI::Resource
585
-
586
- # Only book_admins may see unapproved comments for a book. Using
587
- # a lambda to select the correct relation on the model
588
- has_many :book_comments, relation_name: -> (options = {}) {
589
- context = options[:context]
590
- current_user = context ? context[:current_user] : nil
591
-
592
- unless current_user && current_user.book_admin
593
- :approved_book_comments
594
- else
595
- :book_comments
596
- end
597
- }
598
- ...
599
- end
600
- ```
601
-
602
- The polymorphic relationship will require the resource and controller to exist, although routing to them will cause an
603
- error.
604
-
605
- ```ruby
606
- class TaggableResource < JSONAPI::Resource; end
607
- class TaggablesController < JSONAPI::ResourceController; end
608
- ```
609
-
610
- #### Filters
611
-
612
- Filters for locating objects of the resource type are specified in the resource definition. Single filters can be
613
- declared using the `filter` method, and multiple filters can be declared with the `filters` method on the resource
614
- class.
615
-
616
- For example:
617
-
618
- ```ruby
619
- class ContactResource < JSONAPI::Resource
620
- attributes :name_first, :name_last, :email, :twitter
621
-
622
- filter :id
623
- filters :name_first, :name_last
624
- end
625
- ```
626
-
627
- Then a request could pass in a filter for example `http://example.com/contacts?filter[name_last]=Smith` and the system
628
- will find all people where the last name exactly matches Smith.
629
-
630
- ##### Default Filters
631
-
632
- A default filter may be defined for a resource using the `default` option on the `filter` method. This default is used
633
- unless the request overrides this value.
634
-
635
- For example:
636
-
637
- ```ruby
638
- class CommentResource < JSONAPI::Resource
639
- attributes :body, :status
640
- has_one :post
641
- has_one :author
642
-
643
- filter :status, default: 'published,pending'
644
- end
645
- ```
646
-
647
- The default value is used as if it came from the request.
648
-
649
- ##### Applying Filters
650
-
651
- You may customize how a filter behaves by supplying a callable to the `:apply` option. This callable will be used to
652
- apply that filter. The callable is passed the `records`, which is an `ActiveRecord::Relation`, the `value`, and an
653
- `_options` hash. It is expected to return an `ActiveRecord::Relation`.
654
-
655
- This example shows how you can implement different approaches for different filters.
656
-
657
- ```ruby
658
- filter :visibility, apply: ->(records, value, _options) {
659
- records.where('users.publicly_visible = ?', value == :public)
660
- }
661
- ```
662
-
663
- If you omit the `apply` callable the filter will be applied as `records.where(filter => value)`.
664
-
665
- Note: It is also possible to override the `self.apply_filter` method, though this approach is now deprecated:
666
-
667
- ```ruby
668
- def self.apply_filter(records, filter, value, options)
669
- case filter
670
- when :last_name, :first_name, :name
671
- if value.is_a?(Array)
672
- value.each do |val|
673
- records = records.where(_model_class.arel_table[filter].matches(val))
674
- end
675
- return records
676
- else
677
- records.where(_model_class.arel_table[filter].matches(value))
678
- end
679
- else
680
- return super(records, filter, value)
681
- end
682
- end
683
- ```
684
-
685
- ##### Verifying Filters
686
-
687
- Because filters typically come straight from the request, it's prudent to verify their values. To do so, provide a
688
- callable to the `verify` option. This callable will be passed the `value` and the `context`. Verify should return the
689
- verified value, which may be modified.
690
-
691
- ```ruby
692
- filter :ids,
693
- verify: ->(values, context) {
694
- verify_keys(values, context)
695
- return values
696
- },
697
- apply: -> (records, value, _options) {
698
- records.where('id IN (?)', value)
699
- }
700
- ```
701
-
702
- ##### Finders
703
-
704
- Basic finding by filters is supported by resources. This is implemented in the `find` and `find_by_key` finder methods.
705
- Currently this is implemented for `ActiveRecord` based resources. The finder methods rely on the `records` method to get
706
- an `ActiveRecord::Relation` relation. It is therefore possible to override `records` to affect the three find related
707
- methods.
708
-
709
- ###### Customizing base records for finder methods
710
-
711
- If you need to change the base records on which `find` and `find_by_key` operate, you can override the `records` method
712
- on the resource class.
713
-
714
- For example to allow a user to only retrieve his own posts you can do the following:
715
-
716
- ```ruby
717
- class PostResource < JSONAPI::Resource
718
- attributes :title, :body
719
-
720
- def self.records(options = {})
721
- context = options[:context]
722
- context[:current_user].posts
723
- end
724
- end
725
- ```
726
-
727
- When you create a relationship, a method is created to fetch record(s) for that relationship, using the relation name
728
- for the relationship.
729
-
730
- ```ruby
731
- class PostResource < JSONAPI::Resource
732
- has_one :author
733
- has_many :comments
734
-
735
- # def record_for_author
736
- # relation_name = relationship.relation_name(context: @context)
737
- # records_for(relation_name)
738
- # end
739
-
740
- # def records_for_comments
741
- # relation_name = relationship.relation_name(context: @context)
742
- # records_for(relation_name)
743
- # end
744
- end
745
-
746
- ```
747
-
748
- For example, you may want to raise an error if the user is not authorized to view the related records. See the next
749
- section for additional details on raising errors.
750
-
751
- ```ruby
752
- class BaseResource < JSONAPI::Resource
753
- def records_for(relation_name)
754
- context = options[:context]
755
- records = _model.public_send(relation_name)
756
-
757
- unless context[:current_user].can_view?(records)
758
- raise NotAuthorizedError
759
- end
760
-
761
- records
762
- end
763
- end
764
- ```
765
-
766
- ###### Raising Errors
767
-
768
- Inside the finder methods (like `records_for`) or inside of resource callbacks
769
- (like `before_save`) you can `raise` an error to halt processing. JSONAPI::Resources
770
- has some built in errors that will return appropriate error codes. By
771
- default any other error that you raise will return a `500` status code
772
- for a general internal server error.
773
-
774
- To return useful error codes that represent application errors you
775
- should set the `exception_class_whitelist` config variable, and then you
776
- should use the Rails `rescue_from` macro to render a status code.
777
-
778
- For example, this config setting allows the `NotAuthorizedError` to bubble up out of
779
- JSONAPI::Resources and into your application.
780
-
781
- ```ruby
782
- # config/initializer/jsonapi-resources.rb
783
- JSONAPI.configure do |config|
784
- config.exception_class_whitelist = [NotAuthorizedError]
785
- end
786
- ```
787
-
788
- Handling the error and rendering the appropriate code is now the resonsiblity of the
789
- application and could be handled like this:
790
-
791
- ```ruby
792
- class ApiController < ApplicationController
793
- rescue_from NotAuthorizedError, with: :reject_forbidden_request
794
- def reject_forbidden_request
795
- render json: {error: 'Forbidden'}, :status => 403
796
- end
797
- end
798
- ```
799
-
800
-
801
- ###### Applying Filters
802
-
803
- The `apply_filter` method is called to apply each filter to the `Arel` relation. You may override this method to gain
804
- control over how the filters are applied to the `Arel` relation.
805
-
806
- This example shows how you can implement different approaches for different filters.
807
-
808
- ```ruby
809
- def self.apply_filter(records, filter, value, options)
810
- case filter
811
- when :visibility
812
- records.where('users.publicly_visible = ?', value == :public)
813
- when :last_name, :first_name, :name
814
- if value.is_a?(Array)
815
- value.each do |val|
816
- records = records.where(_model_class.arel_table[filter].matches(val))
817
- end
818
- return records
819
- else
820
- records.where(_model_class.arel_table[filter].matches(value))
821
- end
822
- else
823
- return super(records, filter, value)
824
- end
825
- end
826
- ```
827
-
828
-
829
- ###### Applying Sorting
830
-
831
- You can override the `apply_sort` method to gain control over how the sorting is done. This may be useful in case you'd
832
- like to base the sorting on variables in your context.
833
-
834
- Example:
835
-
836
- ```ruby
837
- def self.apply_sort(records, order_options, context = {})
838
- if order_options.has?(:trending)
839
- records = records.order_by_trending_scope
840
- order_options - [:trending]
841
- end
842
-
843
- super(records, order_options, context)
844
- end
845
- ```
846
-
847
-
848
- ###### Override finder methods
849
-
850
- Finally if you have more complex requirements for finding you can override the `find` and `find_by_key` methods on the
851
- resource class.
852
-
853
- Here's an example that defers the `find` operation to a `current_user` set on the `context` option:
854
-
855
- ```ruby
856
- class AuthorResource < JSONAPI::Resource
857
- attribute :name
858
- model_name 'Person'
859
- has_many :posts
860
-
861
- filter :name
862
-
863
- def self.find(filters, options = {})
864
- context = options[:context]
865
- authors = context[:current_user].find_authors(filters)
866
-
867
- return authors.map do |author|
868
- self.new(author, context)
869
- end
870
- end
871
- end
872
- ```
873
-
874
- #### Pagination
875
-
876
- Pagination is performed using a `paginator`, which is a class responsible for parsing the `page` request parameters and
877
- applying the pagination logic to the results.
878
-
879
- ##### Paginators
880
-
881
- `JSONAPI::Resource` supports several pagination methods by default, and allows you to implement a custom system if the
882
- defaults do not meet your needs.
883
-
884
- ###### Paged Paginator
885
-
886
- The `paged` `paginator` returns results based on pages of a fixed size. Valid `page` parameters are `number` and `size`.
887
- If `number` is omitted the first page is returned. If `size` is omitted the `default_page_size` from the configuration
888
- settings is used.
889
-
890
- ```
891
- GET /articles?page%5Bnumber%5D=10&page%5Bsize%5D=10 HTTP/1.1
892
- Accept: application/vnd.api+json
893
- ```
894
-
895
- ###### Offset Paginator
896
-
897
- The `offset` `paginator` returns results based on an offset from the beginning of the resultset. Valid `page` parameters
898
- are `offset` and `limit`. If `offset` is omitted a value of 0 will be used. If `limit` is omitted the `default_page_size`
899
- from the configuration settings is used.
900
-
901
- ```
902
- GET /articles?page%5Blimit%5D=10&page%5Boffset%5D=10 HTTP/1.1
903
- Accept: application/vnd.api+json
904
- ```
905
-
906
- ###### Custom Paginators
907
-
908
- Custom `paginators` can be used. These should derive from `Paginator`. The `apply` method takes a `relation` and
909
- `order_options` and is expected to return a `relation`. The `initialize` method receives the parameters from the `page`
910
- request parameters. It is up to the paginator author to parse and validate these parameters.
911
-
912
- For example, here is a very simple single record at a time paginator:
913
-
914
- ```ruby
915
- class SingleRecordPaginator < JSONAPI::Paginator
916
- def initialize(params)
917
- # param parsing and validation here
918
- @page = params.to_i
919
- end
920
-
921
- def apply(relation, order_options)
922
- relation.offset(@page).limit(1)
923
- end
924
- end
925
- ```
926
-
927
- ##### Paginator Configuration
928
-
929
- The default paginator, which will be used for all resources, is set using `JSONAPI.configure`. For example, in your
930
- `config/initializers/jsonapi_resources.rb`:
931
-
932
- ```ruby
933
- JSONAPI.configure do |config|
934
- # built in paginators are :none, :offset, :paged
935
- config.default_paginator = :offset
936
-
937
- config.default_page_size = 10
938
- config.maximum_page_size = 20
939
- end
940
- ```
941
-
942
- If no `default_paginator` is configured, pagination will be disabled by default.
943
-
944
- Paginators can also be set at the resource-level, which will override the default setting. This is done using the
945
- `paginator` method:
946
-
947
- ```ruby
948
- class BookResource < JSONAPI::Resource
949
- attribute :title
950
- attribute :isbn
951
-
952
- paginator :offset
953
- end
954
- ```
955
-
956
- To disable pagination in a resource, specify `:none` for `paginator`.
957
-
958
- #### Included relationships (side-loading resources)
959
-
960
- JR supports [request include params](http://jsonapi.org/format/#fetching-includes) out of the box, for side loading related resources.
961
-
962
- Here's an example from the spec:
963
-
964
- ```
965
- GET /articles/1?include=comments HTTP/1.1
966
- Accept: application/vnd.api+json
967
- ```
968
-
969
- Will get you the following payload by default:
970
-
971
- ```
972
- {
973
- "data": {
974
- "type": "articles",
975
- "id": "1",
976
- "attributes": {
977
- "title": "JSON API paints my bikeshed!"
978
- },
979
- "links": {
980
- "self": "http://example.com/articles/1"
981
- },
982
- "relationships": {
983
- "comments": {
984
- "links": {
985
- "self": "http://example.com/articles/1/relationships/comments",
986
- "related": "http://example.com/articles/1/comments"
987
- },
988
- "data": [
989
- { "type": "comments", "id": "5" },
990
- { "type": "comments", "id": "12" }
991
- ]
992
- }
993
- }
994
- },
995
- "included": [{
996
- "type": "comments",
997
- "id": "5",
998
- "attributes": {
999
- "body": "First!"
1000
- },
1001
- "links": {
1002
- "self": "http://example.com/comments/5"
1003
- }
1004
- }, {
1005
- "type": "comments",
1006
- "id": "12",
1007
- "attributes": {
1008
- "body": "I like XML better"
1009
- },
1010
- "links": {
1011
- "self": "http://example.com/comments/12"
1012
- }
1013
- }]
1014
- }
1015
- ```
1016
-
1017
- #### Resource Meta
1018
-
1019
- Meta information can be included for each resource using the meta method in the resource declaration. For example:
1020
-
1021
- ```ruby
1022
- class BookResource < JSONAPI::Resource
1023
- attribute :title
1024
- attribute :isbn
1025
-
1026
- def meta(options)
1027
- {
1028
- copyright: 'API Copyright 2015 - XYZ Corp.',
1029
- computed_copyright: options[:serialization_options][:copyright],
1030
- last_updated_at: _model.updated_at
1031
- }
1032
- end
1033
- end
1034
-
1035
- ```
1036
-
1037
- The `meta` method will be called for each resource instance. Override the `meta` method on a resource class to control
1038
- the meta information for the resource. If a non empty hash is returned from `meta` this will be serialized. The `meta`
1039
- method is called with an `options` hash. The `options` hash will contain the following:
1040
-
1041
- * `:serializer` -> the serializer instance
1042
- * `:serialization_options` -> the contents of the `serialization_options` method on the controller.
1043
-
1044
- #### Custom Links
1045
-
1046
- Custom links can be included for each resource by overriding the `custom_links` method. If a non empty hash is returned from `custom_links`, it will be merged with the default links hash containing the resource's `self` link. The `custom_links` method is called with the same `options` hash used by for [resource meta information](#resource-meta). The `options` hash contains the following:
1047
-
1048
- * `:serializer` -> the serializer instance
1049
- * `:serialization_options` -> the contents of the `serialization_options` method on the controller.
1050
-
1051
- For example:
1052
-
1053
- ```ruby
1054
- class CityCouncilMeeting < JSONAPI::Resource
1055
- attribute :title, :location, :approved
1056
-
1057
- def custom_links(options)
1058
- { minutes: options[:serializer].link_builder.self_link(self) + "/minutes" }
1059
- end
1060
- end
1061
- ```
1062
-
1063
- This will create a custom link with the key `minutes`, which will be merged with the default `self` link, like so:
1064
-
1065
- ```json
1066
- {
1067
- "data": [
1068
- {
1069
- "id": "1",
1070
- "type": "cityCouncilMeetings",
1071
- "links": {
1072
- "self": "http://city.gov/api/city-council-meetings/1",
1073
- "minutes": "http://city.gov/api/city-council-meetings/1/minutes"
1074
- },
1075
- "attributes": {...}
1076
- },
1077
- //...
1078
- ]
1079
- }
1080
- ```
1081
-
1082
- Of course, the `custom_links` method can include logic to include links only when relevant:
1083
-
1084
- ````ruby
1085
- class CityCouncilMeeting < JSONAPI::Resource
1086
- attribute :title, :location, :approved
1087
-
1088
- delegate :approved?, to: :model
1089
-
1090
- def custom_links(options)
1091
- extra_links = {}
1092
- if approved?
1093
- extra_links[:minutes] = options[:serializer].link_builder.self_link(self) + "/minutes"
1094
- end
1095
- extra_links
1096
- end
1097
- end
1098
- ```
1099
-
1100
- It's also possibly to suppress the default `self` link by returning a hash with `{self: nil}`:
1101
-
1102
- ````ruby
1103
- class Selfless < JSONAPI::Resource
1104
- def custom_links(options)
1105
- {self: nil}
1106
- end
1107
- end
1108
- ```
1109
-
1110
- #### Callbacks
1111
-
1112
- `ActiveSupport::Callbacks` is used to provide callback functionality, so the behavior is very similar to what you may be
1113
- used to from `ActiveRecord`.
1114
-
1115
- For example, you might use a callback to perform authorization on your resource before an action.
1116
-
1117
- ```ruby
1118
- class BaseResource < JSONAPI::Resource
1119
- before_create :authorize_create
1120
-
1121
- def authorize_create
1122
- # ...
1123
- end
1124
- end
1125
- ```
1126
-
1127
- The types of supported callbacks are:
1128
- - `before`
1129
- - `after`
1130
- - `around`
1131
-
1132
- ##### `JSONAPI::Resource` Callbacks
1133
-
1134
- Callbacks can be defined for the following `JSONAPI::Resource` events:
1135
-
1136
- - `:create`
1137
- - `:update`
1138
- - `:remove`
1139
- - `:save`
1140
- - `:create_to_many_link`
1141
- - `:replace_to_many_links`
1142
- - `:create_to_one_link`
1143
- - `:replace_to_one_link`
1144
- - `:remove_to_many_link`
1145
- - `:remove_to_one_link`
1146
- - `:replace_fields`
1147
-
1148
- ###### Relationship Reflection
1149
-
1150
- By default updates to relationships only invoke callbacks on the primary
1151
- Resource. By setting the `use_relationship_reflection` [Configuration] (#configuration) option
1152
- updates to `has_many` relationships will occur on the related resource, triggering
1153
- callbacks on both resources.
1154
-
1155
- ##### `JSONAPI::Processor` Callbacks
1156
-
1157
- Callbacks can also be defined for `JSONAPI::Processor` events:
1158
- - `:operation`: Any individual operation.
1159
- - `:find`: A `find` operation is being processed.
1160
- - `:show`: A `show` operation is being processed.
1161
- - `:show_relationship`: A `show_relationship` operation is being processed.
1162
- - `:show_related_resource`: A `show_related_resource` operation is being processed.
1163
- - `:show_related_resources`: A `show_related_resources` operation is being processed.
1164
- - `:create_resource`: A `create_resource` operation is being processed.
1165
- - `:remove_resource`: A `remove_resource` operation is being processed.
1166
- - `:replace_fields`: A `replace_fields` operation is being processed.
1167
- - `:replace_to_one_relationship`: A `replace_to_one_relationship` operation is being processed.
1168
- - `:create_to_many_relationship`: A `create_to_many_relationship` operation is being processed.
1169
- - `:replace_to_many_relationship`: A `replace_to_many_relationship` operation is being processed.
1170
- - `:remove_to_many_relationship`: A `remove_to_many_relationship` operation is being processed.
1171
- - `:remove_to_one_relationship`: A `remove_to_one_relationship` operation is being processed.
1172
-
1173
- See [Operation Processors] (#operation-processors) for details on using OperationProcessors
1174
-
1175
- ##### `JSONAPI::OperationsProcessor` Callbacks (a removed feature)
1176
-
1177
- Note: The `JSONAPI::OperationsProcessor` has been removed and replaced with the `JSONAPI::OperationDispatcher`
1178
- and `Processor` classes per resource. The callbacks have been renamed and moved to the
1179
- `Processor`s, with the exception of the `operations` callback which is now on the controller.
1180
-
1181
- ### Controllers
1182
-
1183
- There are two ways to implement a controller for your resources. Either derive from `ResourceController` or import
1184
- the `ActsAsResourceController` module.
1185
-
1186
- ##### ResourceController
1187
-
1188
- `JSONAPI::Resources` provides a class, `ResourceController`, that can be used as the base class for your controllers.
1189
- `ResourceController` supports `index`, `show`, `create`, `update`, and `destroy` methods. Just deriving your controller
1190
- from `ResourceController` will give you a fully functional controller.
1191
-
1192
- For example:
1193
-
1194
- ```ruby
1195
- class PeopleController < JSONAPI::ResourceController
1196
-
1197
- end
1198
- ```
1199
-
1200
- Of course you are free to extend this as needed and override action handlers or other methods.
1201
-
1202
- A jsonapi-controller generator is avaliable
1203
-
1204
- ```
1205
- rails generate jsonapi:controller contact
1206
- ```
1207
-
1208
- ###### ResourceControllerMetal
1209
-
1210
- `JSONAPI::Resources` also provides an alternative class to `ResourceController` called `ResourceControllerMetal`.
1211
- In order to provide a lighter weight controller option this strips the controller down to just the classes needed
1212
- to work with `JSONAPI::Resources`.
1213
-
1214
- For example:
1215
-
1216
- ```ruby
1217
- class PeopleController < JSONAPI::ResourceControllerMetal
1218
-
1219
- end
1220
- ```
1221
-
1222
- Note: This may not provide all of the expected controller capabilities if you are using additional gems such as DoorKeeper.
1223
-
1224
- ###### Serialization Options
1225
-
1226
- Additional options can be passed to the serializer using the `serialization_options` method.
1227
-
1228
- For example:
1229
-
1230
- ```ruby
1231
- class ApplicationController < JSONAPI::ResourceController
1232
- def serialization_options
1233
- {copyright: 'Copyright 2015'}
1234
- end
1235
- end
1236
- ```
1237
-
1238
- These `serialization_options` are passed to the `meta` method used to generate resource `meta` values.
1239
-
1240
- ##### ActsAsResourceController
1241
-
1242
- `JSONAPI::Resources` also provides a module, `JSONAPI::ActsAsResourceController`. You can include this module to
1243
- mix in all the features of `ResourceController` into your existing controller class.
1244
-
1245
- For example:
1246
-
1247
- ```ruby
1248
- class PostsController < ActionController::Base
1249
- include JSONAPI::ActsAsResourceController
1250
- end
1251
- ```
1252
-
1253
- #### Namespaces
1254
-
1255
- JSONAPI::Resources supports namespacing of controllers and resources. With namespacing you can version your API.
1256
-
1257
- If you namespace your controller it will require a namespaced resource.
1258
-
1259
- In the following example we have a `resource` that isn't namespaced, and one that has now been namespaced. There are
1260
- slight differences between the two resources, as might be seen in a new version of an API:
1261
-
1262
- ```ruby
1263
- class PostResource < JSONAPI::Resource
1264
- attribute :title
1265
- attribute :body
1266
- attribute :subject
1267
-
1268
- has_one :author, class_name: 'Person'
1269
- has_one :section
1270
- has_many :tags, acts_as_set: true
1271
- has_many :comments, acts_as_set: false
1272
- def subject
1273
- @model.title
1274
- end
1275
-
1276
- filters :title, :author, :tags, :comments
1277
- filter :id
1278
- end
1279
-
1280
- ...
1281
-
1282
- module Api
1283
- module V1
1284
- class PostResource < JSONAPI::Resource
1285
- # V1 replaces the non-namespaced resource
1286
- # V1 no longer supports tags and now calls author 'writer'
1287
- attribute :title
1288
- attribute :body
1289
- attribute :subject
1290
-
1291
- has_one :writer, foreign_key: 'author_id'
1292
- has_one :section
1293
- has_many :comments, acts_as_set: false
1294
-
1295
- def subject
1296
- @model.title
1297
- end
1298
-
1299
- filters :writer
1300
- end
1301
-
1302
- class WriterResource < JSONAPI::Resource
1303
- attributes :name, :email
1304
- model_name 'Person'
1305
- has_many :posts
1306
-
1307
- filter :name
1308
- end
1309
- end
1310
- end
1311
- ```
1312
-
1313
- The following controllers are used:
1314
-
1315
- ```ruby
1316
- class PostsController < JSONAPI::ResourceController
1317
- end
1318
-
1319
- module Api
1320
- module V1
1321
- class PostsController < JSONAPI::ResourceController
1322
- end
1323
- end
1324
- end
1325
- ```
1326
-
1327
- You will also need to namespace your routes:
1328
-
1329
- ```ruby
1330
- Rails.application.routes.draw do
1331
-
1332
- jsonapi_resources :posts
1333
-
1334
- namespace :api do
1335
- namespace :v1 do
1336
- jsonapi_resources :posts
1337
- end
1338
- end
1339
- end
1340
- ```
1341
-
1342
- When a namespaced `resource` is used, any related `resources` must also be in the same namespace.
1343
-
1344
- #### Error codes
1345
-
1346
- Error codes are provided for each error object returned, based on the error. These errors are:
1347
-
1348
- ```ruby
1349
- module JSONAPI
1350
- VALIDATION_ERROR = '100'
1351
- INVALID_RESOURCE = '101'
1352
- FILTER_NOT_ALLOWED = '102'
1353
- INVALID_FIELD_VALUE = '103'
1354
- INVALID_FIELD = '104'
1355
- PARAM_NOT_ALLOWED = '105'
1356
- PARAM_MISSING = '106'
1357
- INVALID_FILTER_VALUE = '107'
1358
- COUNT_MISMATCH = '108'
1359
- KEY_ORDER_MISMATCH = '109'
1360
- KEY_NOT_INCLUDED_IN_URL = '110'
1361
- INVALID_INCLUDE = '112'
1362
- RELATION_EXISTS = '113'
1363
- INVALID_SORT_CRITERIA = '114'
1364
- INVALID_LINKS_OBJECT = '115'
1365
- TYPE_MISMATCH = '116'
1366
- INVALID_PAGE_OBJECT = '117'
1367
- INVALID_PAGE_VALUE = '118'
1368
- INVALID_FIELD_FORMAT = '119'
1369
- INVALID_FILTERS_SYNTAX = '120'
1370
- SAVE_FAILED = '121'
1371
- FORBIDDEN = '403'
1372
- RECORD_NOT_FOUND = '404'
1373
- NOT_ACCEPTABLE = '406'
1374
- UNSUPPORTED_MEDIA_TYPE = '415'
1375
- LOCKED = '423'
1376
- end
1377
- ```
1378
-
1379
- These codes can be customized in your app by creating an initializer to override any or all of the codes.
1380
-
1381
- In addition textual error codes can be returned by setting the configuration option `use_text_errors = true`. For
1382
- example:
1383
-
1384
- ```ruby
1385
- JSONAPI.configure do |config|
1386
- config.use_text_errors = true
1387
- end
1388
- ```
1389
-
1390
-
1391
- #### Handling Exceptions
1392
-
1393
- By default, all exceptions raised downstream from a resource controller will be caught, logged, and a ```500 Internal Server Error``` will be rendered. Exceptions can be whitelisted in the config to pass through the handler and be caught manually, or you can pass a callback from a resource controller to insert logic into the rescue block without interrupting the control flow. This can be particularly useful for additional logging or monitoring without the added work of rendering responses.
1394
-
1395
- Pass a block, refer to controller class methods, or both. Note that methods must be defined as class methods on a controller and accept one parameter, which is passed the exception object that was rescued.
1396
-
1397
- ```ruby
1398
- class ApplicationController < JSONAPI::ResourceController
1399
-
1400
- on_server_error :first_callback
1401
-
1402
- #or
1403
-
1404
- # on_server_error do |error|
1405
- #do things
1406
- #end
1407
-
1408
- def self.first_callback(error)
1409
- #env["airbrake.error_id"] = notify_airbrake(error)
1410
- end
1411
- end
1412
-
1413
- ```
1414
-
1415
- #### Action Callbacks
1416
-
1417
- ##### ensure_correct_media_type
1418
-
1419
- By default, when controllers extend functionalities from `jsonapi-resources`, the `ActsAsResourceController#ensure_correct_media_type`
1420
- method will be triggered before `create`, `update`, `create_relationship` and `update_relationship` actions. This method is reponsible
1421
- for checking if client's request corresponds to the correct media type required by [JSON API](http://jsonapi.org/format/#content-negotiation-clients): `application/vnd.api+json`.
1422
-
1423
- In case you need to check the media type for custom actions, just make sure to call the method in your controller's `before_action`:
1424
-
1425
- ```ruby
1426
- class UsersController < JSONAPI::ResourceController
1427
- before_action :ensure_correct_media_type, only: [:auth]
1428
-
1429
- def auth
1430
- # some crazy auth code goes here
1431
- end
1432
- end
1433
- ```
1434
-
1435
- ### Operation Processors
1436
-
1437
- Operation Processors are called to perform the operation(s) that make up a request. The controller (through the `OperationDispatcher`), creates an `OperatorProcessor` to handle each operation. The processor is created based on the resource name, including the namespace. If a processor does not exist for a resource (namespace matters) the default operation processor is used instead. The default processor can be changed by a configuration setting.
1438
-
1439
- Defining a custom `Processor` allows for custom callback handling of each operation type for each resource type. For example:
1440
-
1441
- ```ruby
1442
- class Api::V4::BookProcessor < JSONAPI::Processor
1443
- after_find do
1444
- unless @result.is_a?(JSONAPI::ErrorsOperationResult)
1445
- @result.meta[:total_records_found] = @result.record_count
1446
- end
1447
- end
1448
- end
1449
- ```
1450
-
1451
- This simple example uses a callback to update the result's meta property with the total count of records (a redundant
1452
- feature only for example purposes), if there wasn't an error in the operation. It is also possible to override the
1453
- `find` method as well if a different behavior is needed, for example:
1454
-
1455
- ```ruby
1456
- class Api::V4::BookProcessor < JSONAPI::Processor
1457
- def find
1458
- filters = params[:filters]
1459
- include_directives = params[:include_directives]
1460
- sort_criteria = params.fetch(:sort_criteria, [])
1461
- paginator = params[:paginator]
1462
-
1463
- verified_filters = resource_klass.verify_filters(filters, context)
1464
- resource_records = resource_klass.find(verified_filters,
1465
- context: context,
1466
- include_directives: include_directives,
1467
- sort_criteria: sort_criteria,
1468
- paginator: paginator)
1469
-
1470
- page_options = {}
1471
- # Overriding the default record count logic to always include it in the meta
1472
- #if (JSONAPI.configuration.top_level_meta_include_record_count ||
1473
- # (paginator && paginator.class.requires_record_count))
1474
- page_options[:record_count] = resource_klass.find_count(verified_filters,
1475
- context: context,
1476
- include_directives: include_directives)
1477
- #end
1478
- end
1479
- ```
1480
-
1481
- Note: The authors of this gem expect the most common uses cases to be handled using the callbacks. It is likely that the
1482
- internal functionality of the operation processing methods will change, at least for several revisions. Effort will be
1483
- made to call this out in release notes. You have been warned.
1484
-
1485
- ### Serializer
1486
-
1487
- The `ResourceSerializer` can be used to serialize a resource into JSON API compliant JSON. `ResourceSerializer` must be
1488
- initialized with the primary resource type it will be serializing. `ResourceSerializer` has a `serialize_to_hash`
1489
- method that takes a resource instance or array of resource instances to serialize. For example:
1490
-
1491
- ```ruby
1492
- post = Post.find(1)
1493
- JSONAPI::ResourceSerializer.new(PostResource).serialize_to_hash(PostResource.new(post, nil))
1494
- ```
1495
-
1496
- This returns results like this:
1497
-
1498
- ```json
1499
- {
1500
- "data": {
1501
- "type": "posts",
1502
- "id": "1",
1503
- "links": {
1504
- "self": "http://example.com/posts/1"
1505
- },
1506
- "attributes": {
1507
- "title": "New post",
1508
- "body": "A body!!!",
1509
- "subject": "New post"
1510
- },
1511
- "relationships": {
1512
- "section": {
1513
- "links": {
1514
- "self": "http://example.com/posts/1/relationships/section",
1515
- "related": "http://example.com/posts/1/section"
1516
- },
1517
- "data": null
1518
- },
1519
- "author": {
1520
- "links": {
1521
- "self": "http://example.com/posts/1/relationships/author",
1522
- "related": "http://example.com/posts/1/author"
1523
- },
1524
- "data": {
1525
- "type": "people",
1526
- "id": "1"
1527
- }
1528
- },
1529
- "tags": {
1530
- "links": {
1531
- "self": "http://example.com/posts/1/relationships/tags",
1532
- "related": "http://example.com/posts/1/tags"
1533
- }
1534
- },
1535
- "comments": {
1536
- "links": {
1537
- "self": "http://example.com/posts/1/relationships/comments",
1538
- "related": "http://example.com/posts/1/comments"
1539
- }
1540
- }
1541
- }
1542
- }
1543
- }
1544
- ```
1545
-
1546
- #### Serializer options
1547
-
1548
- The `ResourceSerializer` can be initialized with some optional parameters:
1549
-
1550
- ##### `include`
1551
-
1552
- An array of resources. Nested resources can be specified with dot notation.
1553
-
1554
- *Purpose*: determines which objects will be side loaded with the source objects in an `included` section
1555
-
1556
- *Example*: ```include: ['comments','author','comments.tags','author.posts']```
1557
-
1558
- ##### `fields`
1559
-
1560
- A hash of resource types and arrays of fields for each resource type.
1561
-
1562
- *Purpose*: determines which fields are serialized for a resource type. This encompasses both attributes and
1563
- relationship ids in the links section for a resource. Fields are global for a resource type.
1564
-
1565
- *Example*: ```fields: { people: [:email, :comments], posts: [:title, :author], comments: [:body, :post]}```
1566
-
1567
- ```ruby
1568
- post = Post.find(1)
1569
- include_resources = ['comments','author','comments.tags','author.posts']
1570
-
1571
- JSONAPI::ResourceSerializer.new(PostResource, include: include_resources,
1572
- fields: {
1573
- people: [:email, :comments],
1574
- posts: [:title, :author],
1575
- tags: [:name],
1576
- comments: [:body, :post]
1577
- }
1578
- ).serialize_to_hash(PostResource.new(post, nil))
1579
- ```
1580
-
1581
- #### Formatting
1582
-
1583
- JR by default uses some simple rules to format (and unformat) an attribute for (de-)serialization. Strings and Integers are output to JSON
1584
- as is, and all other values have `.to_s` applied to them. This outputs something in all cases, but it is certainly not
1585
- correct for every situation.
1586
-
1587
- If you want to change the way an attribute is (de-)serialized you have a couple of ways. The simplest method is to create a
1588
- getter (and setter) method on the resource which overrides the attribute and apply the (un-)formatting there. For example:
1589
-
1590
- ```ruby
1591
- class PersonResource < JSONAPI::Resource
1592
- attributes :name, :email, :last_login_time
1593
-
1594
- # Setter example
1595
- def email=(new_email)
1596
- @model.email = new_email.downcase
1597
- end
1598
-
1599
- # Getter example
1600
- def last_login_time
1601
- @model.last_login_time.in_time_zone(@context[:current_user].time_zone).to_s
1602
- end
1603
- end
1604
- ```
1605
-
1606
- This is simple to implement for a one off situation, but not for example if you want to apply the same formatting rules
1607
- to all DateTime fields in your system. Another issue is the attribute on the resource will always return a formatted
1608
- response, whether you want it or not.
1609
-
1610
- ##### Value Formatters
1611
-
1612
- To overcome the above limitations JR uses Value Formatters. Value Formatters allow you to control the way values are
1613
- handled for an attribute. The `format` can be set per attribute as it is declared in the resource. For example:
1614
-
1615
- ```ruby
1616
- class PersonResource < JSONAPI::Resource
1617
- attributes :name, :email, :spoken_languages
1618
- attribute :last_login_time, format: :date_with_utc_timezone
1619
-
1620
- # Getter/Setter for spoken_languages ...
1621
- end
1622
- ```
1623
-
1624
- A Value formatter has a `format` and an `unformat` method. Here's the base ValueFormatter and DefaultValueFormatter for
1625
- reference:
1626
-
1627
- ```ruby
1628
- module JSONAPI
1629
- class ValueFormatter < Formatter
1630
- class << self
1631
- def format(raw_value)
1632
- super(raw_value)
1633
- end
1634
-
1635
- def unformat(value)
1636
- super(value)
1637
- end
1638
- ...
1639
- end
1640
- end
1641
- end
1642
-
1643
- class DefaultValueFormatter < JSONAPI::ValueFormatter
1644
- class << self
1645
- def format(raw_value)
1646
- case raw_value
1647
- when String, Integer
1648
- return raw_value
1649
- else
1650
- return raw_value.to_s
1651
- end
1652
- end
1653
- end
1654
- end
1655
- ```
1656
-
1657
- You can also create your own Value Formatter. Value Formatters must be named with the `format` name followed by
1658
- `ValueFormatter`, i.e. `DateWithUTCTimezoneValueFormatter` and derive from `JSONAPI::ValueFormatter`. It is
1659
- recommended that you create a directory for your formatters, called `formatters`.
1660
-
1661
- The `format` method is called by the `ResourceSerializer` as is serializing a resource. The format method takes the
1662
- `raw_value` parameter. `raw_value` is the value as read from the model.
1663
-
1664
- The `unformat` method is called when processing the request. Each incoming attribute (except `links`) are run through
1665
- the `unformat` method. The `unformat` method takes a `value`, which is the value as it comes in on the
1666
- request. This allows you process the incoming value to alter its state before it is stored in the model.
1667
-
1668
- ###### Use a Different Default Value Formatter
1669
-
1670
- Another way to handle formatting is to set a different default value formatter. This will affect all attributes that do
1671
- not have a `format` set. You can do this by overriding the `default_attribute_options` method for a resource (or a base
1672
- resource for a system wide change).
1673
-
1674
- ```ruby
1675
- def self.default_attribute_options
1676
- {format: :my_default}
1677
- end
1678
- ```
1679
-
1680
- and
1681
-
1682
- ```ruby
1683
- class MyDefaultValueFormatter < JSONAPI::ValueFormatter
1684
- class << self
1685
- def format(raw_value)
1686
- case raw_value
1687
- when String, Integer
1688
- return raw_value
1689
- when DateTime
1690
- return raw_value.in_time_zone('UTC').to_s
1691
- else
1692
- return raw_value.to_s
1693
- end
1694
- end
1695
- end
1696
- end
1697
- ```
1698
-
1699
- This way all DateTime values will be formatted to display in the UTC timezone.
1700
-
1701
- #### Key Format
1702
-
1703
- By default JR uses dasherized keys as per the
1704
- [JSON API naming recommendations](http://jsonapi.org/recommendations/#naming). This can be changed by specifying a
1705
- different key formatter.
1706
-
1707
- For example, to use camel cased keys with an initial lowercase character (JSON's default) create an initializer and add
1708
- the following:
1709
-
1710
- ```ruby
1711
- JSONAPI.configure do |config|
1712
- # built in key format options are :underscored_key, :camelized_key and :dasherized_key
1713
- config.json_key_format = :camelized_key
1714
- end
1715
- ```
1716
-
1717
- This will cause the serializer to use the `CamelizedKeyFormatter`. You can also create your own `KeyFormatter`, for
1718
- example:
1719
-
1720
- ```ruby
1721
- class UpperCamelizedKeyFormatter < JSONAPI::KeyFormatter
1722
- class << self
1723
- def format(key)
1724
- super.camelize(:upper)
1725
- end
1726
- end
1727
- end
1728
- ```
1729
-
1730
- You would specify this in `JSONAPI.configure` as `:upper_camelized`.
1731
-
1732
- ### Routing
1733
-
1734
- JR has a couple of helper methods available to assist you with setting up routes.
1735
-
1736
- ##### `jsonapi_resources`
1737
-
1738
- Like `resources` in `ActionDispatch`, `jsonapi_resources` provides resourceful routes mapping between HTTP verbs and URLs
1739
- and controller actions. This will also setup mappings for relationship URLs for a resource's relationships. For example:
1740
-
1741
- ```ruby
1742
- Rails.application.routes.draw do
1743
- jsonapi_resources :contacts
1744
- jsonapi_resources :phone_numbers
1745
- end
1746
- ```
1747
-
1748
- gives the following routes
1749
-
1750
- ```
1751
- Prefix Verb URI Pattern Controller#Action
1752
- contact_relationships_phone_numbers GET /contacts/:contact_id/relationships/phone-numbers(.:format) contacts#show_relationship {:relationship=>"phone_numbers"}
1753
- POST /contacts/:contact_id/relationships/phone-numbers(.:format) contacts#create_relationship {:relationship=>"phone_numbers"}
1754
- DELETE /contacts/:contact_id/relationships/phone-numbers/:keys(.:format) contacts#destroy_relationship {:relationship=>"phone_numbers"}
1755
- contact_phone_numbers GET /contacts/:contact_id/phone-numbers(.:format) phone_numbers#get_related_resources {:relationship=>"phone_numbers", :source=>"contacts"}
1756
- contacts GET /contacts(.:format) contacts#index
1757
- POST /contacts(.:format) contacts#create
1758
- contact GET /contacts/:id(.:format) contacts#show
1759
- PATCH /contacts/:id(.:format) contacts#update
1760
- PUT /contacts/:id(.:format) contacts#update
1761
- DELETE /contacts/:id(.:format) contacts#destroy
1762
- phone_number_relationships_contact GET /phone-numbers/:phone_number_id/relationships/contact(.:format) phone_numbers#show_relationship {:relationship=>"contact"}
1763
- PUT|PATCH /phone-numbers/:phone_number_id/relationships/contact(.:format) phone_numbers#update_relationship {:relationship=>"contact"}
1764
- DELETE /phone-numbers/:phone_number_id/relationships/contact(.:format) phone_numbers#destroy_relationship {:relationship=>"contact"}
1765
- phone_number_contact GET /phone-numbers/:phone_number_id/contact(.:format) contacts#get_related_resource {:relationship=>"contact", :source=>"phone_numbers"}
1766
- phone_numbers GET /phone-numbers(.:format) phone_numbers#index
1767
- POST /phone-numbers(.:format) phone_numbers#create
1768
- phone_number GET /phone-numbers/:id(.:format) phone_numbers#show
1769
- PATCH /phone-numbers/:id(.:format) phone_numbers#update
1770
- PUT /phone-numbers/:id(.:format) phone_numbers#update
1771
- DELETE /phone-numbers/:id(.:format) phone_numbers#destroy
1772
- ```
1773
-
1774
- ##### `jsonapi_resource`
1775
-
1776
- Like `jsonapi_resources`, but for resources you lookup without an id.
1777
-
1778
- #### Nested Routes
1779
-
1780
- By default nested routes are created for getting related resources and manipulating relationships. You can control the
1781
- nested routes by passing a block into `jsonapi_resources` or `jsonapi_resource`. An empty block will not create
1782
- any nested routes. For example:
1783
-
1784
- ```ruby
1785
- Rails.application.routes.draw do
1786
- jsonapi_resources :contacts do
1787
- end
1788
- end
1789
- ```
1790
-
1791
- gives routes that are only related to the primary resource, and none for its relationships:
1792
-
1793
- ```
1794
- Prefix Verb URI Pattern Controller#Action
1795
- contacts GET /contacts(.:format) contacts#index
1796
- POST /contacts(.:format) contacts#create
1797
- contact GET /contacts/:id(.:format) contacts#show
1798
- PATCH /contacts/:id(.:format) contacts#update
1799
- PUT /contacts/:id(.:format) contacts#update
1800
- DELETE /contacts/:id(.:format) contacts#destroy
1801
- ```
1802
-
1803
- To manually add in the nested routes you can use the `jsonapi_links`, `jsonapi_related_resources` and
1804
- `jsonapi_related_resource` inside the block. Or, you can add the default set of nested routes using the
1805
- `jsonapi_relationships` method. For example:
1806
-
1807
- ```ruby
1808
- Rails.application.routes.draw do
1809
- jsonapi_resources :contacts do
1810
- jsonapi_relationships
1811
- end
1812
- end
1813
- ```
1814
-
1815
- ###### `jsonapi_links`
1816
-
1817
- You can add relationship routes in with `jsonapi_links`, for example:
1818
-
1819
- ```ruby
1820
- Rails.application.routes.draw do
1821
- jsonapi_resources :contacts do
1822
- jsonapi_links :phone_numbers
1823
- end
1824
- end
1825
- ```
1826
-
1827
- Gives the following routes:
1828
-
1829
- ```
1830
- contact_relationships_phone_numbers GET /contacts/:contact_id/relationships/phone-numbers(.:format) contacts#show_relationship {:relationship=>"phone_numbers"}
1831
- POST /contacts/:contact_id/relationships/phone-numbers(.:format) contacts#create_relationship {:relationship=>"phone_numbers"}
1832
- DELETE /contacts/:contact_id/relationships/phone-numbers/:keys(.:format) contacts#destroy_relationship {:relationship=>"phone_numbers"}
1833
- contacts GET /contacts(.:format) contacts#index
1834
- POST /contacts(.:format) contacts#create
1835
- contact GET /contacts/:id(.:format) contacts#show
1836
- PATCH /contacts/:id(.:format) contacts#update
1837
- PUT /contacts/:id(.:format) contacts#update
1838
- DELETE /contacts/:id(.:format) contacts#destroy
1839
-
1840
- ```
1841
-
1842
- The new routes allow you to show, create and destroy the relationships between resources.
1843
-
1844
- ###### `jsonapi_related_resources`
1845
-
1846
- Creates a nested route to GET the related has_many resources. For example:
1847
-
1848
- ```ruby
1849
- Rails.application.routes.draw do
1850
- jsonapi_resources :contacts do
1851
- jsonapi_related_resources :phone_numbers
1852
- end
1853
- end
1854
-
1855
- ```
1856
-
1857
- gives the following routes:
1858
-
1859
- ```
1860
- Prefix Verb URI Pattern Controller#Action
1861
- contact_phone_numbers GET /contacts/:contact_id/phone-numbers(.:format) phone_numbers#get_related_resources {:relationship=>"phone_numbers", :source=>"contacts"}
1862
- contacts GET /contacts(.:format) contacts#index
1863
- POST /contacts(.:format) contacts#create
1864
- contact GET /contacts/:id(.:format) contacts#show
1865
- PATCH /contacts/:id(.:format) contacts#update
1866
- PUT /contacts/:id(.:format) contacts#update
1867
- DELETE /contacts/:id(.:format) contacts#destroy
1868
-
1869
- ```
1870
-
1871
- A single additional route was created to allow you GET the phone numbers through the contact.
1872
-
1873
- ###### `jsonapi_related_resource`
1874
-
1875
- Like `jsonapi_related_resources`, but for has_one related resources.
1876
-
1877
- ```ruby
1878
- Rails.application.routes.draw do
1879
- jsonapi_resources :phone_numbers do
1880
- jsonapi_related_resource :contact
1881
- end
1882
- end
1883
- ```
1884
-
1885
- gives the following routes:
1886
-
1887
- ```
1888
- Prefix Verb URI Pattern Controller#Action
1889
- phone_number_contact GET /phone-numbers/:phone_number_id/contact(.:format) contacts#get_related_resource {:relationship=>"contact", :source=>"phone_numbers"}
1890
- phone_numbers GET /phone-numbers(.:format) phone_numbers#index
1891
- POST /phone-numbers(.:format) phone_numbers#create
1892
- phone_number GET /phone-numbers/:id(.:format) phone_numbers#show
1893
- PATCH /phone-numbers/:id(.:format) phone_numbers#update
1894
- PUT /phone-numbers/:id(.:format) phone_numbers#update
1895
- DELETE /phone-numbers/:id(.:format) phone_numbers#destroy
1896
-
1897
- ```
1898
-
1899
- ### Authorization
1900
-
1901
- Currently `json-api-resources` doesn't come with built-in primitives for authorization. However multiple users of the framework have come up with different approaches, check out:
1902
-
1903
- - [jsonapi-authorization](https://github.com/venuu/jsonapi-authorization)
1904
- - [pundit-resources](https://github.com/togglepro/pundit-resources)
1905
-
1906
- Refer to the comments/discussion [here](https://github.com/cerebris/jsonapi-resources/issues/16#issuecomment-222438975) for the differences between approaches
1907
-
1908
- ## Configuration
1909
-
1910
- JR has a few configuration options. Some have already been mentioned above. To set configuration options create an
1911
- initializer and add the options you wish to set. All options have defaults, so you only need to set the options that
1912
- are different. The default options are shown below.
1913
-
1914
- If using custom classes (such as a CustomPaginator), be sure to require them at the top of the initializer before usage.
1915
-
1916
- ```ruby
1917
- JSONAPI.configure do |config|
1918
- #:underscored_key, :camelized_key, :dasherized_key, or custom
1919
- config.json_key_format = :dasherized_key
1920
-
1921
- #:underscored_route, :camelized_route, :dasherized_route, or custom
1922
- config.route_format = :dasherized_route
1923
-
1924
- # Default Processor, used if a resource specific one is not defined.
1925
- # Must be a class
1926
- config.default_processor_klass = JSONAPI::Processor
1927
-
1928
- #:integer, :uuid, :string, or custom (provide a proc)
1929
- config.resource_key_type = :integer
1930
-
1931
- # optional request features
1932
- config.allow_include = true
1933
- config.allow_sort = true
1934
- config.allow_filter = true
1935
-
1936
- # How to handle unsupported attributes and relationships which are provided in the request
1937
- # true => raises an error
1938
- # false => allows the request to continue. A warning is included in the response meta data indicating
1939
- # the fields which were ignored. This is useful for client libraries which send extra parameters.
1940
- config.raise_if_parameters_not_allowed = true
1941
-
1942
- # :none, :offset, :paged, or a custom paginator name
1943
- config.default_paginator = :none
1944
-
1945
- # Output pagination links at top level
1946
- config.top_level_links_include_pagination = true
1947
-
1948
- config.default_page_size = 10
1949
- config.maximum_page_size = 20
1950
-
1951
- # Output the record count in top level meta data for find operations
1952
- config.top_level_meta_include_record_count = false
1953
- config.top_level_meta_record_count_key = :record_count
1954
-
1955
- # For :paged paginators, the following are also available
1956
- config.top_level_meta_include_page_count = false
1957
- config.top_level_meta_page_count_key = :page_count
1958
-
1959
- config.use_text_errors = false
1960
-
1961
- # List of classes that should not be rescued by the operations processor.
1962
- # For example, if you use Pundit for authorization, you might
1963
- # raise a Pundit::NotAuthorizedError at some point during operations
1964
- # processing. If you want to use Rails' `rescue_from` macro to
1965
- # catch this error and render a 403 status code, you should add
1966
- # the `Pundit::NotAuthorizedError` to the `exception_class_whitelist`.
1967
- # Subclasses of the whitelisted classes will also be whitelisted.
1968
- config.exception_class_whitelist = []
1969
-
1970
- # Resource Linkage
1971
- # Controls the serialization of resource linkage for non compound documents
1972
- # NOTE: always_include_to_many_linkage_data is not currently implemented
1973
- config.always_include_to_one_linkage_data = false
1974
-
1975
- # Relationship reflection invokes the related resource when updates
1976
- # are made to a has_many relationship. By default relationship_reflection
1977
- # is turned off because it imposes a small performance penalty.
1978
- config.use_relationship_reflection = false
1979
-
1980
- # Allows transactions for creating and updating records
1981
- # Set this to false if your backend does not support transactions (e.g. Mongodb)
1982
- config.allow_transactions = true
1983
-
1984
- # Formatter Caching
1985
- # Set to false to disable caching of string operations on keys and links.
1986
- config.cache_formatters = true
1987
- end
1988
- ```
41
+ **For further usage see the [v0.8 Guide](http://jsonapi-resources.com/v0.8/guide/)**
1989
42
 
1990
43
  ## Contributing
1991
44