Activeadmin custom sortable column in index - ruby-on-rails

I have custom sortable column 'Status' in index, how can I make it sortable?
index do
selectable_column
column :title
column :counter
column 'Status', do |purchase|
if purchase.status == 'verified' && purchase.expiration_date < DateTime.now.utc
'ended'
elsif purchase.status == 'verified' && purchase.expiration_date >= DateTime.now.utc
'active'
else
purchase.status
end
end
end

According to the docs you should be able to pass a :sortable key to the column call like so:
index do
# SNIP
column 'Status', sortable: :status do |purchase|
# SNIP
end
end

The documentation for index table custom sorting is on the index as table page.

Solved a problem in this way
column 'Status', sortable: :status do |purchase|
if purchase.status == 'verified' && purchase.expiration_date < DateTime.now.utc
'ended'
elsif purchase.status == 'verified' && purchase.expiration_date >= DateTime.now.utc
span class: "purchase-status-active" do
'active'
end
else
purchase.status
end
end
controller do
def find_collection(options = {})
if params[:order] == 'status_desc'
super.reorder(status: :desc, expiration_date: :desc)
elsif params[:order] == 'status_asc'
super.reorder(status: :asc, expiration_date: :asc)
else
super
end
end
end

Related

Array.select with optional conditionals

I have an Array of Hashes
[
{ :user_id => 123,
:start_date => Date,
:end_date => Date
},
{ :user_id => 345,
:start_date => Date,
:end_date => Date
},
...
]
I want to select some of the objects based on optional parameters, say if params[:user_id], params[:start_date], params[:end_date] is optionally passed from the controller. How do I include multiple conditionals within a single .select statement.
Array.select do |arr|
arr[:user_id] == params[:user_id] if params[:user_id] &&
arr[:start_date] >= params[:start_date] if params[:start_date] &&
arr[:end_date] <= params[:end_date] if params[:end_date]
end
wasn't working as intended. The 2nd and 3rd conditional is ignored.
A pattern I like to use is next. This is basically a way to 'return early' from the block, and split up your conditions into multiple statements. Just make sure you add a true at the end so that if the item passes all the validations, it will be included in the result.
result = array.select do |item|
if params[:user_id]
next if item[:user_id] != params[:user_id]
end
if params[:start_date]
next if item[:start_date] <= params[:start_date]
end
if params[:end_date]
next if item[:end_date] >= params[:end_date]
end
true
end
you could of course change all these if .. else blocks to one liners if you prefer:
result = array.select do |item|
next if params[:user_id] && item[:user_id] != params[:user_id]
next if params[:start_date] && item[:start_date] <= params[:start_date]
next if params[:end_date] && item[:end_date] >= params[:end_date]
true
end

Getting results with a column value prioritized

Rails 3.2.8
I have the following:
def index
params[:direction] ||= "desc"
params[:sort] ||= "status"
#products = product.solr_search( include: [:customer] ){
fulltext params[:query] if params[:query].present?
with :status, (params[:status] || params[:filter]) if (params[:status] || params[:filter]).present?
order_by( params[:sort], params[:direction] ) if params[:sort].present? && params[:direction].present?
paginate page: params[:page], per_page: params[:per_page]
}.results
end
What I would like to do is do the initial sort with a priority when the status column = 'new'. In MySQL, it would be something like this:
SELECT * from product ORDER BY case when status = 'new'
Any idea what my syntax should be?

How to Convert an array object to ActiveRecord relational object

