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:
commit
326f9784ca
29
README.md
29
README.md
@ -245,15 +245,38 @@ class MovieSerializer
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### Meta Per Resource
|
#### Links on a Relationship
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
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
|
```ruby
|
||||||
class MovieSerializer
|
class MovieSerializer
|
||||||
include FastJsonapi::ObjectSerializer
|
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|
|
meta do |movie|
|
||||||
{
|
{
|
||||||
years_since_release: Date.current.year - movie.year
|
years_since_release: Date.current.year - movie.year
|
||||||
|
|||||||
@ -257,7 +257,9 @@ module FastJsonapi
|
|||||||
cached: options[:cached],
|
cached: options[:cached],
|
||||||
polymorphic: fetch_polymorphic_option(options),
|
polymorphic: fetch_polymorphic_option(options),
|
||||||
conditional_proc: options[:if],
|
conditional_proc: options[:if],
|
||||||
transform_method: @transform_method
|
transform_method: @transform_method,
|
||||||
|
links: options[:links],
|
||||||
|
lazy_load_data: options[:lazy_load_data]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
module FastJsonapi
|
module FastJsonapi
|
||||||
class Relationship
|
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(
|
def initialize(
|
||||||
key:,
|
key:,
|
||||||
@ -14,7 +14,9 @@ module FastJsonapi
|
|||||||
cached: false,
|
cached: false,
|
||||||
polymorphic:,
|
polymorphic:,
|
||||||
conditional_proc:,
|
conditional_proc:,
|
||||||
transform_method:
|
transform_method:,
|
||||||
|
links:,
|
||||||
|
lazy_load_data: false
|
||||||
)
|
)
|
||||||
@key = key
|
@key = key
|
||||||
@name = name
|
@name = name
|
||||||
@ -28,14 +30,19 @@ module FastJsonapi
|
|||||||
@polymorphic = polymorphic
|
@polymorphic = polymorphic
|
||||||
@conditional_proc = conditional_proc
|
@conditional_proc = conditional_proc
|
||||||
@transform_method = transform_method
|
@transform_method = transform_method
|
||||||
|
@links = links || {}
|
||||||
|
@lazy_load_data = lazy_load_data
|
||||||
end
|
end
|
||||||
|
|
||||||
def serialize(record, serialization_params, output_hash)
|
def serialize(record, serialization_params, output_hash)
|
||||||
if include_relationship?(record, serialization_params)
|
if include_relationship?(record, serialization_params)
|
||||||
empty_case = relationship_type == :has_many ? [] : nil
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -96,6 +103,12 @@ module FastJsonapi
|
|||||||
record.public_send(id_method_name)
|
record.public_send(id_method_name)
|
||||||
end
|
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)
|
def run_key_transform(input)
|
||||||
if self.transform_method.present?
|
if self.transform_method.present?
|
||||||
input.to_s.send(*self.transform_method).to_sym
|
input.to_s.send(*self.transform_method).to_sym
|
||||||
|
|||||||
71
spec/lib/object_serializer_relationship_links_spec.rb
Normal file
71
spec/lib/object_serializer_relationship_links_spec.rb
Normal 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
|
||||||
@ -61,6 +61,10 @@ RSpec.shared_context 'movie class' do
|
|||||||
def url
|
def url
|
||||||
"http://movies.com/#{id}"
|
"http://movies.com/#{id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def actors_relationship_url
|
||||||
|
"#{url}/relationships/actors"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Actor
|
class Actor
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user