DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Conditioner For ActiveRecord-friendly Conditions From A Collection

08.31.2006
| 12320 views |
  • submit to reddit
        I frequently have a collection of values that I want to match in an ActiveRecord query, but it would be nice if I could let ActiveRecord handle checking the data and escaping it properly.  So, I wrote this method to return ActiveRecord-friendly conditions, such as: 
["user_id=? AND job_id=?", 3, 4]
 based on the 'raw' conditions you feed to it, such as: 
[['user_id', 3], ['job_id', 4]]

# Returns ActiveRecord-friendly conditions based on the given
# raw conditions; handles grouping based on like field names;
# allows different boolean operators in raw conditions;
# allows different comparison operators in raw conditions;
# raw conditions setup:
# [[field name, desired value, bool. op., comp. op.], ...]
# name = condition[0]
# value = condition[1]
# bool_type = condition[2]
# comparison = condition[3]
# raw conditions example:
# [['type_id', '4', 'OR'], ['created_on', Date.new, 'AND', '<=']]
def conditioner( raw_conditions )
  return nil if raw_conditions.nil? || raw_conditions.empty?
  
  conditions = ['(']
  count = 0
  prev_name = raw_conditions[0][0]
  raw_conditions.each do |condition|
    name = condition[0]
      
    conditions[0] << ') AND ' if prev_name != name
    conditions[0] << ' ' << ( condition[2] || 'OR' ) << ' ' unless count == 0 || prev_name != name
    conditions[0] << '(' if prev_name != name
    conditions[0] << name + ' ' << ( condition[3] || '=' ) + ' ?'
      
    conditions << condition[1]
      
    prev_name = name
    count += 1
  end
  conditions[0] << ')'

  conditions
end

This way, you can do something like the following:
model_ids = Model.find( :all ).map( &:id )
raw_conditions = model_ids.collect { |id| ['model_id', id] }
conditions = conditioner( raw_conditions )
desired_collection = OtherModel.find( :all, :conditions => conditions )

If your query needs to depend on more than one factor, you might do something like the following:
if test
  raw_conditions = [['user_id', 3]]
else
  raw_conditions = [['user_id', 4], ['groups.name', 'dev']]
end

team_ids.each { |id| raw_conditions << ['team_id', id, 'AND'] }
    

Comments

Snippets Manager replied on Wed, 2006/11/08 - 12:43pm

Rails 1.2 (RC1) has the syntax suggested by Peter.

Snippets Manager replied on Mon, 2012/05/07 - 2:27pm

find :all, :conditions => ['blah = :blah and foo = :foo', { :blah => 5, :foo => 6 }] which lets you do conditions[0] << " and x = :stuff" conditions[1][:stuff] = "monkeys"

Snippets Manager replied on Fri, 2006/06/02 - 8:45pm

I'll look into 'ez_where' and especially that possible Rails syntax, because that would be awesome.

Snippets Manager replied on Mon, 2012/05/07 - 2:27pm

BTW, 'ez_where' plugin has some things similar to this. (Not to disparage your technique which is also good :)) I also think Rails is going to (or may already) support syntax like this: find(:all, :conditions => { :user_id => 3, :job_id => 4 })