Skip to content
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

Document serializer/each_serializer options #1191

Closed
bf4 opened this issue Sep 22, 2015 · 6 comments
Closed

Document serializer/each_serializer options #1191

bf4 opened this issue Sep 22, 2015 · 6 comments

Comments

@bf4
Copy link
Member

bf4 commented Sep 22, 2015

from: #1156 (comment)

So, AMS doesn't handle primitives, and when they find their way it, they are passed through as is. ( Specifically, when you're not rendering an object with a defined serializer.)

How options are parsed

  1. ActionControllerSerialization
  2. serializable_resource = ActiveModel::SerializableResource.new(resource, options)
  3. ActiveModel::SerializableResource
  4. if serializable_resource.serializer?
    • Where serializer? is use_adapter? && !!(serializer)
      • Where use_adapter?: 'True when no explicit adapter given, or explicit value is truthy (non-nil); False when explicit adapter is falsy (nil or false)'
      • Where serializer:
        1. from explicit :serializer option, else
        2. implicitly from resource ActiveModel::Serializer.serializer_for(resource)
  5. A side-effect of checking serializer is:
    • The :serializer option is removed from the serializer_opts hash
    • If the :each_serializer option is present, it is removed from the serializer_opts hash and set as the :serializer option
  6. The serializer and adapter are created as
    1. serializer_instance = serializer.new(resource, serializer_opts)
    2. adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)
  7. AcitveModel::Serializer::ArraySerializer#new
  8. If the serializer_instance was a ArraySerializer and the :serializer serializer_opts is present, then that serializer is passed into each resource:
    serializer_class = options.fetch(:serializer) do
    ActiveModel::Serializer.serializer_for(resource)
    end
  9. ActiveModel::Serializer#attributes is used by the adapter to get the attributes for resource as defined by the serializer.

The upshot of which is that

  • for a collection, the collection serializer is the :serializer option and :each_serializer is used as the serializer for each resource in the collection
  • for a single resource, the :serializer option is the resource serializer

Aside, if the collection serializer (ArraySerializer) cannot identify a serializer for a resource in its collection, it raises NoSerializerError which is rescued in AcitveModel::Serializer::Reflection#build_association which sets the association value directly: reflection_options[:virtual_value] = association_value.try(:as_json) || association_value (which is called by the adapter as serializer.associations(*)

@beauby
Copy link
Contributor

beauby commented Sep 22, 2015

👍 for the explanation. As I said in #Icantremember, I think the NoSerializerError for associations should not be rescued, or at least should issue a warning when rescued.

@bf4
Copy link
Member Author

bf4 commented Sep 22, 2015

@beauby

I think the NoSerializerError for associations should not be rescued

Actually, that's what it was introduced for, though perhaps try/catch would be a more correct use for control flow.

Ref:

            # 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

@NullVoxPopuli
Copy link
Contributor

It would be great to have this explanation of things in SerializableResource are other related areas.
Sometimes, it's just convenient to see the possible flows in the code comments.

Also, Is there a place currently documented that states that AMS does not serialize primitives?

@bf4
Copy link
Member Author

bf4 commented Sep 22, 2015

@NullVoxPopuli

It would be great to have this explanation

Yeah, that's why I made this issue.. would be good to have a overview something like this but that won't break with changes

Also, Is there a place currently documented that states that AMS does not serialize primitives?

Dunno. It does come up every now and then. I do think ArraySerializer is a misleading name since it really is a Collection.

Probably best would be a blurb in the README

  1. If AMS is not given an serializer and cannot infer one, it will just pass through the render arguments
  2. If any AMS cannot determine a serializer for any association, it will attempt to cast it as_json, and then will just set that value as the association.

@NullVoxPopuli
Copy link
Contributor

If AMS is not given an serializer and cannot infer one, it will just pass through the render arguments
If any AMS cannot determine a serializer for any association, it will attempt to cast it as_json, and then will just set that value as the association.

👍

@bf4 bf4 added the V: 0.10.x label Oct 7, 2015
@bf4
Copy link
Member Author

bf4 commented Oct 7, 2015

@rails-api/ams reminder to myself etc. to write this up, maybe in an ARCHITECTURE.md?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants