Passing options to ActiveModel serializer - ruby-on-rails

When using the serializer from the controller I can pass extra options to it like so
render json: user, some_option: 'foobar
Then I can reference some_option within the serializer as
serialization_options[:some_option]
But, if I call the serializer directly as
MySerializer.new(user, some_option: 'foobar')
I cannot get the extra options since serialization_options is an empty object.

For v0.9
You may call the following:
MySerializer.new(user).as_json({some_option: 'foobar'})
If you are doing that inside another serializer and you need to pass the scope and the current serialization_options as well, you can do this:
class MyParentSerializer
has_one :user
def user
MySerializer.new(object.user, { scope: scope }).as_json(serialization_options.merge({ some_option: 'foobar' }))
end
end

ActiveModel::Serializer's API has not really been consistent, in v0.9, however if you upgrade to v0.10, you could use the instance_options method to access the additional params. However, I'd be curious to learn how the objects were parsed in v0.9, though

Here is how you can pass parameters (options) from the parent serializer and show or hide attributes based on these parameters in the child serializer.
Parent serializer:
class LocationSharesSerializer < ActiveModel::Serializer
attributes :id, :locations, :show_title, :show_address
def locations
ActiveModelSerializers::SerializableResource.new(object.locations, {
each_serializer: PublicLocationSerializer,
params: {
show_title: object.show_title
},
})
end
end
Child serializer
class PublicLocationSerializer < ActiveModel::Serializer
attributes :id, :latitude, :longitude, :title, :directions, :description, :address, :tags, :created_at, :updated_at, :photos
def title
object.title if #instance_options[:params][:show_title]
end
end

Related

Conditional attributes and methods in rails serializer 0.10

