-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rendering array objects that doesn't have serializers #962
Rendering array objects that doesn't have serializers #962
Conversation
ref: #955 (comment) and a fix I wrote that works at a different level, in the serializer itself. # https://github.com/rails-api/active_model_serializers/blob/56a7bd1b6dd9f083b0f3a9d6d24e9dc85efbffac/lib/active_model/serializer.rb
def each_association(&block)
self.class._associations.dup.each do |name, association_options|
next unless object
association_value = send(name)
serializer_class = ActiveModel::Serializer.serializer_for(association_value, association_options)
if serializer_class
- serializer = serializer_class.new(
- association_value,
- options.except(:serializer).merge(serializer_from_options(association_options))
- )
+ begin
+ serializer = serializer_class.new(
+ association_value,
+ options.except(:serializer).merge(serializer_from_options(association_options))
+ )
+ rescue NoMethodError
+ # 1. Failure to serialize an element in a collection, e.g. [ {hi: "Steve" } ] will fail
+ # with NoMethodError when the ArraySerializer finds no serializer for the hash { hi: "Steve" },
+ # and tries to call new on that nil.
+ # 2. Convert association_value to hash using implicit as_json
+ # 3. Set as virtual value (serializer is nil)
+ # 4. Consider warning when this happens
+ virtual_value = association_value
+ virtual_value = virtual_value.as_json if virtual_value.respond_to?(:as_json)
+ association_options[:association_options][:virtual_value] = virtual_value
+ end |
@bf4, indeed a better solution, tried to backport it, but it didn't fixed the problem. def render_json_array_object_without_serializer
render json: [{error: 'Result is Invalid'}]
end
...
def test_render_json_array_object_without_serializer
get :render_json_array_object_without_serializer
assert_equal 'application/json', @response.content_type
assert_equal ([{error: 'Result is Invalid'}]).to_json, @response.body
end |
@joaomdmoura Yeah, looking at that now, was going to make a PR to your branch |
@bf4 tks! I was working on it too, but no worries 😄 make yourself comfortable just let me know if you need help or anything. |
they're closely related, but have somewhat different paths, both triggered by serializable_hash, but no common place that's called, ugh. I think I'm trying to do too much at once by combining those tests... gonna make the PR just for discussion joaomdmoura#1 |
So, I think I got it figured out in that PR. Basically, the special case can be boiled down to object.any? {|elem| ActiveModel::Serializer.serializer_for(object).nil? } |
So, I'm thinking maybe the solution should be in serializer_for rather than in ArraySerializer, but I'm not sure how to efficiently do that without essentially iterating over all the elements twice. |
3f49f51
to
15318b7
Compare
include Enumerable | ||
delegate :each, to: :@objects | ||
|
||
attr_reader :meta, :meta_key | ||
attr_reader :meta, :meta_key, :objects |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to keep objects
reader around? It's no longer used. I could see an argument that it's a private interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, indeed, there is no reason to keep it
15318b7
to
40c4183
Compare
fail NoSerializerError, "No serializer found for object: #{object.inspect}" | ||
else | ||
serializer_class.new(object, options.except(:serializer)) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about having serializer_for return nil for if any elements don't have an associated serializer? That would maintain current PR behavior, I think, might make more sense SRP-wise, but might be a performance hit.
Should we add a test for a collection or association that contains an element with a known serializer and another with a nil serializer class? I'm really not sure how to handle that in AMS without more complexity. I'd rather push that back to the end user, but that could be laziness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would need to update this PR if we change this behaviour at serializer_for
.
I think it's an edge-case. I haven't heard about ppl using AMS to renders arrays that mix serializable and non-serializable objects yet, so let's not worry about it for now 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
winning
super(resource, options) | ||
begin | ||
serialized = serializer.new(resource, @_serializer_opts) | ||
rescue ActiveModel::Serializer::ArraySerializer::NoSerializerError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add some docs on building your own array serializer as described in https://github.com/rails-api/active_model_serializers#2-for-an-array-resource and the use of this exception class at some point. Want me to make an issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm on a quest to keep the readme as clean as possible, but I agree that this can be helpful for some ppl, like others details that are poping up.
I'm thinking about start the AMS wiki (using gh wiki feature). For now, make yourself comfortable to open the issue to make sure we won't forget it while I think it trough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like wikis, but then it's another thing to garden. For AMS I think it could be a plus.
c6b030c
to
a34a5e7
Compare
Failure are Rails 4.0 only and have to do with deep munging of json keys. I think you can configure that with |
Submitted PR to this branch in joaomdmoura#2 |
..where no serializer is defined for the thing that is has_many'd
Comparing as a JSON string vs. as the Hash that is convert to JSON works around the different Hash representations. This likely has to do with the introduction of config.action_dispatch.perform_deep_munge in Rails 4.1 See Rails issue 13420 1) Failure: ActiveModel::Serializer::Adapter::Json::HasManyTestTest#test_has_many_with_no_serializer [active_model_serializers/test/adapter/json/has_many_test.rb:36]: --- expected +++ actual @@ -1 +1 @@ -{:id=>42, :tags=>[{"attributes"=>{"id"=>1, "name"=>"#hash_tag"}}]} +{:id=>42, :tags=>[{"attributes"=>{:id=>1, :name=>"#hash_tag"}}]} 2) Failure: ActiveModel::Serializer::AssociationsTest#test_has_many_with_no_serializer [active_model_serializers/test/serializers/associations_test.rb:74]: --- expected +++ actual @@ -1 +1 @@ -[{"attributes"=>{"name"=>"#hashtagged"}}] +[{"attributes"=>{:name=>"#hashtagged"}}]
f6db5b8
to
17d560e
Compare
Great team work here folks! Tks so much @JustinAiken and @bf4 |
Rendering objects that doesn't have serializers
As reported on #772 and #945.
There was a crash when rendering arrays that don't consist of "serializables" classes.
This PR is a first implementation to quickly fix it.
P.S. It won't fix #877 yet.
P.S. #954 will need to be updated based on this once merged, around here