Skip to content

Commit

Permalink
Merge pull request #925 from benedikt/json-api
Browse files Browse the repository at this point in the history
Updates JSON API Adapter to generate RC4 schema
  • Loading branch information
kurko committed May 27, 2015
2 parents 5f05944 + 2f6c431 commit 16f7512
Show file tree
Hide file tree
Showing 14 changed files with 300 additions and 230 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ AMS does this through two components: **serializers** and **adapters**.
Serializers describe _which_ attributes and relationships should be serialized.
Adapters describe _how_ attributes and relationships should be serialized.

By default AMS will use the JsonApi Adapter that follows RC3 of the format specified in [jsonapi.org/format](http://jsonapi.org/format).
By default AMS will use the JsonApi Adapter that follows RC4 of the format specified in [jsonapi.org/format](http://jsonapi.org/format).
Check how to change the adapter in the sections bellow.

# RELEASE CANDIDATE, PLEASE READ
Expand Down Expand Up @@ -178,7 +178,7 @@ end

#### JSONAPI

This adapter follows RC3 of the format specified in
This adapter follows RC4 of the format specified in
[jsonapi.org/format](http://jsonapi.org/format). It will include the associated
resources in the `"included"` member when the resource names are included in the
`include` option.
Expand Down
63 changes: 34 additions & 29 deletions lib/active_model/serializer/adapter/json_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def serializable_hash(options = {})
end
else
@hash[:data] = attributes_for_serializer(serializer, @options)
add_resource_links(@hash[:data], serializer)
add_resource_relationships(@hash[:data], serializer)
end
@hash
end
Expand All @@ -41,18 +41,18 @@ def fragment_cache(cached_hash, non_cached_hash)

private

def add_links(resource, name, serializers)
resource[:links] ||= {}
resource[:links][name] ||= { linkage: [] }
resource[:links][name][:linkage] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } }
def add_relationships(resource, name, serializers)
resource[:relationships] ||= {}
resource[:relationships][name] ||= { data: [] }
resource[:relationships][name][:data] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } }
end

def add_link(resource, name, serializer, val=nil)
resource[:links] ||= {}
resource[:links][name] = { linkage: nil }
def add_relationship(resource, name, serializer, val=nil)
resource[:relationships] ||= {}
resource[:relationships][name] = { data: nil }

if serializer && serializer.object
resource[:links][name][:linkage] = { type: serializer.type, id: serializer.id.to_s }
resource[:relationships][name][:data] = { type: serializer.type, id: serializer.id.to_s }
end
end

Expand All @@ -68,7 +68,7 @@ def add_included(resource_name, serializers, parent = nil)
serializers.each do |serializer|
attrs = attributes_for_serializer(serializer, @options)

add_resource_links(attrs, serializer, add_included: false)
add_resource_relationships(attrs, serializer, add_included: false)

@hash[:included].push(attrs) unless @hash[:included].include?(attrs)
end
Expand All @@ -85,26 +85,31 @@ def attributes_for_serializer(serializer, options)
if serializer.respond_to?(:each)
result = []
serializer.each do |object|
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
result << cache_check(object) do
options[:required_fields] = [:id, :type]
attributes = object.attributes(options)
attributes[:id] = attributes[:id].to_s
result << attributes
end
result << resource_object_for(object, options)
end
else
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
options[:required_fields] = [:id, :type]
result = cache_check(serializer) do
result = serializer.attributes(options)
result[:id] = result[:id].to_s
result
end
result = resource_object_for(serializer, options)
end
result
end

def resource_object_for(serializer, options)
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
options[:required_fields] = [:id, :type]

cache_check(serializer) do
attributes = serializer.attributes(options)

result = {
id: attributes.delete(:id).to_s,
type: attributes.delete(:type)
}

result[:attributes] = attributes if attributes.any?
result
end
end

def include_assoc?(assoc)
return false unless @options[:include]
check_assoc("#{assoc}$")
Expand All @@ -123,19 +128,19 @@ def check_assoc(assoc)
end
end

def add_resource_links(attrs, serializer, options = {})
def add_resource_relationships(attrs, serializer, options = {})
options[:add_included] = options.fetch(:add_included, true)

serializer.each_association do |name, association, opts|
attrs[:links] ||= {}
attrs[:relationships] ||= {}

if association.respond_to?(:each)
add_links(attrs, name, association)
add_relationships(attrs, name, association)
else
if opts[:virtual_value]
add_link(attrs, name, nil, opts[:virtual_value])
add_relationship(attrs, name, nil, opts[:virtual_value])
else
add_link(attrs, name, association)
add_relationship(attrs, name, association)
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ def fragment_cache(root, cached_hash, non_cached_hash)
core_non_cached = non_cached_hash.first
no_root_cache = cached_hash.delete_if {|key, value| key == core_cached[0] }
no_root_non_cache = non_cached_hash.delete_if {|key, value| key == core_non_cached[0] }
cached_resource = (core_cached[1]) ? core_cached[1].merge(core_non_cached[1]) : core_non_cached[1]
cached_resource = (core_cached[1]) ? core_cached[1].deep_merge(core_non_cached[1]) : core_non_cached[1]
hash = (root) ? { root => cached_resource } : cached_resource
hash.merge no_root_non_cache.merge no_root_cache

