Skip to content

Commit c7b2916

Browse files
committed
Merge pull request #1547 from bf4/jsonapi_renderer
Basic Jsonapi Renderer registration
2 parents 5af8536 + d364c4f commit c7b2916

File tree

2 files changed

+74
-9
lines changed

2 files changed

+74
-9
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Based on discussion in https://github.com/rails/rails/pull/23712#issuecomment-184977238,
2+
# the JSON API media type will have its own format/renderer.
3+
#
4+
# > We recommend the media type be registered on its own as jsonapi
5+
# when a jsonapi Renderer and deserializer (Http::Parameters::DEFAULT_PARSERS) are added.
6+
#
7+
# Usage:
8+
#
9+
# ActiveSupport.on_load(:action_controller) do
10+
# require 'active_model_serializers/register_jsonapi_renderer'
11+
# end
12+
#
13+
# And then in controllers, use `render jsonapi: model` rather than `render json: model, adapter: :json_api`.
14+
#
15+
# For example, in a controller action, we can:
16+
# respond_to do |format|
17+
# format.jsonapi { render jsonapi: model }
18+
# end
19+
#
20+
# or
21+
#
22+
# render jsonapi: model
23+
#
24+
# No wrapper format needed as it does not apply (i.e. no `wrap_parameters format: [jsonapi]`)
25+
26+
module ActiveModelSerializers::Jsonapi
27+
MEDIA_TYPE = 'application/vnd.api+json'.freeze
28+
HEADERS = {
29+
response: { 'CONTENT_TYPE'.freeze => MEDIA_TYPE },
30+
request: { 'ACCEPT'.freeze => MEDIA_TYPE }
31+
}.freeze
32+
module ControllerSupport
33+
def serialize_jsonapi(json, options)
34+
options[:adapter] = :json_api
35+
options.fetch(:serialization_context) { options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request) }
36+
get_serializer(json, options)
37+
end
38+
end
39+
end
40+
41+
# actionpack/lib/action_dispatch/http/mime_types.rb
42+
Mime::Type.register ActiveModelSerializers::Jsonapi::MEDIA_TYPE, :jsonapi
43+
44+
parsers = Rails::VERSION::MAJOR >= 5 ? ActionDispatch::Http::Parameters : ActionDispatch::ParamsParser
45+
media_type = Mime::Type.lookup(ActiveModelSerializers::Jsonapi::MEDIA_TYPE)
46+
47+
# Proposal: should actually deserialize the JSON API params
48+
# to the hash format expected by `ActiveModel::Serializers::JSON`
49+
# actionpack/lib/action_dispatch/http/parameters.rb
50+
parsers::DEFAULT_PARSERS[media_type] = lambda do |body|
51+
data = JSON.parse(body)
52+
data = { :_json => data } unless data.is_a?(Hash)
53+
data.with_indifferent_access
54+
end
55+
56+
# ref https://github.com/rails/rails/pull/21496
57+
ActionController::Renderers.add :jsonapi do |json, options|
58+
json = serialize_jsonapi(json, options).to_json(options) unless json.is_a?(String)
59+
self.content_type ||= media_type
60+
headers.merge! ActiveModelSerializers::Jsonapi::HEADERS[:response]
61+
self.response_body = json
62+
end
63+
64+
ActionController::Base.send :include, ActiveModelSerializers::Jsonapi::ControllerSupport

test/action_controller/json_api/linked_test.rb

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module Serialization
55
class JsonApi
66
class LinkedTest < ActionController::TestCase
77
class LinkedTestController < ActionController::Base
8+
require 'active_model_serializers/register_jsonapi_renderer'
89
def setup_post
910
ActionController::Base.cache_store.clear
1011
@role1 = Role.new(id: 1, name: 'admin')
@@ -38,49 +39,49 @@ def setup_post
3839

3940
def render_resource_without_include
4041
setup_post
41-
render json: @post, adapter: :json_api
42+
render jsonapi: @post
4243
end
4344

4445
def render_resource_with_include
4546
setup_post
46-
render json: @post, include: [:author], adapter: :json_api
47+
render jsonapi: @post, include: [:author]
4748
end
4849

4950
def render_resource_with_include_of_custom_key_by_original
5051
setup_post
51-
render json: @post, include: [:reviews], adapter: :json_api, serializer: PostWithCustomKeysSerializer
52+
render jsonapi: @post, include: [:reviews], serializer: PostWithCustomKeysSerializer
5253
end
5354

5455
def render_resource_with_nested_include
5556
setup_post
56-
render json: @post, include: [comments: [:author]], adapter: :json_api
57+
render jsonapi: @post, include: [comments: [:author]]
5758
end
5859

5960
def render_resource_with_nested_has_many_include_wildcard
6061
setup_post
61-
render json: @post, include: 'author.*', adapter: :json_api
62+
render jsonapi: @post, include: 'author.*'
6263
end
6364

6465
def render_resource_with_missing_nested_has_many_include
6566
setup_post
6667
@post.author = @author2 # author2 has no roles.
67-
render json: @post, include: [author: [:roles]], adapter: :json_api
68+
render jsonapi: @post, include: [author: [:roles]]
6869
end
6970

7071
def render_collection_with_missing_nested_has_many_include
7172
setup_post
7273
@post.author = @author2
73-
render json: [@post, @post2], include: [author: [:roles]], adapter: :json_api
74+
render jsonapi: [@post, @post2], include: [author: [:roles]]
7475
end
7576

7677
def render_collection_without_include
7778
setup_post
78-
render json: [@post], adapter: :json_api
79+
render jsonapi: [@post]
7980
end
8081

8182
def render_collection_with_include
8283
setup_post
83-
render json: [@post], include: 'author, comments', adapter: :json_api
84+
render jsonapi: [@post], include: 'author, comments'
8485
end
8586
end
8687

0 commit comments

Comments
 (0)