Skip to content

Commit 03ac94b

Browse files
committed
Merge pull request #962 from joaomdmoura/render-array-objects
Rendering objects that doesn't have serializers
2 parents 01a225f + 17d560e commit 03ac94b

File tree

8 files changed

+99
-19
lines changed

8 files changed

+99
-19
lines changed

lib/action_controller/serialization.rb

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,18 @@ def use_adapter?
3939
options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] }
4040

4141
if use_adapter? && (serializer = get_serializer(resource))
42-
4342
@_serializer_opts[:scope] ||= serialization_scope
4443
@_serializer_opts[:scope_name] = _serialization_scope
4544

46-
# omg hax
47-
object = serializer.new(resource, @_serializer_opts)
48-
adapter = ActiveModel::Serializer::Adapter.create(object, @_adapter_opts)
49-
super(adapter, options)
50-
else
51-
super(resource, options)
45+
begin
46+
serialized = serializer.new(resource, @_serializer_opts)
47+
rescue ActiveModel::Serializer::ArraySerializer::NoSerializerError
48+
else
49+
resource = ActiveModel::Serializer::Adapter.create(serialized, @_adapter_opts)
50+
end
5251
end
52+
53+
super(resource, options)
5354
end
5455
end
5556

lib/active_model/serializer.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,16 @@ def each_association(&block)
206206
serializer_class = ActiveModel::Serializer.serializer_for(association_value, association_options)
207207

208208
if serializer_class
209-
serializer = serializer_class.new(
210-
association_value,
211-
options.except(:serializer).merge(serializer_from_options(association_options))
212-
)
209+
begin
210+
serializer = serializer_class.new(
211+
association_value,
212+
options.except(:serializer).merge(serializer_from_options(association_options))
213+
)
214+
rescue ActiveModel::Serializer::ArraySerializer::NoSerializerError
215+
virtual_value = association_value
216+
virtual_value = virtual_value.as_json if virtual_value.respond_to?(:as_json)
217+
association_options[:association_options][:virtual_value] = virtual_value
218+
end
213219
elsif !association_value.nil? && !association_value.instance_of?(Object)
214220
association_options[:association_options][:virtual_value] = association_value
215221
end

lib/active_model/serializer/array_serializer.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module ActiveModel
22
class Serializer
33
class ArraySerializer
4+
NoSerializerError = Class.new(StandardError)
45
include Enumerable
56
delegate :each, to: :@objects
67

@@ -13,7 +14,12 @@ def initialize(objects, options = {})
1314
:serializer,
1415
ActiveModel::Serializer.serializer_for(object)
1516
)
16-
serializer_class.new(object, options.except(:serializer))
17+
18+
if serializer_class.nil?
19+
fail NoSerializerError, "No serializer found for object: #{object.inspect}"
20+
else
21+
serializer_class.new(object, options.except(:serializer))
22+
end
1723
end
1824
@meta = options[:meta]
1925
@meta_key = options[:meta_key]

test/action_controller/serialization_test.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ def render_object_with_cache_enabled
4747
render json: @post
4848
end
4949

50+
def render_json_object_without_serializer
51+
render json: {error: 'Result is Invalid'}
52+
end
53+
54+
def render_json_array_object_without_serializer
55+
render json: [{error: 'Result is Invalid'}]
56+
end
57+
5058
def update_and_render_object_with_cache_enabled
5159
@post.updated_at = DateTime.now
5260

@@ -160,6 +168,20 @@ def test_render_using_default_root
160168
assert_equal expected.to_json, @response.body
161169
end
162170

171+
def test_render_json_object_without_serializer
172+
get :render_json_object_without_serializer
173+
174+
assert_equal 'application/json', @response.content_type
175+
assert_equal ({error: 'Result is Invalid'}).to_json, @response.body
176+
end
177+
178+
def test_render_json_array_object_without_serializer
179+
get :render_json_array_object_without_serializer
180+
181+
assert_equal 'application/json', @response.content_type
182+
assert_equal ([{error: 'Result is Invalid'}]).to_json, @response.body
183+
end
184+
163185
def test_render_array_using_implicit_serializer
164186
get :render_array_using_implicit_serializer
165187
assert_equal 'application/json', @response.content_type

