views:

413

answers:

3

Hi,

I'm attempting to build a set of conditions dynamically using an array as suggested in the first answer here: http://stackoverflow.com/questions/1658990/one-or-more-params-in-model-find-conditions-with-ruby-on-rails. However I seem to be doing something incorrectly and I'm not sure if what I'm trying is fundamentally unsound or if I'm simply botching my syntax.

I'm simplifying down to a single condition here to try to illustrate the issue as I've tried to built a simple Proof of concept along these lines before layering on the 5 different condition styles I'm contending with.

This works:

excluded.push 12
excluded.push 30
@allsites = Site.all(:conditions => ["id not in (?)", excluded])

This results in a private method 'scan' called error:

conditionsSet = []
excluded.push 12
excluded.push 30
conditionsSet << ["id not in (?)", excluded]
@allsites = Site.all(:conditions => conditionsSet)

Thanks for any advice. I wasn't sure if the proper thing was to put this as a followup item to the related question/answers I noted at the top. Since I've got a problem not an answer. If there is a better way to post this related to the existing post please let me know.

+3  A: 

You want:

conditionsSet += ["id not in (?)", excluded]

instead of:

conditionsSet << ["id not in (?)", excluded]

+= adds the two arrays together (think of it as merging the two into one array) while << pushes a new element onto the array. So you are getting: [["id not in (?)", excluded]] when using <<, and :conditions wants an array where this first element is a string (not an array).

Tony Fontenot
A: 

Try this:

class Site < ActiveRecord::Base

  def self.build_conditions(ids, name=nil, state=nil)
     cond = []
     cond << send(:sanitize_sql_array, ["id NOT IN (?)", ids]) unless ids.empty?
     cond << send(:sanitize_sql_array, ["name = ? ", name]) unless name
     cond << send(:sanitize_sql_array, ["state = ? ", state]) unless state
     cond.join(" and ")
  end    
end

Now somewhere in your controller:

Site.all(:conditions => Site.build_conditions([1,2])) 
Site.all(:conditions => Site.build_conditions(nil, "ABC")) 
KandadaBoggu
This is absolutely perfect. I appreciate the sample which is more illustrative than what I had posted in my question. I'm now clear on how to properly build and sanitize the conditions block - I had tried something similar but was missing the sanitize_aql_array call and the technique of using a join to build the string with ands. This is working like a charm.Thanks (again) KandadaBoggu!
Nick
A: 

Try SmartTuple, it's designed specifically for cases like this.

def self.build_conditions(options = {})
  [
    SmartTuple.new(" AND "),
    (["id NOT IN (?)", ids] if options[:ids].present?),
    ({:name => options[:name]} if options[:name].present?),
    ({:state => options[:state]} if options[:state].present?),
  ].sum.compile
end

...

Site.all(:conditions => Site.build_conditions(:ids => {1,2]))
Site.all(:conditions => Site.build_conditions(:name => "abc", :state => "disabled")

To me it's also preferrable to use options hash instead of ordered arguments. As your project grows, more conditions may appear and you'll lose track of which comes which. Hash looks and behaves clearer plus you can easily validate it to avoid software errors.

dadooda