Forum: Ruby on Rails Not sure how to do this

Posted by jhaagmans (Guest)
on 2009-07-02 23:43
(Received via mailing list)
In an app I'm working on, I want to have a filter in which you can
specify certain attributes and click "filter" to activate an AJAX
request and return a list of, say, machines that match with those
attributes.

No I'm not sure how to build the controller.

If I would specify all the attributes, I would simply do:

def filter
  @machines = Machine.find(:all, :conditions => { :attr_1 => params
[:attr_1], :attr_2 ... }
end

But how would I do this if only some of the attributes are specified
and the others are nil? Would I have to create @machines including all
machines and have Rails do the rest of the work or is there a way to
get find to do what I want?
Posted by Pat Nakajima (patnakajima)
on 2009-07-02 23:56
(Received via mailing list)
Off the top of my head:
  class Machine < ActiveRecord::Base
    # Define a list of acceptable params for filtering
    FILTERS = [:name, :code]

    # Finds records by params
    def self.filter_by_params(params)
      conditions = params.select { |key,val| 
FILTERS.include?(key.to_sym)
and val }
      all(:conditions => conditions)
    end
  end

  class MachinesController < ApplicationController
    def index
      # Where params is { :filter => { :name => 'Pat', :code => '123' }}
      @machines = Machine.filter_by_params(params[:filter])
    end
  end
Posted by jhaagmans (Guest)
on 2009-07-03 11:07
(Received via mailing list)
Could you explain why the first part would go entirely into the
Machine model?
Posted by Pat Nakajima (patnakajima)
on 2009-07-03 22:52
(Received via mailing list)
>
> Could you explain why the first part would go entirely into the

Machine model?


Because it's better to encapsulate
model logic in the models (and custom finders are model logic).
Controllers should only be responsible for receiving a request,
calling the appropriate model methods,
then returning a response.

- Pat
Posted by Matt Jones (Guest)
on 2009-07-04 19:09
(Received via mailing list)
You could try building up a chain of scopes - see this post:

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/a7df6a8b9628bb4b/d6b738f83084ec91?lnk=gst&q=scoped#d6b738f83084ec91

As noted there, you can avoid cluttering up your model by using
'scoped' to create anonymous scopes.

So your example would look like:

# in the controller action
proxy = Machine
[:attr1, :attr2, :attr3].each do { |k|
  proxy = proxy.scoped(:conditions => Hash[k, params[k]]) unless params
[k].blank?
}
@machines = proxy.find(:all)

That last find can also be a paginate, have limits, sorts, etc.

--Matt Jones
Posted by Pat Nakajima (patnakajima)
on 2009-07-05 11:15
(Received via mailing list)
That works, though it's definitely a good idea to sanitize which params 
can
be used in such a manner. Otherwise, someone could send params like {
:destroy_all => true } or something, and then there goes your database.
Pat
Posted by Pat Nakajima (patnakajima)
on 2009-07-05 11:26
(Received via mailing list)
Now that I read again, scoped won't allow destroy_all to get through, 
but I
still stand by my point :)
Posted by jhaagmans (Guest)
on 2009-07-05 19:09
(Received via mailing list)
On 3 jul, 22:43, Pat Nakajima <patnakaj...@gmail.com> wrote:
> - Pat
Thank you, that's very clear!

I've decided to try and go with this solution as this actually does
what I inteded to do. I just had some trouble understanding why in
most examples this piece of code is put into the controller and you
don't.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.