Skip to content

Commit 5ff3fa9

Browse files
authored
Merge pull request #273 from Netflix/release-1.3
Release 1.3
2 parents 81375cf + 115a01a commit 5ff3fa9

17 files changed

+647
-164
lines changed

README.md

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ Fast JSON API serialized 250 records in 3.01 ms
3030
* [Collection Serialization](#collection-serialization)
3131
* [Caching](#caching)
3232
* [Params](#params)
33+
* [Conditional Attributes](#conditional-attributes)
34+
* [Conditional Relationships](#conditional-relationships)
35+
* [Sparse Fieldsets](#sparse-fieldsets)
3336
* [Contributing](#contributing)
3437

3538

@@ -205,6 +208,18 @@ class MovieSerializer
205208
end
206209
```
207210

211+
Attributes can also use a different name by passing the original method or accessor with a proc shortcut:
212+
213+
```ruby
214+
class MovieSerializer
215+
include FastJsonapi::ObjectSerializer
216+
217+
attributes :name
218+
219+
attribute :released_in_year, &:year
220+
end
221+
```
222+
208223
### Links Per Object
209224
Links are defined in FastJsonapi using the `link` method. By default, link are read directly from the model property of the same name.In this example, `public_url` is expected to be a property of the object being serialized.
210225

@@ -259,6 +274,26 @@ hash = MovieSerializer.new([movie, movie], options).serializable_hash
259274
json_string = MovieSerializer.new([movie, movie], options).serialized_json
260275
```
261276

277+
#### Control Over Collection Serialization
278+
279+
You can use `is_collection` option to have better control over collection serialization.
280+
281+
If this option is not provided or `nil` autedetect logic is used to try understand
282+
if provided resource is a single object or collection.
283+
284+
Autodetect logic is compatible with most DB toolkits (ActiveRecord, Sequel, etc.) but
285+
**cannot** guarantee that single vs collection will be always detected properly.
286+
287+
```ruby
288+
options[:is_collection]
289+
```
290+
291+
was introduced to be able to have precise control this behavior
292+
293+
- `nil` or not provided: will try to autodetect single vs collection (please, see notes above)
294+
- `true` will always treat input resource as *collection*
295+
- `false` will always treat input resource as *single object*
296+
262297
### Caching
263298
Requires a `cache_key` method be defined on model:
264299

@@ -284,7 +319,6 @@ block you opt-in to using params by adding it as a block parameter.
284319

285320
```ruby
286321
class MovieSerializer
287-
class MovieSerializer
288322
include FastJsonapi::ObjectSerializer
289323

290324
attributes :name, :year
@@ -308,6 +342,68 @@ serializer.serializable_hash
308342
Custom attributes and relationships that only receive the resource are still possible by defining
309343
the block to only receive one argument.
310344

345+
### Conditional Attributes
346+
347+
Conditional attributes can be defined by passing a Proc to the `if` key on the `attribute` method. Return `true` if the attribute should be serialized, and `false` if not. The record and any params passed to the serializer are available inside the Proc as the first and second parameters, respectively.
348+
349+
```ruby
350+
class MovieSerializer
351+
include FastJsonapi::ObjectSerializer
352+
353+
attributes :name, :year
354+
attribute :release_year, if: Proc.new do |record|
355+
# Release year will only be serialized if it's greater than 1990
356+
record.release_year > 1990
357+
end
358+
359+
attribute :director, if: Proc.new do |record, params|
360+
# The director will be serialized only if the :admin key of params is true
361+
params && params[:admin] == true
362+
end
363+
end
364+
365+
# ...
366+
current_user = User.find(cookies[:current_user_id])
367+
serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? }})
368+
serializer.serializable_hash
369+
```
370+
371+
### Conditional Relationships
372+
373+
Conditional relationships can be defined by passing a Proc to the `if` key. Return `true` if the relationship should be serialized, and `false` if not. The record and any params passed to the serializer are available inside the Proc as the first and second parameters, respectively.
374+
375+
```ruby
376+
class MovieSerializer
377+
include FastJsonapi::ObjectSerializer
378+
379+
# Actors will only be serialized if the record has any associated actors
380+
has_many :actors, if: Proc.new { |record| record.actors.any? }
381+
382+
# Owner will only be serialized if the :admin key of params is true
383+
belongs_to :owner, if: Proc.new { |record, params| params && params[:admin] == true }
384+
end
385+
386+
# ...
387+
current_user = User.find(cookies[:current_user_id])
388+
serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? }})
389+
serializer.serializable_hash
390+
```
391+
392+
### Sparse Fieldsets
393+
394+
Attributes and relationships can be selectively returned per record type by using the `fields` option.
395+
396+
```ruby
397+
class MovieSerializer
398+
include FastJsonapi::ObjectSerializer
399+
400+
attributes :name, :year
401+
end
402+
403+
serializer = MovieSerializer.new(movie, { fields: { movie: [:name] } })
404+
serializer.serializable_hash
405+
```
406+
311407
### Customizable Options
312408

313409
Option | Purpose | Example

lib/extensions/has_one.rb

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
# frozen_string_literal: true
22

3-
if defined?(::ActiveRecord)
4-
::ActiveRecord::Associations::Builder::HasOne.class_eval do
5-
# Based on
6-
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/collection_association.rb#L50
7-
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/singular_association.rb#L11
8-
def self.define_accessors(mixin, reflection)
9-
super
10-
name = reflection.name
11-
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
12-
def #{name}_id
13-
# if an attribute is already defined with this methods name we should just use it
14-
return read_attribute(__method__) if has_attribute?(__method__)
15-
association(:#{name}).reader.try(:id)
16-
end
17-
CODE
18-
end
3+
::ActiveRecord::Associations::Builder::HasOne.class_eval do
4+
# Based on
5+
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/collection_association.rb#L50
6+
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/singular_association.rb#L11
7+
def self.define_accessors(mixin, reflection)
8+
super
9+
name = reflection.name
10+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
11+
def #{name}_id
12+
# if an attribute is already defined with this methods name we should just use it
13+
return read_attribute(__method__) if has_attribute?(__method__)
14+
association(:#{name}).reader.try(:id)
15+
end
16+
CODE
1917
end
2018
end

lib/fast_jsonapi.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,9 @@
22

33
module FastJsonapi
44
require 'fast_jsonapi/object_serializer'
5-
require 'extensions/has_one'
5+
if defined?(::Rails)
6+
require 'fast_jsonapi/railtie'
7+
elsif defined?(::ActiveRecord)
8+
require 'extensions/has_one'
9+
end
610
end

lib/fast_jsonapi/attribute.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module FastJsonapi
2+
class Attribute
3+
attr_reader :key, :method, :conditional_proc
4+
5+
def initialize(key:, method:, options: {})
6+
@key = key
7+
@method = method
8+
@conditional_proc = options[:if]
9+
end
10+
11+
def serialize(record, serialization_params, output_hash)
12+
if include_attribute?(record, serialization_params)
13+
output_hash[key] = if method.is_a?(Proc)
14+
method.arity.abs == 1 ? method.call(record) : method.call(record, serialization_params)
15+
else
16+
record.public_send(method)
17+
end
18+
end
19+
end
20+
21+
def include_attribute?(record, serialization_params)
22+
if conditional_proc.present?
23+
conditional_proc.call(record, serialization_params)
24+
else
25+
true
26+
end
27+
end
28+
end
29+
end

lib/fast_jsonapi/link.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module FastJsonapi
2+
class Link
3+
attr_reader :key, :method
4+
5+
def initialize(key:, method:)
6+
@key = key
7+
@method = method
8+
end
9+
10+
def serialize(record, serialization_params, output_hash)
11+
output_hash[key] = if method.is_a?(Proc)
12+
method.arity == 1 ? method.call(record) : method.call(record, serialization_params)
13+
else
14+
record.public_send(method)
15+
end
16+
end
17+
end
18+
end

0 commit comments

Comments
 (0)