Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 4425

Show
Ignore:
Timestamp:
06/03/06 22:15:06 (2 years ago)
Author:
david
Message:

Added simple hash conditions to find that'll just convert hash to an AND-based condition string (closes #5143) [hcatlin@gmail.com]

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activerecord/CHANGELOG

    r4424 r4425  
    11*SVN* 
     2 
     3* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [hcatlin@gmail.com]. Example: 
     4 
     5    Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)  
     6 
     7...is the same as: 
     8 
     9    Person.find(:all, :conditions => [ "last_name = ? and status = ?", "Catlin", 1 ], :limit => 2) 
     10   
     11  This makes it easier to pass in the options from a form or otherwise outside. 
     12     
    213 
    314* Fixed issues with BLOB limits, charsets, and booleans for Firebird #5194, #5191, #5189 [kennethkunz@gmail.com] 
  • trunk/activerecord/lib/active_record/base.rb

    r4415 r4425  
    8282  # == Conditions 
    8383  # 
    84   # Conditions can either be specified as a string or an array representing the WHERE-part of an SQL statement. 
     84  # Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement. 
    8585  # The array form is to be used when the condition input is tainted and requires sanitization. The string form can 
    86   # be used for statements that don't involve tainted data. Examples: 
     86  # be used for statements that don't involve tainted data. The hash form works much like the array form, except 
     87  # only equality is possible. Examples: 
    8788  # 
    8889  #   class User < ActiveRecord::Base 
     
    9495  #       find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ]) 
    9596  #     end 
     97  # 
     98  #     def self.authenticate_safely_simply(user_name, password) 
     99  #       find(:first, :conditions => { :user_name => user_name, :password => password }) 
     100  #     end 
    96101  #   end 
    97102  # 
    98103  # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query and is thus susceptible to SQL-injection 
    99   # attacks if the <tt>user_name</tt> and +password+ parameters come directly from a HTTP request. The <tt>authenticate_safely</tt> method, 
    100   # on the other hand, will sanitize the <tt>user_name</tt> and +password+ before inserting them in the query, which will ensure that 
    101   # an attacker can't escape the query and fake the login (or worse). 
     104  # attacks if the <tt>user_name</tt> and +password+ parameters come directly from a HTTP request. The <tt>authenticate_safely</tt> and 
     105  # <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+ before inserting them in the query,  
     106  # which will ensure that an attacker can't escape the query and fake the login (or worse). 
    102107  # 
    103108  # When using multiple parameters in the conditions, it can easily become hard to read exactly what the fourth or fifth 
     
    109114  #     { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' } 
    110115  #   ]) 
     116  # 
     117  # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND 
     118  # operator. For instance: 
     119  # 
     120  #   Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 }) 
     121  #   Student.find(:all, :conditions => params[:student]) 
     122  # 
    111123  # 
    112124  # == Overwriting default accessors 
     
    12741286        end 
    12751287 
    1276         # Accepts an array or string.  The string is returned untouched, but the array has each value 
     1288        #Accepts an array, hash, or string of sql conditions and  
     1289        #deals with them accordingly 
     1290        #   ["name='%s' and group_id='%s'", "foo'bar", 4]  returns  "name='foo''bar' and group_id='4'" 
     1291        #   { :name => "foo'bar", :group_id => 4 }  returns "name='foo''bar' and group_id='4'" 
     1292        #   "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'" 
     1293        def sanitize_sql(condition) 
     1294          return sanitize_sql_array(condition) if condition.is_a?(Array) 
     1295          return sanitize_sql_hash(condition) if condition.is_a?(Hash) 
     1296          condition 
     1297        end 
     1298         
     1299        # Accepts a hash of conditions.  The hash has each key/value or attribute/value pair 
     1300        # sanitized and interpolated into the sql statement. 
     1301        #   { :name => "foo'bar", :group_id => 4 }  returns "name='foo''bar' and group_id= 4" 
     1302        def sanitize_sql_hash(hash) 
     1303          hash.collect { |attrib, value| 
     1304            "#{table_name}.#{connection.quote_column_name(attrib)} = #{quote(value)}" 
     1305          }.join(" AND ") 
     1306        end 
     1307         
     1308        # Accepts an array of conditions.  The array has each value 
    12771309        # sanitized and interpolated into the sql statement. 
    12781310        #   ["name='%s' and group_id='%s'", "foo'bar", 4]  returns  "name='foo''bar' and group_id='4'" 
    1279         def sanitize_sql(ary) 
    1280           return ary unless ary.is_a?(Array) 
    1281  
     1311        def sanitize_sql_array(ary) 
    12821312          statement, *values = ary 
    12831313          if values.first.is_a?(Hash) and statement =~ /:\w+/ 
  • trunk/activerecord/test/finder_test.rb

    r4393 r4425  
    118118  end 
    119119 
    120   def test_find_on_conditions 
     120  def test_find_on_array_conditions 
    121121    assert Topic.find(1, :conditions => ["approved = ?", false]) 
    122122    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => ["approved = ?", true]) } 
    123123  end 
    124124   
    125   def test_condition_interpolation 
     125  def test_find_on_hash_conditions 
     126    assert Topic.find(1, :conditions => { :approved => false }) 
     127    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :approved => true }) } 
     128  end 
     129   
     130  def test_find_on_multiple_hash_conditions 
     131    assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false }) 
     132    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) } 
     133    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }) } 
     134    assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) } 
     135  end 
     136   
     137  def test_condition_array_interpolation 
    126138    assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"]) 
    127139    assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"]) 
    128140    assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"]) 
    129141    assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on 
     142  end 
     143   
     144  def test_condition_hash_interpolation 
     145    assert_kind_of Firm, Company.find(:first, :conditions => { :name => "37signals"}) 
     146    assert_nil Company.find(:first, :conditions => { :name => "37signals!"}) 
     147    assert_kind_of Time, Topic.find(:first, :conditions => {:id => 1}).written_on 
     148  end 
     149   
     150  def test_hash_condition_find_malformed 
     151    assert_raises(ActiveRecord::StatementInvalid) { 
     152      Company.find(:first, :conditions => { :id => 2, :dhh => true }) 
     153    } 
     154  end 
     155   
     156  def test_hash_condition_find_with_escaped_characters 
     157    Company.create("name" => "Ain't noth'n like' \#stuff") 
     158    assert Company.find(:first, :conditions => { :name => "Ain't noth'n like' \#stuff"}) 
    130159  end 
    131160