Rails 3 respond_to json, with custom attributes/methods - ruby-on-rails

In a rails app I have an action that returns a json representation of a collection of different models. It looks something like this:
respond_to :json
def index
#cars = Car.all
#vans = Van.all
respond_with({
:cars => #cars,
:vans => #vans
})
end
However, I want to customise the attributes and methods that are passed to the json object. A bit like:
respond_with({
:cars => #cars.to_json(:only => [:make, :model], :methods => [:full_name]),
:vans => #vans
})
Doing the above, causes the json representation of the "cars" to be escaped as one big string, like:
{
"cars":"[{\"car\":{\"make\":\"Ford\" ... etc
"vans": [{"van":{"make":"Citreon" ... vans not escaped
}
Obviously I'm approaching this the wrong way. Can anyone point me in the right direction?

Since you're nesting the to_json in another Hash, I think you need to use as_json (which returns a Hash instead of a String) instead:
respond_with({
:cars => #cars.as_json(:only => [:make, :model], :methods => [:full_name]),
:vans => #vans
})

Related

Rails to_json including association with limit

I am rendering one record with with association like below
render :json => Scheme.where("id=?", params[:id]).first
.to_json(:include => { :navs => { :only => [:schemeCode,:navDate,:navValue] }})
Associations
Scheme has_many navs
Nav belongs_to scheme
I need to render only last record in Nav , but above will print all the navs since its one to many. I tried :limit => 1 and ordering it in desc , buts limit itself not working.Any help will be appreciated.
render :json => Scheme.where("id=?", params[:id]).first
.to_json(:include => { :navs => { :only => [:schemeCode,:navDate,:navValue], :limit => 1 }})
If you're creating a complicated json format, try gem 'jbuilder' introduced in RailsCast to leave the structure to view.
In your show.json.jbuilder view, make it something like:
#scheme = Scheme.find(params[:id])
json.scheme #scheme.as_json
json.extract! #scheme.navs.last, :schemeCode, :navDate, :navValue
which will render the data in json format as well as keep your controller clean & neat.
The use of Scheme.where("id=?", params[:id]).first in your code can be shortened as Scheme.find(params[:id]) if your id is unique.

Add virtual attribute to json output

Let's say I have an app that handles a TODO list. The list has finished and unfinished items. Now I want to add two virtual attributes to the list object; the count of finished and unfinished items in the list. I also need these to be displayed in the json output.
I have two methods in my model which fetches the unfinished/finished items:
def unfinished_items
self.items.where("status = ?", false)
end
def finished_items
self.items.where("status = ?", true)
end
So, how can I get the count of these two methods in my json output?
I'm using Rails 3.1
The serialization of objects in Rails has two steps:
First, as_json is called to convert the object to a simplified Hash.
Then, to_json is called on the as_json return value to get the final JSON string.
You generally want to leave to_json alone so all you need to do is add your own as_json implementation sort of like this:
def as_json(options = { })
# just in case someone says as_json(nil) and bypasses
# our default...
super((options || { }).merge({
:methods => [:finished_items, :unfinished_items]
}))
end
You could also do it like this:
def as_json(options = { })
h = super(options)
h[:finished] = finished_items
h[:unfinished] = unfinished_items
h
end
if you wanted to use different names for the method-backed values.
If you care about XML and JSON, have a look at serializable_hash.
With Rails 4, you can do the following -
render json: #my_object.to_json(:methods => [:finished_items, :unfinished_items])
Hope this helps somebody who is on the later / latest version
Another way to do this is add this to your model:
def attributes
super.merge({'unfinished' => unfinished_items, 'finished' => finished_items})
end
This would also automatically work for xml serialization.
http://api.rubyonrails.org/classes/ActiveModel/Serialization.html
Be aware though, you might want use strings for the keys, since the method can not deal with symbols when sorting the keys in rails 3. But it is not sorted in rails 4, so there shouldn't be a problem anymore.
just close all of your data into one hash, like
render json: {items: items, finished: finished, unfinished: unfinished}
I just thought I'd provide this answer for anyone like myself, who was trying to integrate this into an existing as_json block:
def as_json(options={})
super(:only => [:id, :longitude, :latitude],
:include => {
:users => {:only => [:id]}
}
).merge({:premium => premium?})
Just tack .merge({}) on to the end of your super()
This will do, without having to do some ugly overridings. If you got a model List for example, you can put this in your controller:
render json: list.attributes.merge({
finished_items: list.finished_items,
unfinished_items: list.unfinished_items
})
As Aswin listed above, :methods will enable you to return a specific model's method/function as a json attribute, in case you have complex assosiations this will do the trick since it will add functions to the existing model/assossiations :D it will work like a charm if you dont want to redefine as_json
Check this code, and please notice how i'm using :methods as well as :include [N+Query is not even an option ;)]
render json: #YOUR_MODEL.to_json(:methods => [:method_1, :method_2], :include => [:company, :surveys, :customer => {:include => [:user]}])
Overwritting as_json function will be way harder in this scenario (specially because you have to add the :include assossiations manually :/
def as_json(options = { })
end
If you want to render an array of objects with their virtual attributes, you can use
render json: many_users.as_json(methods: [:first_name, :last_name])
where first_name and last_name are virtual attributes defined on your model

Rails3-amf and bringing an association to amf

i am changing from WebORB to Warhammerkids Rails3-amf - great choice, even tehre are some open issues. One those issues is, how can I get records from an association to the array, which is sent back to the Flex-Application.
In WebORB the method of the controller looks like:
def getClients(clientFilter,myMandant)
clients = Client.find(:all, :conditions => {:account_id => myMandant}, :include => [:addresses, :contacts, :proofs])
end
in Rails3-amf I have a similar construction:
def getClients()
#clients = Client.find(:all, :conditions => {:account_id => params[1]}, :include => [:addresses, :contacts, :proofs])
respond_with(#clients) do |format|
format.amf { render :amf => #clients}
end
With this code I get back all correctly typed Client Objects as an Array, but without the records from the ":include" argument.
How can I handle this ??
I also tried another way with:
....
respond_with(#clients) do |format|
format.amf { render :amf => #clients.to_amf(:include => [:addresses, :contacts, :proofs])}
....
With this try I got an error message" undefined method to_amf for #.
Thanks for any help.
I don't know about rail3-amf, but you might find it worthwhile having a look at restfulx - https://github.com/dima/restfulx/tree/rails3
It consists of a library for rails, and a library for flex. It supports data transfer through json, xml, or amf.
The actionscript api for working with models is very nice too:
var user:User = new User();
user.first_name = "Ed";
user.create();
It can also keep track of rails associations etc.:
trace(user.account.title);
See more usage here - https://github.com/dima/restfulx_framework/wiki/Working-with-RestfulX-Models

using different key for to_json :methods

When using :methods in to_json, is there a way to rename the key? I'm trying to replace the real id with a base62 version of it, and I want the value of base62_id to have the key :id.
#obj.to_json(
:except => :id
:methods => :base62_id
)
I tried to do
#obj.to_json(
:except => :id
:methods => { :id => :base62_id }
)
but that didn't work.
Any advice?
The to_json serializer uses the name of the method as the key for serialization. So you can't use the methods option for this.
Unfortunately to_json method doesnt acceptblock` parameter, otherwise you could have done something similar to
#obj.to_json(:except => :id) {|json| json.id = base62_id }
So that leaves us with a ugly hack such as:
def to_json(options={})
oid, self.id = self.id, self.base62_id(self.id)
super
ensure
self.id = oid
end
Now to_json will return the expected result.

Adding further attributes to a ActiveRecord serialization?

I have my json serialization working fine
render :json => "#{current_object.serialize(:json, :attributes => [:id, :name])}
But I also want to add further data to the json before it gets set back to the client. Mainly the auth_token.
Googled around like crazy but I can not find what option serialize will take to allow me to append/merge my other data into the JSON.
Hopting to find something like this...
current_object.serialize(:json, :attriubtes => [:id, name], :magic_option => {:form_authenticity_token => "#{form_authenticity_token}"})
You want the :methods key, which works like :attributes, but will include the results of the methods given. In your case:
current_object.to_json(
:attributes => [:id, :name],
:methods => [:form_authenticity_token]
)
For what it's worth, in a recent Rails I hacked together what you want like this:
sr = ActiveRecord::Serialization::Serializer.new(your_object, some_serialization_options).serializable_record
sr['extra'] = my_extra_calculation(some_parameters)
format.json { render :json => sr }
Where your_object is what you want to serialize, some_serialization_options are your standard :include, :only, etc parameters, and my_extra_calculation is whatever you want to do to set the value.
Jimmy
Hacked it in and moving on...
current_object.serialize(:json, :attributes => [:id, :name]).gsub(/\}$/, ", \"form_authenticity_token\": \"#{form_authenticity_token}\"}")

Resources