activerecord 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +250 -0
 - data/README +17 -9
 - data/dev-utils/eval_debugger.rb +1 -1
 - data/install.rb +3 -1
 - data/lib/active_record.rb +9 -2
 - data/lib/active_record/acts/list.rb +178 -0
 - data/lib/active_record/acts/tree.rb +44 -0
 - data/lib/active_record/associations.rb +45 -8
 - data/lib/active_record/associations/association_collection.rb +18 -9
 - data/lib/active_record/associations/has_and_belongs_to_many_association.rb +14 -13
 - data/lib/active_record/associations/has_many_association.rb +21 -12
 - data/lib/active_record/base.rb +137 -37
 - data/lib/active_record/callbacks.rb +30 -25
 - data/lib/active_record/connection_adapters/abstract_adapter.rb +57 -33
 - data/lib/active_record/connection_adapters/mysql_adapter.rb +4 -0
 - data/lib/active_record/connection_adapters/sqlite_adapter.rb +3 -2
 - data/lib/active_record/connection_adapters/sqlserver_adapter.rb +298 -0
 - data/lib/active_record/fixtures.rb +241 -147
 - data/lib/active_record/support/class_inheritable_attributes.rb +5 -2
 - data/lib/active_record/support/inflector.rb +13 -12
 - data/lib/active_record/support/misc.rb +6 -0
 - data/lib/active_record/timestamp.rb +33 -0
 - data/lib/active_record/transactions.rb +1 -1
 - data/lib/active_record/validations.rb +294 -16
 - data/rakefile +3 -7
 - data/test/abstract_unit.rb +1 -4
 - data/test/associations_test.rb +17 -4
 - data/test/base_test.rb +37 -5
 - data/test/connections/native_sqlserver/connection.rb +15 -0
 - data/test/deprecated_associations_test.rb +40 -38
 - data/test/finder_test.rb +82 -4
 - data/test/fixtures/accounts.yml +8 -0
 - data/test/fixtures/company.rb +6 -0
 - data/test/fixtures/company_in_module.rb +1 -1
 - data/test/fixtures/db_definitions/mysql.sql +13 -0
 - data/test/fixtures/db_definitions/postgresql.sql +13 -0
 - data/test/fixtures/db_definitions/sqlite.sql +14 -0
 - data/test/fixtures/db_definitions/sqlserver.sql +110 -0
 - data/test/fixtures/db_definitions/sqlserver2.sql +4 -0
 - data/test/fixtures/developer.rb +2 -2
 - data/test/fixtures/developers.yml +13 -0
 - data/test/fixtures/fixture_database.sqlite +0 -0
 - data/test/fixtures/fixture_database_2.sqlite +0 -0
 - data/test/fixtures/mixin.rb +17 -0
 - data/test/fixtures/mixins.yml +14 -0
 - data/test/fixtures/naked/csv/accounts.csv +1 -0
 - data/test/fixtures/naked/yml/accounts.yml +1 -0
 - data/test/fixtures/naked/yml/companies.yml +1 -0
 - data/test/fixtures/naked/yml/courses.yml +1 -0
 - data/test/fixtures/project.rb +6 -0
 - data/test/fixtures/reply.rb +14 -1
 - data/test/fixtures/topic.rb +2 -2
 - data/test/fixtures/topics/first +1 -0
 - data/test/fixtures_test.rb +42 -12
 - data/test/inflector_test.rb +2 -1
 - data/test/inheritance_test.rb +22 -12
 - data/test/mixin_test.rb +138 -0
 - data/test/pk_test.rb +4 -2
 - data/test/reflection_test.rb +3 -3
 - data/test/transactions_test.rb +15 -0
 - data/test/validations_test.rb +229 -4
 - metadata +24 -10
 - data/lib/active_record/associations.rb.orig +0 -555
 - data/test/deprecated_associations_test.rb.orig +0 -334
 - data/test/fixtures/accounts/signals37 +0 -3
 - data/test/fixtures/accounts/unknown +0 -2
 - data/test/fixtures/developers/david +0 -2
 - data/test/fixtures/developers/jamis +0 -2
 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ActiveRecord
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Acts #:nodoc:
         
     | 
| 
      
 3 
     | 
    
         
            +
                module Tree #:nodoc:
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def self.append_features(base)
         
     | 
| 
      
 5 
     | 
    
         
            +
                    super        
         
     | 
| 
      
 6 
     | 
    
         
            +
                    base.extend(ClassMethods)              
         
     | 
| 
      
 7 
     | 
    
         
            +
                  end  
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  # Specify this act if you want to model a tree structure by providing a parent association and an children 
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # association. This act assumes that requires that you have a foreign key column, which by default is called parent_id.
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # 
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #   class Category < ActiveRecord::Base
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #     acts_as_tree :order => "name"
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #   
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #   Example : 
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #   root
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #    \_ child1 
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #         \_ sub-child1
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #   root      = Category.create("name" => "root")
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #   child1      = root.children.create("name" => "child1")
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #   subchild1   = child1.children.create("name" => "subchild1")
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   root.parent # => nil
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #   child1.parent # => root
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #   root.children # => [child1]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #   root.children.first.children.first # => subchild1
         
     | 
| 
      
 29 
     | 
    
         
            +
                  module ClassMethods
         
     | 
| 
      
 30 
     | 
    
         
            +
                    # Configuration options are:
         
     | 
| 
      
 31 
     | 
    
         
            +
                    #
         
     | 
| 
      
 32 
     | 
    
         
            +
                    # * <tt>foreign_key</tt> - specifies the column name to use for track of the tree (default: parent_id)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    # * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet.
         
     | 
| 
      
 34 
     | 
    
         
            +
                    def acts_as_tree(options = {})
         
     | 
| 
      
 35 
     | 
    
         
            +
                      configuration = { :foreign_key => "parent_id", :order => nil }
         
     | 
| 
      
 36 
     | 
    
         
            +
                      configuration.update(options) if options.is_a?(Hash)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      
         
     | 
| 
      
 38 
     | 
    
         
            +
                      belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key]
         
     | 
| 
      
 39 
     | 
    
         
            +
                      has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => true
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -3,6 +3,10 @@ require 'active_record/associations/has_many_association' 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'active_record/associations/has_and_belongs_to_many_association'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'active_record/deprecated_associations'
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
      
 6 
     | 
    
         
            +
            unless Object.respond_to?(:require_association)
         
     | 
| 
      
 7 
     | 
    
         
            +
              Object.send(:define_method, :require_association) { |file_name| ActiveRecord::Base.require_association(file_name) }
         
     | 
| 
      
 8 
     | 
    
         
            +
            end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       6 
10 
     | 
    
         
             
            module ActiveRecord
         
     | 
| 
       7 
11 
     | 
    
         
             
              module Associations # :nodoc:
         
     | 
| 
       8 
12 
     | 
    
         
             
                def self.append_features(base)
         
     | 
| 
         @@ -166,6 +170,8 @@ module ActiveRecord 
     | 
|
| 
       166 
170 
     | 
    
         
             
                  #   May not be set if :dependent is also set.
         
     | 
| 
       167 
171 
     | 
    
         
             
                  # * <tt>:finder_sql</tt>  - specify a complete SQL statement to fetch the association. This is a good way to go for complex
         
     | 
| 
       168 
172 
     | 
    
         
             
                  #   associations that depends on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
         
     | 
| 
      
 173 
     | 
    
         
            +
                  # * <tt>:counter_sql</tt>  - specify a complete SQL statement to fetch the size of the association. If +:finder_sql+ is
         
     | 
| 
      
 174 
     | 
    
         
            +
                  #   specified but +:counter_sql+, +:counter_sql+ will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM.
         
     | 
| 
       169 
175 
     | 
    
         
             
                  #
         
     | 
| 
       170 
176 
     | 
    
         
             
                  # Option examples:
         
     | 
| 
       171 
177 
     | 
    
         
             
                  #   has_many :comments, :order => "posted_on"
         
     | 
| 
         @@ -177,16 +183,18 @@ module ActiveRecord 
     | 
|
| 
       177 
183 
     | 
    
         
             
                  #       'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
         
     | 
| 
       178 
184 
     | 
    
         
             
                  #       'ORDER BY p.first_name'
         
     | 
| 
       179 
185 
     | 
    
         
             
                  def has_many(association_id, options = {})
         
     | 
| 
       180 
     | 
    
         
            -
                    validate_options([ :foreign_key, :class_name, :exclusively_dependent, :dependent, :conditions, :order, :finder_sql ], options.keys)
         
     | 
| 
      
 186 
     | 
    
         
            +
                    validate_options([ :foreign_key, :class_name, :exclusively_dependent, :dependent, :conditions, :order, :finder_sql, :counter_sql ], options.keys)
         
     | 
| 
       181 
187 
     | 
    
         
             
                    association_name, association_class_name, association_class_primary_key_name =
         
     | 
| 
       182 
188 
     | 
    
         
             
                          associate_identification(association_id, options[:class_name], options[:foreign_key])
         
     | 
| 
       183 
189 
     | 
    
         | 
| 
      
 190 
     | 
    
         
            +
                    require_association_class(association_class_name)
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
       184 
192 
     | 
    
         
             
                    if options[:dependent] and options[:exclusively_dependent]
         
     | 