hash.deep_merge no_root_non_cache.deep_merge no_root_cache
end

end
Expand Down
8 changes: 5 additions & 3 deletions test/action_controller/adapter_selector_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ def test_render_using_adapter_override

expected = {
data: {
name: "Name 1",
description: "Description 1",
id: assigns(:profile).id.to_s,
type: "profiles"
type: "profiles",
attributes: {
name: "Name 1",
description: "Description 1",
}
}
}

Expand Down
40 changes: 23 additions & 17 deletions test/action_controller/json_api_linked_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_render_resource_with_include
response = JSON.parse(@response.body)
assert response.key? 'included'
assert_equal 1, response['included'].size
assert_equal 'Steve K.', response['included'].first['name']
assert_equal 'Steve K.', response['included'].first['attributes']['name']
end

def test_render_resource_with_nested_has_many_include
Expand All @@ -101,29 +101,35 @@ def test_render_resource_with_nested_has_many_include
{
"id" => "1",
"type" => "authors",
"name" => "Steve K.",
"links" => {
"posts" => { "linkage" => [] },
"roles" => { "linkage" => [{ "type" =>"roles", "id" => "1" }, { "type" =>"roles", "id" => "2" }] },
"bio" => { "linkage" => nil }
"attributes" => {
"name" => "Steve K."
},
"relationships" => {
"posts" => { "data" => [] },
"roles" => { "data" => [{ "type" =>"roles", "id" => "1" }, { "type" =>"roles", "id" => "2" }] },
"bio" => { "data" => nil }
}
}, {
"id" => "1",
"type" => "roles",
"name" => "admin",
"description" => nil,
"slug" => "admin-1",
"links" => {
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
"attributes" => {
"name" => "admin",
"description" => nil,
"slug" => "admin-1"
},
"relationships" => {
"author" => { "data" => { "type" =>"authors", "id" => "1" } }
}
}, {
"id" => "2",
"type" => "roles",
"name" => "colab",
"description" => nil,
"slug" => "colab-2",
"links" => {
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
"attributes" => {
"name" => "colab",
"description" => nil,
"slug" => "colab-2"
},
"relationships" => {
"author" => { "data" => { "type" =>"authors", "id" => "1" } }
}
}
]
Expand All @@ -135,7 +141,7 @@ def test_render_resource_with_nested_include
response = JSON.parse(@response.body)
assert response.key? 'included'
assert_equal 1, response['included'].size
assert_equal 'Anonymous', response['included'].first['name']
assert_equal 'Anonymous', response['included'].first['attributes']['name']
end

def test_render_collection_without_include
Expand Down
4 changes: 2 additions & 2 deletions test/action_controller/serialization_scope_name_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def render_new_user

def test_default_scope_name
get :render_new_user
assert_equal '{"data":{"admin?":false,"id":"1","type":"users"}}', @response.body
assert_equal '{"data":{"id":"1","type":"users","attributes":{"admin?":false}}}', @response.body
end
end

Expand Down Expand Up @@ -58,6 +58,6 @@ def render_new_user

def test_override_scope_name_with_controller
get :render_new_user
assert_equal '{"data":{"admin?":true,"id":"1","type":"users"}}', @response.body
assert_equal '{"data":{"id":"1","type":"users","attributes":{"admin?":true}}}', @response.body
end
end
24 changes: 15 additions & 9 deletions test/action_controller/serialization_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,12 @@ def test_render_using_default_root

expected = {
data: {
name: "Name 1",
description: "Description 1",
id: assigns(:profile).id.to_s,
type: "profiles"
type: "profiles",
attributes: {
name: "Name 1",
description: "Description 1"
}
}
}

Expand All @@ -182,10 +184,12 @@ def test_render_using_custom_root_in_adapter_with_a_default

expected = {
data: {
name: "Name 1",
description: "Description 1",
id: assigns(:profile).id.to_s,
type: "profiles"
type: "profiles",
attributes: {
name: "Name 1",
description: "Description 1"
}
}
}

Expand Down Expand Up @@ -217,10 +221,12 @@ def test_render_array_using_implicit_serializer_and_meta
expected = {
data: [
{
name: "Name 1",
description: "Description 1",
id: assigns(:profiles).first.id.to_s,
type: "profiles"
type: "profiles",
attributes: {
name: "Name 1",
description: "Description 1"
}
}
],
meta: {
Expand Down
Loading

0 comments on commit 16f7512

Please sign in to comment.