will_paginate and .sort => - ruby-on-rails

I recently got the will_paginate gem installed and it works great, but I would like it to work with the setup I currently have. Right now I show all feeds (or posts) on the same page, but they are sorted after how many people clicked on that page:
controller.rb
#t = Time.now
#h1 = #t - 1.hour
#feeds = Feed.find(:all, :order => "created_at DESC").sort { |p1, p2| p2.impressionist_count(:filter=>:all, :start_date=>#h1) <=> p1.impressionist_count(:filter=>:all, :start_date=>#h1)}
Now... I tested the paginate gem and it works fine if I do this in the controller:
controller.rb
#feeds = Feed.paginate(:per_page => 10, :page => params[:page], :order => "created_at DESC")
So I thought 1+1=2 and tried to combine the two by doing:
controller.rb
#feeds = Feed.paginate(:per_page => 10, :page=>params[:page], :order => "created_at DESC").sort { |p1, p2| p2.impressionist_count(:filter=>:all, :start_date=>#h1) <=> p1.impressionist_count(:filter=>:all, :start_date=>#h1)}
I'm not able to sort my posts and paginate them. I get an error when I try to load the page:
undefined method `total_pages' for #
I would like this to work, it would be pretty sweet :). However, if it does not work, is there any other way of doing this?
Thanks in advance!

It's because will_paginate works by default only on ActiveRecord relationships. Once you use the sort, it's converted to an array. If you want will_paginate to work with an array, you'll need to require support for it in your controller.
require 'will_paginate/array'

Related

Will_Paginate gem error undefined method "total_pages"

The will_paginate gem isn't working after I changed a query to get followers/followed_users
How can I use will_paginate with this??
#users = #user.reverse_relationships.order("created_at DESC").collect { |r| User.find(r.follower) }
I've tried several options like:
#users = #user.reverse_relationships.order("created_at DESC").collect { |r| User.find(r.follower) }
#users = #users.paginate(:page => params[:page])
#users = #user.reverse_relationships.paginate(:page => params[:page]).order("created_at DESC").collect { |r| User.find(r.follower) }
Each time I get an error like undefined method "total_pages" or undefined method "paginate"
You should re-order your query so that you can call paginate and total_pages on an ActiveRecord::Relation instance, as will_paginate requires.
This would remove the collect which effectively turns your relation into an array.
This could be done with something like:
#relationships = #user.reverse_relationships.includes(:follower).order("created_at DESC")
And then just access the follower of each relationship in your view or whatnot.
This will also be more efficient - you won't be issuing a separate query for each follower, as your original code is doing.

Why cant I do this in my controller

def search
#location = Location.find(params[:location_id])
start_date = DateTime.strptime(params[:start_date], "%m-%d-%Y")
end_date = DateTime.strptime(params[:end_date], "%m-%d-%Y")
#songs = #location.songs.find(:all, :conditions => {:play_date => start_date..end_date}).paginate(:page => params[:page], :per_page => 40)
render 'show'
end
Here is my error
undefined method `paginate' for #<Array:0x007fb00e49e6c8>
all works if i remove the will_paginate but i need it...any ideas or is there a better way to write this controller
Try writing
#songs = #location.songs.where(:play_date => start_date..end_date).paginate(:page => params[:page], :per_page => 40)
The difference? where returns an ActiveRelation object, while find retrieves all the matching objects in an array.
Hope this helps.
NoMethodError: undefined method `paginate' for []:Array
My will_paninate works perfectly but above error jumped out after upgrading to version 3.0.0.
Add following require will solve the issue:
require 'will_paginate/array'
Check out this post for the backward compatibility of will_paginate 3.0.
The will_paginate documentation states that combining .paginate with .find is not the way to go, because .find will load everything from your DB before .paginate has a chance to restrict the fetch.

Rails: Where does this code belong?

I currently have the following in my controller:
#items = Item.scoped
#items = #items.where('price >= ?', params[:price_min]) if params[:price_min]
#items = #items.where('price <= ?', params[:price_max]) if params[:price_max]
#items = #items.where(:size => params[:size]) if params[:size]
#items = #items.where(:colour => params[:colour]) if params[:colour]
# ...
#items = #items.paginate(:page => params[:page], :per_page => 10)
Is this the right place for this code, or should it really belong in my model with a single method call in the controller? i.e.
#items = Item.apply_filters(params)
I am trying to stick to convention as much as possible.
Many thanks.
You are correct that this all belongs in your model. This is very similar to someone's code that I reviewed the other day. The MetaWhere gem might be a good fit for your project as well.
https://codereview.stackexchange.com/questions/500/how-to-filter-search-a-model-with-multiple-params-in-hash/501#501
Also, if your items are always going to be scoped, you can make that the default_scope. In your model:
default_scope order("item_number"), where('price >= ?', 100)
(I'm not totally sure I got all of that syntax correct, but it's something like that.)
named_scope might also help you out.

Is it possible to filter by conditions before paginating?

I'm using ruby on rails 2.3.8 and will_paginate plugin.
I've just noticed that if I write something like this:
Announcement.paginate :page => params[:page], :per_page => 10, :conditions => some_condition
it will work.
But, if I write something like this:
announcements = Announcement.all :conditions => some_condition
#ann = announcements.paginate :page => params[:page], :per_page => 10
it won't recognize conditions.
EDIT:
I've developed a Search functionality and, due to a Sort functionality I had to implement, I had to put the search feat inside a model's method to call it from the controller every time I need either to search or sort by some field.
So, my model's methods look like this:
def self.search_by_relevance(words)
conditions = get_search_conditions(words)
Announcement.published.descend_by_featured.order_by_rate :conditions => conditions
end
where "published" and "order_by_rate" are named scopes and "descend_by_feature" belongs to "searchlogic" gem.
def self.get_search_conditions(words)
unless words.empty? or words.nil?
conditions = ''
words.each do |word|
if conditions.nil? or conditions.empty?
conditions = '(title like "%' + word + '%" or description like "%' + word + '%")'
else
conditions += ' and (title like "%' + word + '%" or description like "%' + word + '%")'
end
end
conditions
end
end
My controller's action looks like this:
def search
#announcements = Announcement.search_by_relevance(params[:txtSearch].to_s.split).paginate :page => params[:page], :per_page => 10 unless params[:txtSearch].nil? or params[:txtSearch].empty?
end
This syntax won't recognize the conditions specified in the model's method.
EDIT 2:
Thanks for the posts. Testing my code a little more I found out that if I write ".all" right after "order_by_rate" at this line Announcement.published.descend_by_featured.order_by_rate :conditions => conditions, in search_by_relevance method it will return the correct query, but will_paginate plugin will give me the following error(just if I add ".all"):
NoMethodError in AnnouncementsController#search
undefined method `to_i' for {:page=>nil, :per_page=>10}:Hash
D:/Proyectos/Cursometro/www/vendor/plugins/will_paginate/lib/will_paginate/collection.rb:15:in `initialize'
D:/Proyectos/Cursometro/www/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb:37:in `new'
D:/Proyectos/Cursometro/www/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb:37:in `paginate'
D:/Proyectos/Cursometro/www/app/controllers/announcements_controller.rb:276:in `search'
First of all, I don't understand why I have to add the ".all" to the query to work right, and second, I don't see why will_paginate won't work when I include ".all"(I also tried to add the following code but didn't work: :page => params[:page] || 1).
Also, if I include the ".all" syntax to the query, it will return:
SELECT * FROM announcements WHERE
((title like "%anuncio%" or
description like "%anuncio%")) AND
(announcements.state = 'published')
ORDER BY announcements.featured DESC
If I don't, it will return:
SELECT * FROM announcements WHERE
(announcements.state = 'published')
ORDER BY announcements.featured DESC
Do you see that no conditions are being included in the last one? This is causing the problem.
I don't know if this will work for you, but you can use paginate just like find, I mean, something like:
#announcements = Announcement.paginate_all_by_id params[:id],
:page => params[:page], :per_page => 10
Edit
#announcements is an array, right?
Well, I found this post and this other one that may help you.
Well, I kind of solve this by adding ".paginate"(instead of ".all") to my query in the model's method, passing by parameters the "page" and "per_page" values. It was not my idea to include pagination in models, but well...it's the solution I have for now. If you come up with a better one, I'll be glad to hear it :)

