Using Simple Search with Kaminari Pagination Gem - ruby-on-rails

I am trying to apply pagination to my rails app using Kaminari. I am also incorporating a simple search form based on the Railscast Episode #37. When I try to apply the kaminari page and per methods I get the error 'undefined method page'. Below is the code I'm using.
posts_controller.rb
def index
#posts = Post.search(params[:search]).page(params[:page]).per(2)
end
post.rb
def self.search(search)
if search
find(:all, conditions: ['title || body LIKE ?', "%#{search}%"], order: "created_at DESC")
else
find(:all)
end
end
index.html.erb
<%= paginate #posts %>
When I remove the pagination the search works fine. When I remove the search the pagination works fine. I just can't seem to use them both and have the code function properly. Please advise if there is something in my code that I am missing that is causing this not to work properly.

In your case, you are returning array object from the search method not ActiveRecord::Relation object.
find(:all, conditions: ...) # find method will return an array object.
Add check in your controller,
def index
#posts = Post.search(params[:search])
if #posts.class == Array
#posts = Kaminari.paginate_array(#posts).page(params[:page]).per(10)
else
#posts = #posts.page(params[:page]).per(10) # if #posts is AR::Relation object
end
end
Kaminari pagination with an array https://github.com/amatsuda/kaminari#paginating-a-generic-array-object
for ActiveRecord::Relation object, checkout this http://railscasts.com/episodes/239-activerecord-relation-walkthrough

Related

Why is my instance variable being overwritten inside my Rails controller action?

I have two variables in my index action in my post controller. However I am using the kaminari gem and it requires that the variable for how many post per pages go in the index action.
However, I have another variable for my search. I don't want to over ride so what is a work around?
def index
#posts = Post.where(["title LIKE ?", "%#{params[:search]}%"])
#posts = Post.page(params[:page]).per(10)
end
Have you tried
#posts = Post.where(["title LIKE ?", "%#{params[:search]}%"]).page(params[:page]).per(10)
or this
#posts = Post.where(["title LIKE ?", "%#{params[:search]}%"])
#posts = #posts.page(params[:page]).per(10)
The way you have it set now the second #posts variable will override the first #posts variable every time 100% because you are just simply reassigning it.

How do I create a variable in the controller for single or multiple params

def index
#users = User.all.paginate(page: params[:page])
#users = User.named(params[:name]).paginate(page: params[:page]) if params[:name].present?
#users = User.countryname(params[:country]).paginate(page: params[:page]) if params[:country].present?
#users = User.gender(params[:gender_type]).paginate(page: params[:page]) if params[:gender_type].present?
end
The following code works fine if only :name or :country or :gender_type is present. But it does not work if multiple params are present. What is the DRY way of writing this code for multiple params? Obviously, I do not want to create a different line of code for each possible combination of params.
Here are the scopes:
class User
scope :countryname, -> (country) { where("country ILIKE ?", "%#{country}%")}
scope :gender, -> (gender_type) { where gender_type: gender_type}
scope :named, -> (name) { where("name ILIKE ?", "%#{name}%")}
If I have a query string of
example.com/users?name=sam&gender_type=male
it simply returns all users with names like sam and ignores their gender... I would need to code:
#users = User.gender(params[:gender_type]).named(params[:name]).paginate(page: params[:page]) if params[:gender_type] && params[:name].present?
but I do not want to have to write a new line of code for every single combination of parameters.
You could use the ruby try method. For example, you could write something like
#users = User.try(:gender, params[:gender_type]).try(:paginate, page: params[:page])
Look at try in api docs for other ways to use it.
the problem was the code should be
#users = User.all.paginate(page: params[:page])
#users = #users.named(params[:name]).paginate(page: params[:page]) if params[:name].present?
etc
Rails will then allow chain scoping automatically. the previous code creates separate independent non-chained instance variables.

Links to change ordering of a posts loop

I'm trying to achieve links in my page that allow me to change the order that my posts display in. Similar to 'Recent', 'Hot' and 'Oldest' on Reddit.
I currently have by default
PostsController.rb
def index
#posts = Post.order('created_at DESC').all.paginate(page: params[:page], per_page: 20)
end
How would I go about adding links to a method to reverse the flow of posts to ASC or to display posts descending by a specific column on the model like views?
Jon
I'd create some scopes and a method in the model to handle all the possibilities,
# Post.rb
scope :recent, -> { order(created_at: :desc) }
scope :hot, -> { order(something: :desc) }
scope :oldest, -> { order(created_at: :asc) }
def self.sort_by(sort_param)
case sort_param
when 'recent'
recent
when 'hot'
hot
when 'oldest'
oldest
else
all
end
end
# controller
#posts = Post.sort_by(params[:order]).paginate(page: params[:page], per_page: 20)
Since I'm whitelisting, I don't really need to sanitize, any wrong param will return the default order.
If you want you could use #send and add method names to the whitelist array, but you need to make sure that the scope exists
def self.sort_by(sort_param)
if %w(recent hot oldest).include? sort_param
send sort_param
else
all
end
end
Use will_paginate gem in your app for Sorting in both directions and Paginate.Visit this link to implement it https://github.com/mislav/will_paginate.
firstly you want to remove the all so that pagination works better.
def index
#posts = Post.order('created_at DESC').paginate(page: params[:page], per_page: 20)
end
then you can use a variable to control the order. The important thing is to sanitise before passing to mysql.
def index
sort_by = ActiveRecord::Base::sanitize(params.permit(:sort_by))
#posts = Post.order(sort_by).paginate(page: params[:page], per_page: 20)
end

Deprecation warning: #apply_finder_options

I get DEPRECATION WARNING: #apply_finder_options is deprecated. when trying this in my user.rb:
def User.search(search, page)
paginate page: page,
per_page: 10,
conditions: ['name LIKE ?', "%#{search}%"],
order: 'name'
end
Called through UsersController:
def index
#users = User.search(params[:search], params[:page])
end
The pagination is done with the will_paginate gem.
What is triggering the warning and how can I fix it? Been trying some googling, but I find the docs not too comprehensive!
I'm pretty sure you just need to pull the order and conditions options out of the paginate method and use Active Record for that instead:
def User.search(search, page)
order('name').where('name LIKE ?', "%#{search}%").paginate(page: page, per_page: 10)
end

Activeadmin: disable pagination when exporting xml/json

How can I disable pagination for json/xml export in activeadmin? I could't figure out any solution for this. I'm getting only current page when hitting export to xml or json.
One solution (no the best) is disable de pagination with a before_filter
controller do
before_filter :disable_pagination, :only => [:index]
def disable_pagination
#per_page = YourModel.count
end
end
This make a pagination with only one page for all the records, so it is going to export all the records.
This can also be done like this,
controller do
def index
super do |format|
per_page = (request.format == 'text/html') ? 30 : 10_000 # to skip the pagination
params[:page] = nil unless (request.format == 'text/html') #It will be working even after we export the CSV on the paginated sections.
#users = #users.order("first_name asc, last_name asc").page(params[:page]).per(per_page)
#users ||= end_of_association_chain.paginate if #users.present?
end
end
end

Resources