Can I get total_pages using WillPaginate::Collection.create()? - ruby-on-rails

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

Related

How o use Ransack with find_by_sql

How can I use Ransack with "find_by_sql"?
I generally do this:
def index
#m = Mymodel.ransack(params[:q])
#mymodels = #m.result.page(params[:page])
end
Can I use Ransack when doing a custom query?:
#mymodels = Mymodel.find_by_sql(["SELECT ... ", current_user])
If I get it correctly, this is want you want:
def index
#m = Mymodel.ransack(params[:q])
#mymodels = #m.result.page(param[:page]).where(user: current_user)
end
To answer your question directly though, you can convert it to an SQL string, but you won't (or would be hard) be able to use current_user as an argument:
def index
#m = Mymodel.ransack(params[:q])
sql = #m.result.page(param[:page]).to_sql
#mymodels = Mymodel.find_by_sql(sql, current_user)
# the line just above won't work immediately because you'll need to
# modify the `sql` variable manually by inserting the `?` that you'll
# need to map that to the `current_user` that you passed as an argument
end

How to query records with and without params in Rails?

I have model Places and I have the index method in a controller. I need to get all places via request
/places
And filter places via request with query
/places?tlat=xxxx&tlong=xxxx&blat=xxxxx&blong=xxxx
What the best way to get this records? Should I check an existence of each param or are there Rails way?
#places = if params[tlat]&&params[blat]....
Places.all.where("lat > ? AND long > ? AND lat < ? AND long < ?", tlat, tlong, blat, blong)
else
Places.all
If you want to set WHERE clauses depending on params, you can use Ursus' code which is fine.
However, if you need to apply those WHERE clauses only if a set of params are present, you can use the following:
#places = Place.all
if params[:blat].present? && params[:tlat].present?
#places = #places.where(blat: params[:blat], tlat: params[:tlat])
end
# etc.
You could use an array of arrays to pair the associated params, kind of like what Ursus did.
I'd do something like this if possible. Important to note the this is just one query, composed dynamically.
#places = Place.all
%i(tlat tlong blat blong).each do |field|
if params[field].present?
#places = #places.where(field => params[field])
end
end
IMO, truly the "Rails way" (but actually just the "Ruby way") would be to extract this long conditional, and the query itself, out to their own private method. It becomes much easier to understand what's going on in the index action
class MyController < ApplicationController
def index
#places = Place.all
apply_geo_scope if geo_params_present?
end
private
def geo_params_present?
!!(params[:tlat] && params[:blat] && params[:tlong] && params[:blong])
end
# A scope in the model would be better than defining this in the controller
def apply_geo_scope
%i(tlat tlong blat blong).each do |field|
#places = #places.where(field => params[field])
end
end
end

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)

kaminari undefined method 'page'

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.

Rails each do average

Im trying to get the average of price in the products table and store it in a variable called average.
Heres my code:
def index
#products = Product.all
#average = 0
#i = 0
#products.each do |p|
i += 1
average += p.price
end
average = average / i
end
Im getting error "undefined method `+' for nil:NilClass" on the "i += 1 statement"
In Ruby on Rails there are predefined aggregate methods for your models to calculate average, sum, count, minimum, maximum, etc.
In your special case, you can do the following:
#average = Product.average(:price)
It will dispatch the actual work to the SQL which will do a better job than a Ruby code. It will produce an SQL query like (this is a MySQL example):
SELECT AVG(`products`.`price`) AS avg_id FROM `products`
You're setting up i as an instance variable (#i), but calling it as a regular variable (i).
Either take away the # when you initialize it to zero, or change references to i to #i.
You probably don't want to re-use your i after this method, so you probably just want a regular variable. Like so. (You also have a similar problem with your #average, which you probably do want to persist after the method.)
def index
#products = Product.all
#average = 0
i = 0
#products.each do |p|
i += 1
#average += p.price
end
#average = #average / i
end
There is a much cleaner way of working out averages from ActiveRecord collections (you could be doing it directly in the SQL), but I won't mention those since what you're doing is probably a good way to learn Ruby.
Use.. i think you forgot # sign
def index
#products = Product.all
#average = 0
#i = 0
#products.each do |p|
#i += 1
#average += p.price
end
#average = #average / #i
end

Resources