| 
       185 
     | 
    
         
            -
                      raise ArgumentError, ':dependent and :exclusively_dependent are mutually exclusive options.  You may specify one or the other.'
         
     | 
| 
      
 193 
     | 
    
         
            +
                      raise ArgumentError, ':dependent and :exclusively_dependent are mutually exclusive options.  You may specify one or the other.' # ' ruby-mode
         
     | 
| 
       186 
194 
     | 
    
         
             
                    elsif options[:dependent]
         
     | 
| 
       187 
195 
     | 
    
         
             
                      module_eval "before_destroy '#{association_name}.each { |o| o.destroy }'"
         
     | 
| 
       188 
196 
     | 
    
         
             
                    elsif options[:exclusively_dependent]
         
     | 
| 
       189 
     | 
    
         
            -
                      module_eval "before_destroy { |record| #{association_class_name}.delete_all(%(#{association_class_primary_key_name} =  
     | 
| 
      
 197 
     | 
    
         
            +
                      module_eval "before_destroy { |record| #{association_class_name}.delete_all(%(#{association_class_primary_key_name} = \#{record.quoted_id})) }"
         
     | 
| 
       190 
198 
     | 
    
         
             
                    end
         
     | 
| 
       191 
199 
     | 
    
         | 
| 
       192 
200 
     | 
    
         
             
                    define_method(association_name) do |*params|
         
     | 
| 
         @@ -260,6 +268,8 @@ module ActiveRecord 
     | 
|
| 
       260 
268 
     | 
    
         
             
                    association_name, association_class_name, class_primary_key_name =
         
     | 
| 
       261 
269 
     | 
    
         
             
                        associate_identification(association_id, options[:class_name], options[:foreign_key], false)
         
     | 
| 
       262 
270 
     | 
    
         | 
| 
      
 271 
     | 
    
         
            +
                    require_association_class(association_class_name)
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
       263 
273 
     | 
    
         
             
                    has_one_writer_method(association_name, association_class_name, class_primary_key_name)
         
     | 
| 
       264 
274 
     | 
    
         
             
                    build_method("build_", association_name, association_class_name, class_primary_key_name)
         
     | 
| 
       265 
275 
     | 
    
         
             
                    create_method("create_", association_name, association_class_name, class_primary_key_name)
         
     | 
| 
         @@ -310,12 +320,14 @@ module ActiveRecord 
     | 
|
| 
       310 
320 
     | 
    
         
             
                      association_name, association_class_name, class_primary_key_name =
         
     | 
| 
       311 
321 
     | 
    
         
             
                          associate_identification(association_id, options[:class_name], options[:foreign_key], false)
         
     | 
| 
       312 
322 
     | 
    
         | 
| 
      
 323 
     | 
    
         
            +
                      require_association_class(association_class_name)
         
     | 
| 
      
 324 
     | 
    
         
            +
             
     | 
| 
       313 
325 
     | 
    
         
             
                      association_class_primary_key_name = options[:foreign_key] || Inflector.underscore(Inflector.demodulize(association_class_name)) + "_id"
         
     | 
| 
       314 
326 
     | 
    
         | 
| 
       315 
327 
     | 
    
         
             
                      if options[:remote]
         
     | 
| 
       316 
328 
     | 
    
         
             
                        association_finder = <<-"end_eval"
         
     | 
| 
       317 
329 
     | 
    
         
             
                          #{association_class_name}.find_first(
         
     | 
| 
       318 
     | 
    
         
            -
                            "#{class_primary_key_name} =  
     | 
| 
      
 330 
     | 
    
         
            +
                            "#{class_primary_key_name} = \#{quoted_id}#{options[:conditions] ? " AND " + options[:conditions] : ""}",
         
     | 
| 
       319 
331 
     | 
    
         
             
                            #{options[:order] ? "\"" + options[:order] + "\"" : "nil" }
         
     | 
| 
       320 
332 
     | 
    
         
             
                          )
         
     | 
| 
       321 
333 
     | 
    
         
             
                        end_eval
         
     | 
| 
         @@ -411,9 +423,10 @@ module ActiveRecord 
     | 
|
| 
       411 
423 
     | 
    
         
             
                    association_name, association_class_name, association_class_primary_key_name =
         
     | 
| 
       412 
424 
     | 
    
         
             
                          associate_identification(association_id, options[:class_name], options[:foreign_key])
         
     | 
| 
       413 
425 
     | 
    
         | 
| 
       414 
     | 
    
         
            -
                     
     | 
| 
      
 426 
     | 
    
         
            +
                    require_association_class(association_class_name)
         
     | 
| 
      
 427 
     | 
    
         
            +
             
     | 
| 
      
 428 
     | 
    
         
            +
                    join_table = options[:join_table] || 
         
     | 
| 
       415 
429 
     | 
    
         
             
                      join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(association_class_name))
         
     | 
| 
       416 
     | 
    
         
            -
             
         
     | 
| 
       417 
430 
     | 
    
         | 
| 
       418 
431 
     | 
    
         
             
                    define_method(association_name) do |*params|
         
     | 
| 
       419 
432 
     | 
    
         
             
                      force_reload = params.first unless params.empty?
         
     | 
| 
         @@ -428,7 +441,7 @@ module ActiveRecord 
     | 
|
| 
       428 
441 
     | 
    
         
             
                      association
         
     | 
| 
       429 
442 
     | 
    
         
             
                    end
         
     | 
| 
       430 
443 
     | 
    
         | 
| 
       431 
     | 
    
         
            -
                    before_destroy_sql = "DELETE FROM #{join_table} WHERE #{association_class_primary_key_name} =  
     | 
| 
      
 444 
     | 
    
         
            +
                    before_destroy_sql = "DELETE FROM #{join_table} WHERE #{association_class_primary_key_name} = \\\#{self.quoted_id}"
         
     | 
| 
       432 
445 
     | 
    
         
             
                    module_eval(%{before_destroy "self.connection.delete(%{#{before_destroy_sql}})"}) # "
         
     | 
| 
       433 
446 
     | 
    
         | 
| 
       434 
447 
     | 
    
         
             
                    # deprecated api
         
     | 
| 
         @@ -438,6 +451,20 @@ module ActiveRecord 
     | 
|
| 
       438 
451 
     | 
    
         
             
                    deprecated_has_collection_method(association_name)
         
     | 
| 
       439 
452 
     | 
    
         
             
                  end
         
     | 
| 
       440 
453 
     | 
    
         | 
| 
      
 454 
     | 
    
         
            +
                  # Loads the <tt>file_name</tt> if reload_associations is true or requires if it's false.
         
     | 
| 
      
 455 
     | 
    
         
            +
                  def require_association(file_name)
         
     | 
| 
      
 456 
     | 
    
         
            +
                    if !associations_loaded.include?(file_name)
         
     | 
| 
      
 457 
     | 
    
         
            +
                      associations_loaded << file_name
         
     | 
| 
      
 458 
     | 
    
         
            +
                      reload_associations ? silence_warnings { load("#{file_name}.rb") } : require(file_name)
         
     | 
| 
      
 459 
     | 
    
         
            +
                    end
         
     | 
| 
      
 460 
     | 
    
         
            +
                  end
         
     | 
| 
      
 461 
     | 
    
         
            +
             
     | 
| 
      
 462 
     | 
    
         
            +
                  # Resets the list of dependencies loaded (typically to be called by the end of a request), so when require_association is
         
     | 
| 
      
 463 
     | 
    
         
            +
                  # called for that dependency it'll be loaded anew.
         
     | 
| 
      
 464 
     | 
    
         
            +
                  def reset_associations_loaded
         
     | 
| 
      
 465 
     | 
    
         
            +
                    self.associations_loaded = []
         
     | 
| 
      
 466 
     | 
    
         
            +
                  end
         
     | 
| 
      
 467 
     | 
    
         
            +
             
     | 
| 
       441 
468 
     | 
    
         
             
                  private
         
     | 
| 
       442 
469 
     | 
    
         
             
                    # Raises an exception if an invalid option has been specified to prevent misspellings from slipping through 
         
     | 
| 
       443 
470 
     | 
    
         
             
                    def validate_options(valid_option_keys, supplied_option_keys)
         
     | 
| 
         @@ -552,6 +579,16 @@ module ActiveRecord 
     | 
|
| 
       552 
579 
     | 
    
         
             
                        end
         
     | 
| 
       553 
580 
     | 
    
         
             
                      end_eval
         
     | 
| 
       554 
581 
     | 
    
         
             
                    end
         
     | 
| 
      
 582 
     | 
    
         
            +
             
     | 
| 
      
 583 
     | 
    
         
            +
                    def require_association_class(class_name)
         
     | 
| 
      
 584 
     | 
    
         
            +
                      return unless class_name
         
     | 
| 
      
 585 
     | 
    
         
            +
                      
         
     | 
| 
      
 586 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 587 
     | 
    
         
            +
                        require_association(Inflector.underscore(class_name))
         
     | 
| 
      
 588 
     | 
    
         
            +
                      rescue LoadError
         
     | 
| 
      
 589 
     | 
    
         
            +
                        # Failed to load the associated class -- let's hope the developer is doing the requiring himself.
         
     | 
| 
      
 590 
     | 
    
         
            +
                      end
         
     | 
