will_paginate generates wrong number of page links - ruby-on-rails-3

I am Using will paginate 3.0.2 and Rails 3.1.0.
The following code lives within my controller.
#users = User.visible_for(current_user).
includes(:study_courses).
ordered_by_last_name.
page(params[:page]).per_page(20)
In a partial where #users has been assigned with users from above I do:
= will_paginate users, previous_label: h("<"), next_label: h(">")
If there are 20 Users it gives me 6 page links, where the first page contains 20 users, the second page contains 10 users and of course the remaining pages contain zero users.
I can not figure out why there are 6 page links generated instead of 3.
UPDATE:
Figured out that will_paginate does not use distinct to count the records. Any ideas how to do this?

OK, I found a solution on my own:
user_count = User.visible_for(current_user).count(distinct: true)
#users = User.visible_for(current_user).
includes(:study_courses).
ordered_by_last_name.
paginate(page: params[:page],
per_page: 20,
total_entries: user_count)
In my scope I use disctinct, but calling count on the relation seems to overwrite that. So one has to count by hand and pass the count into the paginate method.

This way looks cleaner: https://stackoverflow.com/a/10740732/2599681
I tried it and works nicely with Rails 3.2.11 and WillPaginate 3.0.4.
Regards!

In my case, .count gives wrong number, too. Because .count runs SQL whitch includes "distinct". I changed it to .length and it works fine.
Like this
user_count = User.some_scopes.length
#users = User.some_scopes.paginate(page: params[:page],
per_page: 20,
total_entries: user_count)

Related

Rails Order by frequency of a column in another table

I have a table KmRelationship which associates Keywords and Movies
In keyword index I would like to list all keywords that appear most frequently in the KmRelationships table and only take(20)
.order doesn't seem to work no matter how I use it and where I put it and same for sort_by
It sounds relatively straight forward but i just can't seem to get it to work
Any ideas?
Assuming your KmRelationship table has keyword_id:
top_keywords = KmRelationship.select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20)
This may not look right in your console output, but that's because rails doesn't build out an object attribute for the calculated frequency column.
You can see the results like this:
top_keywords.each {|k| puts "#{k.keyword_id} : #{k.freqency}" }
To put this to good use, you can then map out your actual Keyword objects:
class Keyword < ActiveRecord::Base
# other stuff
def self.most_popular
KmRelationship.
select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20).
map(&:keyword)
end
end
And call with:
Keyword.most_popular
#posts = Post.select([:id, :title]).order("created_at desc").limit(6)
I have this listed in my controller index method which allows the the order to show the last post with a limit of 6. It might be something similar to what you are trying to do. This code actually reflects a most recent post on my home page.

Possible to use ActiveRecord methods against AR collections?

