How to sort when fetching a collection of models? - ruby-on-rails

I have a call on my posts_controller.rb index action:
#articles = Article.order("id desc")
I now want to be able to order by:
date
id
some_counter_attribute
My querystring will have sort=date/id/count like:
www.example.com/articles/?sort=date
How should I implement this in my controller now? Should I just use if statements?
if params[:sort] == "date"
#articles = Article.order("created_at desc")
elsif params[:sort] == "count"
#articles = ...
..
Or is there a better way?
Should this logic be in the controller or Model ideally?

Try this:
class ArticlesController
def index
#articles = Article.order(sort_order)
end
private
def sort_order
##sort_order ||= {
"date" => "created_at DESC",
"id" => "id DESC",
"comments" => "comment_count ASC"
}
##sort_order[params[:sort]]
end
end
Off course there are gems for doing this sort of things:
MetaSearch
SearchLogic

Straightforward approach could be:
#articles = Article.order("#{params[:sort]} desc")
But for "date" you have to sort by created_at. So try this:
mylist = {"date" => "created_at",
"id" => "id",
"counter" => "some_counter_attribute"}
#articles = Article.order("#{mylist[params[:sort]]} desc")

Related

How to override build_query method in role_adapter rolify?

I am using rolify gem for authorization. When i create a new user dynamically through UI, my users_roles (in my case role_user_map) table insert is not happening. Is there a way to override build_query method? Please, guide me with this. Thank You. `
users_controller.rb
def index
store_list = #query_stores.split(" ").join("','")
profile_list = #profiles.map {|role| role.role_name}.join("','")
if params[:search_by].present?
search_by = params[:search_by]
if current_user.admin? || current_user.radial_readonly?
#users = User.distinct.joins('LEFT JOIN stores_users ON dim_user.user_id = stores_users.user_id')
.joins('LEFT JOIN stores ON stores.id = stores_users.store_id')
.joins('LEFT JOIN clients_users ON dim_user.user_id = clients_users.user_id')
.joins('LEFT JOIN clients ON clients_users.client_id = clients.id')
.joins(:DIM_ROLE)
.where('dim_user.full_name LIKE :search OR dim_user.email LIKE :search OR DIM_ROLE.role_name LIKE :search OR clients.code LIKE :search OR stores.code LIKE :search', search: "%#{search_by}%")
else
#users = User.distinct.joins(:store_user_assignments)
.joins(:stores)
.where("stores.code IN ('#{store_list}')")
.joins('LEFT JOIN clients_users ON dim_user.user_id = clients_users.user_id')
.joins('LEFT JOIN clients ON clients_users.client_id = clients.id')
.joins(:DIM_ROLE)
.where("DIM_ROLE.role_name IN ('#{profile_list}')")
.where('dim_user.full_name LIKE :search OR dim_user.email LIKE :search OR DIM_ROLE.role_name LIKE :search OR clients.code LIKE :search OR stores.code LIKE :search', search: "%#{search_by}%")
end
else
if current_user.admin? || current_user.radial_readonly?
#users = User.distinct.joins('LEFT JOIN stores_users ON dim_user.user_id = stores_users.store_id')
else
#users = User.distinct.joins(:store_user_assignments).joins(:DIM_ROLE).where("DIM_ROLE.role_name IN ('#{profile_list}')").joins(:stores).where("stores.code IN ('#{store_list}')")
end
end
if current_user.pmt_ptl_accnt_manager?
#users = #users.find_all {|user| !user.admin? and current_user.client_list.include? user.client_list}
end
# added #users_total to get the total users count before paginate
=begin
#users_total = #users
#users = #users.paginate(:per_page => params[:per_page] || 5, :page => params[:page])
#users_per_page = [{"name" => "5 per page", "id" => "5"},
{"name" => "10 per page", "id" => "10"},
{"name" => "15 per page", "id" => "15"},
{"name" => "20 per page", "id" => "20"}]
=end
end
`

Sorting the table not working

Following the #240 Railscasts i have a list of names like this
arnold
Arnold
Victor
And when I order this by that method, i get this in asc
Arnold
Victor
arnold
And this in desc
arnold
Victor
Arnold
In my controller I have:
def index
#alunos = Aluno.search(params[:search]).order(sort_column + " " + sort_direction).paginate(:per_page => params[:npage], :page => params[:page])
end
def sort_column
Aluno.column_names.include?(params[:sort]) ? params[:sort] : "nome"
end
And this in my application_helper
module ApplicationHelper
def sortable(column, title = nil)
title ||= column.titleize
css_class = column == sort_column ? "current #{sort_direction}" : nil
direction = column == sort_column && sort_direction == "asc" ? "desc" : "asc"
link_to title, params.merge(:sort => column.downcase, :direction => direction, :page => nil), {:class => css_class}
end
end
And I'm getting this (desc, in this example):
There is something that i must include in my controller or models to order in case INsensitive?
--edited
Im using sqlite3
If you're using SQL as the underlying datastore, you could do
#alunos = Aluno.search(params[:search]).order("LOWER(#{sort_column}) #{sort_direction}").paginate(:per_page => params[:npage], :page => params[:page])