| 
      
 591 
     | 
    
         
            +
                    end
         
     | 
| 
       555 
592 
     | 
    
         
             
                end
         
     | 
| 
       556 
593 
     | 
    
         
             
              end
         
     | 
| 
       557 
     | 
    
         
            -
            end
         
     | 
| 
      
 594 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -37,11 +37,14 @@ module ActiveRecord 
     | 
|
| 
       37 
37 
     | 
    
         
             
                  # Add +records+ to this association.  Returns +self+ so method calls may be chained.  
         
     | 
| 
       38 
38 
     | 
    
         
             
                  # Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
         
     | 
| 
       39 
39 
     | 
    
         
             
                  def <<(*records)
         
     | 
| 
       40 
     | 
    
         
            -
                     
     | 
| 
       41 
     | 
    
         
            -
                       
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
      
 40 
     | 
    
         
            +
                    @owner.transaction do
         
     | 
| 
      
 41 
     | 
    
         
            +
                      flatten_deeper(records).each do |record|
         
     | 
| 
      
 42 
     | 
    
         
            +
                        raise_on_type_mismatch(record)
         
     | 
| 
      
 43 
     | 
    
         
            +
                        insert_record(record)
         
     | 
| 
      
 44 
     | 
    
         
            +
                        @collection << record if loaded?
         
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
       44 
46 
     | 
    
         
             
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
       45 
48 
     | 
    
         
             
                    self
         
     | 
| 
       46 
49 
     | 
    
         
             
                  end
         
     | 
| 
       47 
50 
     | 
    
         | 
| 
         @@ -51,13 +54,19 @@ module ActiveRecord 
     | 
|
| 
       51 
54 
     | 
    
         
             
                  # Remove +records+ from this association.  Does not destroy +records+.
         
     | 
| 
       52 
55 
     | 
    
         
             
                  def delete(*records)
         
     | 
| 
       53 
56 
     | 
    
         
             
                    records = flatten_deeper(records)
         
     | 
| 
       54 
     | 
    
         
            -
                     
     | 
| 
       55 
     | 
    
         
            -
                     
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
      
 57 
     | 
    
         
            +
                    
         
     | 
| 
      
 58 
     | 
    
         
            +
                    @owner.transaction do
         
     | 
| 
      
 59 
     | 
    
         
            +
                      records.each { |record| raise_on_type_mismatch(record) }
         
     | 
| 
      
 60 
     | 
    
         
            +
                      delete_records(records)
         
     | 
| 
      
 61 
     | 
    
         
            +
                      records.each { |record| @collection.delete(record) } if loaded?
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
       57 
63 
     | 
    
         
             
                  end
         
     | 
| 
       58 
64 
     | 
    
         | 
| 
       59 
65 
     | 
    
         
             
                  def destroy_all
         
     | 
| 
       60 
     | 
    
         
            -
                     
     | 
| 
      
 66 
     | 
    
         
            +
                    @owner.transaction do
         
     | 
| 
      
 67 
     | 
    
         
            +
                      each { |record| record.destroy }
         
     | 
| 
      
 68 
     | 
    
         
            +
                    end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
       61 
70 
     | 
    
         
             
                    @collection = []
         
     | 
| 
       62 
71 
     | 
    
         
             
                  end
         
     | 
| 
       63 
72 
     | 
    
         | 
| 
         @@ -81,7 +90,7 @@ module ActiveRecord 
     | 
|
| 
       81 
90 
     | 
    
         
             
                    end
         
     | 
| 
       82 
91 
     | 
    
         | 
| 
       83 
92 
     | 
    
         
             
                    def quoted_record_ids(records)
         
     | 
