respond_to :json + respond_with + except (for all actions) - ruby-on-rails

AlbumsController:
respond_to :json
def index
respond_with albums.ordered
end
Now how can I make it so that the underlying call to_json always executes with this options: except => [:created_at, :updated_at]? And I don't mean just for this action, but for all others actions defined in this controller.

The as_json method is what is used to serialize into json
class Album
def as_json(params={})
params.merge! {:except=>[:created_at, :updated_at]}
super(params)
end
end

You could define serializable_hash on your model which defines the keys and values to return. Rails will then return JSON / XML based on this hash:
def serializable_hash
{ :name => "Stack Overflow",
:created_at => created_at",
:posts_count => posts.count
}
end

Related

Ruby on Rails "render json:" ignoring alias_attribute

I have the following code in my account.rb model file:
class Account < ActiveRecord::Base
alias_attribute :id, :accountID
alias_attribute :name, :awzAccountName
alias_attribute :description, :awzAccountDescription
end
And the following code in the index method from my accounts_controller.rb file:
def index
#accounts = Account.all
if params["page"]
page = params["page"]
items_per_page = params["per_page"]
render :json => {:total => #accounts.count,:accounts => #accounts.page(page).per(items_per_page) }
else
render json: #accounts
end
end
As expected, render json: #accounts returns a result set that contains the alias_attribute column names defined in the model file. However, the render :json => {:total => #accounts.count,:accounts => #accounts.page(page).per(items_per_page) } code returns a result set that contains the original column names. Is there any way to change this so that the alias_attribute column names are used?
I wouldn't expect render json: #accounts to include the aliased attributes at all. The alias_attribute just gives you the luxury of referring to the attribute with another name - it doesn't replace the original name at all.
If you do want to include the aliases in your json output for a model you can override as_json and add those methods explicitly:
def as_json(options = {})
options[:methods] ||= []
options[:methods] += [:name, :description]
super(options)
end
(I've deliberately omitted :id as that may be a special case - not entirely sure and can't test locally at the moment)
I was able to solve this by overwriting serializable_hash method.
def serializable_hash(options = {})
options[:methods] ||= []
options[:methods] += [:name, :description]
super(options)
end
You can achieve the same result by passing methods argument to as_json without changing your default serialization of your models. like this:
render json: #accounts.as_json(methods: [:name, :description])

How to pass an argument in an method which in being represented as symbol?

I have an action as :
def get_data
#people = Person.all
respond_to do |format|
format.json do
render :json => {
:success => true,
:people => #people.as_json({
:only => [:person_name, :text_description, :text_heading],
:methods => [:title,:age_group],
})
}
end
end
end
Here title and age_group are my methods in model Person
def age_group
self.name
end
Now i want to method to look like this
def age_group(age)
# ...
end
How do i pass this argument from the controller as the methods representation there is as symbol.
Hi as per my suggestion you can override method or create a instance method depending upon options it will generate hash or json.If you want to use as_json then you can dig into code this line is helpful for digging code https://github.com/rails/rails/blob/2-3-stable/activerecord/lib/active_record/serialization.rb#L33 which will give you how methods being passed.

Overriding as_json method with params

