Rails DRY up similar controller actions - ruby-on-rails

I have two separate controllers that inherit from Admin::UserBaseController, display a searchable, sortable table of users, and use the same partial views.
Admin::UsersController - Display users within the context of a given organization.
Admin::OrganizationsController - Displays all users for the system.
Here is the index method of Admin::UsersController:
def index
q = "%#{params[:search]}%"
#users = User.where("first_name like ? or last_name like ? or username like ?", q, q, q).order(sort_column + ' ' + sort_direction).paginate(:page => params[:page])
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #users }
end
end
Here is the edit method of Admin::OrganizationsController:
def edit
#organization = Organization.find(params[:id])
q = "%#{params[:search]}%"
#users = #organization.users.where("first_name like ? or last_name like ? or username like ?", q, q, q).order(sort_column + ' ' + sort_direction).paginate(:page => params[:page])
end
There is a lot of similarity between the two methods in the way that the #users variable is assigned. It's a difference of User and #organization.users and that's it. How do I DRY this up?

So what this screams is scopes. This removes the duplicate queries into a single place in the model and enables you to chain scopes onto the class and associations.
class User < ActiveRecord::Base
scope :search_identity, lambda { |identity| where("first_name like ? or last_name like ? or username like ?", identity, identity, identity) }
scope :user_order, lambda { |column,direction| order("#{column} #{direction}") }
end
Then in Admin::UsersController
q = "%#{params[:search]}%"
#users = User.search_identity( q ).user_order( sort_column, sort_direction).paginate(:page => params[:page])
In Admin::OrganizationsController:
q = "%#{params[:search]}%"
#users = #organization.users.search_identity( q ).user_order( sort_column, sort_direction).paginate(:page => params[:page])
Making everything nice and succinct.

Move
where("first_name like ? or last_name like ? or username like ?", q, q, q).order(sort_column + ' ' + sort_direction).paginate(:page => params[:page])
to a method in User
such as:
def self.method_name(q,params)
where("first_name like ? or last_name like ? or username like ?", q, q, q).order(sort_column + ' ' + sort_direction).paginate(:page => params[:page])
end
then just use that method in place of the where

Related

Rails - jqGrid cannot sort and search using will_paginate/array

