 
 This page contains automated test results for code from O'Reilly's Ruby Cookbook. If this code looks interesting or useful, you might want to buy the whole book.
| Building Queries Programmatically | ||
|---|---|---|
| Code | Expected | Actual | 
| require 'cookbook_dbconnect'
class ActiveRecord::Base 
  def self.find_by_map(id, args={}.freeze)
    sql = []
    values = []
    args[:conditions].each do |field, value|
      sql << "#{field} = ?"
      values << value
    end if args[:conditions]
    args[:conditions] = [sql.join(' AND '), values]
    find(id, args)
  end
end
activerecord_connect
class BlogPost < ActiveRecord::Base
end
BlogPost.create(:title => 'Game Review: Foosball Carnage',
                :content => 'Four stars!')
BlogPost.create(:title => 'Movie Review: Foosball Carnage: The Movie', 
                :content => 'Zero stars!')
BlogPost.find_by_map(:first, 
                     :conditions => {:title => 
	                             'Game Review: Foosball Carnage' }
                    ).content | "Four stars!" | Error! (Exception?) Here's stdout: TypeError: singleton method called for a different object from /usr/lib/ruby/gems/1.8/gems/facets_more-1.0.2/lib/facet/basicobject.rb:96:in `bind' from /usr/lib/ruby/gems/1.8/gems/facets_more-1.0.2/lib/facet/basicobject.rb:96:in `method_added' from /usr/lib/ruby/gems/1.8/gems/facets_more-1.0.2/lib/facet/basicobject.rb:95 from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:21:in `require' from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.0/lib/active_support/dependencies.rb:136:in `require' from /usr/lib/ruby/gems/1.8/gems/facets_more-1.0.2/lib/facet/openobject.rb:51 from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:21:in `require' from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.0/lib/active_support/dependencies.rb:136:in `require' from /usr/lib/ruby/gems/1.8/gems/facets_more-1.0.2/lib/facet/annotation.rb:53 from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:21:in `require' from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.0/lib/active_support/dependencies.rb:136:in `require' from /usr/lib/ruby/gems/1.8/gems/glue-0.28.0/lib/glue/property.rb:1 from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:21:in `require' from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.0/lib/active_support/dependencies.rb:136:in `require' from /usr/lib/ruby/gems/1.8/gems/glue-0.28.0/lib/glue.rb:18 from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:21:in `require' from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.0/lib/active_support/dependencies.rb:136:in `require' from /usr/lib/ruby/gems/1.8/gems/og-0.28.0/lib/og.rb:14 from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require' from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.0/lib/active_support/dependencies.rb:136:in `require' from ./cookbook_dbconnect.rb:5 from (irb):1NameError: undefined local variable or method `activerecord_connect' for #<Object:0xb7d66970> from (irb):14 ActiveRecord::ConnectionNotEstablished: ActiveRecord::ConnectionNotEstablished from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/connection_adapters/abstract/connection_specification.rb:225:in `retrieve_connection' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/connection_adapters/abstract/connection_specification.rb:78:in `connection' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:696:in `columns' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1963:in `attributes_from_column_definition' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1347:in `initialize_without_callbacks' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/callbacks.rb:236:in `initialize' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:406:in `create' from (irb):17 ActiveRecord::ConnectionNotEstablished: ActiveRecord::ConnectionNotEstablished from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/connection_adapters/abstract/connection_specification.rb:225:in `retrieve_connection' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/connection_adapters/abstract/connection_specification.rb:78:in `connection' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:696:in `columns' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1963:in `attributes_from_column_definition' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1347:in `initialize_without_callbacks' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/callbacks.rb:236:in `initialize' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:406:in `create' from (irb):19 ActiveRecord::ConnectionNotEstablished: ActiveRecord::ConnectionNotEstablished from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/connection_adapters/abstract/connection_specification.rb:225:in `retrieve_connection' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/connection_adapters/abstract/connection_specification.rb:78:in `connection' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1299:in `quote_bound_value' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1299:in `quote_bound_value' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1283:in `replace_bind_variables' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1283:in `replace_bind_variables' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1272:in `sanitize_sql' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1056:in `add_conditions!' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:1012:in `construct_finder_sql' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:924:in `find_every' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:918:in `find_initial' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.0/lib/active_record/base.rb:380:in `find' from (irb):11:in `find_by_map' from (irb):21 | 
| class Criteria < Hash
  def initialize(values)
    values.each { |k,v| add(k, *v) }
    @or_criteria = nil
    @and_criteria = nil
  end
  :private
  attr_accessor :or_criteria, :and_criteria
  :public
  def add(field, value, operation='=')
    self[field] = [value, operation]
  end
  def or(criteria)
    c = self
    while c.or_criteria != nil
      break if c == criteria
      c = c.or_criteria 
    end
    c.or_criteria = criteria
    return self
  end
  def and(criteria)
    c = self
    while c.and_criteria != nil
      break if c == criteria
      c = c.and_criteria
    end
    c.and_criteria = criteria
    return self
  end   
class Criteria
  def to_where_clause
    sql = []
    values = []
    each do |field, value|
      if value.respond_to? :to_str
        value, operation = value, '='
      else
        value, operation = value[0..1]
      end
      sql << "#{field} #{operation} ?"
      values << value      
    end
    sql = '(' + sql.join(' AND ') + ')'
    if or_criteria
      or_where = or_criteria.to_where_clause
      sql = "(#{sql} OR #{or_where.shift})"
      values += or_where
    end
    if and_criteria
      and_where = and_criteria.to_where_clause
      sql = "(#{sql} AND #{and_where.shift})"
      values += and_where
    end
    return values.unshift(sql)
  end    
end
class ActiveRecord::Base 
  def self.find_by_criteria(id, criteria, args={}.freeze)
    args = args.dup
    args[:conditions] = criteria.to_where_clause
    find(id, args)
  end
end
review = Criteria.new(:title => ['%Review%', 'LIKE'])
bad_movie = Criteria.new(:title => ["%Movie%", 'LIKE'],
                         :content => 'Zero stars!')
good_game = Criteria.new(:title => ['%Game%', 'LIKE'],
                          :content => 'Four stars!')
no_cricket = Criteria.new(:title => ['%Cricket%', 'NOT LIKE'])
review.and(bad_movie.or(good_game)).and(no_cricket)
review.to_where_clause | ["((title LIKE ?) AND | Error! (Exception?) Here's stdout: | 
| BlogPost.find_by_criteria(:all, review).each { |post| puts post.title } | Game Review: Foosball Carnage Movie Review: Foosball Carnage: The Movie Just an idea... | Error! (Exception?) Here's stdout: | 
| order_by = [[:title, 'ASC']] | ... | |