First of all, I'm using Rails 3.0.6 and Ruby 1.9.2
I have a controller with two different actions, both should return a json object, but with different formats. Therefore I'm overriding the as_json method to write the JSON object in my own format. Problem is that I don't know how to pass params to as_json method since it's being automatically called by Rails.
My code looks like this:
class MyController < ApplicationController
def action1
# my code
respond_to do |format|
# Render with :json option automatically calls to_json and this calls as_json
format.js { render :json => #myobjects }
end
end
def action2
# a different code
respond_to do |format|
# This action should return a JSON object but using a different format
format.js { render :json => #myobjects }
end
end
end
class MyModel < ActiveRecord::Base
def as_json(options = {})
# I would like to add a conditional statement here
# to write a different array depending on one param from the controller
{
:id => self.id,
:title => self.description,
:description => self.description || "",
:start => start_date1.rfc822,
:end => (start_date1 && start_date1.rfc822) || "",
:allDay => true,
:recurring => false
}
end
end
Note that #myobjects are a collection of objects which class is MyModel.
Any help would be appreciated. Thank you!
Call it explicitly in controller and pass params. as_json will return string and calling as_json on string returns itself. It is quite common practice.
respond_to do |format|
# Render with :json option automatically calls to_json and this calls as_json
format.js { render :json => #myobjects.as_json(params) }
end

How do I use respond_with with custom classes in Rails 3?

I am making a JSON API with Rails and it seemed to work fine except for when I use respond_with custom classes (not an ActiveRecord inherited one).
Here is my class:
class JsonResponse
def initialize(data, status)
#data = data
#status = status
end
def as_json(options={})
{
:data => #data,
:status => #status
}
end
end
which is a simple response wrapper. When I try doing this:
def create
unless(Match.find_by_user_id(params[:user_id]))
Match.create(:user_id => params[:user_id])
end
time_response = JsonResponse.new("5", "success")
respond_with(time_response)
end
I get this error:
NoMethodError (undefined method `model_name' for JsonResponse:Class):
app/controllers/matches_controller.rb:9:in `create'
Any ideas? respond_with is driving me crazy.
Your class should response to to_json method
Obviously set :location option in respond_with method. Rails try to create restful route from the object you pass to the method, but because your object is not resource, the error is raised.
I am not sure if this helps but I do not see respond_to...
respond_with works together with respond_to...
respond_to :html, :xml, :json
This can be defined on Controller level
example:
class UsersController < ApplicationController::Base
respond_to :html, :xml, :json
def index
respond_with(#users = User.all)
end
def create
#user = User.create(params[:user])
respond_with(#user, :location => users_url)
end
end
and then you can define your json template... don't know if you leave the json template empty if it takes your "JSONResponse" class...
just a thought...

How to build a JSON response made up of multiple models in Rails

First, the desired result
I have User and Item models. I'd like to build a JSON response that looks like this:
{
"user":
{"username":"Bob!","foo":"whatever","bar":"hello!"},
"items": [
{"id":1, "name":"one", "zim":"planet", "gir":"earth"},
{"id":2, "name":"two", "zim":"planet", "gir":"mars"}
]
}
However, my User and Item model have more attributes than just those. I found a way to get this to work, but beware, it's not pretty... Please help...
Update
The next section contains the original question. The last section shows the new solution.
My hacks
home_controller.rb
class HomeController < ApplicationController
def observe
respond_to do |format|
format.js { render :json => Observation.new(current_user, #items).to_json }
end
end
end
observation.rb
# NOTE: this is not a subclass of ActiveRecord::Base
# this class just serves as a container to aggregate all "observable" objects
class Observation
attr_accessor :user, :items
def initialize(user, items)
self.user = user
self.items = items
end
# The JSON needs to be decoded before it's sent to the `to_json` method in the home_controller otherwise the JSON will be escaped...
# What a mess!
def to_json
{
:user => ActiveSupport::JSON.decode(user.to_json(:only => :username, :methods => [:foo, :bar])),
:items => ActiveSupport::JSON.decode(auctions.to_json(:only => [:id, :name], :methods => [:zim, :gir]))
}
end
end
Look Ma! No more hacks!
Override as_json instead
The ActiveRecord::Serialization#as_json docs are pretty sparse. Here's the brief:
as_json(options = nil)
[show source]
For more information on to_json vs as_json, see the accepted answer for Overriding to_json in Rails 2.3.5
The code sans hacks
user.rb
class User < ActiveRecord::Base
def as_json(options)
options = { :only => [:username], :methods => [:foo, :bar] }.merge(options)
super(options)
end
end
item.rb
class Item < ActiveRecord::Base
def as_json(options)
options = { :only => [:id, name], :methods => [:zim, :gir] }.merge(options)
super(options)
end
end
home_controller.rb
class HomeController < ApplicationController
def observe
#items = Items.find(...)
respond_to do |format|
format.js do
render :json => {
:user => current_user || {},
:items => #items
}
end
end
end
end
EDITED to use as_json instead of to_json. See How to override to_json in Rails? for a detailed explanation. I think this is the best answer.
You can render the JSON you want in the controller without the need for the helper model.
def observe
respond_to do |format|
format.js do
render :json => {
:user => current_user.as_json(:only => [:username], :methods => [:foo, :bar]),
:items => #items.collect{ |i| i.as_json(:only => [:id, :name], :methods => [:zim, :gir]) }
}
end
end
end
Make sure ActiveRecord::Base.include_root_in_json is set to false or else you'll get a 'user' attribute inside of 'user'. Unfortunately, it looks like Arrays do not pass options down to each element, so the collect is necessary.
Incase anyone is looking for an alternative solution for this, this is how I solved this in Rails 4.2:
def observe
#item = some_item
#user = some_user
respond_to do |format|
format.js do
serialized_item = ItemSerializer.new(#item).attributes
serialized_user = UserSerializer.new(#user).attributes
render :json => {
:item => serialized_item,
:user => serialized_user
}
end
end
end
This returns the serialized version of both objects as JSON, accessible via response.user and response.item.
There are a lot of new Gems for building JSON now, for this case the most suitable I have found is Jsonify:
https://github.com/bsiggelkow/jsonify
https://github.com/bsiggelkow/jsonify-rails
This allows you to build up the mix of attributes and arrays from your models.
Working answer #2 To avoid the issue of your json being "escaped", build up the data structure by hand, then call to_json on it once. It can get a little wordy, but you can do it all in the controller, or abstract it out to the individual models as to_hash or something.
def observe
respond_to do |format|
format.js do
render :json => {
:user => {:username => current_user.username, :foo => current_user.foo, :bar => current_user.bar},
:items => #items.collect{ |i| {:id => i.id, :name => i.name, :zim => i.zim, :gir => i.gir} }
}
end
end
end

Resources