jsonapi-serializers 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/README.md +39 -10
- data/lib/jsonapi-serializers/serializer.rb +25 -4
- data/lib/jsonapi-serializers/version.rb +1 -1
- data/spec/serializer_spec.rb +112 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a63fa6a767f6236335d147e099ca81b13e24d72
|
4
|
+
data.tar.gz: e8fe7e954d1b5a14d2903ff0d72923eb24d0abb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64417e501b68bb8e686eb362e8ea53c65afff161d5c9f0f881e08d15436c8f4b27fd06491ee5d479d74c99ce7ed5c78e72e001a82a18a019d56cbd7b99ef28e9
|
7
|
+
data.tar.gz: da8f7e8f9c78c18f5d6f6a2ae7b1399116a7876f40c0c71b4d59d36615be5ba88e7b5c978b3f536dbd61e071ea6fd103940044a3b3a3d89d08a66649f113ddf1
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -18,12 +18,13 @@ This library is up-to-date with the finalized v1 JSON API spec.
|
|
18
18
|
* [Custom attributes](#custom-attributes)
|
19
19
|
* [More customizations](#more-customizations)
|
20
20
|
* [Base URL](#base-url)
|
21
|
-
* [Root jsonapi object](#root-jsonapi-object)
|
22
21
|
* [Root metadata](#root-metadata)
|
23
22
|
* [Root links](#root-links)
|
24
23
|
* [Root errors](#root-errors)
|
24
|
+
* [Root jsonapi object](#root-jsonapi-object)
|
25
25
|
* [Explicit serializer discovery](#explicit-serializer-discovery)
|
26
26
|
* [Namespace serializers](#namespace-serializers)
|
27
|
+
* [Sparse fieldsets](#sparse-fieldsets)
|
27
28
|
* [Relationships](#relationships)
|
28
29
|
* [Compound documents and includes](#compound-documents-and-includes)
|
29
30
|
* [Relationship path handling](#relationship-path-handling)
|
@@ -318,14 +319,6 @@ JSONAPI::Serializer.serialize(post, base_url: 'http://example.com')
|
|
318
319
|
|
319
320
|
Note: if you override `self_link` in your serializer and leave out `base_url`, it will not be included.
|
320
321
|
|
321
|
-
### Root 'jsonapi' object
|
322
|
-
|
323
|
-
You can pass a `jsonapi` argument to specify a [top-level "jsonapi" key](http://jsonapi.org/format/#document-jsonapi-object) containing the version of JSON:API in use:
|
324
|
-
|
325
|
-
```ruby
|
326
|
-
JSONAPI::Serializer.serialize(post, jsonapi: {version: '1.0'})
|
327
|
-
```
|
328
|
-
|
329
322
|
### Root metadata
|
330
323
|
|
331
324
|
You can pass a `meta` argument to specify top-level metadata:
|
@@ -372,6 +365,14 @@ class Api::V1::ReposController < Api::V1::BaseController
|
|
372
365
|
end
|
373
366
|
```
|
374
367
|
|
368
|
+
### Root 'jsonapi' object
|
369
|
+
|
370
|
+
You can pass a `jsonapi` argument to specify a [top-level "jsonapi" key](http://jsonapi.org/format/#document-jsonapi-object) containing the version of JSON:API in use:
|
371
|
+
|
372
|
+
```ruby
|
373
|
+
JSONAPI::Serializer.serialize(post, jsonapi: {version: '1.0'})
|
374
|
+
```
|
375
|
+
|
375
376
|
### Explicit serializer discovery
|
376
377
|
|
377
378
|
By default, jsonapi-serializers assumes that the serializer class for `Namespace::User` is `Namespace::UserSerializer`. You can override this behavior on a per-object basis by implementing the `jsonapi_serializer_class_name` method.
|
@@ -416,6 +417,35 @@ JSONAPI::Serializer.serialize(post, namespace: Api::V2)
|
|
416
417
|
|
417
418
|
This option overrides the `jsonapi_serializer_class_name` method.
|
418
419
|
|
420
|
+
### Sparse fieldsets
|
421
|
+
|
422
|
+
The JSON:API spec allows to return only [specific fields](http://jsonapi.org/format/#fetching-sparse-fieldsets) from attributes and relationships.
|
423
|
+
|
424
|
+
For example, if you wanted to return only the `title` field and `author` relationship link for `posts`:
|
425
|
+
|
426
|
+
```ruby
|
427
|
+
fields =
|
428
|
+
JSONAPI::Serializer.serialize(post, fields: {posts: [:title]})
|
429
|
+
```
|
430
|
+
|
431
|
+
Sparse fieldsets also affect relationship links. In this case, only the `author` relationship link would be included:
|
432
|
+
|
433
|
+
``` ruby
|
434
|
+
JSONAPI::Serializer.serialize(post, fields: {posts: [:title, :author]})
|
435
|
+
```
|
436
|
+
|
437
|
+
Sparse fieldsets operate on a per-type basis, so they affect all resources in the response including in compound documents. For example, this will affect both the `posts` type in the primary data and the `users` type in the compound data:
|
438
|
+
|
439
|
+
``` ruby
|
440
|
+
JSONAPI::Serializer.serialize(
|
441
|
+
post,
|
442
|
+
fields: {posts: ['title', 'author'], users: ['name']},
|
443
|
+
include: 'author',
|
444
|
+
)
|
445
|
+
```
|
446
|
+
|
447
|
+
Sparse fieldsets support comma-separated strings (`fields: {posts: 'title,author'}`, arrays of strings (`fields: {posts: ['title', 'author']}`), single symbols (`fields: {posts: :title}`), and arrays of symbols (`fields: {posts: [:title, :author]}`).
|
448
|
+
|
419
449
|
## Relationships
|
420
450
|
|
421
451
|
You can easily specify relationships with the `has_one` and `has_many` directives.
|
@@ -783,7 +813,6 @@ See [Releases](https://github.com/fotinakis/jsonapi-serializers/releases).
|
|
783
813
|
|
784
814
|
## Unfinished business
|
785
815
|
|
786
|
-
* Support for the `fields` spec is planned, would love a PR contribution for this.
|
787
816
|
* Support for pagination/sorting is unlikely to be supported because it would likely involve coupling to ActiveRecord, but please open an issue if you have ideas of how to support this generically.
|
788
817
|
|
789
818
|
## Contributing
|
@@ -33,6 +33,7 @@ module JSONAPI
|
|
33
33
|
@base_url = options[:base_url]
|
34
34
|
|
35
35
|
# Internal serializer options, not exposed through attr_accessor. No touchie.
|
36
|
+
@_fields = options[:fields] || {}
|
36
37
|
@_include_linkages = options[:include_linkages] || []
|
37
38
|
end
|
38
39
|
|
@@ -165,7 +166,7 @@ module JSONAPI
|
|
165
166
|
return {} if self.class.attributes_map.nil?
|
166
167
|
attributes = {}
|
167
168
|
self.class.attributes_map.each do |attribute_name, attr_data|
|
168
|
-
next if !should_include_attr?(
|
169
|
+
next if !should_include_attr?(attribute_name, attr_data)
|
169
170
|
value = evaluate_attr_or_block(attribute_name, attr_data[:attr_or_block])
|
170
171
|
attributes[format_name(attribute_name)] = value
|
171
172
|
end
|
@@ -176,7 +177,7 @@ module JSONAPI
|
|
176
177
|
return {} if self.class.to_one_associations.nil?
|
177
178
|
data = {}
|
178
179
|
self.class.to_one_associations.each do |attribute_name, attr_data|
|
179
|
-
next if !should_include_attr?(
|
180
|
+
next if !should_include_attr?(attribute_name, attr_data)
|
180
181
|
data[attribute_name] = attr_data
|
181
182
|
end
|
182
183
|
data
|
@@ -190,7 +191,7 @@ module JSONAPI
|
|
190
191
|
return {} if self.class.to_many_associations.nil?
|
191
192
|
data = {}
|
192
193
|
self.class.to_many_associations.each do |attribute_name, attr_data|
|
193
|
-
next if !should_include_attr?(
|
194
|
+
next if !should_include_attr?(attribute_name, attr_data)
|
194
195
|
data[attribute_name] = attr_data
|
195
196
|
end
|
196
197
|
data
|
@@ -200,11 +201,14 @@ module JSONAPI
|
|
200
201
|
evaluate_attr_or_block(attribute_name, attr_data[:attr_or_block])
|
201
202
|
end
|
202
203
|
|
203
|
-
def should_include_attr?(
|
204
|
+
def should_include_attr?(attribute_name, attr_data)
|
204
205
|
# Allow "if: :show_title?" and "unless: :hide_title?" attribute options.
|
206
|
+
if_method_name = attr_data[:options][:if]
|
207
|
+
unless_method_name = attr_data[:options][:unless]
|
205
208
|
show_attr = true
|
206
209
|
show_attr &&= send(if_method_name) if if_method_name
|
207
210
|
show_attr &&= !send(unless_method_name) if unless_method_name
|
211
|
+
show_attr &&= @_fields[type.to_s].include?(attribute_name) if @_fields[type.to_s]
|
208
212
|
show_attr
|
209
213
|
end
|
210
214
|
protected :should_include_attr?
|
@@ -252,6 +256,7 @@ module JSONAPI
|
|
252
256
|
options[:jsonapi] = options.delete('jsonapi') || options[:jsonapi]
|
253
257
|
options[:meta] = options.delete('meta') || options[:meta]
|
254
258
|
options[:links] = options.delete('links') || options[:links]
|
259
|
+
options[:fields] = options.delete('fields') || options[:fields] || {}
|
255
260
|
|
256
261
|
# Deprecated: use serialize_errors method instead
|
257
262
|
options[:errors] = options.delete('errors') || options[:errors]
|
@@ -260,12 +265,27 @@ module JSONAPI
|
|
260
265
|
includes = options[:include]
|
261
266
|
includes = (includes.is_a?(String) ? includes.split(',') : includes).uniq if includes
|
262
267
|
|
268
|
+
# Transforms input so that the comma-separated fields are separate symbols in array
|
269
|
+
# and keys are stringified
|
270
|
+
# Example:
|
271
|
+
# {posts: 'title,author,long_comments'} => {'posts' => [:title, :author, :long_comments]}
|
272
|
+
# {posts: ['title', 'author', 'long_comments'} => {'posts' => [:title, :author, :long_comments]}
|
273
|
+
#
|
274
|
+
fields = {}
|
275
|
+
# Normalize fields to accept a comma-separated string or an array of strings.
|
276
|
+
options[:fields].map do |type, whitelisted_fields|
|
277
|
+
whitelisted_fields = [whitelisted_fields] if whitelisted_fields.is_a?(Symbol)
|
278
|
+
whitelisted_fields = whitelisted_fields.split(',') if whitelisted_fields.is_a?(String)
|
279
|
+
fields[type.to_s] = whitelisted_fields.map(&:to_sym)
|
280
|
+
end
|
281
|
+
|
263
282
|
# An internal-only structure that is passed through serializers as they are created.
|
264
283
|
passthrough_options = {
|
265
284
|
context: options[:context],
|
266
285
|
serializer: options[:serializer],
|
267
286
|
namespace: options[:namespace],
|
268
287
|
include: includes,
|
288
|
+
fields: fields,
|
269
289
|
base_url: options[:base_url]
|
270
290
|
}
|
271
291
|
|
@@ -328,6 +348,7 @@ module JSONAPI
|
|
328
348
|
included_passthrough_options = {}
|
329
349
|
included_passthrough_options[:base_url] = passthrough_options[:base_url]
|
330
350
|
included_passthrough_options[:context] = passthrough_options[:context]
|
351
|
+
included_passthrough_options[:fields] = passthrough_options[:fields]
|
331
352
|
included_passthrough_options[:serializer] = find_serializer_class(data[:object], options)
|
332
353
|
included_passthrough_options[:namespace] = passthrough_options[:namespace]
|
333
354
|
included_passthrough_options[:include_linkages] = data[:include_linkages]
|
data/spec/serializer_spec.rb
CHANGED
@@ -441,12 +441,12 @@ describe JSONAPI::Serializer do
|
|
441
441
|
it 'can include a top level errors node' do
|
442
442
|
errors = [
|
443
443
|
{
|
444
|
-
'source' => {
|
444
|
+
'source' => {'pointer' => '/data/attributes/first-name'},
|
445
445
|
'title' => 'Invalid Attribute',
|
446
446
|
'detail' => 'First name must contain at least three characters.'
|
447
447
|
},
|
448
448
|
{
|
449
|
-
'source' => {
|
449
|
+
'source' => {'pointer' => '/data/attributes/first-name'},
|
450
450
|
'title' => 'Invalid Attribute',
|
451
451
|
'detail' => 'First name must contain an emoji.'
|
452
452
|
}
|
@@ -477,15 +477,15 @@ describe JSONAPI::Serializer do
|
|
477
477
|
user = DummyUser.new
|
478
478
|
jsonapi_errors = [
|
479
479
|
{
|
480
|
-
'source' => {
|
480
|
+
'source' => {'pointer' => '/data/attributes/email'},
|
481
481
|
'detail' => 'Email is invalid'
|
482
482
|
},
|
483
483
|
{
|
484
|
-
'source' => {
|
484
|
+
'source' => {'pointer' => '/data/attributes/email'},
|
485
485
|
'detail' => "Email can't be blank"
|
486
486
|
},
|
487
487
|
{
|
488
|
-
'source' => {
|
488
|
+
'source' => {'pointer' => '/data/attributes/first-name'},
|
489
489
|
'detail' => "First name can't be blank"
|
490
490
|
}
|
491
491
|
]
|
@@ -854,6 +854,113 @@ describe JSONAPI::Serializer do
|
|
854
854
|
})
|
855
855
|
end
|
856
856
|
end
|
857
|
+
|
858
|
+
context 'sparse fieldsets' do
|
859
|
+
it 'allows to limit fields(attributes) for serialized resource' do
|
860
|
+
first_user = create(:user)
|
861
|
+
second_user = create(:user)
|
862
|
+
first_comment = create(:long_comment, user: first_user)
|
863
|
+
second_comment = create(:long_comment, user: second_user)
|
864
|
+
long_comments = [first_comment, second_comment]
|
865
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
866
|
+
|
867
|
+
serialized_data = JSONAPI::Serializer.serialize(post, fields: {posts: :title})
|
868
|
+
expect(serialized_data).to eq ({
|
869
|
+
'data' => {
|
870
|
+
'type' => 'posts',
|
871
|
+
'id' => post.id.to_s,
|
872
|
+
'attributes' => {
|
873
|
+
'title' => post.title,
|
874
|
+
},
|
875
|
+
'links' => {
|
876
|
+
'self' => '/posts/1'
|
877
|
+
}
|
878
|
+
}
|
879
|
+
})
|
880
|
+
end
|
881
|
+
it 'allows to limit fields(relationships) for serialized resource' do
|
882
|
+
first_user = create(:user)
|
883
|
+
second_user = create(:user)
|
884
|
+
first_comment = create(:long_comment, user: first_user)
|
885
|
+
second_comment = create(:long_comment, user: second_user)
|
886
|
+
long_comments = [first_comment, second_comment]
|
887
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
888
|
+
|
889
|
+
fields = {posts: 'title,author,long_comments'}
|
890
|
+
serialized_data = JSONAPI::Serializer.serialize(post, fields: fields)
|
891
|
+
expect(serialized_data['data']['relationships']).to eq ({
|
892
|
+
'author' => {
|
893
|
+
'links' => {
|
894
|
+
'self' => '/posts/1/relationships/author',
|
895
|
+
'related' => '/posts/1/author'
|
896
|
+
}
|
897
|
+
},
|
898
|
+
'long-comments' => {
|
899
|
+
'links' => {
|
900
|
+
'self' => '/posts/1/relationships/long-comments',
|
901
|
+
'related' => '/posts/1/long-comments'
|
902
|
+
}
|
903
|
+
}
|
904
|
+
})
|
905
|
+
end
|
906
|
+
it "allows also to pass specific fields as array instead of comma-separates values" do
|
907
|
+
first_user = create(:user)
|
908
|
+
second_user = create(:user)
|
909
|
+
first_comment = create(:long_comment, user: first_user)
|
910
|
+
second_comment = create(:long_comment, user: second_user)
|
911
|
+
long_comments = [first_comment, second_comment]
|
912
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
913
|
+
|
914
|
+
serialized_data = JSONAPI::Serializer.serialize(post, fields: {posts: ['title', 'author']})
|
915
|
+
expect(serialized_data['data']['attributes']).to eq ({
|
916
|
+
'title' => post.title
|
917
|
+
})
|
918
|
+
expect(serialized_data['data']['relationships']).to eq ({
|
919
|
+
'author' => {
|
920
|
+
'links' => {
|
921
|
+
'self' => '/posts/1/relationships/author',
|
922
|
+
'related' => '/posts/1/author'
|
923
|
+
}
|
924
|
+
}
|
925
|
+
})
|
926
|
+
end
|
927
|
+
it 'allows to limit fields(attributes and relationships) for included resources' do
|
928
|
+
first_user = create(:user)
|
929
|
+
second_user = create(:user)
|
930
|
+
first_comment = create(:long_comment, user: first_user)
|
931
|
+
second_comment = create(:long_comment, user: second_user)
|
932
|
+
long_comments = [first_comment, second_comment]
|
933
|
+
post = create(:post, :with_author, long_comments: long_comments)
|
934
|
+
|
935
|
+
expected_primary_data = serialize_primary(post, {
|
936
|
+
serializer: MyApp::PostSerializer,
|
937
|
+
include_linkages: ['author'],
|
938
|
+
fields: {'posts' => [:title, :author] }
|
939
|
+
})
|
940
|
+
|
941
|
+
fields = {posts: 'title,author', users: ''}
|
942
|
+
serialized_data = JSONAPI::Serializer.serialize(post, fields: fields, include: 'author')
|
943
|
+
expect(serialized_data).to eq ({
|
944
|
+
'data' => expected_primary_data,
|
945
|
+
'included' => [
|
946
|
+
serialize_primary(
|
947
|
+
post.author,
|
948
|
+
serializer: MyAppOtherNamespace::UserSerializer,
|
949
|
+
fields: {'users' => []},
|
950
|
+
)
|
951
|
+
]
|
952
|
+
})
|
953
|
+
|
954
|
+
fields = {posts: 'title,author'}
|
955
|
+
serialized_data = JSONAPI::Serializer.serialize(post, fields: fields, include: 'author')
|
956
|
+
expect(serialized_data).to eq ({
|
957
|
+
'data' => expected_primary_data,
|
958
|
+
'included' => [
|
959
|
+
serialize_primary(post.author, serializer: MyAppOtherNamespace::UserSerializer)
|
960
|
+
]
|
961
|
+
})
|
962
|
+
end
|
963
|
+
end
|
857
964
|
end
|
858
965
|
|
859
966
|
describe 'serialize (class method)' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-serializers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Fotinakis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|