test/adapter/json/has_many_test.rb

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class HasManyTestTest < Minitest::Test
88
def setup
99
ActionController::Base.cache_store.clear
1010
@author = Author.new(id: 1, name: 'Steve K.')
11-
@post = Post.new(title: 'New Post', body: 'Body')
11+
@post = Post.new(id: 42, title: 'New Post', body: 'Body')
1212
@first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
1313
@second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
1414
@post.comments = [@first_comment, @second_comment]
@@ -17,20 +17,31 @@ def setup
1717
@second_comment.post = @post
1818
@blog = Blog.new(id: 1, name: "My Blog!!")
1919
@post.blog = @blog
20-
21-
@serializer = PostSerializer.new(@post)
22-
@adapter = ActiveModel::Serializer::Adapter::Json.new(@serializer)
20+
@tag = Tag.new(id: 1, name: "#hash_tag")
21+
@post.tags = [@tag]
2322
end
2423

2524
def test_has_many
25+
serializer = PostSerializer.new(@post)
26+
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
2627
assert_equal([
2728
{id: 1, body: 'ZOMG A COMMENT'},
2829
{id: 2, body: 'ZOMG ANOTHER COMMENT'}
29-
], @adapter.serializable_hash[:post][:comments])
30+
], adapter.serializable_hash[:post][:comments])
31+
end
32+
33+
def test_has_many_with_no_serializer
34+
serializer = PostWithTagsSerializer.new(@post)
35+
adapter = ActiveModel::Serializer::Adapter::Json.new(serializer)
36+
assert_equal({
37+
id: 42,
38+
tags: [
39+
{"attributes"=>{"id"=>1, "name"=>"#hash_tag"}}
40+
]
41+
}.to_json, adapter.serializable_hash[:post_with_tags].to_json)
3042
end
3143
end
3244
end
3345
end
3446
end
3547
end
36-

test/adapter/json_api/has_many_test.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def setup
2727
@blog.articles = [@post]
2828
@post.blog = @blog
2929
@post_without_comments.blog = nil
30-
30+
@tag = Tag.new(id: 1, name: "#hash_tag")
31+
@post.tags = [@tag]
3132
@serializer = PostSerializer.new(@post)
3233
@adapter = ActiveModel::Serializer::Adapter::JsonApi.new(@serializer)
3334
end
@@ -95,6 +96,7 @@ def test_include_type_for_association_when_different_than_name
9596
serializer = BlogSerializer.new(@blog)
9697
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
9798
actual = adapter.serializable_hash[:data][:relationships][:articles]
99+
98100
expected = {
99101
data: [{
100102
type: "posts",
@@ -103,6 +105,21 @@ def test_include_type_for_association_when_different_than_name
103105
}
104106
assert_equal expected, actual
105107
end
108+
109+
def test_has_many_with_no_serializer
110+
serializer = PostWithTagsSerializer.new(@post)
111+
adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer)
112+
113+
assert_equal({
114+
data: {
115+
id: "1",
116+
type: "posts",
117+
relationships: {
118+
tags: { data: nil }
119+
}
120+
}
121+
}, adapter.serializable_hash)
122+
end
106123
end
107124
end
108125
end

test/fixtures/poro.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class ProfilePreviewSerializer < ActiveModel::Serializer
7676
User = Class.new(Model)
7777
Location = Class.new(Model)
7878
Place = Class.new(Model)
79+
Tag = Class.new(Model)
7980
Comment = Class.new(Model) do
8081
# Uses a custom non-time-based cache key
8182
def cache_key
@@ -224,6 +225,12 @@ def self.root_name
224225
belongs_to :author, serializer: AuthorPreviewSerializer
225226
end
226227

228+
PostWithTagsSerializer = Class.new(ActiveModel::Serializer) do
229+
attributes :id
230+
231+
has_many :tags
232+
end
233+
227234
Spam::UnrelatedLinkSerializer = Class.new(ActiveModel::Serializer) do
228235
attributes :id
229236
end

test/serializers/associations_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ def setup
2929
@author.roles = []
3030
@blog = Blog.new({ name: 'AMS Blog' })
3131
@post = Post.new({ title: 'New Post', body: 'Body' })
32+
@tag = Tag.new({name: '#hashtagged'})
3233
@comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
3334
@post.comments = [@comment]
35+
@post.tags = [@tag]
3436
@post.blog = @blog
3537
@comment.post = @post
3638
@comment.author = nil
@@ -65,6 +67,14 @@ def test_has_many_and_has_one
6567
end
6668
end
6769

70+
def test_has_many_with_no_serializer
71+
PostWithTagsSerializer.new(@post).each_association do |name, serializer, options|
72+
assert_equal name, :tags
73+
assert_equal serializer, nil
74+
assert_equal [{ attributes: { name: "#hashtagged" }}].to_json, options[:virtual_value].to_json
75+
end
76+
end
77+
6878
def test_serializer_options_are_passed_into_associations_serializers
6979
@post_serializer.each_association do |name, association|
7080
if name == :comments

0 commit comments

Comments
 (0)