Hi,
I have the below code which forms the bulk of a helper. I use it to take a set of URL query parameters and to filter down a Model to only return the stuff the user has chosen to see.
I feel like the code is getting rather “spaghetti-ish” and wanted to post here to see if anyone could suggest ways of refactoring this or simplifying it.
I am also getting an N+1 alert from the Bullet gem which is asking me to add .includes(:park)
. I have done this as you can see below but the N+1 alert is not going away.
I have thought about turning this into a whole load of scopes but some of the parts do a bit more than what I thought scopes should manage.
Any suggestions?
def filter_coasters
unless params[:sort]
params[:sort] = 'order'
end
if params[:sort] == 'order'
#Added .includes(:park) on the lines below to try to solve N+1 but it doesn't appear to work
@coasters = Coaster.includes(:park).originals
else
@coasters = Coaster.includes(:park).latest
end
if params[:material]
@coasters = @coasters.where("material LIKE ?", params[:material])
end
# if params[:letters]
# params[:sort] = 'alphabetically' unless params[:sort]
# else
# params[:sort] = 'order' unless params[:sort]
# end
if params[:letter]
letter = params[:letter]
if letter == "numbers"
conditions = (0..9).to_a.map{ |number| " coaster_sort = '#{number}' " }.join('OR')
else
# If single letter
if params[:letter] =~ /^[a-zA-Z]$/
letter = params[:letter]
conditions = " coaster_sort = '#{letter}' "
# If range of letters
else
letter = letter.split('-')
letter = (letter[0]..letter[1]).to_a
conditions = letter.map{ |letter| " coaster_sort = '#{letter}' " }.join('OR')
end
end
@coasters = @coasters.where(conditions)
if params[:type]
params[:sort] = 'park' unless params[:sort]
else
params[:sort] = 'alphabetically' unless params[:sort]
end
end
if params[:milestones]
@coasters = @coasters.where(is_milestone: true)
end
if params[:soundtracks]
@coasters = @coasters.where(soundtrack: true)
end
if params[:country]
@coasters = @coasters.where("country = ?", params[:country].upcase).references(:park)
end
if params[:manufacturer]
@coasters = @coasters.where(manufacturer: params[:manufacturer])
end
if params[:position]
@coasters = @coasters.where("position <> ''")
end
if params[:ridden_in]
year = params[:ridden_in].to_s
if params[:new_in_riy]
cycles_in_year = Cycle.where(first_time: :true).where("EXTRACT(year FROM date) = ?", year).map(&:cyclable_id)
@coasters = @coasters.where(id: cycles_in_year)
else
@coasters = @coasters.joins(:cycles).where("EXTRACT(year FROM date) = ?", year)
end
end
if params[:sort] == 'order'
@coasters = @coasters.except(:order).order('coasters.order_ridden ASC')
elsif params[:sort] == 'park'
@coasters = @coasters.except(:order).order('parks.name ASC').order('coasters.name ASC')
elsif params[:sort] == 'alphabetical'
@coasters = @coasters.except(:order).order('coasters.coaster_sort ASC, coasters.name ASC')
elsif params[:sort] == 'random'
@coasters = @coasters.except(:order).order("RAND()")
elsif params[:sort] == 'date_opened'
@coasters = @coasters.except(:order).order('coasters.date_opened ASC')
elsif params[:sort] == 'speed'
@coasters = @coasters.except(:order).order('coasters.speed DESC')
elsif params[:sort] == 'length'
@coasters = @coasters.except(:order).order('coasters.length DESC')
elsif params[:sort] == 'height'
@coasters = @coasters.except(:order).order('coasters.height DESC')
elsif params[:sort] == 'inversions'
@coasters = @coasters.except(:order).order('coasters.inversions DESC')
elsif params[:sort] == 'vertical_angle'
@coasters = @coasters.except(:order).order('coasters.vertical_angle DESC')
else
params[:sort] = 'park'
@coasters = @coasters.except(:order).order('parks.name ASC').order('coasters.name ASC')
end
if params[:reverse]
@coasters = @coasters.reverse_order
end
if params[:stat]
# if params[:year].blank?
# year = 2017
# else
year = params[:year].to_i
# end
if params[:stat] == 'new_coasters'
@coasters = @coasters.to_a
@coasters_in_year = Coaster.new_coasters_in_year(year)
intersection = (@coasters & @coasters_in_year)
@coasters = Kaminari.paginate_array(intersection, { total_count: intersection.count })
end
if params[:stat] == 'unique_coasters'
@coasters = @coasters.to_a
@unique_coasters_in_year = Coaster.unique_coasters(year: year)
intersection = (@coasters & @unique_coasters_in_year)
@coasters = Kaminari.paginate_array(intersection, { total_count: intersection.count })
end
end
if params[:coaster_attributes]
unless params[:coaster_attributes] == ''
atts = params[:coaster_attributes].split("|")
@coasters = @coasters.tagged_with(atts, on: :coaster_attributes)
end
end
if params[:coaster_styles]
@coasters = @coasters.tagged_with(params[:coaster_styles], on: :coaster_styles)
end
# Split and recombine to put nil values at end
if params[:sort] == 'speed'
nils, not_nils = @coasters.partition { |c| c.speed.nil? }
not_nils.each do |c|
c.misc = c.speed.to_s + ' mph'
end
@coasters = not_nils + nils
end
if params[:sort] == 'length'
nils, not_nils = @coasters.partition { |c| c.length.nil? }
not_nils.each do |c|
c.misc = c.length.to_s + ' feet'
end
@coasters = not_nils + nils
end
if params[:sort] == 'height'
nils, not_nils = @coasters.partition { |c| c.height.nil? }
not_nils.each do |c|
c.misc = c.height.to_s + ' feet'
end
@coasters = not_nils + nils
end
if params[:sort] == 'inversions'
nils, not_nils = @coasters.partition { |c| c.inversions.nil? }
not_nils.each do |c|
c.misc = helpers.pluralize(c.inversions, 'inversion')
end
@coasters = not_nils + nils
end
if params[:sort] == 'vertical_angle'
nils, not_nils = @coasters.partition { |c| c.vertical_angle.nil? }
not_nils.each do |c|
c.misc = c.vertical_angle.to_s + '°'
end
@coasters = not_nils + nils
end
end