Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ JSONAPI.render(data: resources,

This returns a JSON API compliant hash representing the described document.

#### Rendering a relationship
```ruby
JSONAPI.render(data: resource,
relationship: :posts,
include: include_string,
fields: fields_hash,
meta: meta_hash,
links: links_hash)
```

This returns a JSON API compliant hash representing the described document.

### Rendering errors

```ruby
Expand Down
52 changes: 42 additions & 10 deletions lib/jsonapi/renderer/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

module JSONAPI
class Renderer
# @private
class Document
def initialize(params = {})
@data = params.fetch(:data, :no_data)
Expand All @@ -13,7 +14,8 @@ def initialize(params = {})
@fields = _symbolize_fields(params[:fields] || {})
@jsonapi = params[:jsonapi]
@include = JSONAPI::IncludeDirective.new(params[:include] || {})
@cache = params[:cache]
@relationship = params[:relationship]
@cache = params[:cache]
end

def to_hash
Expand All @@ -23,18 +25,24 @@ def to_hash

private

# rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity
def document_hash
{}.tap do |hash|
if @data != :no_data
if @relationship
hash.merge!(relationship_hash)
elsif @data != :no_data
hash.merge!(data_hash)
elsif @errors.any?
hash.merge!(errors_hash)
end
hash[:links] = @links if @links.any?
hash[:meta] = @meta unless @meta.nil?
hash[:jsonapi] = @jsonapi unless @jsonapi.nil?
hash[:links] = @links if @links.any?
hash[:meta] = @meta unless @meta.nil?
hash[:jsonapi] = @jsonapi unless @jsonapi.nil?
end
end
# rubocop:enable Metrics/PerceivedComplexity, Metrics/MethodLength
# rubocop:enable Metrics/CyclomaticComplexity

def data_hash
primary, included =
Expand All @@ -45,20 +53,44 @@ def data_hash
end
end

def resources_processor
if @cache
CachedResourcesProcessor.new(@cache)
else
SimpleResourcesProcessor.new
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
def relationship_hash
rel_name = @relationship.to_sym
data = @data.jsonapi_related([rel_name])[rel_name]
included =
if @include.key?(rel_name)
resources_processor.process(data, @include[rel_name], @fields)
.flatten!
else
[]
end

res = @data.as_jsonapi(fields: [rel_name], include: [rel_name])
rel = res[:relationships][rel_name]
@links = rel[:links].merge!(@links)
@meta ||= rel[:meta]

{}.tap do |hash|
hash[:data] = rel[:data]
hash[:included] = included if included.any?
end
end
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize

def errors_hash
{}.tap do |hash|
hash[:errors] = @errors.flat_map(&:as_jsonapi)
end
end

def resources_processor
if @cache
CachedResourcesProcessor.new(@cache)
else
SimpleResourcesProcessor.new
end
end

def _symbolize_fields(fields)
fields.each_with_object({}) do |(k, v), h|
h[k.to_sym] = v.map(&:to_sym)
Expand Down
2 changes: 1 addition & 1 deletion lib/jsonapi/renderer/resources_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module JSONAPI
class Renderer
# @api private
# @private
class ResourcesProcessor
def process(resources, include, fields)
@resources = resources
Expand Down
100 changes: 100 additions & 0 deletions spec/renderer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -336,4 +336,104 @@ def as_jsonapi

expect(actual).to eq(expected)
end

context 'when rendering a relationship' do
it 'renders the linkage data only' do
actual = subject.render(data: @users[0], relationship: :posts)
expected = {
data: [{ type: 'posts', id: '2' }],
links: {
self: 'http://api.example.com/users/1/relationships/posts',
related: {
href: 'http://api.example.com/users/1/posts',
meta: {
do_not_use: true
}
}
},
meta: {
deleted_posts: 5
}
}

expect(actual).to eq(expected)
end

it 'renders supports include parameter' do
actual = subject.render(data: @users[0], relationship: :posts,
include: 'posts.author')
actual_included = actual.delete(:included)

expected = {
data: [{ type: 'posts', id: '2' }],
links: {
self: 'http://api.example.com/users/1/relationships/posts',
related: {
href: 'http://api.example.com/users/1/posts',
meta: {
do_not_use: true
}
}
},
meta: {
deleted_posts: 5
}
}
expected_included = [
{
type: 'users',
id: '1',
attributes: {
name: 'User 1',
address: '123 Example st.'
},
relationships: {
posts: {
links: {
self: 'http://api.example.com/users/1/relationships/posts',
related: {
href: 'http://api.example.com/users/1/posts',
meta: {
do_not_use: true
}
}
},
meta: {
deleted_posts: 5
}
}
},
links: {
self: 'http://api.example.com/users/1'
},
meta: {
user_meta: 'is_meta'
}
},
{
type: 'posts',
id: '2',
attributes: {
title: 'Post 2',
date: 'today'
},
relationships: {
author: {
data: { type: 'users', id: '1' },
links: {
self: 'http://api.example.com/posts/2/relationships/author',
related: 'http://api.example.com/posts/2/author'
},
meta: {
author_active: true
}
}
}
}
]

expect(actual).to eq(expected)
expect(actual_included).to match_array(expected_included)
end
end
end