ActiveModel Serializer with has_and_belongs_to_many - ruby-on-rails

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

Related

How to pass parameters Active Model Serializer to another Serializer?

having a problem with sending parameters.
I have user_serializer and book_serializer and I want to send user_id to book_serializer inside user_serializer. Like this:
class UserSerializer < ActiveModel::Serializer
attributes :id
has_many :books, serializer: BookSerializer, your_option_name: object.id
end
and then BookSerializer
class BookSerializer < ActiveModel::Serializer
attributes :id, :test
def test
#instance_options[:your_option_name]
end
end
but it's not working, getting null.
are there any ideas? great thanks.
The arguments that you passed into has_many ... is actually used to create a HasManyReflection object which only accepts some very specific values and it also does not pass its argument any further.
The correct way to pass custom argument to nested serializers is through the build_association method or directly like below:
class UserSerializer < ActiveModel::Serializer
attributes :id, books
def books
ActiveModel::SerializableResource.new(object.books, each_serializer: BookSerializer, your_option_name: object.id)
end
end

how to optionally include / delete values in an Active Model Serializer 0.10 class

I have the folowing ASM 0.10 :
class UserMicroSerializer < ActiveModel::Serializer
attributes :id, :name, :is_friend
def is_friend
#instance_options[:is_friend]
end
end
but would also like to support not having the is_friend attribute.
I have tried various things like:
class UserMicroSerializer < ActiveModel::Serializer
attributes :id, :name
if #instance_options[:is_friend]
attributes :is_friend
end
def is_friend
#instance_options[:is_friend]
end
end
but get error msg:
NoMethodError: undefined method `[]' for nil:NilClass
How would I make the #instane_options conditionally include is_friend?
If you can conditionally use a different serializer in the controller then you may be able to do this
class SimpleUserMicroSerializer < ActiveModel::Serializer
attributes :id, :name
end
By subclassing the simple serializer, you don't have much code overlap
class UserMicroSerializer < SimpleUserMicroSerializer
attributes :is_friend
def is_friend
#instance_options[:is_friend]
end
end
You can also send { scope: 'is_friend' } from controller and then check it into serializer.
class UserMicroSerializer < ActiveModel::Serializer
attributes :id, :name, :is_friend
def filter(keys)
keys.delete :is_friend if scope and scope[:is_friend]
super(keys)
end
end

Passing options to ActiveModel serializer

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

has_ancestry and active model serializers

I have a model Category
class Category < ActiveRecord::Base
attributes :id, :name, :order, :x, :y, :z
has_ancestry
end
In my controller, I can use the following to get the whole tree as JSON
Category.first.subtree.arrange_serializable
But this returns all DB attributes such as created_at or id
I wanted to use the active model serializer to shape my output without losing the tree structure.
class CategorySerializer < ActiveModel::Serializer
# Children is the subtree provided by ancestry
attributes :name, :x, :children
end
Controller
class CategoryController < ActionController::Base
def index
category = Category.first
render :json => category
end
end
The code above will only show the first sub level, but not the children of the children.
Any help appreciated
To use arrangement, we need to pass an additional parameter to serializer, you can do it this way:
category.subtree.arrange_serializable do |parent, children|
CategorySerializer.new(parent, scope: { children: children })
end
And here's how you can take that parameter in serializer:
class CategorySerializer < ActiveModel::Serializer
attributes :id, :name, :order, :children
def children
scope.to_h[:children]
end
end
You may also want to take a look at this test to have a better understanding how arrange_serializable works.
In AMS 10.x (master branch) we can support external parameters in such way:
class CategorySerializer < ActiveModel::Serializer
attributes :id, :name, :order, :children
def children
instance_options[:children]
# or instance_options[:children]&.as_json
end
end
Next you can simply pass children to the serializer:
category.subtree.arrange_serializable do |parent, children|
CategorySerializer.new(parent, children: children)
end
or
category.subtree.arrange_serializable do |parent, children|
ActiveModelSerializers::SerializableResource(parent, children: children)
end

Rails Serializer for Devise User model not being used

Here's the response my Rails app is giving me:
{"users":[{"users":{"id":20,"email":"subscriber#email.com", ...
Here's what it should looks like (this is for another working resource):
{"companies":[{"id":448,"name":"Microsoft Security ...
Notice the wrapping users: object.
Here's my Serializer for the User model:
class UserSerializer < ApplicationSerializer
attributes :id,
:first_name,
:last_name
end
And for the Company model:
class CompanySerializer < ApplicationSerializer
attributes :id, :name
end
What am I missing? What I need to do so the json response uses this UserSerializer class?
def index
load_users
render json: #users
end
I knew it was a problem with Rails somehow ignoring my Serializer.
In my controller I wrote this:
# app/controllers/api/v1/users_controller.rb:
class Api::V1::UsersController < ApplicationController
def default_serializer_options
{ each_serializer: UserSerializer } # I use each_serializer instead of serializer because it's rendering a collection.
end
end
Basically I man-handled the code to use the right serializer and it's working fine now.

Resources