kaminari undefined method 'page' - ruby-on-rails

I am trying to add Kaminari to my Rails app. I have included the gem and this is what my controller looks like:
def index
if params[:year]
if params[:year].size > 0
#songs = Song.where("year like ?", params[:year]).page(params[:page])
elsif params[:artist].size > 0
#songs = Song.where("artist_name like ?", params[:artist]).page(params[:page])
elsif params[:song].size > 0
#songs = Song.where("title like ?", params[:song]).page(params[:page])
end
else
#songs = Song.first(10).page(params[:page])
end
end
and then adding
<%= paginate #songs %>
in my view, the error I am getting is:
undefined method `page' for #<Array:0x007fab0455b4a8>
Not sure why this is coming up as I followed the docs step for step.

Kaminari uses paginate_array to paginate an array. 2 solutions:
First, you can use limit(10) instead of first(10):
#songs = Song.limit(10).page(params[:page])
Second, use paginate_array
#songs = Kaminari.paginate_array(Song.first(10)).page(params[:page])

I'd advise you rewrite your controller slightly. Better yet, move your filters to the model or a filter class. Look into present? for testing existence of params as that will check for nil and empty.
def index
#songs = Song
#songs = #songs.where("year like ?", params[:year]) if params[:year]
#songs = #songs.where("artist_name like ?", params[:artist]) if params[:artist]
#songs = #songs.where("title like ?", params[:song]) if params[:song]
#songs = #songs.limit(10).page(params[:page])
end

Tl;DR If you use Mongoid, use kaminari-mongoid instead of kaminari alone.
At Github it said, Kaminari supports Mongoid...so I went and installed gem 'kaminari' with the result: unknown method :page...later i found the mongoid adapter: kaminari-mongoid and that works now.

Related

Kaminari error when collection is smaller than per(x)

I am running the Kaminari gem for my pagination.
Controller
def dashboard
#projects = Project.find_by_user_id(current_user)
if #projects.size > 10
#projects.page(params[:page]).per(10)
end
end
Dashboard view
= paginate #projects, :theme => 'twitter-bootstrap-3', :remote => true
In my case, the #projects is sometimes only 1 record or even zero records. When it is nil, I get an error on the params[:page] being nil.
So this works
def dashboard
#projects = Project.page(params[:page]).per(10)
end
This gets error undefined method 'page' for #<Project:0x007f8cac5f14b0>
def dashboard
#projects = Project.find_by_user_id(current_user).page(params[:page]).per(10)
end
I think it is because the #projects is only a couple of records which is less than the 10 specified in .per
I tried adding a #projects.count or #projects.size but I get the error undefined method 'size' for #<Project:0x007f8c996865f0>
def dashboard
#projects = Project.find_by_user_id(current_user)
if #projects.size > 10
#projects.page(params[:page]).per(10)
end
end
What the hell am I doing wrong!? haha
I am guessing I can fix this in the first instance instead of trying to fix the second or third options. Any help would be greatly appreciated.
The issue is Project.find_by_user_id(current_user) returns an Array, not an ActiveRecord::Relation
You should do something like:
current_user.projects.page(params[:page]).per(10)
If your relationships are correctly setup.
Or:
Project.where(user_id: current_user.id).page(params[:page]).per(10)

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.

How to get a string into a where clause in Ruby on Rails 3?

I have this class method:
def self.default_column
"created_at"
end
How can I rewrite the following function, so that I can make use of my default_column method?
def next
User.where("created_at > ?", created_at).order('created_at ASC').first
end
I tried things like these...
def next
User.where("#{default_column} > ?", default_column).order('#{default_column} ASC').first
end
... but I must be awfully wrong here because it doesn't work at all.
Thanks for any help.
You can use:
def next
User.where("#{User.default_column} > ?", self.send(User.default_column)).order("#{User.default_column} ASC").first
end
Or even better
def next
klass = self.class # This is supposing you are inside User model
# Otherwise just use klass = User
klass.where("#{klass.default_column} > ?", self.send(klass.default_column))
.order(klass.arel_table[klass.default_column].asc)
end
Notice that if you handle the method in this way, you cannot chain it: like User.where(name: 'something').next
If you want to achieve this, you have to move next to be def self.next and in that case, you have to pass an instance of the user to it, like this:
def self.next(user)
klass = user.class
klass.where("#{klass.default_column} > ?", user.send(klass.default_column))
.order(klass.arel_table[klass.default_column].asc)
end
In this way you can write something like: User.where(name: 'test').next(#user). You can optionally chain .first to get directly the result, but in this way you will not be able to chain other things, like User.where(name: 'test').next(#user).where(email: 'my#mail.com')
Finally, if you want pure AREL (for portability)
def self.next(user)
klass = user.class
arel = klass.arel_table
column = klass.default_column # This helps cleaning up code
column_value = user.send(column)
klass.where(arel[column].gt(column_value))
.order(arel[column].asc)
end
def next
default_column = self.class.default_column
User
.where("#{default_column} > ?", send(default_column))
.order("#{default_column} ASC")
.first
end

Can I get total_pages using WillPaginate::Collection.create()?

Using .paginate on any model in Rails gives the total_pages parameter. Can anyone tell me if there is a way to get total_pages while using WillPaginate::Collection.create()?
Thanks Vishal
The gem sets the total_pages when the total_entries is set. You need to either pass the total entries to the create method OR assign it inside the block.
total_entires = ..
#entries = WillPaginate::Collection.create(1, 10, total_entires) do |pager|
#..
end
OR
#entries = WillPaginate::Collection.create(1, 10) do |pager|
#..
pager.total_entries = # assign the count..
end

Moving of will_paginate to model

On my Question model I have some scopes
scope :recent, order("created_at DESC")
scope :approved, where("status = ?", "approved")
scope :answered, approved.recent.where("answers_count > ?", 0)
On my question controller I'm retrieving questions using the scopes
example 1:
#questions = Question.approved.recent
example 2:
#questions = User.find(session[:user_id]).topics.map { |t| t.questions.approved.recent }.flatten.uniq
I'm trying to put will_paginate on my model to make things easier on the controller but the 2nd example is very tricky as it is using mapping to retrieve questions according to preferences.
I've tried to add this on my model
def self.pagination(page = 1)
self.paginate(:page => page, :per_page => 5)
end
and then on my controller I have
#questions = Question.approved.recent.pagination.(params[:page])
That works fine for the 1st example but I Dont know how to implement that on the 2nd example
Any hints?
This looks like Rails 3. Be sure to use the ~> 3.0.pre2 version of the will_paginate gem.
You can use the paginate method at the end of your chain of scopes. For example, your "example 1" would be:
#questions = Question.approved.recent.paginate(:page => params[:page], :per_page => 20)
I see you created a custom method (pagination) to wrap this pattern, but it's best that you keep this syntax in original form for now, especially since you're dealing with scopes and Relation objects in Rails 3 and will_paginate doesn't have proper support for this yet (but it's coming).
In your "example 2" it seems you only need to fetch the first few recent questions from each topic and that you won't perform a full-blown pagination here (like, going to page 2 and forward). You don't have to use the paginate method here; you can simply use ActiveRecord's limit:
current_user = User.find(session[:user_id])
#questions = current_user.topics.map { |topic|
topic.questions.approved.recent.limit(5).to_a
}.flatten.uniq

Resources