| 
       84 
     | 
    
         
            -
                      records.map { |record|  
     | 
| 
      
 93 
     | 
    
         
            +
                      records.map { |record| record.quoted_id }.join(',')
         
     | 
| 
       85 
94 
     | 
    
         
             
                    end
         
     | 
| 
       86 
95 
     | 
    
         | 
| 
       87 
96 
     | 
    
         
             
                    def interpolate_sql_options!(options, *keys)
         
     | 
| 
         @@ -3,17 +3,17 @@ module ActiveRecord 
     | 
|
| 
       3 
3 
     | 
    
         
             
                class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
         
     | 
| 
       4 
4 
     | 
    
         
             
                  def initialize(owner, association_name, association_class_name, association_class_primary_key_name, join_table, options)
         
     | 
| 
       5 
5 
     | 
    
         
             
                    super(owner, association_name, association_class_name, association_class_primary_key_name, options)
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
                    @association_foreign_key = options[:association_foreign_key] || Inflector.underscore(Inflector.demodulize(association_class_name 
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                    @association_foreign_key = options[:association_foreign_key] || Inflector.underscore(Inflector.demodulize(association_class_name)) + "_id"
         
     | 
| 
       8 
8 
     | 
    
         
             
                    association_table_name = options[:table_name] || @association_class.table_name(association_class_name)
         
     | 
| 
       9 
9 
     | 
    
         
             
                    @join_table = join_table
         
     | 
| 
       10 
     | 
    
         
            -
                    @order = options[:order] || "t.#{@ 
     | 
| 
      
 10 
     | 
    
         
            +
                    @order = options[:order] || "t.#{@association_class.primary_key}"
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                    interpolate_sql_options!(options, :finder_sql, :delete_sql)
         
     | 
| 
       13 
13 
     | 
    
         
             
                    @finder_sql = options[:finder_sql] ||
         
     | 
| 
       14 
14 
     | 
    
         
             
                          "SELECT t.*, j.* FROM #{association_table_name} t, #{@join_table} j " +
         
     | 
| 
       15 
     | 
    
         
            -
                          "WHERE t.#{@ 
     | 
| 
       16 
     | 
    
         
            -
                          "j.#{association_class_primary_key_name} =  
     | 
| 
      
 15 
     | 
    
         
            +
                          "WHERE t.#{@association_class.primary_key} = j.#{@association_foreign_key} AND " +
         
     | 
| 
      
 16 
     | 
    
         
            +
                          "j.#{association_class_primary_key_name} = #{@owner.quoted_id} " +
         
     | 
| 
       17 
17 
     | 
    
         
             
                          (options[:conditions] ? " AND " + options[:conditions] : "") + " " +
         
     | 
| 
       18 
18 
     | 
    
         
             
                          "ORDER BY #{@order}"
         
     | 
| 
       19 
19 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -26,11 +26,11 @@ module ActiveRecord 
     | 
|
| 
       26 
26 
     | 
    
         
             
                      each { |record| @owner.connection.execute(sql) }
         
     | 
| 
       27 
27 
     | 
    
         
             
                    elsif @options[:conditions] 
         
     | 
| 
       28 
28 
     | 
    
         
             
                      sql = 
         
     | 
| 
       29 
     | 
    
         
            -
                        "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} =  
     | 
| 
      
 29 
     | 
    
         
            +
                        "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} = #{@owner.quoted_id} " +
         
     | 
| 
       30 
30 
     | 
    
         
             
                        "AND #{@association_foreign_key} IN (#{collect { |record| record.id }.join(", ")})"
         
     | 
| 
       31 
31 
     | 
    
         
             
                      @owner.connection.execute(sql)
         
     | 
| 
       32 
32 
     | 
    
         
             
                    else
         
     | 
| 
       33 
     | 
    
         
            -
                      sql = "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} =  
     | 
| 
      
 33 
     | 
    
         
            +
                      sql = "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} = #{@owner.quoted_id}"
         
     | 
| 
       34 
34 
     | 
    
         
             
                      @owner.connection.execute(sql)
         
     | 
| 
       35 
35 
     | 
    
         
             
                    end
         
     | 
| 
       36 
36 
     | 
    
         | 
| 
         @@ -46,7 +46,7 @@ module ActiveRecord 
     | 
|
| 
       46 
46 
     | 
    
         
             
                      if loaded?
         
     | 
| 
       47 
47 
     | 
    
         
             
                        find_all { |record| record.id == association_id.to_i }.first
         
     | 
| 
       48 
48 
     | 
    
         
             
                      else
         
     | 
| 
       49 
     | 
    
         
            -
                        find_all_records(@finder_sql.sub(/ORDER BY/, "AND j.#{@association_foreign_key} =  
     | 
| 
      
 49 
     | 
    
         
            +
                        find_all_records(@finder_sql.sub(/ORDER BY/, "AND j.#{@association_foreign_key} = #{@owner.send(:quote, association_id)} ORDER BY")).first
         
     | 
| 
       50 
50 
     | 
    
         
             
                      end
         
     | 
| 
       51 
51 
     | 
    
         
             
                    end
         
     | 
| 
       52 
52 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -80,28 +80,29 @@ module ActiveRecord 
     | 
|
| 
       80 
80 
     | 
    
         
             
                      if @options[:insert_sql]
         
     | 
| 
       81 
81 
     | 
    
         
             
                        @owner.connection.execute(interpolate_sql(@options[:insert_sql], record))
         
     | 
| 
       82 
82 
     | 
    
         
             
                      else
         
     | 
| 
       83 
     | 
    
         
            -
                        sql = "INSERT INTO #{@join_table} (#{@association_class_primary_key_name}, #{@association_foreign_key})  
     | 
| 
      
 83 
     | 
    
         
            +
                        sql = "INSERT INTO #{@join_table} (#{@association_class_primary_key_name}, #{@association_foreign_key}) " + 
         
     | 
| 
      
 84 
     | 
    
         
            +
                              "VALUES (#{@owner.quoted_id},#{record.quoted_id})"
         
     | 
| 
       84 
85 
     | 
    
         
             
                        @owner.connection.execute(sql)
         
     | 
| 
       85 
86 
     | 
    
         
             
                      end
         
     | 
| 
       86 
87 
     | 
    
         
             
                    end
         
     | 
| 
       87 
88 
     | 
    
         | 
| 
       88 
89 
     | 
    
         
             
                    def insert_record_with_join_attributes(record, join_attributes)
         
     | 
| 
       89 
     | 
    
         
            -
                      attributes = { @association_class_primary_key_name => @owner.id, @association_foreign_key => record.id }.update(join_attributes) 
     | 
| 
      
 90 
     | 
    
         
            +
                      attributes = { @association_class_primary_key_name => @owner.id, @association_foreign_key => record.id }.update(join_attributes)
         
     | 
| 
       90 
91 
     | 
    
         
             
                      sql = 
         
     | 
| 
       91 
92 
     | 
    
         
             
                        "INSERT INTO #{@join_table} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
         
     | 
| 
       92 
93 
     | 
    
         
             
                        "VALUES (#{attributes.values.collect { |value| @owner.send(:quote, value) }.join(', ')})"
         
     | 
| 
       93 
94 
     | 
    
         
             
                      @owner.connection.execute(sql)
         
     | 
| 
       94 
95 
     | 
    
         
             
                    end
         
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
      
 96 
     | 
    
         
            +
                    
         
     | 
| 
       96 
97 
     | 
    
         
             
                    def delete_records(records)
         
     | 
| 
       97 
98 
     | 
    
         
             
                      if sql = @options[:delete_sql]
         
     | 
| 
       98 
99 
     | 
    
         
             
                        records.each { |record| @owner.connection.execute(sql) }
         
     | 
| 
       99 
100 
     | 
    
         
             
                      else
         
     | 
| 
       100 
101 
     | 
    
         
             
                        ids = quoted_record_ids(records)
         
     | 
| 
       101 
     | 
    
         
            -
                        sql = "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} =  
     | 
| 
      
 102 
     | 
    
         
            +
                        sql = "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} = #{@owner.quoted_id} AND #{@association_foreign_key} IN (#{ids})"
         
     | 
| 
       102 
103 
     | 
    
         
             
                        @owner.connection.execute(sql)
         
     | 
| 
       103 
104 
     | 
    
         
             
                      end
         
     | 
| 
       104 
105 
     | 
    
         
             
                    end
         
     | 
| 
       105 
106 
     | 
    
         
             
                  end
         
     | 
| 
       106 
107 
     | 
    
         
             
              end
         
     | 
| 
       107 
     | 
    
         
            -
            end
         
     | 
| 
      
 108 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -7,10 +7,16 @@ module ActiveRecord 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                    if options[:finder_sql]
         
     | 
| 
       9 
9 
     | 
    
         
             
                      @finder_sql = interpolate_sql(options[:finder_sql])
         
     | 
| 
       10 
     | 
    
         
            -
                      @counter_sql = @finder_sql.gsub(/SELECT (.*) FROM/i, "SELECT COUNT(*) FROM")
         
     | 
| 
       11 
10 
     | 
    
         
             
                    else
         
     | 
| 
       12 
     | 
    
         
            -
                      @finder_sql = "#{@association_class_primary_key_name} =  
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 11 
     | 
    
         
            +
                      @finder_sql = "#{@association_class_primary_key_name} = #{@owner.quoted_id} #{@conditions ? " AND " + interpolate_sql(@conditions) : ""}"
         
     | 
| 
      
 12 
     | 
    
         
            +
                    end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    if options[:counter_sql]
         
     | 
| 
      
 15 
     | 
    
         
            +
                      @counter_sql = interpolate_sql(options[:counter_sql])
         
     | 
| 
      
 16 
     | 
    
         
            +
                    elsif options[:finder_sql]
         
     | 
| 
      
 17 
     | 
    
         
            +
                      @counter_sql = options[:counter_sql] = @finder_sql.gsub(/SELECT (.*) FROM/i, "SELECT COUNT(*) FROM")
         
     | 
| 
      
 18 
     | 
    
         
            +
                    else
         
     | 
| 
      
 19 
     | 
    
         
            +
                      @counter_sql = "#{@association_class_primary_key_name} = #{@owner.quoted_id}#{@conditions ? " AND " + interpolate_sql(@conditions) : ""}"
         
     | 
| 
       14 
20 
     | 
    
         
             
                    end
         
     | 
| 
       15 
21 
     | 
    
         
             
                  end
         
     | 
| 
       16 
22 
     | 
    
         | 
| 
         @@ -34,8 +40,8 @@ module ActiveRecord 
     | 
|
| 
       34 
40 
     | 
    
         
             
                      @collection.find_all(&block)
         
     | 
| 
       35 
41 
     | 
    
         
             
                    else
         
     | 
| 
       36 
42 
     | 
    
         
             
                      @association_class.find_all(
         
     | 
| 
       37 
     | 
    
         
            -
                        "#{@association_class_primary_key_name} =  
     | 
| 
       38 
     | 
    
         
            -
                        "#{@conditions ? " AND " + @conditions : ""} 
     | 
| 
      
 43 
     | 
    
         
            +
                        "#{@association_class_primary_key_name} = #{@owner.quoted_id}" +
         
     | 
| 
      
 44 
     | 
    
         
            +
                        "#{@conditions ? " AND " + @conditions : ""}#{runtime_conditions ? " AND " + @association_class.send(:sanitize_conditions, runtime_conditions) : ""}",
         
     | 
| 
       39 
45 
     | 
    
         
             
                        orderings, 
         
     | 
| 
       40 
46 
     | 
    
         
             
                        limit, 
         
     | 
| 
       41 
47 
     | 
    
         
             
                        joins
         
     | 
| 
         @@ -49,7 +55,7 @@ module ActiveRecord 
     | 
|
| 
       49 
55 
     | 
    
         
             
                      @collection.find(&block)
         
     | 
| 
       50 
56 
     | 
    
         
             
                    else
         
     | 
| 
       51 
57 
     | 
    
         
             
                      @association_class.find_on_conditions(association_id,
         
     | 
| 
       52 
     | 
    
         
            -
                        "#{@association_class_primary_key_name} =  
     | 
| 
      
 58 
     | 
    
         
            +
                        "#{@association_class_primary_key_name} = #{@owner.quoted_id}#{@conditions ? " AND " + @conditions : ""}"
         
     | 
| 
       53 
59 
     | 
    
         
             
                      )
         
     | 
| 
       54 
60 
     | 
    
         
             
                    end
         
     | 
| 
       55 
61 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -57,7 +63,7 @@ module ActiveRecord 
     | 
|
| 
       57 
63 
     | 
    
         
             
                  # Removes all records from this association.  Returns +self+ so
         
     | 
| 
       58 
64 
     | 
    
         
             
                  # method calls may be chained.
         
     | 
| 
       59 
65 
     | 
    
         
             
                  def clear
         
     | 
| 
       60 
     | 
    
         
            -
                    @association_class.update_all("#{@association_class_primary_key_name} = NULL", "#{@association_class_primary_key_name} =  
     | 
| 
      
 66 
     | 
    
         
            +
                    @association_class.update_all("#{@association_class_primary_key_name} = NULL", "#{@association_class_primary_key_name} = #{@owner.quoted_id}")
         
     | 
| 
       61 
67 
     | 
    
         
             
                    @collection = []
         
     | 
| 
       62 
68 
     | 
    
         
             
                    self
         
     | 
| 
       63 
69 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -70,21 +76,21 @@ module ActiveRecord 
     | 
|
| 
       70 
76 
     | 
    
         
             
                        @association_class.find_all(@finder_sql, @options[:order] ? @options[:order] : nil)
         
     | 
| 
       71 
77 
     | 
    
         
             
                      end
         
     | 
| 
       72 
78 
     | 
    
         
             
                    end
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
       74 
80 
     | 
    
         
             
                    def count_records
         
     | 
| 
       75 
81 
     | 
    
         
             
                      if has_cached_counter?
         
     | 
| 
       76 
82 
     | 
    
         
             
                        @owner.send(:read_attribute, cached_counter_attribute_name)
         
     | 
| 
       77 
     | 
    
         
            -
                      elsif @options[: 
     | 
| 
      
 83 
     | 
    
         
            +
                      elsif @options[:counter_sql]
         
     | 
| 
       78 
84 
     | 
    
         
             
                        @association_class.count_by_sql(@counter_sql)
         
     | 
| 
       79 
85 
     | 
    
         
             
                      else
         
     | 
| 
       80 
86 
     | 
    
         
             
                        @association_class.count(@counter_sql)
         
     | 
| 
       81 
87 
     | 
    
         
             
                      end
         
     | 
| 
       82 
88 
     | 
    
         
             
                    end
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
       84 
90 
     | 
    
         
             
                    def has_cached_counter?
         
     | 
| 
       85 
91 
     | 
    
         
             
                      @owner.attribute_present?(cached_counter_attribute_name)
         
     | 
| 
       86 
92 
     | 
    
         
             
                    end
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
       88 
94 
     | 
    
         
             
                    def cached_counter_attribute_name
         
     | 
| 
       89 
95 
     | 
    
         
             
                      "#{@association_name}_count"
         
     | 
| 
       90 
96 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -95,7 +101,10 @@ module ActiveRecord 
     | 
|
| 
       95 
101 
     | 
    
         | 
| 
       96 
102 
     | 
    
         
             
                    def delete_records(records)
         
     | 
| 
       97 
103 
     | 
    
         
             
                      ids = quoted_record_ids(records)
         
     | 
| 
       98 
     | 
    
         
            -
                      @association_class.update_all( 
     | 
| 
      
 104 
     | 
    
         
            +
                      @association_class.update_all(
         
     | 
| 
      
 105 
     | 
    
         
            +
                        "#{@association_class_primary_key_name} = NULL", 
         
     | 
| 
      
 106 
     | 
    
         
            +
                        "#{@association_class_primary_key_name} = #{@owner.quoted_id} AND #{@association_class.primary_key} IN (#{ids})"
         
     | 
| 
      
 107 
     | 
    
         
            +
                      )
         
     | 
| 
       99 
108 
     | 
    
         
             
                    end
         
     | 
| 
       100 
109 
     | 
    
         
             
                end
         
     | 
| 
       101 
110 
     | 
    
         
             
              end
         
     | 
    
        data/lib/active_record/base.rb
    CHANGED
    
    | 
         @@ -6,6 +6,8 @@ require 'yaml' 
     | 
|
| 
       6 
6 
     | 
    
         
             
            module ActiveRecord #:nodoc:
         
     | 
| 
       7 
7 
     | 
    
         
             
              class ActiveRecordError < StandardError #:nodoc:
         
     | 
| 
       8 
8 
     | 
    
         
             
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
              class SubclassNotFound < ActiveRecordError #:nodoc:
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
       9 
11 
     | 
    
         
             
              class AssociationTypeMismatch < ActiveRecordError #:nodoc:
         
     | 
| 
       10 
12 
     | 
    
         
             
              end
         
     | 
| 
       11 
13 
     | 
    
         
             
              class SerializationTypeMismatch < ActiveRecordError #:nodoc:
         
     | 
| 
         @@ -22,6 +24,8 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       22 
24 
     | 
    
         
             
              end
         
     | 
| 
       23 
25 
     | 
    
         
             
              class StatementInvalid < ActiveRecordError #:nodoc:
         
     | 
| 
       24 
26 
     | 
    
         
             
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
              class PreparedStatementInvalid < ActiveRecordError #:nodoc:
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
       25 
29 
     | 
    
         | 
| 
       26 
30 
     | 
    
         
             
              # Active Record objects doesn't specify their attributes directly, but rather infer them from the table definition with
         
     | 
| 
       27 
31 
     | 
    
         
             
              # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change
         
     | 
| 
         @@ -63,15 +67,15 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       63 
67 
     | 
    
         
             
              #     end
         
     | 
| 
       64 
68 
     | 
    
         
             
              # 
         
     | 
| 
       65 
69 
     | 
    
         
             
              #     def self.authenticate_safely(user_name, password)
         
     | 
| 
       66 
     | 
    
         
            -
              #       find_first([ "user_name =  
     | 
| 
      
 70 
     | 
    
         
            +
              #       find_first([ "user_name = ? AND password = ?", user_name, password ])
         
     | 
| 
       67 
71 
     | 
    
         
             
              #     end
         
     | 
| 
       68 
72 
     | 
    
         
             
              #   end
         
     | 
| 
       69 
73 
     | 
    
         
             
              # 
         
     | 
| 
       70 
     | 
    
         
            -
              # The  
     | 
| 
       71 
     | 
    
         
            -
              # attacks if the  
     | 
| 
       72 
     | 
    
         
            -
              # the other hand, will sanitize the  
     | 
| 
      
 74 
     | 
    
         
            +
              # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query and is thus susceptible to SQL-injection
         
     | 
| 
      
 75 
     | 
    
         
            +
              # attacks if the <tt>user_name</tt> and +password+ parameters come directly from a HTTP request. The <tt>authenticate_safely</tt> method, 
         
     | 
| 
      
 76 
     | 
    
         
            +
              # on the other hand, will sanitize the <tt>user_name</tt> and +password+ before inserting them in the query, which will ensure that
         
     | 
| 
       73 
77 
     | 
    
         
             
              # an attacker can't escape the query and fake the login (or worse).
         
     | 
| 
       74 
     | 
    
         
            -
              # 
     | 
| 
      
 78 
     | 
    
         
            +
              #
         
     | 
| 
       75 
79 
     | 
    
         
             
              # == Overwriting default accessors
         
     | 
| 
       76 
80 
     | 
    
         
             
              # 
         
     | 
| 
       77 
81 
     | 
    
         
             
              # All column values are automatically available through basic accessors on the Active Record object, but some times you
         
     | 
| 
         @@ -126,6 +130,9 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       126 
130 
     | 
    
         
             
              # When you do Firm.create("name" => "37signals"), this record with be saved in the companies table with type = "Firm". You can then
         
     | 
| 
       127 
131 
     | 
    
         
             
              # fetch this row again using Company.find_first "name = '37signals'" and it will return a Firm object.
         
     | 
| 
       128 
132 
     | 
    
         
             
              #
         
     | 
| 
      
 133 
     | 
    
         
            +
              # If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just
         
     | 
| 
      
 134 
     | 
    
         
            +
              # like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
         
     | 
| 
      
 135 
     | 
    
         
            +
              #
         
     | 
| 
       129 
136 
     | 
    
         
             
              # Note, all the attributes for all the cases are kept in the same table. Read more:
         
     | 
| 
       130 
137 
     | 
    
         
             
              # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
         
     | 
| 
       131 
138 
     | 
    
         
             
              # 
         
     | 
| 
         @@ -187,6 +194,9 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       187 
194 
     | 
    
         | 
| 
       188 
195 
     | 
    
         
             
                @@subclasses = {}
         
     | 
| 
       189 
196 
     | 
    
         | 
| 
      
 197 
     | 
    
         
            +
                cattr_accessor :configurations
         
     | 
| 
      
 198 
     | 
    
         
            +
                @@primary_key_prefix_type = {}
         
     | 
| 
      
 199 
     | 
    
         
            +
                
         
     | 
| 
       190 
200 
     | 
    
         
             
                # Accessor for the prefix type that will be prepended to every primary key column name. The options are :table_name and 
         
     | 
| 
       191 
201 
     | 
    
         
             
                # :table_name_with_underscore. If the first is specified, the Product class will look for "productid" instead of "id" as
         
     | 
| 
       192 
202 
     | 
    
         
             
                # the primary column. If the latter is specified, the Product class will look for "product_id" instead of "id". Remember
         
     | 
| 
         @@ -211,6 +221,15 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       211 
221 
     | 
    
         
             
                cattr_accessor :pluralize_table_names
         
     | 
| 
       212 
222 
     | 
    
         
             
                @@pluralize_table_names = true
         
     | 
| 
       213 
223 
     | 
    
         | 
| 
      
 224 
     | 
    
         
            +
                # When turned on (which is default), all associations are included using "load". This mean that any change is instant in cached
         
     | 
| 
      
 225 
     | 
    
         
            +
                # environments like mod_ruby or FastCGI. When set to false, "require" is used, which is faster but requires server restart to
         
     | 
| 
      
 226 
     | 
    
         
            +
                # reflect changes.
         
     | 
| 
      
 227 
     | 
    
         
            +
                @@reload_associations = true
         
     | 
| 
      
 228 
     | 
    
         
            +
                cattr_accessor :reload_associations
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                @@associations_loaded = []
         
     | 
| 
      
 231 
     | 
    
         
            +
                cattr_accessor :associations_loaded
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
       214 
233 
     | 
    
         
             
                class << self # Class methods
         
     | 
| 
       215 
234 
     | 
    
         
             
                  # Returns objects for the records responding to either a specific id (1), a list of ids (1, 5, 6) or an array of ids. 
         
     | 
| 
       216 
235 
     | 
    
         
             
                  # If only one ID is specified, that object is returned directly. If more than one ID is specified, an array is returned.
         
     | 
| 
         @@ -218,12 +237,14 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       218 
237 
     | 
    
         
             
                  #   Person.find(1)       # returns the object for ID = 1
         
     | 
| 
       219 
238 
     | 
    
         
             
                  #   Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
         
     | 
| 
       220 
239 
     | 
    
         
             
                  #   Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
         
     | 
| 
      
 240 
     | 
    
         
            +
                  #   Person.find([1])     # returns an array for objects the object with ID = 1
         
     | 
| 
       221 
241 
     | 
    
         
             
                  # +RecordNotFound+ is raised if no record can be found.
         
     | 
| 
       222 
242 
     | 
    
         
             
                  def find(*ids)
         
     | 
| 
      
 243 
     | 
    
         
            +
                    expects_array = ids.first.kind_of?(Array)
         
     | 
| 
       223 
244 
     | 
    
         
             
                    ids = ids.flatten.compact.uniq
         
     | 
| 
       224 
245 
     | 
    
         | 
| 
       225 
246 
     | 
    
         
             
                    if ids.length > 1
         
     | 
| 
       226 
     | 
    
         
            -
                      ids_list = ids.map{ |id| " 
     | 
| 
      
 247 
     | 
    
         
            +
                      ids_list = ids.map{ |id| "#{sanitize(id)}" }.join(", ")
         
     | 
| 
       227 
248 
     | 
    
         
             
                      objects  = find_all("#{primary_key} IN (#{ids_list})", primary_key)
         
     | 
| 
       228 
249 
     | 
    
         | 
| 
       229 
250 
     | 
    
         
             
                      if objects.length == ids.length
         
     | 
| 
         @@ -233,11 +254,11 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       233 
254 
     | 
    
         
             
                      end
         
     | 
| 
       234 
255 
     | 
    
         
             
                    elsif ids.length == 1
         
     | 
| 
       235 
256 
     | 
    
         
             
                      id = ids.first
         
     | 
| 
       236 
     | 
    
         
            -
                      sql = "SELECT * FROM #{table_name} WHERE #{primary_key} =  
     | 
| 
      
 257 
     | 
    
         
            +
                      sql = "SELECT * FROM #{table_name} WHERE #{primary_key} = #{sanitize(id)}"
         
     | 
| 
       237 
258 
     | 
    
         
             
                      sql << " AND #{type_condition}" unless descends_from_active_record?
         
     | 
| 
       238 
259 
     | 
    
         | 
| 
       239 
260 
     | 
    
         
             
                      if record = connection.select_one(sql, "#{name} Find")
         
     | 
| 
       240 
     | 
    
         
            -
                        instantiate(record)
         
     | 
| 
      
 261 
     | 
    
         
            +
                        expects_array ? [instantiate(record)] : instantiate(record)
         
     | 
| 
       241 
262 
     | 
    
         
             
                      else 
         
     | 
| 
       242 
263 
     | 
    
         
             
                        raise RecordNotFound, "Couldn't find #{name} with ID = #{id}"
         
     | 
| 
       243 
264 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -251,28 +272,32 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       251 
272 
     | 
    
         
             
                  # Example:
         
     | 
| 
       252 
273 
     | 
    
         
             
                  #   Person.find_on_conditions 5, "first_name LIKE '%dav%' AND last_name = 'heinemeier'"
         
     | 
| 
       253 
274 
     | 
    
         
             
                  def find_on_conditions(id, conditions)
         
     | 
| 
       254 
     | 
    
         
            -
                    find_first("#{primary_key} =  
     | 
| 
      
 275 
     | 
    
         
            +
                    find_first("#{primary_key} = #{sanitize(id)} AND #{sanitize_conditions(conditions)}") || 
         
     | 
| 
       255 
276 
     | 
    
         
             
                        raise(RecordNotFound, "Couldn't find #{name} with #{primary_key} = #{id} on the condition of #{conditions}")
         
     | 
| 
       256 
277 
     | 
    
         
             
                  end
         
     | 
| 
       257 
278 
     | 
    
         | 
| 
       258 
279 
     | 
    
         
             
                  # Returns an array of all the objects that could be instantiated from the associated
         
     | 
| 
       259 
280 
     | 
    
         
             
                  # table in the database. The +conditions+ can be used to narrow the selection of objects (WHERE-part),
         
     | 
| 
       260 
281 
     | 
    
         
             
                  # such as by "color = 'red'", and arrangement of the selection can be done through +orderings+ (ORDER BY-part),
         
     | 
| 
       261 
     | 
    
         
            -
                  # such as by "last_name, first_name DESC". A maximum of returned objects can be specified in  
     | 
| 
      
 282 
     | 
    
         
            +
                  # such as by "last_name, first_name DESC". A maximum of returned objects and their offset can be specified in 
         
     | 
| 
      
 283 
     | 
    
         
            +
                  # +limit+ (LIMIT...OFFSET-part). Examples:
         
     | 
| 
       262 
284 
     | 
    
         
             
                  #   Project.find_all "category = 'accounts'", "last_accessed DESC", 15
         
     | 
| 
      
 285 
     | 
    
         
            +
                  #   Project.find_all ["category = ?", category_name], "created ASC", ["? OFFSET ?", 15, 20]
         
     | 
| 
       263 
286 
     | 
    
         
             
                  def find_all(conditions = nil, orderings = nil, limit = nil, joins = nil)
         
     | 
| 
       264 
287 
     | 
    
         
             
                    sql  = "SELECT * FROM #{table_name} " 
         
     | 
| 
       265 
288 
     | 
    
         
             
                    sql << "#{joins} " if joins
         
     | 
| 
       266 
289 
     | 
    
         
             
                    add_conditions!(sql, conditions)
         
     | 
| 
       267 
290 
     | 
    
         
             
                    sql << "ORDER BY #{orderings} " unless orderings.nil?
         
     | 
| 
       268 
     | 
    
         
            -
                    sql << "LIMIT #{limit} " unless limit.nil?
         
     | 
| 
      
 291 
     | 
    
         
            +
                    sql << "LIMIT #{sanitize_conditions(limit)} " unless limit.nil?
         
     | 
| 
       269 
292 
     | 
    
         | 
| 
       270 
293 
     | 
    
         
             
                    find_by_sql(sql)
         
     | 
| 
       271 
294 
     | 
    
         
             
                  end
         
     | 
| 
       272 
295 
     | 
    
         | 
| 
       273 
     | 
    
         
            -
                  # Works like find_all, but requires a complete SQL string.  
     | 
| 
      
 296 
     | 
    
         
            +
                  # Works like find_all, but requires a complete SQL string. Examples:
         
     | 
| 
       274 
297 
     | 
    
         
             
                  #   Post.find_by_sql "SELECT p.*, c.author FROM posts p, comments c WHERE p.id = c.post_id"
         
     | 
| 
      
 298 
     | 
    
         
            +
                  #   Post.find_by_sql ["SELECT * FROM posts WHERE author = ? AND created > ?", author_id, start_date]
         
     | 
| 
       275 
299 
     | 
    
         
             
                  def find_by_sql(sql)
         
     | 
| 
      
 300 
     | 
    
         
            +
                    sql = sanitize_conditions(sql)
         
     | 
| 
       276 
301 
     | 
    
         
             
                    connection.select_all(sql, "#{name} Load").inject([]) { |objects, record| objects << instantiate(record) }
         
     | 
| 
       277 
302 
     | 
    
         
             
                  end
         
     | 
| 
       278 
303 
     | 
    
         | 
| 
         @@ -344,6 +369,7 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       344 
369 
     | 
    
         
             
                  # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
         
     | 
| 
       345 
370 
     | 
    
         
             
                  #   Product.count "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
         
     | 
| 
       346 
371 
     | 
    
         
             
                  def count_by_sql(sql)
         
     | 
| 
      
 372 
     | 
    
         
            +
                    sql = sanitize_conditions(sql)
         
     | 
| 
       347 
373 
     | 
    
         
             
                    count = connection.select_one(sql, "#{name} Count").values.first
         
     | 
| 
       348 
374 
     | 
    
         
             
                    return count ? count.to_i : 0
         
     | 
| 
       349 
375 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -354,12 +380,12 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       354 
380 
     | 
    
         
             
                  # for looping over a collection where each element require a number of aggregate values. Like the DiscussionBoard
         
     | 
| 
       355 
381 
     | 
    
         
             
                  # that needs to list both the number of posts and comments.
         
     | 
| 
       356 
382 
     | 
    
         
             
                  def increment_counter(counter_name, id)
         
     | 
| 
       357 
     | 
    
         
            -
                    update_all "#{counter_name} = #{counter_name} + 1", "#{primary_key} = #{id}"
         
     | 
| 
      
 383 
     | 
    
         
            +
                    update_all "#{counter_name} = #{counter_name} + 1", "#{primary_key} = #{quote(id)}"
         
     | 
| 
       358 
384 
     | 
    
         
             
                  end
         
     | 
| 
       359 
385 
     | 
    
         | 
| 
       360 
386 
     | 
    
         
             
                  # Works like increment_counter, but decrements instead.
         
     | 
| 
       361 
387 
     | 
    
         
             
                  def decrement_counter(counter_name, id)
         
     | 
| 
       362 
     | 
    
         
            -
                    update_all "#{counter_name} = #{counter_name} - 1", "#{primary_key} = #{id}"
         
     | 
| 
      
 388 
     | 
    
         
            +
                    update_all "#{counter_name} = #{counter_name} - 1", "#{primary_key} = #{quote(id)}"
         
     | 
| 
       363 
389 
     | 
    
         
             
                  end
         
     | 
| 
       364 
390 
     | 
    
         | 
| 
       365 
391 
     | 
    
         
             
                  # Attributes named in this macro are protected from mass-assignment, such as <tt>new(attributes)</tt> and 
         
     | 
| 
         @@ -499,6 +525,15 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       499 
525 
     | 
    
         
             
                      methods
         
     | 
| 
       500 
526 
     | 
    
         
             
                    end
         
     | 
| 
       501 
527 
     | 
    
         
             
                  end
         
     | 
| 
      
 528 
     | 
    
         
            +
                  
         
     | 
| 
      
 529 
     | 
    
         
            +
                  # Resets all the cached information about columns, which will cause they to be reloaded on the next request.
         
     | 
| 
      
 530 
     | 
    
         
            +
                  def reset_column_information
         
     | 
| 
      
 531 
     | 
    
         
            +
                    @columns = @columns_hash = @content_columns = @dynamic_methods_hash = nil
         
     | 
| 
      
 532 
     | 
    
         
            +
                  end
         
     | 
| 
      
 533 
     | 
    
         
            +
             
     | 
| 
      
 534 
     | 
    
         
            +
                  def reset_column_information_and_inheritable_attributes_for_all_subclasses
         
     | 
| 
      
 535 
     | 
    
         
            +
                    subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
         
     | 
| 
      
 536 
     | 
    
         
            +
                  end
         
     | 
| 
       502 
537 
     | 
    
         | 
| 
       503 
538 
     | 
    
         
             
                  # Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example:
         
     | 
| 
       504 
539 
     | 
    
         
             
                  #   Person.human_attribute_name("first_name") # => "First name"
         
     | 
| 
         @@ -507,13 +542,16 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       507 
542 
     | 
    
         
             
                  end
         
     | 
| 
       508 
543 
     | 
    
         | 
| 
       509 
544 
     | 
    
         
             
                  def descends_from_active_record? # :nodoc:
         
     | 
| 
       510 
     | 
    
         
            -
                    superclass == Base
         
     | 
| 
      
 545 
     | 
    
         
            +
                    superclass == Base || !columns_hash.has_key?(inheritance_column)
         
     | 
| 
      
 546 
     | 
    
         
            +
                  end
         
     | 
| 
      
 547 
     | 
    
         
            +
             
     | 
| 
      
 548 
     | 
    
         
            +
                  def quote(object)
         
     | 
| 
      
 549 
     | 
    
         
            +
                    connection.quote(object)
         
     | 
| 
       511 
550 
     | 
    
         
             
                  end
         
     | 
| 
       512 
551 
     | 
    
         | 
| 
       513 
     | 
    
         
            -
                  # Used to sanitize objects before they're used in an SELECT SQL-statement.
         
     | 
| 
      
 552 
     | 
    
         
            +
                  # Used to sanitize objects before they're used in an SELECT SQL-statement. Delegates to <tt>connection.quote</tt>.
         
     | 
| 
       514 
553 
     | 
    
         
             
                  def sanitize(object) # :nodoc:
         
     | 
| 
       515 
     | 
    
         
            -
                     
     | 
| 
       516 
     | 
    
         
            -
                    object.to_s.gsub(/([;:])/, "").gsub('##', '\#\#').gsub(/'/, "''") # ' (for ruby-mode)
         
     | 
| 
      
 554 
     | 
    
         
            +
                    connection.quote(object)
         
     | 
| 
       517 
555 
     | 
    
         
             
                  end
         
     | 
| 
       518 
556 
     | 
    
         | 
| 
       519 
557 
     | 
    
         
             
                  # Used to aggregate logging and benchmark, so you can measure and represent multiple statements in a single block.
         
     | 
| 
         @@ -537,7 +575,20 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       537 
575 
     | 
    
         
             
                    # Finder methods must instantiate through this method to work with the single-table inheritance model
         
     | 
| 
       538 
576 
     | 
    
         
             
                    # that makes it possible to create objects of different types from the same table.
         
     | 
| 
       539 
577 
     | 
    
         
             
                    def instantiate(record)
         
     | 
| 
       540 
     | 
    
         
            -
                       
     | 
| 
      
 578 
     | 
    
         
            +
                      require_association_class(record[inheritance_column])
         
     | 
| 
      
 579 
     | 
    
         
            +
             
     | 
| 
      
 580 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 581 
     | 
    
         
            +
                        object = record_with_type?(record) ? compute_type(record[inheritance_column]).allocate : allocate
         
     | 
| 
      
 582 
     | 
    
         
            +
                      rescue NameError
         
     | 
| 
      
 583 
     | 
    
         
            +
                        raise(
         
     | 
| 
      
 584 
     | 
    
         
            +
                          SubclassNotFound, 
         
     | 
| 
      
 585 
     | 
    
         
            +
                          "The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " +
         
     | 
| 
      
 586 
     | 
    
         
            +
                          "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
         
     | 
| 
      
 587 
     | 
    
         
            +
                          "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
         
     | 
| 
      
 588 
     | 
    
         
            +
                          "or overwrite #{self.to_s}.inheritance_column to use another column for that information."
         
     | 
| 
      
 589 
     | 
    
         
            +
                        )
         
     | 
| 
      
 590 
     | 
    
         
            +
                      end
         
     | 
| 
      
 591 
     | 
    
         
            +
             
     | 
| 
       541 
592 
     | 
    
         
             
                      object.instance_variable_set("@attributes", record)
         
     | 
| 
       542 
593 
     | 
    
         
             
                      return object
         
     | 
| 
       543 
594 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -562,7 +613,7 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       562 
613 
     | 
    
         | 
| 
       563 
614 
     | 
    
         
             
                    def type_condition
         
     | 
| 
       564 
615 
     | 
    
         
             
                      " (" + subclasses.inject("#{inheritance_column} = '#{Inflector.demodulize(name)}' ") do |condition, subclass| 
         
     | 
| 
       565 
     | 
    
         
            -
                        condition << "OR #{inheritance_column} = '#{Inflector.demodulize(subclass.name)}'"
         
     | 
| 
      
 616 
     | 
    
         
            +
                        condition << "OR #{inheritance_column} = '#{Inflector.demodulize(subclass.name)}' "
         
     | 
| 
       566 
617 
     | 
    
         
             
                      end + ") "
         
     | 
| 
       567 
618 
     | 
    
         
             
                    end
         
     | 
| 
       568 
619 
     | 
    
         | 
| 
         @@ -602,13 +653,54 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       602 
653 
     | 
    
         
             
                    # Accepts either a condition array or string. The string is returned untouched, but the array has each of
         
     | 
| 
       603 
654 
     | 
    
         
             
                    # the condition values sanitized.
         
     | 
| 
       604 
655 
     | 
    
         
             
                    def sanitize_conditions(conditions)
         
     | 
| 
       605 
     | 
    
         
            -
                       
     | 
| 
       606 
     | 
    
         
            -
             
     | 
| 
       607 
     | 
    
         
            -
             
     | 
| 
       608 
     | 
    
         
            -
             
     | 
| 
      
 656 
     | 
    
         
            +
                      return conditions unless conditions.is_a?(Array)
         
     | 
| 
      
 657 
     | 
    
         
            +
             
     | 
| 
      
 658 
     | 
    
         
            +
                      statement, *values = conditions
         
     | 
| 
      
 659 
     | 
    
         
            +
             
     | 
| 
      
 660 
     | 
    
         
            +
                      if values[0].is_a?(Hash) && statement =~ /:\w+/
         
     | 
| 
      
 661 
     | 
    
         
            +
                        replace_named_bind_variables(statement, values[0])
         
     | 
| 
      
 662 
     | 
    
         
            +
                      elsif statement =~ /\?/ 
         
     | 
| 
      
 663 
     | 
    
         
            +
                        replace_bind_variables(statement, values)
         
     | 
| 
      
 664 
     | 
    
         
            +
                      else
         
     | 
| 
      
 665 
     | 
    
         
            +
                        statement % values.collect { |value| connection.quote_string(value.to_s) }
         
     | 
| 
      
 666 
     | 
    
         
            +
                      end
         
     | 
| 
      
 667 
     | 
    
         
            +
                    end
         
     | 
| 
      
 668 
     | 
    
         
            +
             
     | 
| 
      
 669 
     | 
    
         
            +
                    def replace_bind_variables(statement, values)
         
     | 
| 
      
 670 
     | 
    
         
            +
                      orig_statement = statement.clone
         
     | 
| 
      
 671 
     | 
    
         
            +
                      expected_number_of_variables = statement.count('?')
         
     | 
| 
      
 672 
     | 
    
         
            +
                      provided_number_of_variables = values.size
         
     | 
| 
      
 673 
     | 
    
         
            +
             
     | 
| 
      
 674 
     | 
    
         
            +
                      unless expected_number_of_variables == provided_number_of_variables
         
     | 
| 
      
 675 
     | 
    
         
            +
                        raise PreparedStatementInvalid, "wrong number of bind variables (#{provided_number_of_variables} for #{expected_number_of_variables})"
         
     | 
| 
      
 676 
     | 
    
         
            +
                      end
         
     | 
| 
      
 677 
     | 
    
         
            +
             
     | 
| 
      
 678 
     | 
    
         
            +
                      until values.empty?
         
     | 
| 
      
 679 
     | 
    
         
            +
                        statement.sub!(/\?/, encode_quoted_value(values.shift))
         
     | 
| 
       609 
680 
     | 
    
         
             
                      end
         
     | 
| 
       610 
681 
     | 
    
         | 
| 
       611 
     | 
    
         
            -
                       
     | 
| 
      
 682 
     | 
    
         
            +
                      statement.gsub('?') { |all, match| connection.quote(values.shift) }
         
     | 
| 
      
 683 
     | 
    
         
            +
                    end
         
     | 
| 
      
 684 
     | 
    
         
            +
             
     | 
| 
      
 685 
     | 
    
         
            +
                    def replace_named_bind_variables(statement, values_hash)
         
     | 
| 
      
 686 
     | 
    
         
            +
                      orig_statement = statement.clone
         
     | 
| 
      
 687 
     | 
    
         
            +
                      values_hash.keys.each do |k|
         
     | 
| 
      
 688 
     | 
    
         
            +
                        if statement.sub!(/:#{k.id2name}/, encode_quoted_value(values_hash.delete(k))).nil?
         
     | 
| 
      
 689 
     | 
    
         
            +
                          raise PreparedStatementInvalid, ":#{k} is not a variable in [#{orig_statement}]"
         
     | 
| 
      
 690 
     | 
    
         
            +
                        end
         
     | 
| 
      
 691 
     | 
    
         
            +
                      end
         
     | 
| 
      
 692 
     | 
    
         
            +
             
     | 
| 
      
 693 
     | 
    
         
            +
                      if statement =~ /(:\w+)/
         
     | 
| 
      
 694 
     | 
    
         
            +
                        raise PreparedStatementInvalid, "No value provided for #{$1} in [#{orig_statement}]"
         
     | 
| 
      
 695 
     | 
    
         
            +
                      end
         
     | 
| 
      
 696 
     | 
    
         
            +
             
     | 
| 
      
 697 
     | 
    
         
            +
                      return statement
         
     | 
| 
      
 698 
     | 
    
         
            +
                    end
         
     | 
| 
      
 699 
     | 
    
         
            +
                    
         
     | 
| 
      
 700 
     | 
    
         
            +
                    def encode_quoted_value(value)
         
     | 
| 
      
 701 
     | 
    
         
            +
                      quoted_value = connection.quote(value)
         
     | 
| 
      
 702 
     | 
    
         
            +
                      quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'")          
         
     | 
| 
      
 703 
     | 
    
         
            +
                      quoted_value
         
     | 
| 
       612 
704 
     | 
    
         
             
                    end
         
     | 
| 
       613 
705 
     | 
    
         
             
                end
         
     | 
| 
       614 
706 
     | 
    
         | 
| 
         @@ -628,7 +720,11 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       628 
720 
     | 
    
         
             
                  # Every Active Record class must use "id" as their primary ID. This getter overwrites the native
         
     | 
| 
       629 
721 
     | 
    
         
             
                  # id method, which isn't being used in this context.
         
     | 
| 
       630 
722 
     | 
    
         
             
                  def id
         
     | 
| 
       631 
     | 
    
         
            -
                    read_attribute(self.class.primary_key) 
     | 
| 
      
 723 
     | 
    
         
            +
                    read_attribute(self.class.primary_key)
         
     | 
| 
      
 724 
     | 
    
         
            +
                  end
         
     | 
| 
      
 725 
     | 
    
         
            +
                  
         
     | 
| 
      
 726 
     | 
    
         
            +
                  def quoted_id
         
     | 
| 
      
 727 
     | 
    
         
            +
                    quote(id, self.class.columns_hash[self.class.primary_key])
         
     | 
| 
       632 
728 
     | 
    
         
             
                  end
         
     | 
| 
       633 
729 
     | 
    
         | 
| 
       634 
730 
     | 
    
         
             
                  # Sets the primary ID.
         
     | 
| 
         @@ -654,7 +750,7 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       654 
750 
     | 
    
         
             
                    unless new_record?
         
     | 
| 
       655 
751 
     | 
    
         
             
                      connection.delete(
         
     | 
| 
       656 
752 
     | 
    
         
             
                        "DELETE FROM #{self.class.table_name} " + 
         
     | 
| 
       657 
     | 
    
         
            -
                        "WHERE #{self.class.primary_key} =  
     | 
| 
      
 753 
     | 
    
         
            +
                        "WHERE #{self.class.primary_key} = #{quote(id)}", 
         
     | 
| 
       658 
754 
     | 
    
         
             
                        "#{self.class.name} Destroy"
         
     | 
| 
       659 
755 
     | 
    
         
             
                      )
         
     | 
| 
       660 
756 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -755,7 +851,7 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       755 
851 
     | 
    
         
             
                  def respond_to?(method)
         
     | 
| 
       756 
852 
     | 
    
         
             
                    self.class.column_methods_hash[method.to_sym] || respond_to_without_attributes?(method)
         
     | 
| 
       757 
853 
     | 
    
         
             
                  end
         
     | 
| 
       758 
     | 
    
         
            -
             
     | 
| 
      
 854 
     | 
    
         
            +
                  
         
     | 
| 
       759 
855 
     | 
    
         
             
                private
         
     | 
| 
       760 
856 
     | 
    
         
             
                  def create_or_update
         
     | 
| 
       761 
857 
     | 
    
         
             
                    if new_record? then create else update end
         
     | 
| 
         @@ -765,8 +861,8 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       765 
861 
     | 
    
         
             
                  def update
         
     | 
| 
       766 
862 
     | 
    
         
             
                    connection.update(
         
     | 
| 
       767 
863 
     | 
    
         
             
                      "UPDATE #{self.class.table_name} " +
         
     | 
| 
       768 
     | 
    
         
            -
                      "SET #{quoted_comma_pair_list(connection, attributes_with_quotes)} " +
         
     | 
| 
       769 
     | 
    
         
            -
                      "WHERE #{self.class.primary_key} =  
     | 
| 
      
 864 
     | 
    
         
            +
                      "SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))} " +
         
     | 
| 
      
 865 
     | 
    
         
            +
                      "WHERE #{self.class.primary_key} = #{quote(id)}",
         
     | 
| 
       770 
866 
     | 
    
         
             
                      "#{self.class.name} Update"
         
     | 
| 
       771 
867 
     | 
    
         
             
                    )
         
     | 
| 
       772 
868 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -825,12 +921,16 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       825 
921 
     | 
    
         
             
                  # Returns the value of attribute identified by <tt>attr_name</tt> after it has been type cast (for example, 
         
     | 
| 
       826 
922 
     | 
    
         
             
                  # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
         
     | 
| 
       827 
923 
     | 
    
         
             
                  def read_attribute(attr_name) #:doc:
         
     | 
| 
       828 
     | 
    
         
            -
                    if  
     | 
| 
       829 
     | 
    
         
            -
                       
     | 
| 
       830 
     | 
    
         
            -
                         
     | 
| 
      
 924 
     | 
    
         
            +
                    if @attributes.keys.include? attr_name
         
     | 
| 
      
 925 
     | 
    
         
            +
                      if column = column_for_attribute(attr_name)
         
     | 
| 
      
 926 
     | 
    
         
            +
                        @attributes[attr_name] = unserializable_attribute?(attr_name, column) ?
         
     | 
| 
      
 927 
     | 
    
         
            +
                          unserialize_attribute(attr_name) : column.type_cast(@attributes[attr_name])
         
     | 
| 
      
 928 
     | 
    
         
            +
                      end
         
     | 
| 
      
 929 
     | 
    
         
            +
                      
         
     | 
| 
      
 930 
     | 
    
         
            +
                      @attributes[attr_name]
         
     | 
| 
      
 931 
     | 
    
         
            +
                    else
         
     | 
| 
      
 932 
     | 
    
         
            +
                      nil
         
     | 
| 
       831 
933 
     | 
    
         
             
                    end
         
     | 
| 
       832 
     | 
    
         
            -
                    
         
     | 
| 
       833 
     | 
    
         
            -
                    @attributes[attr_name]
         
     | 
| 
       834 
934 
     | 
    
         
             
                  end
         
     | 
| 
       835 
935 
     | 
    
         | 
| 
       836 
936 
     | 
    
         
             
                  # Returns true if the attribute is of a text column and marked for serialization.
         
     | 
| 
         @@ -897,10 +997,10 @@ module ActiveRecord #:nodoc: 
     | 
|
| 
       897 
997 
     | 
    
         | 
| 
       898 
998 
     | 
    
         
             
                  # Returns copy of the attributes hash where all the values have been safely quoted for use in
         
     | 
| 
       899 
999 
     | 
    
         
             
                  # an SQL statement. 
         
     | 
| 
       900 
     | 
    
         
            -
                  def attributes_with_quotes
         
     | 
| 
      
 1000 
     | 
    
         
            +
                  def attributes_with_quotes(include_primary_key = true)
         
     | 
| 
       901 
1001 
     | 
    
         
             
                    columns_hash = self.class.columns_hash
         
     | 
| 
       902 
1002 
     | 
    
         
             
                    @attributes.inject({}) do |attrs_quoted, pair| 
         
     | 
| 
       903 
     | 
    
         
            -
                      attrs_quoted[pair.first] = quote(pair.last, columns_hash[pair.first])
         
     | 
| 
      
 1003 
     | 
    
         
            +
                      attrs_quoted[pair.first] = quote(pair.last, columns_hash[pair.first]) unless !include_primary_key && pair.first == self.class.primary_key
         
     | 
| 
       904 
1004 
     | 
    
         
             
                      attrs_quoted
         
     | 
| 
       905 
1005 
     | 
    
         
             
                    end
         
     | 
| 
       906 
1006 
     | 
    
         
             
                  end
         
     |