I would like to be able to pull all records from the db:
u = User.all
And then once loaded be able to apply AR methods to the resulting collection:
u.first
Is this possible in rails?
Once you actually query the database, the results become an array instead of an ActiveRecord::Relation. (Though #first would still work fine, since it's a method that also exists on Array).
If you just need a starting point to build an ActiveRecord::Relation though, you can use scoped:
# Doesn't execute a query yet
u = User.scoped
# This now executes a query similar to SELECT * FROM users LIMIT 1
u.first
Note that in Rails 4.0, #all now does the same thing as #scoped (whereas in Rails 3, it returns an array).
Why don't you try it?
User.all doesn't return an AR collection it returns an Array. Get rid of the .all and you will have a working example.

Reverse pagination with kaminari

I want to create pagination for a messaging system in which the first page shown contains the oldest messages, with subsequent pages showing newer messages.
For example, if normal pagination for {a,b,c,d,e,f,g,h,i} with 3 per page is:
{a,b,c}, {d,e,f}, {g,h,i}
Then reverse pagination would be:
{g,h,i}, {d,e,f}, {a,b,c}
I plan to prepend the pages so the result is the same as normal pagination, only starting from the last page.
Is this possible with kaminari?
Kaminary.paginate_array does not produce query with offset and limit. For optimization reason, you shouldn't use this.
Instead you can do this:
#messages = query_for_message.order('created_at DESC').page(params[:page]).per(3)
Where query_for_message stands for any query which you use to retrieve the records for pagination. For example, it can be all the messages of a particular conversation.
Now in the view file, you just need to display #messages in the reverse order. For example:
<%= render :collection => #messages.reverse, :partial => 'message' %>
<%= paginate #messages %>
There's a good example repo on Github called reverse_kaminari on github. It suggests an implementation along these lines (Source).
class CitiesController < ApplicationController
def index
#cities = prepare_cities City.order('created_at DESC')
end
private
def prepare_cities(scope)
#per_page = City.default_per_page
total_count = scope.count
rest_count = total_count > #per_page ? (total_count % #per_page) : 0
#num_pages = total_count > #per_page ? (total_count / #per_page) : 1
if params[:page]
offset = params[:page].sub(/-.*/, '').to_i
current_page = #num_pages - (offset - 1) / #per_page
scope.page(current_page).per(#per_page).padding(rest_count)
else
scope.page(1).per(#per_page + rest_count)
end
end
end
All credits go to Andrew Djoga. He also hosted the app as a working demo.
One way to solve this problem would be this one:
Reverse pagination with kaminari?
It does not look very clean nor optimal, but it works :)
Yes, but the method I have come up with isn't exactly pretty. Effectively, you have to set your own order:
Message.page(1).per(3).order("created_at DESC").reverse!
The problem with this approach is twofold:
First the reverse! call resolves the scope to an array and does the query, nerfing some of the awesome aspects of kaminari using AR scopes.
Second, as with any reverse pagination your offset is going to move, meaning that between two repeat calls, you could have exactly 3 new messages send and you would get the exact same data back. This problem is inherent with reverse pagination.
An alternative approach would be to interrogate the "last" page number and increment your page number down towards 1.

Rails 3 Sunspot Limiting Selected Records

at the moment I'm doing a Select from multiple tables using Sunspot. I'm limiting the records selected with the condition that the creation date must have been within the past 5 days. However I would like to take this further and limit the number of records selected to say, 20? Is there any way to do this with sunspot? I'm not doing any fancy groupings or anything. Simply selecting records belonging to various models and displaying them onto the screen.
My current code:
#updates = Sunspot.search(Upload,Help, User...) do
with(:created_at).greater_than(5.days.ago)
order_by(:created_at, :desc)
end
I tried adding the 'limit' clause as specified by the readme on Github but that produced an undefined method error probably because I'm doing a global Sunspot search as opposed to a search on a specific model.
Error code:
#updates = Sunspot.search(Upload,Help, User...) do
with(:created_at).greater_than(5.days.ago)
order_by(:created_at, :desc)
limit(20)
end
Error message:
undefined method `limit' for #<Sunspot::DSL::Search:0x0000000790b8c8>
The "limit" method is for "groups". See the readme section on groups. Sunspot provides pagination (and works like a charm with the kaminari gem btw). See the section on solr pagination in the readme. Specifically, try this:
#updates = Sunspot.search(Upload,Help, User...) do
with(:created_at).greater_than(5.days.ago)
order_by(:created_at, :desc)
paginate page: 1, per_page: 20
end

Rails3 Kaminari undefined with .all

Hi
I wonder how to work around the problem I have with the pagination gem "Kaminari".
For what I've understood you cant paginate #user = User.all.page(5)?
But what if I have this code and want to paginate that, is it possible or do I need to change the code?
#price = Price.joins(:retailer, :retailer => :profile).
where(['product_id=? AND size_id=?', params[:prod_id], params[:si_id]]).
group(:retailer_id).order("SUM((prices.price * #{params[:amount].to_i}) + profiles.shippingCost)").all
The only thing I receive right now when applying.page(5) to that code is
undefined method `page' for #<Class:0x000001023c4558>
You don't need the .all because the joins call, along with where and group, is returning an array of objects for you that meet your criteria. Remove your .all and call page on the instance variable (which you might want to rename to #pages or something else plural).