will_paginate without use of activerecord

I apologize if this is a trivial question or my understanding of rails is weak.
I have 2 actions in my controller, index and refine_data.
index fetches and displays all the data from a database table.
refine_data weeds out unwanted data using regex and returns a subset of the data.
Controller looks like:
def index
Result.paginate :per_page => 5, :page => params[:page], :order => 'created_at DESC'
end
def refine_data
results = Result.all
new_results = get_subset(results)
redirect_to :action => 'index'
end
I would like to redirect the refine_data action to the same view (index) with new_results.
As new_results are not from the database table (or model), how do I go about constructing my paginate?
As I wrote in my answer to Sort by ranking algorithm using will-paginate, it is possible to use a custom find method to order the data.
It could be used similar to filter out unwanted data, since you just need to return a set of data. By modifying the name of your refine_data to something like find_refined_data you can use
Result.paginate_refined_data :per_page => 5, :page => params[:page], :order => 'created_at DESC'
in your index method. (Of course you need to return a set of records instead redirect to the index action)
BTW you could also use the paginate_by_sql method, if you are able specify your filter as a SQL query. This is probably more efficient than grabbing all records and performing a regex on them. But more complex I guess.
I was not successful in getting will_paginate to work by creating my own find method.
I was almost successful but not quite.
Here's what I've tried:
In Controller:
def refine_data
Result.paginate_refined_data :per_page => 5, :page => params[:page], :order => 'created_at DESC', :exclude =>"somestring"
end
In Model:
def find_refined_data(args)
exclude_string = args[:exclude];
new_results = do_some_work_and_exclude_records(#results,exclude_string)
end
will_paginate had trouble with me passing an additional parameter :exclude which it did not understand.
The simplest solution for me was to create my own WillPaginate::Collection object.
So here's how mine works now:
#The method name does not cause will_paginate to intercept and try to do its magic.
def new_find_refined_data(args)
exclude_string = args[:exclude];
new_results = do_some_work_and_exclude_records(#results,exclude_string)
#entries = WillPaginate::Collection.create(1, args[:per_page]) do |pager|
# inject the result array into the paginated collection:
pager.replace(new_results)
unless pager.total_entries
# the pager didn't manage to guess the total count, do it manually
pager.total_entries = new_results.length
end
end
end
Hope this will help any of the guys facing the same problem:
I'm not sure about the other answers - you may well want to move that method into your model class.
But answering your actual question.
The way to show the same view as another action is not to redirect but use:
render :action => :index
Yet another alternative might be to create a named_scope for your refined result set.
Can you formulate your "get_subset" business logic into database-style commands?
If so you can reconstruct your finder as a named_scope (with those conditions)
eg:
named_scope :blue_things, :conditions => {:colour => 'blue'}
and then just call paginate on that:
Result.blue_things.paginate :per_page => 5, :page => params[:page]

Resources