3
\$\begingroup\$

I'm brand new to both Ruby and Rails, and while all the following works, it seems a bit messy.

This is on a classified model to generate WHERE/AND WHERE clauses based on whether or not the user supplies a value through a dropdown. I'm not sure if there's a more Active Recordy way of doing this:

class Classified < ActiveRecord::Base
 attr_accessible :car_id, :owner_id, :owner, :price, :condition, :description, :mileage
 belongs_to :car
 belongs_to :owner, :polymorphic => true
 has_one :model, :through => :car
 has_one :make, :through => :model
 has_many :pictures
 scope :filter_active_cars, lambda { |options| 
 filter = {}
 filter[:cars] = {:year => options[:year]} unless options[:year].empty?
 filter[:makes] = {:id => options[:make]} unless options[:make].empty?
 filter[:models] = {:id => options[:model]} unless options[:model].empty?
 joins(:car, :model, :make) 
 .where(filter)
 .includes(:car, :model, :make)
 }

The view has this

=select_tag('car[year]', options_for_select(years, :selected => @selected_year))
=collection_select(:car, :make, Make.all, :id, :name, {:include_blank => '- Make -', :selected => @selected_make}) 
=collection_select(:car, :model, @selected_make ? Model.where(:make_id => @selected_make) : Model.all, :id, :name, {:include_blank => '- Model -', :selected => @selected_model}) 

This is the way I'm passing the "select state" on the classified view. The controller has this snippet in the index action. This is so when a user filters a result and submits the form, the corresponding select box has the same value for the form helpers above.

if params[:car]
 params[:car].each do |option, value|
 next if value.empty?
 eval("@selected_#{option} = #{value}")
 end
end

On the form partial to generate and edit a classified. Obviously the chaining of &&s sucks, but it's the only way to get the default selection on new, edit, and when a user submits a new form that contains errors.

=select_tag('car[year]', options_for_select(years, :selected => @classified.car.nil? ? nil : @classified.car.year), :prompt => "- Year -")
=collection_select(:make, :id, Make.all, :id, :name, {:prompt => '- Make -', :selected => (@classified.car && @classified.car.make && @classified.car.make.try(:id))}) 
=collection_select(:car, :model_id, Model.all, :id, :name, {:include_blank => '- Model -', :selected => (@classified.car && @classified.car.model && @classified.car.model.try(:id))}) 
asked Jul 18, 2012 at 14:52
\$\endgroup\$
1
  • \$\begingroup\$ I think you'd better split your question into several \$\endgroup\$ Commented Aug 7, 2012 at 21:54

1 Answer 1

1
\$\begingroup\$

Maybe you can convert this:

scope :filter_active_cars, lambda { |options| 
 filter = {}
 filter[:cars] = {:year => options[:year]} unless options[:year].empty?
 filter[:makes] = {:id => options[:make]} unless options[:make].empty?
 filter[:models] = {:id => options[:model]} unless options[:model].empty?
 joins(:car, :model, :make) 
 .where(filter)
 .includes(:car, :model, :make)
 }
}

to this:

scope :filter_active_cars, lambda do |options| 
 with_option(:car, 'cars.year', :year).
 with_option(:make, 'makes.id', :make).
 with_option(:model, 'models.id', :model)
end
scope :with_option, lambda do |assoc, column, option|
 (options[option].present? ?
 joins(assoc).where(column => options[option]) :
 self).
 includes(assoc)
end

You can reuse the with_option scope in other models

answered Aug 7, 2012 at 21:46
\$\endgroup\$
2
  • \$\begingroup\$ I would further just stop using scopes, and instead use methods. Scopes are pretty much a weird way to define a singleton method on a ActiveRecord-behaving class that MUST return an ActiveRecord::Relation & has infinite Arity. Better to have a real method with real Arity that can return anything. \$\endgroup\$ Commented Feb 20, 2013 at 17:46
  • \$\begingroup\$ the fact that a scope MUST return a relation is the reason we use scopes. I'm not a big fan of those, but at least when you use one you know what to expect from it. As to @Alexey's solution, +1 - that's probably what i'll come up with ; one could go further and abstract this behavior with metaprogramming. This would make a nice macro like filter :active_cars, options: {cars: {year: :year}, makes: {make: :id}, models: {model: :id}}. The macro would reflect on associations, build necessary scopes and map params keys to columns. \$\endgroup\$ Commented Jun 4, 2013 at 7:11

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.