I use jqGrid and will_paginate to make a table.
Here's my code in the controller
def index
index_columns ||= [:pid,:name,:gender,:birthday,:school]
current_page = params[:page] ? params[:page].to_i : 1
rows_per_page = params[:rows] ? params[:rows].to_i : 10
conditions={:page => current_page, :per_page => rows_per_page}
conditions[:order] = params["sidx"] + " " + params["sord"] unless (params[:sidx].blank? || params[:sord].blank?)
if params[:_search] == "true"
conditions[:conditions]=filter_by_conditions(index_columns)
end
#people = Person.paginate(conditions)
total_entries=#people.total_entries
respond_with(#people) do |format|
format.html
format.json { render :json => #people.to_jqgrid_json(index_columns, current_page, rows_per_page, total_entries)}
end
end
With these code, the data can be sort and search correctly in jqGrid.
However, after I modifying it to
#temp = Person.limit(0).all
KlassesPeople.where(:klass_id => 1).each do |stu|
#temp.concat( Person.where(:id => stu.person_id) )
end
#people = #temp.paginate(conditions)
total_entries=#people.total_entries
The data still can be shown in jqGrid but can't be sort and search
I do add require 'will_paginate/array' in my controller
any idea?
I have uploaded jqgrid server side search and CRUD operation using ruby on rails 4.0
find the below link -
https://github.com/Rameshwar007/jqgrid_rails_4_sample
Let me know If you any further query.

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)

Scope is not recognizing both columns?

On my Tokeninput autocomplete field I am trying to make the returned columns be both my :address and :website when it goes by the defined :store method.
class BusinessStore < ActiveRecord::Base
scope :search_by_store, lambda { |q|
(q ? where(["address LIKE ? or website LIKE ? like ?", '%'+ q + '%', '%'+ q + '%','%'+ q + '%' ]) : {})}
def store
if self.online_store
"#{business_name} - #{website}"
else
"#{business_name} - #{address}"
end
end
end
class BusinessStoresController < ApplicationController
def index
#business_stores = BusinessStore.all
#business_stores = BusinessStore.search_by_store(params[:q])
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #business_stores }
format.json { render :json => #business_stores.collect{|b|{:id => b.id, :name => b.store } } }
end
end
end
My json page : http://localhost:3000/business_stores.json shows all the results correctly but the Token field only shows :address results and not website ones. How do I fix this?
Try this:
(q ? where(["address LIKE ? OR website LIKE ?", "%#{q}%", "%#{q}%" ]) : {})}

Making a fat controller in rails 3 skinny

I have this terribly large controller in my app. I'd really like to make it as skinny as possible. Below is some of the code, showing the types of things I'm currently doing.. I'm wondering what things I can move out of this?
A note - this is not my exact code, a lot of it is similar. Essentially every instance variable is used in the views - which is why I dont understand how to put the logic in the models? Can models return the values for instance variables?
def mine
#For Pusher
#push_ch = "#{current_user.company.id}"+"#{current_user.id}"+"#{current_user.profile.id}"
#Creating a limit for how many items to show on the page
#limit = 10
if params[:limit].to_i >= 10
#limit = #limit + params[:limit].to_i
end
#Setting page location
#ploc="mine"
#yourTeam = User.where(:company_id => current_user.company.id)
#Set the user from the param
if params[:user]
#selectedUser = #yourTeam.find_by_id(params[:user])
end
#Get all of the user tags
#tags = Tag.where(:user_id => current_user.id)
#Load the user's views
#views = View.where(:user_id => current_user.id)
if !params[:inbox]
#Hitting the DB just once for all the posts
#main_posts = Post.where(:company_id => current_user.company.id).includes(:status).includes(:views)
#main_posts.group_by(&:status).each do |status, posts|
if status.id == #status.id
if #posts_count == nil
#posts_count = posts
else
#posts_count = #posts_count + posts
end
elsif status.id == #status_act.id
if #posts_count == nil
#posts_count = posts
else
#posts_count = #posts_count + posts
end
end
end
if params[:status] == "All" || params[:status] == nil
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).order(sort_column + " " + sort_direction).where(:company_id => current_user.company.id, :status_id => [#status.id, #status_act.id, #status_def.id, #status_dep.id, #status_up.id]).limit(#limit).includes(:views)
else
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).order(sort_column + " " + sort_direction).where(:company_id => current_user.company.id).limit(#limit).includes(:views)
end
elsif params[:inbox] == "sent"
#yourcompanylist = User.where(:company_id => current_user.company.id).select(:id).map(&:id)
#yourcompany = []
#yourcompanylist.each do |user|
if user != current_user.id
#yourcompany=#yourcompany.concat([user])
end
end
if params[:t]=="all"
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).tag_filter(params[:tag], current_user).order(sort_column + " " + sort_direction).where(:user_id => current_user.id).includes(:views, :tags).limit(#limit)
elsif params[:status]!="complete"
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).tag_filter(params[:tag], current_user).order(sort_column + " " + sort_direction).where(:user_id => current_user.id).includes(:views, :tags).limit(#limit)
elsif params[:status]!=nil
#posts = Post.search(params[:search]).status_filter(params[:status]).user_filter(params[:user]).tag_filter(params[:tag], current_user).order(sort_column + " " + sort_direction).where(:user_id => current_user.id).includes(:views, :tags).limit(#limit)
end
end
respond_to do |format|
format.html # index.html.erb
format.js # index.html.erb
format.xml { render :xml => #posts }
end
end
You can start by moving logic into the model...
A line like this screams of feature envy:
#push_ch = "#{current_user.company.id}"+"#{current_user.id}"+"#{current_user.profile.id}"
I would recommend moving it into the model:
#user.rb
def to_pusher_identity
"#{self.company_id}#{self.id}#{self.profile_id}"
end
And then in your controller
#push_ch = current_user.to_pusher_identity
At this point you could even move this into a before_filter.
before_filter :supports_pusher, :only => :mine
Another thing you can do is create richer associations, so you can express:
#tags = Tag.where(:user_id => current_user.id)
as
#tags = current_user.tags
Another example would be for main posts, instead of
Post.where(:company_id => current_user.company.id).includes(:status).includes(:views)
you would go through the associations:
current_user.company.posts.includes(:status).includes(:views)
When I'm drying out a controller/action I try to identify what code could be (should be?) offloaded into the model or even a new module. I don't know enough about your application to really point to where these opportunities might lie, but that's where I'd start.
Few quick ideas:
Consider using respond_to/respond_with. This controller action can be splitted up to two separate ones - one for displaying #main_posts, another for params[:inbox] == "sent". The duplicate code can be removed using before_filters.
Also, a couple of gem suggestions:
use kaminari or will_paginate for pagination
meta_search for search and sorting

check if Tag exists before adding it to array

I have the following code:
unless params[:search_tags].nil?
logger.debug "Going through tags now #{params[:search_tags]}"
params[:search_tags].split(",").each{ |tag|
tag.strip!
tag = '%' + tag + '%'
tags = Tag.find(:all, :conditions => ["name LIKE ?", tag])
if tags.nil? || tags.empty? # I'm searching for something that does not actually exist!
#listings = []
else
tags.each {|tag|
logger.debug "Checking #{tag}"; #listings = #listings & tag.listings
}
end
}
logger.debug "I have #{#listings.size} listings left after hashtag stripping"
end
Problem is, if I enter 2 tags and 1 does not exist it returns no results. I'd like to add a check check if a tag exists before it is added.
Try this:
unless params[:search_tags].nil?
logger.debug "Going through tags now #{params[:search_tags]}"
params[:search_tags].split(",").each do |tag|
tag.strip!
tag = '%' + tag + '%'
tags = Tag.find(:all, :conditions => ["name LIKE ?", tag])
#listings = []
unless tags.nil || tags.empty?
tags.each do |tag|
logger.debug "Checking #{tag}";
#listings << tag.listings
end
end
#listings = #listings.uniq
end
logger.debug "I have #{#listings.size} listings left after hashtag stripping"
end

Resources