I am using jqgrid_for_rails gem to design a table from the database. In my table few column data are coming from virtual attributes. And my jqgrid rails in loadonce => false(ie. it loads the data with the pagination unlike single time).
In my search function i have to provide the searchField what to search, but it directly hitting the database columns. So i wanted to apply the search function into an array. But not possible because it is by default searching in ActiveRecord model. So i want to convert my array object to database ActiveRecord Model.
How is it possible?
Here is my code - Helper file
def order_tracker_jqgrid
options = {:on_document_ready => true, :html_tags => false}
grid = [{
:url => '/order_tracker',
:datatype => 'json',
:loadonce => false,
:mtype => 'GET',
:height => 350,
:altRows => true,
:rownumbers => true,
:colNames => ['#' , 'Order #', 'Tracking #', 'Items', 'Delivery Date', 'Current Status', 'Pickup Location', 'Created By', 'Retailer'],
:colModel => [
{ :name => 'id', :index => 'id', :align => 'center', :search => false, :width => 100, :hidden => true},
{ :name => 'order_number', :index => 'order_number', :classes => 'order-number', :width => 100, :searchoptions => { :sopt => ['eq', 'cn']}},
{ :name => 'tracking_number', :index => 'tracking_number', :width => 110, :classes => 'tracking-number', :searchoptions => { :sopt => ['eq', 'cn']}},
{ :name => 'item_count', :index => 'item_count', :align => 'center' , :width => 80, :search => false},
{ :name => 'delivery_date_with_slot', :index => 'delivery_date_with_slot', :width => 200, :search => false},
{ :name => 'order_state', :index => 'order_state', :search => false},
{ :name => 'pickup_location_data', :index => 'pickup_location_data', :jsonmap => 'pickup_location_data', :search => false},
{ :name => 'creator_name', :index => 'creator_name', :search => false},
{ :name => 'company_name', :index => 'company_name', :search => false}
],
:pager => '#grid-pager',
:rowNum => 10,
:rowList => [10, 20, 30],
:sortname => 'id',
:sortorder => 'asc',
:viewrecords => true,
:autowidth => true,
:loadComplete => "function(){var table = this;setTimeout(function(){updatePagerIcons(table);}, 0);}".to_json_var,
:gridComplete => "function(){
var ids = jQuery('#grid-table').jqGrid('getRowData');
for(var i = 0; i < ids.length; i++ )
{
var id = ids[i].id;
var o_number = ids[i].order_number;
var t_number = ids[i].tracking_number;
var order_link = ''+o_number+'';
var tracker_link = ''+t_number+'';
$('#grid-table tr#'+id).find('.order-number').html(order_link);
$('#grid-table tr#'+id).find('.tracking-number').html(tracker_link);
}
}".to_json_var
}]
pager = [:navGrid, "#grid-pager", {:del => false, :add => false, :edit => false, :searchicon => 'icon-search orange', :refreshicon => 'icon-refresh blue'}, {:closeAfterEdit => true, :closeOnEscape => true}, {}, {}, {}, {}]
jqgrid_api 'grid-table', grid, pager, options
end
On my controller
#If current user is fulflmnt admin
#orders = OrderBucket.orders_from_cate2_3.where(search_params).order("created_at desc") #("created_at >= ? AND created_at <= ?", Time.zone.now.beginning_of_month, Time.zone.now.end_of_month).order("created_at desc")
#columns = ['id' , 'order_number' , 'tracking_number', 'item_count', 'delivery_date_with_slot', 'order_state', 'pickup_location_data', 'creator_name', 'company_name']
if params[:_search] == "true"
#orders = #orders.search(params[:_search],params[:searchField],params[:searchString], params[:page],params[:rows],params[:searchOper],params[:sord])
else
sort_params = params[:sidx]
if sort_params == "order_number"
#orders = params[:sord] == "asc" ? #orders.sort_by{|p| p.order_number}.flatten : #orders.sort_by{|p| p.order_number}.reverse.flatten
elsif sort_params == "tracking_number"
#orders = params[:sord] == "asc" ? #orders.sort_by{|p| p.tracking_number}.flatten : #orders.sort_by{|p| p.tracking_number}.reverse.flatten
elsif sort_params == "pickup_location_data"
#orders = params[:sord] == "asc" ? #orders.sort_by{|p| p.pickup_location_data}.flatten : #orders.sort_by{|p| p.pickup_location_data}.reverse.flatten
elsif sort_params == "order_state"
#orders = params[:sord] == "asc" ? #orders.sort_by{|p| p.order_state}.flatten : #orders.sort_by{|p| p.order_state}.reverse.flatten
elsif sort_params == "creator_name"
#orders = params[:sord] == "asc" ? #orders.sort_by{|p| p.creator_name}.flatten : #orders.sort_by{|p| p.creator_name}.reverse.flatten
elsif sort_params == "company_name"
#orders = params[:sord] == "asc" ? #orders.sort_by{|p| p.company_name}.flatten : #orders.sort_by{|p| p.company_name}.reverse.flatten
elsif sort_params == "delivery_date_with_slot"
#orders = params[:sord] == "asc" ? #orders.sort_by{|p| p.delivery_date_with_slot}.flatten : #orders.sort_by{|p| p.delivery_date_with_slot}.reverse.flatten
elsif sort_params == "item_count"
#orders = params[:sord] == "asc" ? #orders.sort_by{|p| p.item_count}.flatten : #orders.sort_by{|p| p.item_count}.reverse.flatten
end
#orders = #orders.paginate(
:page => params[:page],
:per_page => params[:rows],
:order => order_by_from_params(params))
end
if request.xhr?
render :json => json_for_jqgrid(#orders, #columns)
end
and on my model
search method to find data for jqGrid order table
def self.search(_search,searchField, searchString,page,rows,searchOper,sord)
if searchOper == 'cn' && _search # contains
wildcard_search = "%#{searchString}%"
where("#{searchField} LIKE :search", search: wildcard_search).page(page).per_page(rows)
elsif searchOper == 'nc' && _search # does not contain
wildcard_search = "%#{searchString}%"
where.not("#{searchField} LIKE :search", search: wildcard_search).page(page).per_page(rows)
elsif searchOper == 'bw' && _search # begin with
wildcard_search = "#{searchString}%"
where("#{searchField} LIKE :search", search: wildcard_search).page(page).per_page(rows)
elsif searchOper == 'bn' && _search # does not begin with
wildcard_search = "#{searchString}%"
where.not("#{searchField} LIKE :search", search: wildcard_search).page(page).per_page(rows)
elsif searchOper == 'en' && _search # does not end with
wildcard_search = "%#{searchString}"
where.not("#{searchField} LIKE :search", search: wildcard_search).page(page).per_page(rows)
elsif searchOper == 'ew' && _search # ends with
wildcard_search = "%#{searchString}"
where("#{searchField} LIKE :search", search: wildcard_search).page(page).per_page(rows)
elsif searchOper == 'eq' && _search # equal
where("#{searchField} = :search", search: searchString).page(page).per_page(rows)
elsif searchOper == 'ne' && _search # not equal
where.not("#{searchField} = :search", search: searchString).page(page).per_page(rows)
elsif searchOper == 'in' && _search # is in
# where("#{searchField} = :search", search: searchString).page(page).per_page(rows)
elsif searchOper == 'ni' && _search # is in
# where("#{searchField} = :search", search: searchString).page(page).per_page(rows)
elsif searchOper == 'lt' && _search # less
where("#{searchField} < :search", search: searchString).page(page).per_page(rows)
elsif searchOper == 'le' && _search # less or equal
where("#{searchField} <= :search", search: searchString).page(page).per_page(rows)
elsif searchOper == 'gt' && _search # greater
where("#{searchField} > :search", search: searchString).page(page).per_page(rows)
elsif searchOper == 'ge' && _search # greater or equal
where("#{searchField} >= :search", search: searchString).page(page).per_page(rows)
else
wildcard_search = "%#{searchString}%" # contains
where("name LIKE :search", search: wildcard_search).page(page).per_page(rows)
end
end
I think I got something that can work. I never used it though so I can't guarantee about its stability (and if you use it I would like your feedback please)
So let's work this example with User model. You got an array with few user records
array = [User.find(5), User.find(50), User.find(500)]
You can create an empty relation object
# Rails 4: `none` is going to be the easiest and most efficient
relation = User.none
# Rails 3: we have to cheat
relation = User.where('1 = 0')
And add them one by one
relation << array[0]
relation << array[2]
Or all at once
relation += array
To convert an array object to ActiveRecord relational object, just perform the following:
MyModel.where(id: my_arr.map(&:id))
(a) MyModel is the class which the objects in array are
(b) arr is the array object

How can validate params in the view?

Here is the table:
|policies|
|id| |name| |date|
1 ABC 2013-01-01
2 DEF 2013-01-21
Here is the controller:
class PolicyController < ApplicationController
def index
#policies = Policy.find(:all,:conditions=>['date BETWEEN ? AND ?',params[cam],params[:cam2] ])
end
end
Here is the model:
class Policy < ActiveRecord::Base
end
Here is the view:
<% CalendarDateSelect.format=(:hyphen_ampm )%>
<% calendar_date_select_style "silver" %>
<% translation_calendar %>
<% form_tag :controller=>"policy",:action=>"index" do %>
From: <%= calendar_date_select_tag "cam", params[:cam] %>
To: <%= calendar_date_select_tag "cam2",params[:cam2] %>
<%= submit_tag "Search", :name => nil %></p>
<% end %>
<% #policies.each |p| do %>
<%= p.date %>
<% end %>
How can block the SEARCH button until my 2 calendar text have values?
I tried this:
class Policy < ActiveRecord::Base
validates_presence_of :cam
end
Please somebody can help me please or maybe a javascript?
I will appreciate all kind of help.
Here is what friends told me:
named_scope :by_calendar_date,lambda { |cam1, cam2| } {
if cam1.nil? && cam2.nil?
scoped
elsif cam1.nil?
where("date < ?",cam2)
elsif cam2.nil?
where("date > ?", cam1)
else
where("date BETWEEN ? AND ?",cam1,cam2)
end
}
#this would perform the same functionality so both are not needed but just for edification
def self.by_calendar_date(cam1,cam2)
if cam1.nil? && cam2.nil?
scoped
elsif cam1.nil?
where("date < ?",cam2)
elsif cam2.nil?
where("date > ?", cam1)
else
where("date BETWEEN ? AND ?",cam1,cam2)
end
end
Since this is a search feature you do not need to validate in my opinion you could just do this
class Policy < ActiveRecord::Base
named_scope :by_calendar_date,lambda{|cam1,cam2|
if cam1.nil? && cam2.nil?
{:conditions => {}}
elsif cam1.nil?
{:conditions => ["date < ?",cam2]}
elsif cam2.nil?
{:conditions => ["date > ?", cam1]}
else
{:conditions => ["date >= ? AND date <= ?",cam1,cam2]}
end
}
end
call with
def index
#policies = Policy.by_calendar_date(params[:cam1],params[:cam2])
end
This way the user can set either param individually or none at all
Check out this fiddle... http://jsfiddle.net/qKG5F/641/
HTML
// note the change... I set the disabled property right away
<input type="submit" id="register" value="Register" disabled="disabled" />
JavaScript
(function() {
$('form > input').keyup(function() {
var empty = false;
$('form > input').each(function() {
if ($(this).val() == '') {
empty = true;
}
});
if (empty) {
$('#register').attr('disabled', 'disabled'); // updated according to http://stackoverflow.com/questions/7637790/how-to-remove-disabled-attribute-with-jquery-ie
} else {
$('#register').removeAttr('disabled'); // updated according to http://stackoverflow.com/questions/7637790/how-to-remove-disabled-attribute-with-jquery-ie
}
});
})()
The nice thing about this is that it doesn't matter how many input fields you have in your form, it will always keep the button disabled if there is at least 1 that is empty. It also checks emptiness on the .keyup() which I think makes it more convenient for usability.
I hope this helps.

Previous/Next links based on model's function

How do I display previous/next links according to vehicle.vehicle_status
In my show view:
- if #vehicle.previous
= link_to "< Previous", #vehicle.previous
- if #vehicle.next
= link_to "Next >", #vehicle.next
In my model:
def previous
?
end
def next
?
end
vehicles/index.html.haml view:
- #vehicles.each do |vehicle|
%tr
%td.dashbox{:class => vehicle.vehicle_status, :style =>'width:18px;', :onclick=>"top.location=#{vehicle_url(vehicle)}"}
vehicle_status in model:
def vehicle_status
if self.maintenance_status=='c1' or self.fuel_efficiency_status=='c1' or self.system_status=='c1'
'c1'
elsif self.maintenance_status=='c2' or self.fuel_efficiency_status=='c2' or self.system_status=='c2'
'c2'
elsif self.maintenance_status=='c4' or self.fuel_efficiency_status=='c4' or self.system_status=='c4'
'c4'
else
'c3'
end
end
scope :previous, lambda { |vehicle|
where("vehicles.vehicle_status < ?", vehicle.vehicle_status).
order(:vehicle_status).reverse
}
scope :next, lambda { |vehicle|
where("vehicles.vehicle_status > ?", vehicle.vehicle_status).
order(:vehicle_status)
}
def previous
#previous ||= Vehicle.previous(self).first
end
def next
#next ||= Vehicle.next(self).first
end
In light of the fact that vehicle_status is a calculation and not a static field, and that there are other factors involved in the sorting, you can modify the scopes to look like this (using a helper method for the CASE string):
def self.vehicle_status_sql
<<-SQL
CASE
WHEN 'c1' IN (maintentance_status, fuel_efficiency_status, system_status) THEN 'c1'
WHEN 'c2' IN (maintentance_status, fuel_efficiency_status, system_status) THEN 'c2'
WHEN 'c4' IN (maintentance_status, fuel_efficiency_status, system_status) THEN 'c4'
ELSE 'c3'
END
SQL
end
scope :previous, lambda { |vehicle|
where("#{vehicle_status_sql} < :status OR (#{vehicle_status_sql} = :status AND (vehicles.odometer < :odometer OR (vehicles.odometer = :odometer AND vehicles.id < :id)))",
status: vehicle.vehicle_status, odometer: vehicle.odometer, id: vehicle.id).
order("#{vehicle_status_sql} DESC, vehicles.odometer DESC")
}
scope :next, lambda { |vehicle|
where("#{vehicle_status_sql} > :status OR ((#{vehicle_status_sql} = :status AND (vehicles.odometer > :odometer OR (vehicles.odometer = :odometer AND vehicles.id > :id)))",
status: vehicle.vehicle_status, odometer: vehicle.odometer, id: vehicle.id).
order("#{vehicle_status_sql}, vehicles.odometer)
}

Resources