problem with will_paginate with render json in rails 2.3.11 - ruby-on-rails

I am trying to upgrade my rails gem from 2.3.2 to 2.3.11. However, I got some problems with will_paginate 2.3.15 and render json back.
module WillPaginateHelpers
WillPaginate::Collection.class_eval do
alias :to_json_without_paginate :to_json
def to_json(options = {})
hash = { :current_page => current_page,
:per_page => per_page,
:total_entries => total_entries,
:total_pages => total_pages,
:items => to_a
}
hash.to_json(options)
end
end
end
Previously, the code above could work with:
#products = Product.paginate(:page => 1, :per_page => 20)
render :json => #products
However, with rails 2.3.11, it comes up with error "object references itself" unless i need to code this way: render :json => #products.to_json.
How to fix this? What happened with render :json => #products?

I've added this to an initializer:
class WillPaginate::Collection
def as_json options={}
{
:total_entries => self.total_entries,
:current_page => self.current_page,
:total_pages => self.total_pages,
:per_page => self.per_page,
:items => super
}
end
end

Related

Pass one variable to next in JSON object in Rails 5

I need to be able to access user data in comments.
def index
#user = User.all
#libraries = Library.all.order('created_at ASC')
#comments = Comment.all
#user_likes = UserLike.all
render :json => #libraries, :include => [:user, :comments, :user_likes]
end
I tried this:
render :json => #libraries, :include => [:user, {:comments => :user}, :user_likes]
And it did not work. Any ideas?
Assuming you have the associations set up, you need to add include for the nested associations:
render :json => #libraries,
:include => {:user, {:comments => { include: :user}}, :user_likes}
When rendering JSON like this, Rails calls as_json first to convert the payload to hash. That's when the option include is used. You can check the documentation to see how it works.

Serialize JSON nested model in Ruby on Rails

I am new to rails and I have two models, one has a foreign key on the other model.
I created a controller and defined and index method which is working fine:
def index
#collections = Collection.all
render json: #collections
end
Which is rendering something like this:
[{"id":1,"title":"Collection
A","book_id":1,"created_at":"2016-04-10T18:41:32.709Z","updated_at":"2016-04-10T18:41:32.709Z"}]
I would like to transform that book_id field into a list of book objects, something like this:
[{"id":1,"title":"Collection A","books": [{"book_id": 1, "title:
"book_title"},],"created_at":"2016-04-10T18:41:32.709Z","updated_at":"2016-04-10T18:41:32.709Z"}]
Then I tried with:
def index
#collections = Collection.all
render :json => collections.as_json(
:include => { :book_title }
)
end
But is giving me syntax error and I cannot see how to do it properly in this doc http://guides.rubyonrails.org/layouts_and_rendering.html
I am using Rails 4.1.0
Try:
def index
#collections = Collection.all
render :json => #collections.as_json(
:include => :book
)
end
or if you would like just the :id and :title:
def index
#collections = Collection.all
render :json => #collections.as_json(
:include => {:book => {:only => [:id, :title]}}
)
end

paginate in rails 2.3.8

can anyone suggest me how to remove undefined method 'paginate' in rails 2.3.8
here is my index method which shows index page and code of this is
def index
#clients = Client.paginate :page => params[:page], :per_page => 5
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #clients }
end
end
Undefined method 'paginate' probably indicates that you don't have will_paginate installed.
To get rid of your error you have to choices:
1) Get rid of pagination.
Replace
#clients = Client.paginate :page => params[:page], :per_page => 5
With
#clients = Client.all
2) Install will_paginate by placing the following in your environment file:
config.gem 'will_paginate', :version => '~> 2.3.16'
and then running rake gems:install of cause. :)

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

How to use will_paginate with a nested resource in Rails?

I'm new to Rails, and I'm having major trouble getting will_paginate to work with a nested resource.
I have two models, Statement and Invoice. will_paginate is working on Statement, but I can't get it to work on Invoice. I know I'd doing something silly, but I can't figure it out and the examples I've found on google won't work for me.
statement.rb
class Statement < ActiveRecord::Base
has_many :invoices
def self.search(search, page)
paginate :per_page => 19, :page => page,
:conditions => ['company like ?', "%#{search}%"],
:order => 'date_due DESC, company, supplier'
end
end
statements_controller.rb <irrelevant code clipped for readability>
def index #taken from the RAILSCAST 51, will_paginate podcast
#statements = Statement.search(params[:search], params[:page])
end
I call this in the view like so, and it works:
<%= will_paginate #statements %>
But I can't figure out how to get it to work for Invoices:
invoice.rb
class Invoice < ActiveRecord::Base
belongs_to :statement
def self.search(search, page)
paginate :per_page => 19, :page => page,
:conditions => ['company like ?', "%#{search}%"],
:order => 'employee'
end
end
invoices_controller.rb
class InvoicesController < ApplicationController
before_filter :find_statement
#TODO I can't get will_paginate to work w a nested resource
def index #taken from the RAILSCAST 51, will_paginate podcast
#invoices = Invoice.search(params[:search], params[:page])
end
def find_statement
#statement_id = params[:statement_id]
return(redirect_to(statements_url)) unless #statement_id
#statement = Statement.find(#statement_id)
end
end
And I try to call it like this:
<%= will_paginate (#invoices) %>
The most common error message, as I play with this, is:
"The #statements variable appears to be empty. Did you forget to pass the collection object for will_paginate?"
I don't have a clue what the problem is, or how to fix it. Thanks for any help and guidance!
Solved -
I moved the invoices pagination into Statement's controller, like this:
def show
#statement = Statement.find(params[:id])
#TODO move the :per_page stuff out to a constant
#invoices = #statement.invoices.paginate :per_page => 10,
:page => params[:page],
:order => 'created_at DESC'
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #statement }
end
end
and call it in the view like this (code trimmed for readability>
<div id="pagination">
<%= will_paginate #invoices %>
</div>
<table>
<%# #statement.invoices.each do |invoice| -
shows all invoices with no pagination,
use #invoices instead%>
<%
#invoices.each do |invoice|
%>

Resources