Skip to content

Commit 89f007d

Browse files
committed
Adds a :links option to the relationship macros
This allows specifying a `:links` option to a has_many/has_one relationship, which means you can specify `self` or `related` links as per the JSON API spec (these are often useful for not loading all associated objects in a single payload)
1 parent 5ff3fa9 commit 89f007d

4 files changed

Lines changed: 73 additions & 6 deletions

File tree

lib/fast_jsonapi/object_serializer.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def add_relationship(relationship)
194194
self.relationships_to_serialize = {} if relationships_to_serialize.nil?
195195
self.cachable_relationships_to_serialize = {} if cachable_relationships_to_serialize.nil?
196196
self.uncachable_relationships_to_serialize = {} if uncachable_relationships_to_serialize.nil?
197-
197+
198198
if !relationship.cached
199199
self.uncachable_relationships_to_serialize[relationship.name] = relationship
200200
else
@@ -240,7 +240,8 @@ def create_relationship(base_key, relationship_type, options, block)
240240
relationship_type: relationship_type,
241241
cached: options[:cached],
242242
polymorphic: fetch_polymorphic_option(options),
243-
conditional_proc: options[:if]
243+
conditional_proc: options[:if],
244+
links: options[:links]
244245
)
245246
end
246247

lib/fast_jsonapi/relationship.rb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module FastJsonapi
22
class Relationship
3-
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc
3+
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc, :links
44

55
def initialize(
66
key:,
@@ -13,7 +13,8 @@ def initialize(
1313
relationship_type:,
1414
cached: false,
1515
polymorphic:,
16-
conditional_proc:
16+
conditional_proc:,
17+
links:
1718
)
1819
@key = key
1920
@name = name
@@ -26,14 +27,16 @@ def initialize(
2627
@cached = cached
2728
@polymorphic = polymorphic
2829
@conditional_proc = conditional_proc
30+
@links = links || {}
2931
end
3032

31-
def serialize(record, serialization_params, output_hash)
33+
def serialize(record, serialization_params, output_hash, &block)
3234
if include_relationship?(record, serialization_params)
3335
empty_case = relationship_type == :has_many ? [] : nil
3436
output_hash[key] = {
35-
data: ids_hash_from_record_and_relationship(record, serialization_params) || empty_case
37+
data: ids_hash_from_record_and_relationship(record, serialization_params) || empty_case,
3638
}
39+
add_links_hash(record, serialization_params, output_hash) if links.present?
3740
end
3841
end
3942

@@ -95,5 +98,11 @@ def fetch_id(record, params)
9598

9699
record.public_send(id_method_name)
97100
end
101+
102+
def add_links_hash(record, params, output_hash)
103+
output_hash[key][:links] = links.each_with_object({}) do |(key, method), hash|
104+
Link.new(key: key, method: method).serialize(record, params, hash)
105+
end
106+
end
98107
end
99108
end
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
require 'spec_helper'
2+
3+
describe FastJsonapi::ObjectSerializer do
4+
include_context 'movie class'
5+
6+
context "params option" do
7+
let(:hash) { serializer.serializable_hash }
8+
9+
before(:context) do
10+
class MovieSerializer
11+
has_many :actors, links: {
12+
self: :actors_relationship_url,
13+
related: -> (object, params = {}) {
14+
"#{params.has_key?(:secure) ? "https" : "http"}://movies.com/movies/#{object.name.parameterize}/actors/"
15+
}
16+
}
17+
end
18+
end
19+
20+
context "generating links for a serializer relationship" do
21+
let(:params) { { } }
22+
let(:options_with_params) { { params: params } }
23+
let(:relationship_url) { "http://movies.com/#{movie.id}/relationships/actors" }
24+
let(:related_url) { "http://movies.com/movies/#{movie.name.parameterize}/actors/" }
25+
26+
context "with a single record" do
27+
let(:serializer) { MovieSerializer.new(movie, options_with_params) }
28+
let(:links) { hash.dig(:data, :relationships, :actors, :links) }
29+
30+
it "handles relationship links that call a method" do
31+
expect(links).to be_present
32+
expect(links[:self]).to eq(relationship_url)
33+
end
34+
35+
it "handles relationship links that call a proc" do
36+
expect(links).to be_present
37+
expect(links[:related]).to eq(related_url)
38+
end
39+
40+
context "with serializer params" do
41+
let(:params) { { secure: true } }
42+
let(:secure_related_url) { related_url.gsub("http", "https") }
43+
44+
it "passes the params to the link serializer correctly" do
45+
expect(links).to be_present
46+
expect(links[:related]).to eq(secure_related_url)
47+
end
48+
end
49+
end
50+
51+
end
52+
end
53+
end

spec/shared/contexts/movie_context.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ def local_name(locale = :english)
6161
def url
6262
"http://movies.com/#{id}"
6363
end
64+
65+
def actors_relationship_url
66+
"#{url}/relationships/actors"
67+
end
6468
end
6569

6670
class Actor

0 commit comments

Comments
 (0)