Order array. undefined method `order' for Array. Convert array into hash?

I have a City model and in city's show action I want to render hotels nearby specific locations in the city. Cities has_many locations; hotels are being searched using Geocoder near method.
To add order functionality I've followed Ryan Bates screencasts #228, but this approach doesn't seem to work with arrays, giving error undefined method `order' for #< Array:0x007f960d003430>
cities_controller.rb
helper_method :sort_column, :sort_direction
def show
session[:search_radius] = 2 if session[:search_radius].blank?
#city = City.find(params[:id])
#locations = #city.locations
#hotels = []
#locations.each do |location|
unless location.longitude.blank? || location.latitude.blank?
center_point = [location.latitude, location.longitude]
box = Geocoder::Calculations.bounding_box(center_point, session[:search_radius])
thotels = Hotel.near(center_point, session[:search_radius]).within_bounding_box(box)
else
thotels = Hotel.near(center_point, session[:search_radius])
end
#hotels += thotels if thotels
#hotels = #hotels.uniq
end
#hotels = #hotels.order(sort_column + " " + sort_direction).paginate(:page => params[:page], :per_page => 5)
#json = #locations.to_gmaps4rails
respond_with #json, :location => city_url
end
private
def sort_column
Hotel.column_names.include?(params[:sort]) ? params[:sort] : "name"
end
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
end
My question is: should I concentrate in converting an array into hash or should I initially create hash of hotels, or maybe find completely different approach to perform sorting?
order is a method used for sorting at the database level. since #hotels is an array, you won't be able to sort using order. Try the following (not tested and you may want to include array pagination if you haven't included it yet)
#hotels = #hotels.sort_by(&:"#{sort_column}")
#hotels = #hotels.reverse if sort_direction == 'DESC'
#hotels = #hotels.paginate(:page => params[:page], :per_page => 5)

ActiveRecord condition syntax question

Is there a better way of writing this? Is it possible to do cleanly in one line?
conditions = ["category = ?", params[:category]] if params[:category]
#events = CalendarEvent.all( :conditions => conditions )
Not really too much to consolidate but you don't have a lot going on so shouldn't matter.
def action
options = {:conditions => ['category=?', params[:category]]} if params[:category]
#events = CalendarEvent.find(:all, options)
end
#events = CalendarEvent.all(
:conditions => (params[:category] ? ["category = ?", params[:category]] : nil))

How to refactor this Ruby on Rails code?

I want to fetch posts based on their status, so I have this code inside my PostsController index action. It seems to be cluttering the index action, though, and I'm not sure it belongs here.
How could I make it more concise and where would I move it in my application so it doesn't clutter up my index action (if that is the correct thing to do)?
if params[:status].empty?
status = 'active'
else
status = ['active', 'deleted', 'commented'].include?(params[:status]) ? params[:status] : 'active'
end
case status
when 'active'
#active posts are not marked as deleted and have no comments
is_deleted = false
comments_count_sign = "="
when 'deleted'
#deleted posts are marked as deleted and have no comments
is_deleted = true
comments_count_sign = "="
when 'commented'
#commented posts are not marked as deleted and do have comments
is_deleted = false
comments_count_sign = ">"
end
#posts = Post.find(:all, :conditions => ["is_deleted = ? and comments_count_sign #{comments_count_sign} 0", is_deleted])
class Post < ActiveRecord::Base
named_scope :active, :conditions => { :is_deleted => false, :emails_count => 0 }
named_scope :sent, :conditions => ["is_deleted = ? AND emails_count > 0", true]
...
end
use it like Post.active.all, Post.active.first, Post.active.each, etc
and then
status = %w'active deleted sent'.include?(params[:status]) : params[:status] : 'active'
#posts = Post.send(status).all
I would consider adding a class method to Post
def file_all_based_on_status status
# custom logic for queries based on the given status here
# handling nils and other cases
end
That way your Controller index is simple
def index
#posts = Post.find_all_based_on_status params[:status]
end

Resources