class Api::V1::BookSerializer < ActiveModel::Serializer
attributes :id, :status, :name, :author_name, :published_date
attributes :conditional_attributes if condition_1?
belongs_to :user if condition_2?
end
Here I want to put condition on action basic of the controller.
For example I will like to send conditional_attributes for only index action and not for other actions.
But rails "active_model_serializers", "~> 0.10.0" does not give any such things according to my knowledge.
Something like this should do the trick:
class Api::V1::BookSerializer < ActiveModel::Serializer
attributes :id, :status, :name, :author_name, :published_date
attribute :conditional_attribute, if: :some_condition?
belongs_to :conditional_association, if: :some_other_condition?
private
def some_condition?
# some condition
end
def some_other_condition?
# some other condition
end
end
You can also use :unless for negated conditions.
You can use instance_options or instance_reflections in your conditions if you need them (see https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/howto/passing_arbitrary_options.md) or you can use scopes (see https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/serializers.md#scope)
Note: To the best of my knowledge, this only works with attribute and association methods – it doesn't work with attributes (see https://github.com/rails-api/active_model_serializers/blob/0-10-stable/lib/active_model/serializer.rb#L204-L210) since it doesn't pass options along.
I read your comment regarding sticking with AM Serializers, but I'll still point it out: If you're looking for a more robust and flexible solution than AM Serializers, jsonapi-serializer or Blueprinter work quite well and both have support for conditional fields as well as conditional associations.
I assume you're trying to render from the controller.
You can pass options to your serializer from the call to render:
render json: #track, serializer: Api::V1::BookSerializer, return_user: return_user?, return_extra_attributes: return_extra_attributes?
You can then access that option in your serializer definition, via #instance_options[:your_option].
Here, you would likely have something like:
class Api::V1::BookSerializer < ActiveModel::Serializer
attributes :id, :status, :name, :author_name, :published_date
attributes :conditional_attributes if return_conditional_attributes?
belongs_to :user if return_user?
def return_conditional_attributes?
#instance_options[:return_extra_attributes]
end
def return_user?
#instance_options[:return_user]
end
end
return_extra_attributes? and return_extra_attributes? would be method defined in your controller
documentation here: https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/howto/passing_arbitrary_options.md

Active Model Serializer use no serializer

At times I'd like to use no serializer for a model, and other times I do. I have tried to request nil serializer, but it seems that a serializer is used regardless.
class API::FinancialDashboardSerializer < ActiveModel::Serializer
attributes :id, :name, :weeks_remaining
has_many :check_ins, serializer: nil
end
In this instance I'd like to return the association without any serializer, but it still uses the CheckInSerializer anyway.
Is there a way around this?
I think you could just do:
class API::FinancialDashboardSerializer < ActiveModel::Serializer
attributes :check_ins, :id, :name, :weeks_remaining
end
Or if that doesn't work:
class API::FinancialDashboardSerializer < ActiveModel::Serializer
attributes :check_ins, :id, :name, :weeks_remaining
def check_ins
object.check_ins.map(&:as_json)
end
end

Apply filter to ActiveModel Serializer attributes to change the key

I have a serializer for my rest API. Currently, it looks like:
class TestSerializer < ActiveModel::Serializer
attributes :id, :name, :field_one__c, :field_two__c
end
I'm wondering if there are any ways to filter all the fields to have the __c removed when serializing, if there is a way to apply logic to ALL fields.
The case is I have a lot of fields with __c on the end, and I'd like to remove them all with a minimal amount of code on the serializer level.
Yes you can customize an attribute name in the serializer using the :key option:
attribute :field_one__c, key: :field_one
attribute :field_two__c, key: :field_two
You can also make any attribute conditional using :if or :unless options.
Doc: https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/serializers.md#attribute
Update:
For your special case, you can hack this by defining the attributes class method before the attributes list:
class TestSerializer < ActiveModel::Serializer
class << self
def attributes(*attrs)
attrs.each do |attr|
options = {}
options[:key] = attr.to_s[0..-4].to_sym if attr.to_s.end_with?('__c')
attribute(attr, options)
end
end
end
attributes :id, :name, :field_one__c, :field_two__c
end
If you have multiple serializer classes with the same requirement of filtering lots of attributes, you can apply the DRY principle in your solution by creating another serializer class which will inherit from ActiveModel::Serializer. Put the above class method definition inside this new serializer and inherit all the serializers from this new one which have list of attributes with __c.
class KeyFilterSerializer < ActiveModel::Serializer
class << self
def attributes(*attrs)
attrs.each do |attr|
options = {}
options[:key] = attr.to_s[0..-4].to_sym if attr.to_s.end_with?('__c')
attribute(attr, options)
end
end
end
end
class TestSerializer < KeyFilterSerializer
attributes :id, :name, :field_one__c, :field_two__c
end
class AnotherTestSerializer < KeyFilterSerializer
attributes :id, :name, :field_one__c, :field_two__c
end

ActiveModel Serializer with has_and_belongs_to_many

I have a model called Event. An Event has_and_belongs_to_many :event_sub_categories and a EventSubCategory has_and_belongs_to_many :events. I have the following action:
def index
#events = Event.where(begins_at: DateTime.now.beginning_of_day..1.week.from_now).group_by{|e| e.begins_at.beginning_of_day}.to_a.to_json
render json: #events
end
The action returns the data exactly as needed except for one problem, it doesn't have subcategories. I need the json to contain the subcategories. I tried making the following ActiveModel Serializer:
class EventSerializer < ActiveModel::Serializer
attributes :id, :name, :event_sub_categories
end
but the serializer above doesn't change the json at all. How do I fix this?
try
class EventSerializer < ActiveModel::Serializer
attributes :id, :name
has_many :event_sub_categories
end
Try this:
1- In your controller modify the query in a way it includes the event_sub_categories:
def index
#events = Event.includes(:event_sub_categories).where(begins_at: DateTime.now.beginning_of_day..1.week.from_now).group_by{|e| e.begins_at.beginning_of_day}.to_a.to_json
render json: #events
end
2- create a Serializer for EventSubCategory model
3- in your Event serializer create the method event_sub_categories
class EventSerializer < ActiveModel::Serializer
attributes :id, :name, :event_sub_categories
def event_sub_categories
object.event_sub_categories.map do |sub_category|
EventSubCategorySerializer.new(sub_category)
end
end
end

Serializing a custom attribute

I am using the Active Model Serializer gem for my application. Now I have this situation where a user can have an avatar which is just the ID of a medium.
I have the avatar info saved into Redis. So currently what I do to show the avatar at all in the serialized JSON, is this:
class UserSerializer < ActiveModel::Serializer
include Avatar
attributes :id,
:name,
:email,
:role,
:avatar
def avatar
Medium.find(self.current_avatar)[0]
end
#has_one :avatar, serializer: AvatarSerializer
has_many :media, :comments
url :user
end
I query Redis to know what medium to look for in the database, and then use the result in the :avatar key.
Down in the code there is also a line commented out, that was the only way I found on the https://github.com/rails-api/active_model_serializers/ page about using a custom serializer for something inside of serializer.
So to get to my problem. Right now the :avatar comes just like it is in the database but I want it to be serialized before I serve it as JSON.
How can I do that in this case?
You need to serialize Avatar Class:
class Avatar
def active_model_serializer
AvatarSerializer
end
end
Then you just use this way:
class UserSerializer < ActiveModel::Serializer
include Avatar
attributes :id,
:name,
:email,
:role,
:avatar
def avatar
# Strange you query another object
avatar = Medium.find(object.current_avatar).first
avatar.active_model_serializer.new(avatar, {}).to_json
end
has_many :media, :comments
url :user
end
According to the docs if you want a custom serializer you can just add:
render json: avatar, serializer: AvatarSerializer
or whatever your serializer name could be, here are the docs:
https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/serializers.md#scope

Resources