Skip to content

Commit 33f3a88

Browse files
committed
Implement included and id and type as per spec
1 parent d82c599 commit 33f3a88

File tree

12 files changed

+203
-130
lines changed

12 files changed

+203
-130
lines changed

lib/active_model/serializer.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@ def json_key
164164
end
165165
end
166166

167+
def id
168+
object.id if object
169+
end
170+
171+
def type
172+
object.class.to_s.demodulize.underscore.pluralize
173+
end
174+
167175
def attributes(options = {})
168176
attributes =
169177
if options[:fields]
@@ -172,6 +180,8 @@ def attributes(options = {})
172180
self.class._attributes.dup
173181
end
174182

183+
attributes += options[:required_fields] if options[:required_fields]
184+
175185
attributes.each_with_object({}) do |name, hash|
176186
hash[name] = send(name)
177187
end

lib/active_model/serializer/adapter/json_api.rb

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,17 @@ def serializable_hash(options = {})
3535
private
3636

3737
def add_links(resource, name, serializers)
38-
type = serialized_object_type(serializers)
3938
resource[:links] ||= {}
40-
4139
resource[:links][name] ||= { linkage: [] }
42-
resource[:links][name][:linkage] += serializers.map { |serializer| { type: type, id: serializer.id.to_s } }
40+
resource[:links][name][:linkage] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } }
4341
end
4442

4543
def add_link(resource, name, serializer)
4644
resource[:links] ||= {}
4745
resource[:links][name] = { linkage: nil }
4846

4947
if serializer && serializer.object
50-
type = serialized_object_type(serializer)
51-
52-
resource[:links][name][:linkage] = { type: type, id: serializer.id.to_s }
48+
resource[:links][name][:linkage] = { type: serializer.type, id: serializer.id.to_s }
5349
end
5450
end
5551

@@ -58,17 +54,15 @@ def add_included(resource_name, serializers, parent = nil)
5854

5955
resource_path = [parent, resource_name].compact.join('.')
6056

61-
if include_assoc?(resource_path) && resource_type = serialized_object_type(serializers)
62-
plural_name = resource_type.pluralize.to_sym
63-
@top[:linked] ||= {}
64-
@top[:linked][plural_name] ||= []
57+
if include_assoc?(resource_path)
58+
@top[:included] ||= []
6559

6660
serializers.each do |serializer|
6761
attrs = attributes_for_serializer(serializer, @options)
6862

6963
add_resource_links(attrs, serializer, add_included: false)
7064

71-
@top[:linked][plural_name].push(attrs) unless @top[:linked][plural_name].include?(attrs)
65+
@top[:included].push(attrs) unless @top[:included].include?(attrs)
7266
end
7367
end
7468

@@ -85,14 +79,16 @@ def attributes_for_serializer(serializer, options)
8579
result = []
8680
serializer.each do |object|
8781
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
82+
options[:required_fields] = [:id, :type]
8883
attributes = object.attributes(options)
89-
attributes[:id] = attributes[:id].to_s if attributes[:id]
84+
attributes[:id] = attributes[:id].to_s
9085
result << attributes
9186
end
9287
else
9388
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
89+
options[:required_fields] = [:id, :type]
9490
result = serializer.attributes(options)
95-
result[:id] = result[:id].to_s if result[:id]
91+
result[:id] = result[:id].to_s
9692
end
9793

9894
result
@@ -116,11 +112,6 @@ def check_assoc(assoc)
116112
end
117113
end
118114

119-
def serialized_object_type(serializer)
120-
return false unless Array(serializer).first
121-
Array(serializer).first.object.class.to_s.demodulize.underscore.pluralize
122-
end
123-
124115
def add_resource_links(attrs, serializer, options = {})
125116
options[:add_included] = options.fetch(:add_included, true)
126117

test/action_controller/adapter_selector_test.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,17 @@ def test_render_using_default_adapter
2929

3030
def test_render_using_adapter_override
3131
get :render_using_adapter_override
32-
assert_equal '{"data":{"name":"Name 1","description":"Description 1"}}', response.body
32+
33+
expected = {
34+
data: {
35+
name: "Name 1",
36+
description: "Description 1",
37+
id: assigns(:profile).id.to_s,
38+
type: "profiles"
39+
}
40+
}
41+
42+
assert_equal expected.to_json, response.body
3343
end
3444

3545
def test_render_skipping_adapter

test/action_controller/json_api_linked_test.rb

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -83,80 +83,87 @@ def render_collection_with_include
8383
def test_render_resource_without_include
8484
get :render_resource_without_include
8585
response = JSON.parse(@response.body)
86-
refute response.key? 'linked'
86+
refute response.key? 'included'
8787
end
8888

8989
def test_render_resource_with_include
9090
get :render_resource_with_include
9191
response = JSON.parse(@response.body)
92-
assert response.key? 'linked'
93-
assert_equal 1, response['linked']['authors'].size
94-
assert_equal 'Steve K.', response['linked']['authors'].first['name']
92+
assert response.key? 'included'
93+
assert_equal 1, response['included'].size
94+
assert_equal 'Steve K.', response['included'].first['name']
9595
end
9696

9797
def test_render_resource_with_nested_has_many_include
9898
get :render_resource_with_nested_has_many_include
9999
response = JSON.parse(@response.body)
100-
expected_linked = {
101-
"authors" => [{
100+
expected_linked = [
101+
{
102102
"id" => "1",
103+
"type" => "authors",
103104
"name" => "Steve K.",
104105
"links" => {
105106
"posts" => { "linkage" => [] },
106107
"roles" => { "linkage" => [{ "type" =>"roles", "id" => "1" }, { "type" =>"roles", "id" => "2" }] },
107108
"bio" => { "linkage" => nil }
108109
}
109-
}],
110-
"roles"=>[{
110+
}, {
111111
"id" => "1",
112+
"type" => "roles",
112113
"name" => "admin",
113114
"links" => {
114115
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
115116
}
116117
}, {
117118
"id" => "2",
119+
"type" => "roles",
118120
"name" => "colab",
119121
"links" => {
120122
"author" => { "linkage" => { "type" =>"authors", "id" => "1" } }
121123
}
122-
}]
123-
}
124-
assert_equal expected_linked, response['linked']
124+
}
125+
]
126+
assert_equal expected_linked, response['included']
125127
end
126128

127129
def test_render_resource_with_nested_include
128130
get :render_resource_with_nested_include
129131
response = JSON.parse(@response.body)
130-
assert response.key? 'linked'
131-
assert_equal 1, response['linked']['authors'].size
132-
assert_equal 'Anonymous', response['linked']['authors'].first['name']
132+
assert response.key? 'included'
133+
assert_equal 1, response['included'].size
134+
assert_equal 'Anonymous', response['included'].first['name']
133135
end
134136

135137
def test_render_collection_without_include
136138
get :render_collection_without_include
137139
response = JSON.parse(@response.body)
138-
refute response.key? 'linked'
140+
refute response.key? 'included'
139141
end
140142

141143
def test_render_collection_with_include
142144
get :render_collection_with_include
143145
response = JSON.parse(@response.body)
144-
assert response.key? 'linked'
146+
assert response.key? 'included'
145147
end
146148

147149
def test_render_resource_with_nested_attributes_even_when_missing_associations
148150
get :render_resource_with_missing_nested_has_many_include
149151
response = JSON.parse(@response.body)
150-
assert response.key? 'linked'
151-
refute response['linked'].key? 'roles'
152+
assert response.key? 'included'
153+
refute has_type?(response['included'], 'roles')
152154
end
153155

154156
def test_render_collection_with_missing_nested_has_many_include
155157
get :render_collection_with_missing_nested_has_many_include
156158
response = JSON.parse(@response.body)
157-
assert response.key? 'linked'
158-
assert response['linked'].key? 'roles'
159+
assert response.key? 'included'
160+
assert has_type?(response['included'], 'roles')
161+
end
162+
163+
def has_type?(collection, value)
164+
collection.detect { |i| i['type'] == value}
159165
end
166+
160167
end
161168
end
162169
end

test/action_controller/serialization_scope_name_test.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
require 'pathname'
33

44
class DefaultScopeNameTest < ActionController::TestCase
5-
TestUser = Struct.new(:name, :admin)
5+
TestUser = Struct.new(:id, :name, :admin)
66

77
class UserSerializer < ActiveModel::Serializer
88
attributes :admin?
@@ -17,24 +17,24 @@ class UserTestController < ActionController::Base
1717
before_filter { request.format = :json }
1818

1919
def current_user
20-
TestUser.new('Pete', false)
20+
TestUser.new(1, 'Pete', false)
2121
end
2222

2323
def render_new_user
24-
render json: TestUser.new('pete', false), serializer: UserSerializer, adapter: :json_api
24+
render json: TestUser.new(1, 'pete', false), serializer: UserSerializer, adapter: :json_api
2525
end
2626
end
2727

2828
tests UserTestController
2929

3030
def test_default_scope_name
3131
get :render_new_user
32-
assert_equal '{"data":{"admin?":false}}', @response.body
32+
assert_equal '{"data":{"admin?":false,"id":"1","type":"test_users"}}', @response.body
3333
end
3434
end
3535

3636
class SerializationScopeNameTest < ActionController::TestCase
37-
TestUser = Struct.new(:name, :admin)
37+
TestUser = Struct.new(:id, :name, :admin)
3838

3939
class AdminUserSerializer < ActiveModel::Serializer
4040
attributes :admin?
@@ -50,18 +50,18 @@ class AdminUserTestController < ActionController::Base
5050
before_filter { request.format = :json }
5151

5252
def current_admin
53-
TestUser.new('Bob', true)
53+
TestUser.new(1, 'Bob', true)
5454
end
5555

5656
def render_new_user
57-
render json: TestUser.new('pete', false), serializer: AdminUserSerializer, adapter: :json_api
57+
render json: TestUser.new(1, 'pete', false), serializer: AdminUserSerializer, adapter: :json_api
5858
end
5959
end
6060

6161
tests AdminUserTestController
6262

6363
def test_override_scope_name_with_controller
6464
get :render_new_user
65-
assert_equal '{"data":{"admin?":true}}', @response.body
65+
assert_equal '{"data":{"admin?":true,"id":"1","type":"test_users"}}', @response.body
6666
end
6767
end

0 commit comments

Comments
 (0)