Serialize an array of models using active_model_serializers - ruby-on-rails

I am trying to send the serialized version of a model to a view as a param, using the gem active_model_serializers
#app/serializers/admin_serializer.rb
class AdminSerializer < ActiveModel::Serializer
attributes :id, :email, :access_locked?
end
#app/controllers/dashboard/admins_controller.rb
def index
#search = Admin.search(params[:q])
#admins = #search.result(:distinct => true).page(params[:page]).per(10)
#page_entries_info = view_context.page_entries_info #admins
# render json: #admins
respond_to do |format|
format.html
format.js
format.json {render json: #admins}
end
end
#app/views/dashboard/admins/index.html.erb
<%= debug (ActiveModel::Serializer::Adapter.adapter_class(:json_api).new(ActiveModel::Serializer.serializer_for(#admins.first).new(#admins.first),{}).to_json) %>
<%= debug (#admins.all.map{|admin| AdminSerializer.new(admin).to_json}) %>
Above debugs are yielding the below response:
--- '{"data":{"id":"1","type":"admins","attributes":{"email":"tech#bluesapling.com","access_locked?":false}}}' //returned by the first debug
---
- '{"object":{"id":36,"email":"aubrey_schmitt#feeneykoch.io","created_at":"2016-03-28T05:15:17.546Z","updated_at":"2016-03-28T05:15:17.546Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":20,"email":"alysa_johnston#thompson.io","created_at":"2016-03-28T05:15:16.304Z","updated_at":"2016-03-28T05:15:16.304Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":22,"email":"kristofer.langosh#kunzeluettgen.com","created_at":"2016-03-28T05:15:16.459Z","updated_at":"2016-03-28T05:15:16.459Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":37,"email":"beryl_keler#wiza.biz","created_at":"2016-03-28T05:15:17.624Z","updated_at":"2016-03-28T05:15:17.624Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":5,"email":"wilhelmine_buckridge#crona.io","created_at":"2016-03-28T05:15:15.139Z","updated_at":"2016-03-28T05:15:15.139Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":14,"email":"edward_wisoky#corkery.net","created_at":"2016-03-28T05:15:15.838Z","updated_at":"2016-03-28T05:15:15.838Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":27,"email":"leonor#jerde.biz","created_at":"2016-03-28T05:15:16.848Z","updated_at":"2016-03-28T05:15:16.848Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":2,"email":"carley#wyman.net","created_at":"2016-03-28T05:15:14.873Z","updated_at":"2016-03-28T05:15:14.873Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":10,"email":"ervin.gleichner#cremin.org","created_at":"2016-03-28T05:15:15.527Z","updated_at":"2016-03-28T05:15:15.527Z"},"instance_options":{},"root":null,"scope":null}'
- '{"object":{"id":15,"email":"lonzo.dickens#johnscole.name","created_at":"2016-03-28T05:15:15.916Z","updated_at":"2016-03-28T05:15:15.916Z"},"instance_options":{},"root":null,"scope":null}'
In the first debug I am serializing only one object, while in the second one I am trying to do it for an array of objects.
The first debug is correctly returning the serialized version of the object(in json_api format) while second debug is not.
Tried ArraySerializer as well, with no success: ActiveModel::Serializer::ArraySerializer.new(#admins, each_serializer: AdminSerializer).as_json
how do I achieve the desired serialization. Moreover, if achieved, can I used some other simplified version of this? As this debug statement is way too verbose.
Tried all the solutions mentioned here - How do you initialize an ActiveModel::Serializer class with an ActiveRecord::Relation array?
The basic problem which I am trying to solve is, in the index method of the Admin controller, the Admin object is passed as a PORO to the index.html file. But I want the serialized json version of this object so that I can pass it to my react components as a prop
index method is rendering proper json on firing http://dashboard.localhost.com:3000/admins.json
UPDATE#1 for the index method
def index
#search = Admin.search(params[:q])
#admins_array = #search.result(:distinct => true).to_a
if params[:page]
#admins = #search.result(:distinct => true).page(params[:page][:number]).per(10)
#admins_json_array = Kaminari.paginate_array(#admins_array).page(params[:page][:number]).per(10)
else
#admins = #search.result(:distinct => true).page(1).per(10)
#admins_json_array = Kaminari.paginate_array(#admins_array).page(1).per(10)
end
#admins_json = ActiveModel::SerializableResource.new(#admins_json_array.to_a)
...
...
...
end

I have a controller that I need to specify the serializer in, due to wanting different attributes from the default serializer.
In Controller:
def index
search = User.ransack(search_params)
render json: search.result, each_serializer: MembershipRenewalSerializer::MemberSerializer
end
So, just to get things working, what happens if you specify the each_serializer option?
Edits:
Outside Controller:
ActiveModel::SerializableResource.new(
User.first(2),
each_serializer: MembershipRenewalSerializer::MemberSerializer
).to_json
Note, that without specifying each_serializer, SerializableResource would use the UserSerializer.
Edit #2,
It looks like there is something weird happening with the #admins data.
Try converting to an array:
ActiveModel::SerializableResource.new(#admins.to_a).to_json
Edit #3
To paginate your array, try the following:
#search = Admin.search(params[:q])
#results = #search.result(:distinct => true).to_a
#admins = Kaminari.paginate_array(#results).page(params[:page]).per(10)

Follow the guide: Serializing before controller render
You could use ActiveModel::SerializableResource.new(#admins, adapter: :json_api).to_json
in index.html.erb
<%= debug (ActiveModel::SerializableResource.new(#posts, adapter: :json_api).to_json) %>
below is the output(using posts)
'{"data":[{"id":"1","type":"posts","attributes":{"title":"first post","body":null}},{"id":"2","type":"posts","attributes":{"title":"second post","body":null}}],"links":{}}

I create a concern with some API helper methods and there you can check if its a collection pass find the appropiate serializer and pass it to the collection serializer.
def api_response(data)
render json: wrap_answer(data)
end
def wrap_answer(data)
if data.respond_to?(:each)
ActiveModel::Serializer::CollectionSerializer.new(data, each_serializer: ActiveModel::Serializer.serializer_for(data.first))
else
data
end
end

should have made that into string and use json.stringify to make that as a string and make your life easy

Related

Problem with selecting elements with the same params

What i do wrong? I want to return every products which pass condition:
def show
products = Product.select{|x| x[:category_id] == params[:id]}
render json: products
end
When i write
def show
products = Product.select{|x| x[:category_id] == 1}
render json: products
end
it works why the first example doesn't work?
I am pretty sure that there is mismatch in data type.
1=='1' #will be always false
1==1 #will be true
'1'=='1' #will be true as well
And also check for nil value from params[:id]
Please make sure to change as follows
def show
products = Product.select{|x| x.category_id == params[:id].to_i}
render json: products
end
OR
The best solution as suggested by #Eyeslandic is to use .where as it will not check for mismatch in data type. And also you don't have to take care of nil value from params[:id].
You should really be using a where to stop sql from loading all your products.
#products = Product.where('category_is = ?', params[:id])
The being said, if you are sticking to rails restful conventions, the fact you have a param called :id that is the category_id suggests you are on the category controller. So maybe consider changing your logic to:
#category = Category.includes(:products).find(params[:id])
you can then access products via
#category.products
or if your not interested in the category too much maybe
#products = Category.includes(:products).find(params[:id])&.products

Order_by Rails Serializer transformation

My understanding is that you can only call the AMS when you're using render like this:
render json: foos, each_serializer: FoosSerializer
But what if I wanted to do something with the result from the serialized resource after it's been serialized? Is that possible? My issue is that my serializer calls a method on the resource to transform one of the resource's attributes and I need to order by that attribute before passing it to the front end.
When I do the below:
ActiveModel::SerializableResource.new(
foos,
each_serializer: FoosSerializer
).to_json
I get back json, but can I transform it back to something that I can call order_by on?
EDIT:
I have this:
JSON.parse(ActiveModel::SerializableResource.new(
foos,
each_serializer: FoosSerializer
).to_json).sort {|x,y| x[:name] <=> y[:name]}
But this seems silly to call to_json and parse in order to call order between as I still need to do something with the parsed result after ward.
How about creating another transformer serializer as FooSortSerializer
class FooSortSerializer < ActiveModel::Serializer
def attributes(_options = {}, _reload = false)
object.collect do |foo_instance|
FooSerializer.new(foo_instance).attributes
end.sort {|x,y| x[:name] <=> y[:name]
end
end
In controller
render json: foos, serializer: FooSortSerializer

How to create temporary attribute & use that in json reponse in rails active record

I am new to ROR.
I am having a controller where i am getting the search results in available_users variable..
availble_users //some active record result with id,name & address
available_users.each do |userstatus|
userstatus.class_eval do
attr_accessor :is_friend
end
if current_user.invitations.find_by(:friend_id => userstatus.id) //invitation is another table
userstatus.is_friend = "true"
else
userstatus.is_friend = "false"
end
end
render json: available_users
but when i am getting the response on ajax request it is serving the same array without including is_friend column.
here is my json response.
id: 2
name: abc
address:
please can anyone figure me out why it is not appending this temporary attribute.
Thanks.
what you have will work if you pass the methods option to_json
render json: available_users.to_json(:methods => :is_friend)
Or you could do this
available_users.each do |userstatus|
if current_user.invitations.find_by(:friend_id => userstatus.id) //invitation is another table
userstatus["is_friend"] = "true"
else
userstatus["is_friend"] = "false"
end
end
render json: available_users
[]= is an alias for write_attribute

Ruby: Formatting table data to JSON

I'm trying to make a timeline for an the bugs and updates for an open source project. I'm new to ruby, but I'm getting some experience gradually.
I've created a table called historical_gems, with the following code in the model:
class HistoricalGem < ActiveRecord::Base
attr_accessible :build_date, :version
belongs_to :ruby_gem, :foreign_key => :gem_id
end
I'm using a JS Plugin (http://almende.github.com/chap-links-library/js/timeline/doc) that requires objects with two field names ('start' for the date and 'content' for the title) in the JSON Array to display the timeline using JS.
I believe I have to do something like this in the controller which defines my timeline method to render the JSON:
def timelinem
#name = params[:id]
#rpm = AbcTable.find_by_name(#name)
respond_to do |format|
format.json { render :json => #rpm.json_timelines }
end
end
Then I probably would have to define a 'json_timelines' method inside my model, maybe something like:
def json_timelines(gems = [])
dates = []
gem_id.each { |p|
gems << p
dates << p.build_date(gems)
end
}
end
I'm only starting out with RoR, and even after hours with guides and tutorials and debugging, I'm not able to put together this code. Can anyone help me out, please? I don't think I'm doing it right.
btw, don't be too harsh if I overlooked something obvious, I'm only 16 :)
The render :json => ... in your code should work fine (but with HistoricalGem instead of AbcTable) as long as json_timelines returns an object that's serializable as JSON (e.g., an Array or a Hash).
Try something like this for your method definition:
def json_timelines(gems = [])
gems.map do |g|
{
:content => g.title,
:date => g.build_date
}
end
end
The above snippet assumes your "historical_gems" table has "title" and "build_date" columns; if not, adjust the code to reflect the fields you actually want represented in your JSON.

Array of ActiveRecords to JSON

I am aware that ActiveRecord provides a to_json method which allows fields to be filtered out of the JSON output using :only and :except.
At present I am using the following to format an array from a find as JSON:
#customers = Customer.find(:all)
...
format.js { render :json => #customers}
How would I be able to select the fields to be output in the objects in the array? Is there a shortcut or do I need to do this by hand?
Cheers,
Adam
I think you answered your own question. With Rails 2.3.x you could use the following:
#customers = Customer.all #Shortcut for to Customer.find(:all)
respond_to do |format|
format.js { render :json => #customers.to_json(:only=>[:column_one, :column_two]}
end
You can overwrite the to_json method of the model class if you want to globally apply the change for the model.
For example, to exclude null values from the rendered JSON you could overwrite the original ActiveRecord method to_json
def to_json(options)
hash = Serializer.new(self, options).serializable_record
hash = { self.class.model_name => hash } if include_root_in_json
ActiveSupport::JSON.encode(hash)
end
with this in your model class:
def to_json(options)
hash = Serializer.new(self, options).serializable_record.reject {|key, value| value.nil? }
hash = { self.class.model_name => hash } if include_root_in_json
ActiveSupport::JSON.encode(hash)
end
If you peep into the ActionController::Base class, you'll see that it calls to_json on your collection immediately (no extra options used), so you've got to have it already prepared. So if in your action you don't use the attributes that are not rendered to json, you can replace your find with
#customers = Customer.find(:all, :select => ["id", ...])
to select only the ones that you need.

Resources