Rails Active Model Serializer - Custom Serializer on AMS 0.10 - group_by - ruby-on-rails

I have updated active_model_serializer from 0.9.3 to 0.10.0 as I want to use the json_api adapter. Once updated, I no longer can use both my model serializer along with my custom serializer. Here's my backtrace:
[active_model_serializers] Rendered GroupedProjectsSerializer with
ActiveModelSerializers::Adapter::JsonApi (6.03ms)
Completed 500 Internal Server Error in 24ms (ActiveRecord: 0.3ms)
NoMethodError (undefined method `read_attribute_for_serialization' for
#<Hash:0x007f90cbed0ba8>):...
I have also tried including ActiveModel::Serialization in the custom serializer which then I get another error as below:
NoMethodError (undefined method `id' for #<GroupedItemSerializer:0x007fa47012ad38>):...
adding attributes :id doesn't resolve the issue though.
Here's my setup:
Initializer:
ActiveModel::Serializer.config.adapter = :json_api
Model Serializer:
class ItemSerializer < ActiveModel::Serializer
attributes :id, :code, :name, :item_type, ....
end
Custom Serializer:
class GroupedItemSerializer < ActiveModel::Serializer
def serializable_object(options = {})
#object.map do |group_key, items|
[group_key, serialized_items(items)]
end.to_h
end
private
def serialized_items(items)
items.map do |item|
ItemSerializer.new(item, root: false)
end
end
end
Controller:
#items = Item.all.group_by(&:item_type)
# using each_serializer also doesn't help as it only uses the
# GroupedItemserializer and not applying the ItemSerializer!
render json: #items, serializer: GroupedItemSerializer
Not sure if I'm missing something in the documentation, but I've tried all the possible ways to achieve this. Any help would be appreciated.

Related

Active Model Serializer not responding with the correct attributes

I am working on a rails-api project where I am using active model serialiazer. But unfortunately its not working as expected. This is my V1::HuntsController
class V1::HuntsController < V1::MainController
def index
render json: Hunt.all
end
end
My hunts serializer looks like this
class HuntSerializer < ActiveModel::Serializer
belongs_to :user
attributes :id,:title,:picture_url,:clue
private
def picture_url
object.picture.url
end
end
But in my response i am getting all the attributes from hunt. I tried to explicitly define serializer to avoid versioning issues as well.
render json: {data: Hunt.all } ,each_serializer: HuntSerializer
But nothing seems to work. In the logs I can see,
[active_model_serializers] Rendered V1::HuntSerializer with Hash (32.36ms)
Whats happening here. Any help would be appreciated.
try
render json: Hunt.all, each_serializer: HuntSerializer (no need for data root)
then to verify that the serializer gets hit, put a byebug in the body of the picture_url function. if the byebug gets hit, you are indeed using your serializer. (must have gem byebug included in gemfile)

How to make the belongs_to model's id unnested in JSON response

I get the following response by render json: Oj.dump(ReportTypeSymbol.all.to_a)
Current JSON format
report_type: "com_disagg",
symbol_code_id: {
$oid: "54bf7ff1506f6336e3020000"
}
Expected JSON format (do not nested the symbol_code_id)
report_type: "com_disagg",
symbol_code_id: "54bf7ff1506f6336e3020000"
Model definition
class ReportTypeSymbol
include Mongoid::Document
belongs_to :symbol_code
end
You can override as_json method in your model.
This is what I did. It works nested relations too.
Basically it's monkey-patching the Mongoid Documents and BSON objects.
Create a file Mongoid.rb in initializers (Rails)
module Mongoid
module Document
def as_json(options={})
attrs = super(options)
attrs['id'] = attrs.delete('_id').to_s
attrs
end
end
end
module BSON
class ObjectId
alias :to_json :to_s
alias :as_json :to_s
end
end

RocketPants and ActiveModelSerializers custom serializer

Damn, that's me again...
Quote from RocketPants git:
Support for active_model_serializers - If you want to use ActiveModelSerializers, we'll take care of it. Even better, in your expose call, pass through :serializer as expected and we'll automatically take care of invoking it for you.
So that's what I try to do:
def friends
#user = User.find_by_id(params[:id])
expose #user.friends.first(params[:limit].to_i), serializer: UserJustNameSerializer
end
And that's how I implement my serializers in user_serializer.rb:
class UserSerializer < ActiveModel::Serializer
....
end
class UserJustNameSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name, :full_name, :avatar_thumb
def avatar_thumb
object.avatar.url(:thumb)
end
end
Using expose without serializer option is properly preparing JSON according to UserSerializer. Trying to use UserJustNameSerializer gives this error:
NameError (uninitialized constant Api::V1::UsersController::UserJustNameSerializer)
So the question is: how to properly show RocketPants the way to my serializer? Now it's trying to find it in UsersController.
So, as always, only after posting the question I get to answer =)
The solution is:
Place UserJustNameSerializer into its own file user_just_name_serializer.rb and use each_serializer: instead of serializer:, as I'm trying to serialize an array, not a single object.
For those who google: If I use serializer: UserJustNameSerializer on array, I get
NoMethodError (undefined method `read_attribute_for_serialization' for []:Array):

undefined method `model_name' in rails 3

While rendering xml for an object, I am getting the error
NoMethodError (undefined method `model_name' for OrderResponse:Class):
OrderResponse.rb
class OrderResponse
include ActiveModel::Serialization
attr_accessor :payload
end
In controller
def create
#order_response = OrderResponse.new
#order_response.payload = 12345
respond_to do |format|
format.xml { render :xml => #order_response }
end
end
I found other questions with similar titles while searching, according to that i modified 'respond_to' with 'respond_with' which inturns throws an error
undefinedMethod 'model_name' in OrderResponse
How to solve this?
I found an answer to this somewhere on stackoverflow and wish I could credit the source... This is my interpretation of it.
In Rails 3, if you have a resource listed in your routes which has a model .rb file but no active record table behind it, then you'll see this kind of error. This appeared for me as a form_for trying to reference a :controller and :action in this model. Perhaps it is related to Rails attempting to process associations for the model or something similar. Either way, this is new for me since I upgraded an application from Rails 2.3.8.
For me, the appears as:
undefined method `model_name' for SomeModel:Class
To fix it, at the top of the affected class add:
extend ActiveModel::Naming
include ActiveModel::Conversion
def persisted?
false
end
This has worked for me on two models like this.
You could try defining a class method by that name, which returns the name of the class:
def self.model_name; 'OrderResponse'; end
try including ActiveSupport::CoreExtensions::Module where model_name is defined ActiveSupport::CoreExtensions::Module

Rails 3 ActiveModel::Serializers seem to need lots of support methods

I'm returning to RoR after not using it for a few years and I'm trying to use ActiveModel to serialise a plain object to XML.
I'm doing the following, as per the comments in activemodel/lib/activemodel/serialization.rb:
class XmlError
include ActiveModel::Serializers::Xml
attr_accessor :code
attr_accessor :description
def attributes
#attributes ||= {'code' => 'nil', 'description' => 'nil'}
end
def initialize(error_code)
#code = error_code
#description = "blah"
self
end
end
I use this in a controller as:
render :xml => XmlError.new("invalid_login")
and I get the following stacktrace:
NoMethodError (undefined method `model_name' for XmlError:Class):
app/controllers/users_controller.rb:19:in `login'
app/controllers/users_controller.rb:5:in `login'
If create a model_name class method, I then get the following stacktrace:
NoMethodError (undefined method `element' for "XmlError":String):
app/controllers/users_controller.rb:19:in `login'
app/controllers/users_controller.rb:5:in `login'
It feels like I'm chasing my tail here. Have I just missed something simple in my class? I followed the example closely.
extend ActiveModel::Naming
is what you are looking for.
http://rdoc.info/github/lifo/docrails/master/ActiveModel/Naming
Why not sub-class ActiveModel::Base?

Resources