How add an attribute scoped in JSON in Rails 3 - ruby-on-rails

My aim is to add an attribute scoped in JSON, this some code :
User <ActiveRecord::Base
has_many :reviews
Review<ActiveRecord::Base
scope :best, lambda { |n| where('reviews.rating > ?',n}
And in my UsersController I want to do something like that :
format.json{
render :json => #user.to_json :include=> :reviews.best
I've tried as well something like :
format.json{
render :json => #user.to_json :include =>{ :reviews => {:method => best} }
But it doesn't work...
Any ideas ?
Thank you very much !

I would suggest you to use a gem for this, there is Active Record Serializers which should fit your needs or also Rabl, there is a RailsCast for this one.

Ok, I've figured out ! This is a solution :
In the User model we can add a virtual attribute like
def best_reviews
self.reviews.best
end
Then when we create json we can add :
format.json{
render :json => #user.to_json :methods => %w(best_reviews)
}
It does work ! But I don't know if it's the best solution

Related

Ruby on Rails 4 render :json with conditions

I have this render expression in a Ruby on Rails 4 controller:
render :json => #account_preferences, :include => {:payment_methods => {:include => {:payment_type => {:only => [:id, :name]}}}}
Can I filter the payment_methods somehow in this expression to get only the payment_methods with state = 'Confirmed'?
I don't believe you can do this conditionally in the render :json.
But something like this would work:
class AccountPreference < ActiveRecord::Base
has_many :confirmed_payment_methods,-> { where state: 'Confirmed' }, class_name: 'PaymentMethod'
end
and call that association in your to_json
render :json => #account_preferences, :include => {:confirmed_payment_methods => {:include => {:payment_type => {:only => [:id, :name]}}}}
If you do a lot of includes and conditionals I'd recommend using jbuilder to do this kind of stuff.
Did you take a look at that?
https://github.com/rails/jbuilder
It's included in your Gemfile by default in a rails 4 project.

Rails - Include association in json response

I got 2 Tables/Models: Paths and Questions. Each question belongs to a path
My question.rb:
class Question < ActiveRecord::Base
belongs_to :path
end
My path.rb
class Path < ActiveRecord::Base
has_many :questions
end
Everything works fine like
p = Path.last
Path.questions
returns everything I need but I'm returning a json response like this:
#path = Path.find_by_id(params[:id])
render :status=>200, :json => {:status => "success", :path => #path, :message => "Showing path"}
That answer doesn't include the questions for the path of course. What do I have to change to include all questions belonging to that path? I know I could just add :path_questions => #path.questions but is there no way to include the questions without a new return variable? I hope it's clear what I mean.
I do it like that in a Rails 5 API app:
BooksController
def index
#books = Book.limit(params[:limit])
render json: #books, include: ['author'], meta: { total: Book.count }
end
In the above situation, a Book belongs_to Author.
This is quite hacky, but should work:
:path => #path.as_json.merge(:questions => #path.questions.as_json)
Eventually you can override as_json inside your model:
def as_json(options={})
includes = [*options.delete(:include)]
hash = super(options)
includes.each do |association|
hash[self.class.name.underscore][association.to_s] = self.send(association).as_json
end
hash
end
And then just call: :path => #path.as_json(:include => :questions)
Note it will also add :include option to to_json method.

problem using 'as_json' in my model and 'render :json' => in my controller (rails)

I am trying to create a unique json data structure, and I have run into a problem that I can't seem to figure out.
In my controller, I am doing:
favorite_ids = Favorites.all.map(&:photo_id)
data = { :albums => PhotoAlbum.all.to_json,
:photos => Photo.all.to_json(:favorite => lambda {|photo| favorite_ids.include?(photo.id)}) }
render :json => data
and in my model:
def as_json(options = {})
{ :name => self.name,
:favorite => options[:favorite].is_a?(Proc) ? options[:favorite].call(self) : options[:favorite] }
end
The problem is, rails encodes the values of 'photos' & 'albums' (in my data hash) as JSON twice, and this breaks everything... The only way I could get this to work is if I call 'as_json' instead of 'to_json':
data = { :albums => PhotoAlbum.all.as_json,
:photos => Photo.all.as_json(:favorite => lambda {|photo| favorite_ids.include?(photo.id)}) }
However, when I do this, my :favorite => lambda option no longer makes it into the model's as_json method.......... So, I either need a way to tell 'render :json' not to encode the values of the hash so I can use 'to_json' on the values myself, or I need a way to get the parameters passed into 'as_json' to actually show up there.......
I hope someone here can help... Thanks!
Ok I gave up... I solved this problem by adding my own array methods to handle performing the operations on collections.
class Array
def to_json_objects(*args)
self.map do |item|
item.respond_to?(:to_json_object) ? item.to_json_object(*args) : item
end
end
end
class Asset < ActiveRecord::Base
def to_json_object(options = {})
{:id => self.id,
:name => self.name,
:is_favorite => options[:favorite].is_a?(Proc) ? options[:favorite].call(self) : !!options[:favorite] }
end
end
class AssetsController < ApplicationController
def index
#favorite_ids = current_user.favorites.map(&:asset_id)
render :json => {:videos => Videos.all.to_json_objects(:favorite => lambda {|v| #favorite_ids.include?(v.id)}),
:photos => Photo.all.to_json_objects(:favorite => lambda {|p| #favorite_ids.include?(p.id)}) }
end
end
I think running this line of code
render :json => {:key => "value"}
is equal to
render :text => {:key => "value"}.to_json
In other words, don't use both to_json and :json.

Rails: How can I render multiple objects to JSON?

I am trying to render multiple objects as JSON. This is my render call:
render :json => {:widget => #widget.to_json(:include => :foo),
:updated => Time.now.to_i}
I have to use to_json because of the include, and the addition updated so I know when the last call was made. The problem is that the to_json is rendered as a String instead of the object structure of the widget.
How do I get the full object structure of the widget and the updated information?
Move the :include => :foo into your Widget model.
class Widget < ActiveRecord::Base
def as_json(options = {})
super options.merge(:include => :foo)
end
end

globalize2 with xml/json support

I'm implementing a distributed application, server with rails and mobile clients in objective c (iPhone). To enable internationalization, I use the rails plugin 'globalize2' by joshmh.
However, it turned out that this plugin does not translate attributes when calling to_xml or to_json on an ActiveRecord. Does anyone know of a workaround / patch? Do you have any ideas how to fix this, where to alter globalize2?
Using:
Rails 2.3.5
globalize2: commit from 2010-01-11
With Globalize2 (and with model_translations as well) translated attribute in a model is not a real attribute but is a method. Thus and so when you execute to_json method you can use :methods, as Joris suggested, but in a simpler way:
class Post < ActiveRecord::Base
attr_accessible :title, :text
translates :title, :text
end
class PostsController < ApplicationController
def index
#posts = Post.all
respond_to do |format|
format.html
format.json { render :json => { :posts => #posts.to_json(:only => :id, :methods => :title) }}
format.js
end
end
end
Here I would like to receive only post id and title in json response. For additional information see to_json (Serialization) in Rails API.
I found this fork on github: http://github.com/leword/globalize2
But it looks like it is based on an older version.
I was looking for this myself, but solved my problem using the :methods option:
If you want to translate one attribute in #item, you can use:
class Item < ActiveRecord::Base
translates :name
def t_name
self.name
end
end
And in your controller:
render :text => #item.to_xml(:methods => [ :t_name ])
If your api path is something like /en/api/item.xml, you should get the english translation in the t_name attribute
For a belongs_to relation:
belongs_to :category
def category_name
self.category.name
end
And in your controller:
render :text => #item.to_xml(:methods => [ :category_name ])
Your use case is probably different. Above is a workaround that works for me.

Resources