Merge pull request #294 from nikz/add-links-option-to-relationship-serializer

Adds a :links option to the relationship macros
This commit is contained in:
Shishir Kakaraddi 2018-11-03 11:35:39 -07:00 committed by GitHub
commit 326f9784ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 9 deletions

View File

@ -245,15 +245,38 @@ class MovieSerializer
end
```
### Meta Per Resource
For every resource in the collection, you can include a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.
#### Links on a Relationship
You can specify [relationship links](http://jsonapi.org/format/#document-resource-object-relationships) by using the `links:` option on the serializer. Relationship links in JSON API are useful if you want to load a parent document and then load associated documents later due to size constraints (see [related resource links](http://jsonapi.org/format/#document-resource-object-related-resource-links))
```ruby
class MovieSerializer
include FastJsonapi::ObjectSerializer
has_many :actors, links: {
self: :url,
related: -> (object) {
"https://movies.com/#{object.id}/actors"
}
}
end
```
This will create a `self` reference for the relationship, and a `related` link for loading the actors relationship later. NB: This will not automatically disable loading the data in the relationship, you'll need to do that using the `lazy_load_data` option:
```ruby
has_many :actors, lazy_load_data: true, links: {
self: :url,
related: -> (object) {
"https://movies.com/#{object.id}/actors"
}
}
```
### Meta Per Resource
For every resource in the collection, you can include a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.
```ruby
meta do |movie|
{
years_since_release: Date.current.year - movie.year

View File

@ -257,7 +257,9 @@ module FastJsonapi
cached: options[:cached],
polymorphic: fetch_polymorphic_option(options),
conditional_proc: options[:if],
transform_method: @transform_method
transform_method: @transform_method,
links: options[:links],
lazy_load_data: options[:lazy_load_data]
)
end

View File

@ -1,6 +1,6 @@
module FastJsonapi
class Relationship
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc, :transform_method
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc, :transform_method, :links, :lazy_load_data
def initialize(
key:,
@ -14,7 +14,9 @@ module FastJsonapi
cached: false,
polymorphic:,
conditional_proc:,
transform_method:
transform_method:,
links:,
lazy_load_data: false
)
@key = key
@name = name
@ -28,14 +30,19 @@ module FastJsonapi
@polymorphic = polymorphic
@conditional_proc = conditional_proc
@transform_method = transform_method
@links = links || {}
@lazy_load_data = lazy_load_data
end
def serialize(record, serialization_params, output_hash)
if include_relationship?(record, serialization_params)
empty_case = relationship_type == :has_many ? [] : nil
output_hash[key] = {
data: ids_hash_from_record_and_relationship(record, serialization_params) || empty_case
}
output_hash[key] = {}
unless lazy_load_data
output_hash[key][:data] = ids_hash_from_record_and_relationship(record, serialization_params) || empty_case
end
add_links_hash(record, serialization_params, output_hash) if links.present?
end
end
@ -96,6 +103,12 @@ module FastJsonapi
record.public_send(id_method_name)
end
def add_links_hash(record, params, output_hash)
output_hash[key][:links] = links.each_with_object({}) do |(key, method), hash|
Link.new(key: key, method: method).serialize(record, params, hash)\
end
end
def run_key_transform(input)
if self.transform_method.present?
input.to_s.send(*self.transform_method).to_sym

View File

@ -0,0 +1,71 @@
require 'spec_helper'
describe FastJsonapi::ObjectSerializer do
include_context 'movie class'
context "params option" do
let(:hash) { serializer.serializable_hash }
context "generating links for a serializer relationship" do
let(:params) { { } }
let(:options_with_params) { { params: params } }
let(:relationship_url) { "http://movies.com/#{movie.id}/relationships/actors" }
let(:related_url) { "http://movies.com/movies/#{movie.name.parameterize}/actors/" }
before(:context) do
class MovieSerializer
has_many :actors, lazy_load_data: false, links: {
self: :actors_relationship_url,
related: -> (object, params = {}) {
"#{params.has_key?(:secure) ? "https" : "http"}://movies.com/movies/#{object.name.parameterize}/actors/"
}
}
end
end
context "with a single record" do
let(:serializer) { MovieSerializer.new(movie, options_with_params) }
let(:links) { hash[:data][:relationships][:actors][:links] }
it "handles relationship links that call a method" do
expect(links).to be_present
expect(links[:self]).to eq(relationship_url)
end
it "handles relationship links that call a proc" do
expect(links).to be_present
expect(links[:related]).to eq(related_url)
end
context "with serializer params" do
let(:params) { { secure: true } }
let(:secure_related_url) { related_url.gsub("http", "https") }
it "passes the params to the link serializer correctly" do
expect(links).to be_present
expect(links[:related]).to eq(secure_related_url)
end
end
end
end
context "lazy loading relationship data" do
before(:context) do
class LazyLoadingMovieSerializer < MovieSerializer
has_many :actors, lazy_load_data: true, links: {
related: :actors_relationship_url
}
end
end
let(:serializer) { LazyLoadingMovieSerializer.new(movie) }
let(:actor_hash) { hash[:data][:relationships][:actors] }
it "does not include the :data key" do
expect(actor_hash).to be_present
expect(actor_hash).not_to have_key(:data)
end
end
end
end

View File

@ -61,6 +61,10 @@ RSpec.shared_context 'movie class' do
def url
"http://movies.com/#{id}"
end
def actors_relationship_url
"#{url}/relationships/actors"
end
end
class Actor