<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -138,6 +138,9 @@ module ActiveRecord #:nodoc:
     end
   end
 
+  class IllegalMassAssignmentError &lt; ActiveRecordError #:nodoc:
+  end
+
   # Raised when there are multiple errors while doing a mass assignment through the +attributes+
   # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
   # objects, each corresponding to the error while assigning to an attribute.
@@ -465,6 +468,12 @@ module ActiveRecord #:nodoc:
     cattr_accessor :timestamped_migrations , :instance_writer =&gt; false
     @@timestamped_migrations = true
 
+    # Instead of just dropping attributes that are protected via attr_protected and
+    # attr_accessible from mass assignment, raise an IllegalMassAssignmentError
+    # false by default.
+    @@raise_on_illegal_mass_assignment = false
+    cattr_accessor :raise_on_illegal_mass_assignment
+
     # Determine whether to store the full constant name including namespace when using STI
     superclass_delegating_accessor :store_full_sti_class
     self.store_full_sti_class = false
@@ -557,10 +566,10 @@ module ActiveRecord #:nodoc:
         set_readonly_option!(options)
 
         case args.first
-          when :first then find_initial(options)
-          when :last  then find_last(options)
-          when :all   then find_every(options)
-          else             find_from_ids(args, options)
+        when :first then find_initial(options)
+        when :last  then find_last(options)
+        when :all   then find_every(options)
+        else             find_from_ids(args, options)
         end
       end
 
@@ -964,15 +973,15 @@ module ActiveRecord #:nodoc:
         read_inheritable_attribute(:attr_accessible)
       end
 
-       # Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards.
-       def attr_readonly(*attributes)
-         write_inheritable_attribute(:attr_readonly, Set.new(attributes.map(&amp;:to_s)) + (readonly_attributes || []))
-       end
+      # Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards.
+      def attr_readonly(*attributes)
+        write_inheritable_attribute(:attr_readonly, Set.new(attributes.map(&amp;:to_s)) + (readonly_attributes || []))
+      end
 
-       # Returns an array of all the attributes that have been specified as readonly.
-       def readonly_attributes
-         read_inheritable_attribute(:attr_readonly)
-       end
+      # Returns an array of all the attributes that have been specified as readonly.
+      def readonly_attributes
+        read_inheritable_attribute(:attr_readonly)
+      end
 
       # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
       # then specify the name of that attribute using this method and it will be handled automatically.
@@ -1041,17 +1050,17 @@ module ActiveRecord #:nodoc:
 
         name =
           # STI subclasses always use their superclass' table.
-          unless self == base
-            base.table_name
-          else
-            # Nested classes are prefixed with singular parent table name.
-            if parent &lt; ActiveRecord::Base &amp;&amp; !parent.abstract_class?
-              contained = parent.table_name
-              contained = contained.singularize if parent.pluralize_table_names
-              contained &lt;&lt; '_'
-            end
-            name = &quot;#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}&quot;
+        unless self == base
+          base.table_name
+        else
+          # Nested classes are prefixed with singular parent table name.
+          if parent &lt; ActiveRecord::Base &amp;&amp; !parent.abstract_class?
+            contained = parent.table_name
+            contained = contained.singularize if parent.pluralize_table_names
+            contained &lt;&lt; '_'
           end
+          name = &quot;#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}&quot;
+        end
 
         set_table_name(name)
         name
@@ -1072,10 +1081,10 @@ module ActiveRecord #:nodoc:
       def get_primary_key(base_name) #:nodoc:
         key = 'id'
         case primary_key_prefix_type
-          when :table_name
-            key = base_name.to_s.foreign_key(false)
-          when :table_name_with_underscore
-            key = base_name.to_s.foreign_key
+        when :table_name
+          key = base_name.to_s.foreign_key(false)
+        when :table_name_with_underscore
+          key = base_name.to_s.foreign_key
         end
         key
       end
@@ -1219,7 +1228,7 @@ module ActiveRecord #:nodoc:
       def self_and_descendents_from_active_record#nodoc:
         klass = self
         classes = [klass]
-        while klass != klass.base_class  
+        while klass != klass.base_class
           classes &lt;&lt; klass = klass.superclass
         end
         classes
@@ -1253,7 +1262,7 @@ module ActiveRecord #:nodoc:
       def human_name(options = {})
         defaults = self_and_descendents_from_active_record.map do |klass|
           :&quot;#{klass.name.underscore}&quot;
-        end 
+        end
         defaults &lt;&lt; self.name.humanize
         I18n.translate(defaults.shift, {:scope =&gt; [:activerecord, :models], :count =&gt; 1, :default =&gt; defaults}.merge(options))
       end
@@ -1375,323 +1384,323 @@ module ActiveRecord #:nodoc:
       end
 
       private
-        def find_initial(options)
-          options.update(:limit =&gt; 1)
-          find_every(options).first
-        end
-
-        def find_last(options)
-          order = options[:order]
-
-          if order
-            order = reverse_sql_order(order)
-          elsif !scoped?(:find, :order)
-            order = &quot;#{table_name}.#{primary_key} DESC&quot;
-          end
+      def find_initial(options)
+        options.update(:limit =&gt; 1)
+        find_every(options).first
+      end
 
-          if scoped?(:find, :order)
-            scoped_order = reverse_sql_order(scope(:find, :order))
-            scoped_methods.select { |s| s[:find].update(:order =&gt; scoped_order) }
-          end
+      def find_last(options)
+        order = options[:order]
 
-          find_initial(options.merge({ :order =&gt; order }))
+        if order
+          order = reverse_sql_order(order)
+        elsif !scoped?(:find, :order)
+          order = &quot;#{table_name}.#{primary_key} DESC&quot;
         end
 
-        def reverse_sql_order(order_query)
-          reversed_query = order_query.split(/,/).each { |s|
-            if s.match(/\s(asc|ASC)$/)
-              s.gsub!(/\s(asc|ASC)$/, ' DESC')
-            elsif s.match(/\s(desc|DESC)$/)
-              s.gsub!(/\s(desc|DESC)$/, ' ASC')
-            elsif !s.match(/\s(asc|ASC|desc|DESC)$/)
-              s.concat(' DESC')
-            end
-          }.join(',')
+        if scoped?(:find, :order)
+          scoped_order = reverse_sql_order(scope(:find, :order))
+          scoped_methods.select { |s| s[:find].update(:order =&gt; scoped_order) }
         end
 
-        def find_every(options)
-          include_associations = merge_includes(scope(:find, :include), options[:include])
+        find_initial(options.merge({ :order =&gt; order }))
+      end
 
-          if include_associations.any? &amp;&amp; references_eager_loaded_tables?(options)
-            records = find_with_associations(options)
-          else
-            records = find_by_sql(construct_finder_sql(options))
-            if include_associations.any?
-              preload_associations(records, include_associations)
-            end
+      def reverse_sql_order(order_query)
+        reversed_query = order_query.split(/,/).each { |s|
+          if s.match(/\s(asc|ASC)$/)
+            s.gsub!(/\s(asc|ASC)$/, ' DESC')
+          elsif s.match(/\s(desc|DESC)$/)
+            s.gsub!(/\s(desc|DESC)$/, ' ASC')
+          elsif !s.match(/\s(asc|ASC|desc|DESC)$/)
+            s.concat(' DESC')
           end
+        }.join(',')
+      end
 
-          records.each { |record| record.readonly! } if options[:readonly]
+      def find_every(options)
+        include_associations = merge_includes(scope(:find, :include), options[:include])
 
-          records
+        if include_associations.any? &amp;&amp; references_eager_loaded_tables?(options)
+          records = find_with_associations(options)
+        else
+          records = find_by_sql(construct_finder_sql(options))
+          if include_associations.any?
+            preload_associations(records, include_associations)
+          end
         end
 
-        def find_from_ids(ids, options)
-          expects_array = ids.first.kind_of?(Array)
-          return ids.first if expects_array &amp;&amp; ids.first.empty?
+        records.each { |record| record.readonly! } if options[:readonly]
 
-          ids = ids.flatten.compact.uniq
+        records
+      end
 
-          case ids.size
-            when 0
-              raise RecordNotFound, &quot;Couldn't find #{name} without an ID&quot;
-            when 1
-              result = find_one(ids.first, options)
-              expects_array ? [ result ] : result
-            else
-              find_some(ids, options)
-          end
+      def find_from_ids(ids, options)
+        expects_array = ids.first.kind_of?(Array)
+        return ids.first if expects_array &amp;&amp; ids.first.empty?
+
+        ids = ids.flatten.compact.uniq
+
+        case ids.size
+        when 0
+          raise RecordNotFound, &quot;Couldn't find #{name} without an ID&quot;
+        when 1
+          result = find_one(ids.first, options)
+          expects_array ? [ result ] : result
+        else
+          find_some(ids, options)
         end
+      end
 
-        def find_one(id, options)
-          conditions = &quot; AND (#{sanitize_sql(options[:conditions])})&quot; if options[:conditions]
-          options.update :conditions =&gt; &quot;#{quoted_table_name}.#{connection.quote_column_name(primary_key)} = #{quote_value(id,columns_hash[primary_key])}#{conditions}&quot;
+      def find_one(id, options)
+        conditions = &quot; AND (#{sanitize_sql(options[:conditions])})&quot; if options[:conditions]
+        options.update :conditions =&gt; &quot;#{quoted_table_name}.#{connection.quote_column_name(primary_key)} = #{quote_value(id,columns_hash[primary_key])}#{conditions}&quot;
 
-          # Use find_every(options).first since the primary key condition
-          # already ensures we have a single record. Using find_initial adds
-          # a superfluous :limit =&gt; 1.
-          if result = find_every(options).first
-            result
-          else
-            raise RecordNotFound, &quot;Couldn't find #{name} with ID=#{id}#{conditions}&quot;
-          end
+        # Use find_every(options).first since the primary key condition
+        # already ensures we have a single record. Using find_initial adds
+        # a superfluous :limit =&gt; 1.
+        if result = find_every(options).first
+          result
+        else
+          raise RecordNotFound, &quot;Couldn't find #{name} with ID=#{id}#{conditions}&quot;
         end
+      end
 
-        def find_some(ids, options)
-          conditions = &quot; AND (#{sanitize_sql(options[:conditions])})&quot; if options[:conditions]
-          ids_list   = ids.map { |id| quote_value(id,columns_hash[primary_key]) }.join(',')
-          options.update :conditions =&gt; &quot;#{quoted_table_name}.#{connection.quote_column_name(primary_key)} IN (#{ids_list})#{conditions}&quot;
+      def find_some(ids, options)
+        conditions = &quot; AND (#{sanitize_sql(options[:conditions])})&quot; if options[:conditions]
+        ids_list   = ids.map { |id| quote_value(id,columns_hash[primary_key]) }.join(',')
+        options.update :conditions =&gt; &quot;#{quoted_table_name}.#{connection.quote_column_name(primary_key)} IN (#{ids_list})#{conditions}&quot;
 
-          result = find_every(options)
+        result = find_every(options)
 
-          # Determine expected size from limit and offset, not just ids.size.
-          expected_size =
-            if options[:limit] &amp;&amp; ids.size &gt; options[:limit]
-              options[:limit]
-            else
-              ids.size
-            end
+        # Determine expected size from limit and offset, not just ids.size.
+        expected_size =
+          if options[:limit] &amp;&amp; ids.size &gt; options[:limit]
+          options[:limit]
+        else
+          ids.size
+        end
 
-          # 11 ids with limit 3, offset 9 should give 2 results.
-          if options[:offset] &amp;&amp; (ids.size - options[:offset] &lt; expected_size)
-            expected_size = ids.size - options[:offset]
-          end
+        # 11 ids with limit 3, offset 9 should give 2 results.
+        if options[:offset] &amp;&amp; (ids.size - options[:offset] &lt; expected_size)
+          expected_size = ids.size - options[:offset]
+        end
 
-          if result.size == expected_size
-            result
-          else
-            raise RecordNotFound, &quot;Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions} (found #{result.size} results, but was looking for #{expected_size})&quot;
-          end
+        if result.size == expected_size
+          result
+        else
+          raise RecordNotFound, &quot;Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions} (found #{result.size} results, but was looking for #{expected_size})&quot;
         end
+      end
 
-        # Finder methods must instantiate through this method to work with the
-        # single-table inheritance model that makes it possible to create
-        # objects of different types from the same table.
-        def instantiate(record)
-          object =
-            if subclass_name = record[inheritance_column]
-              # No type given.
-              if subclass_name.empty?
-                allocate
+      # Finder methods must instantiate through this method to work with the
+      # single-table inheritance model that makes it possible to create
+      # objects of different types from the same table.
+      def instantiate(record)
+        object =
+          if subclass_name = record[inheritance_column]
+          # No type given.
+          if subclass_name.empty?
+            allocate
 
-              else
-                # Ignore type if no column is present since it was probably
-                # pulled in from a sloppy join.
-                unless columns_hash.include?(inheritance_column)
-                  allocate
-
-                else
-                  begin
-                    compute_type(subclass_name).allocate
-                  rescue NameError
-                    raise SubclassNotFound,
-                      &quot;The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. &quot; +
-                      &quot;This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. &quot; +
-                      &quot;Please rename this column if you didn't intend it to be used for storing the inheritance class &quot; +
-                      &quot;or overwrite #{self.to_s}.inheritance_column to use another column for that information.&quot;
-                  end
-                end
-              end
-            else
+          else
+            # Ignore type if no column is present since it was probably
+            # pulled in from a sloppy join.
+            unless columns_hash.include?(inheritance_column)
               allocate
-            end
-
-          object.instance_variable_set(&quot;@attributes&quot;, record)
-          object.instance_variable_set(&quot;@attributes_cache&quot;, Hash.new)
 
-          if object.respond_to_without_attributes?(:after_find)
-            object.send(:callback, :after_find)
+            else
+              begin
+                compute_type(subclass_name).allocate
+              rescue NameError
+                raise SubclassNotFound,
+                  &quot;The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. &quot; +
+                  &quot;This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. &quot; +
+                  &quot;Please rename this column if you didn't intend it to be used for storing the inheritance class &quot; +
+                  &quot;or overwrite #{self.to_s}.inheritance_column to use another column for that information.&quot;
+              end
+            end
           end
+        else
+          allocate
+        end
 
-          if object.respond_to_without_attributes?(:after_initialize)
-            object.send(:callback, :after_initialize)
-          end
+        object.instance_variable_set(&quot;@attributes&quot;, record)
+        object.instance_variable_set(&quot;@attributes_cache&quot;, Hash.new)
 
-          object
+        if object.respond_to_without_attributes?(:after_find)
+          object.send(:callback, :after_find)
         end
 
-        # Nest the type name in the same module as this class.
-        # Bar is &quot;MyApp::Business::Bar&quot; relative to MyApp::Business::Foo
-        def type_name_with_module(type_name)
-          if store_full_sti_class
-            type_name
-          else
-            (/^::/ =~ type_name) ? type_name : &quot;#{parent.name}::#{type_name}&quot;
-          end
+        if object.respond_to_without_attributes?(:after_initialize)
+          object.send(:callback, :after_initialize)
+        end
+
+        object
+      end
+
+      # Nest the type name in the same module as this class.
+      # Bar is &quot;MyApp::Business::Bar&quot; relative to MyApp::Business::Foo
+      def type_name_with_module(type_name)
+        if store_full_sti_class
+          type_name
+        else
+          (/^::/ =~ type_name) ? type_name : &quot;#{parent.name}::#{type_name}&quot;
         end
+      end
 
-        def construct_finder_sql(options)
-          scope = scope(:find)
-          sql  = &quot;SELECT #{options[:select] || (scope &amp;&amp; scope[:select]) || ((options[:joins] || (scope &amp;&amp; scope[:joins])) &amp;&amp; quoted_table_name + '.*') || '*'} &quot;
-          sql &lt;&lt; &quot;FROM #{(scope &amp;&amp; scope[:from]) || options[:from] || quoted_table_name} &quot;
+      def construct_finder_sql(options)
+        scope = scope(:find)
+        sql  = &quot;SELECT #{options[:select] || (scope &amp;&amp; scope[:select]) || ((options[:joins] || (scope &amp;&amp; scope[:joins])) &amp;&amp; quoted_table_name + '.*') || '*'} &quot;
+        sql &lt;&lt; &quot;FROM #{(scope &amp;&amp; scope[:from]) || options[:from] || quoted_table_name} &quot;
 
-          add_joins!(sql, options[:joins], scope)
-          add_conditions!(sql, options[:conditions], scope)
+        add_joins!(sql, options[:joins], scope)
+        add_conditions!(sql, options[:conditions], scope)
 
-          add_group!(sql, options[:group], scope)
-          add_order!(sql, options[:order], scope)
-          add_limit!(sql, options, scope)
-          add_lock!(sql, options, scope)
+        add_group!(sql, options[:group], scope)
+        add_order!(sql, options[:order], scope)
+        add_limit!(sql, options, scope)
+        add_lock!(sql, options, scope)
 
-          sql
-        end
+        sql
+      end
 
-        # Merges includes so that the result is a valid +include+
-        def merge_includes(first, second)
-         (safe_to_array(first) + safe_to_array(second)).uniq
-        end
+      # Merges includes so that the result is a valid +include+
+      def merge_includes(first, second)
+        (safe_to_array(first) + safe_to_array(second)).uniq
+      end
 
-        def merge_joins(first, second)
-          if first.is_a?(String) &amp;&amp; second.is_a?(String)
-            &quot;#{first} #{second}&quot;
-          elsif first.is_a?(String) || second.is_a?(String)
-            if first.is_a?(String)
-              join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, second, nil)
-              &quot;#{first} #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join}&quot;
-            else
-              join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, first, nil)
-              &quot;#{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} #{second}&quot;
-            end
+      def merge_joins(first, second)
+        if first.is_a?(String) &amp;&amp; second.is_a?(String)
+          &quot;#{first} #{second}&quot;
+        elsif first.is_a?(String) || second.is_a?(String)
+          if first.is_a?(String)
+            join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, second, nil)
+            &quot;#{first} #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join}&quot;
           else
-            (safe_to_array(first) + safe_to_array(second)).uniq
+            join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, first, nil)
+            &quot;#{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} #{second}&quot;
           end
+        else
+          (safe_to_array(first) + safe_to_array(second)).uniq
         end
+      end
 
-        # Object#to_a is deprecated, though it does have the desired behavior
-        def safe_to_array(o)
-          case o
-          when NilClass
-            []
-          when Array
-            o
-          else
-            [o]
-          end
+      # Object#to_a is deprecated, though it does have the desired behavior
+      def safe_to_array(o)
+        case o
+        when NilClass
+          []
+        when Array
+          o
+        else
+          [o]
         end
+      end
 
-        def add_order!(sql, order, scope = :auto)
+      def add_order!(sql, order, scope = :auto)
+        scope = scope(:find) if :auto == scope
+        scoped_order = scope[:order] if scope
+        if order
+          sql &lt;&lt; &quot; ORDER BY #{order}&quot;
+          sql &lt;&lt; &quot;, #{scoped_order}&quot; if scoped_order
+        else
+          sql &lt;&lt; &quot; ORDER BY #{scoped_order}&quot; if scoped_order
+        end
+      end
+
+      def add_group!(sql, group, scope = :auto)
+        if group
+          sql &lt;&lt; &quot; GROUP BY #{group}&quot;
+        else
           scope = scope(:find) if :auto == scope
-          scoped_order = scope[:order] if scope
-          if order
-            sql &lt;&lt; &quot; ORDER BY #{order}&quot;
-            sql &lt;&lt; &quot;, #{scoped_order}&quot; if scoped_order
-          else
-            sql &lt;&lt; &quot; ORDER BY #{scoped_order}&quot; if scoped_order
+          if scope &amp;&amp; (scoped_group = scope[:group])
+            sql &lt;&lt; &quot; GROUP BY #{scoped_group}&quot;
           end
         end
+      end
 
-        def add_group!(sql, group, scope = :auto)
-          if group
-            sql &lt;&lt; &quot; GROUP BY #{group}&quot;
-          else
-            scope = scope(:find) if :auto == scope
-            if scope &amp;&amp; (scoped_group = scope[:group])
-              sql &lt;&lt; &quot; GROUP BY #{scoped_group}&quot;
-            end
-          end
+      # The optional scope argument is for the current &lt;tt&gt;:find&lt;/tt&gt; scope.
+      def add_limit!(sql, options, scope = :auto)
+        scope = scope(:find) if :auto == scope
+
+        if scope
+          options[:limit] ||= scope[:limit]
+          options[:offset] ||= scope[:offset]
         end
 
-        # The optional scope argument is for the current &lt;tt&gt;:find&lt;/tt&gt; scope.
-        def add_limit!(sql, options, scope = :auto)
-          scope = scope(:find) if :auto == scope
+        connection.add_limit_offset!(sql, options)
+      end
 
-          if scope
-            options[:limit] ||= scope[:limit]
-            options[:offset] ||= scope[:offset]
-          end
+      # The optional scope argument is for the current &lt;tt&gt;:find&lt;/tt&gt; scope.
+      # The &lt;tt&gt;:lock&lt;/tt&gt; option has precedence over a scoped &lt;tt&gt;:lock&lt;/tt&gt;.
+      def add_lock!(sql, options, scope = :auto)
+        scope = scope(:find) if :auto == scope
+        options = options.reverse_merge(:lock =&gt; scope[:lock]) if scope
+        connection.add_lock!(sql, options)
+      end
 
-          connection.add_limit_offset!(sql, options)
+      # The optional scope argument is for the current &lt;tt&gt;:find&lt;/tt&gt; scope.
+      def add_joins!(sql, joins, scope = :auto)
+        scope = scope(:find) if :auto == scope
+        merged_joins = scope &amp;&amp; scope[:joins] &amp;&amp; joins ? merge_joins(scope[:joins], joins) : (joins || scope &amp;&amp; scope[:joins])
+        case merged_joins
+        when Symbol, Hash, Array
+          join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
+          sql &lt;&lt; &quot; #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} &quot;
+        when String
+          sql &lt;&lt; &quot; #{merged_joins} &quot;
         end
+      end
 
-        # The optional scope argument is for the current &lt;tt&gt;:find&lt;/tt&gt; scope.
-        # The &lt;tt&gt;:lock&lt;/tt&gt; option has precedence over a scoped &lt;tt&gt;:lock&lt;/tt&gt;.
-        def add_lock!(sql, options, scope = :auto)
-          scope = scope(:find) if :auto == scope
-          options = options.reverse_merge(:lock =&gt; scope[:lock]) if scope
-          connection.add_lock!(sql, options)
-        end
+      # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed.
+      # The optional scope argument is for the current &lt;tt&gt;:find&lt;/tt&gt; scope.
+      def add_conditions!(sql, conditions, scope = :auto)
+        scope = scope(:find) if :auto == scope
+        conditions = [conditions]
+        conditions &lt;&lt; scope[:conditions] if scope
+        conditions &lt;&lt; type_condition if finder_needs_type_condition?
+        merged_conditions = merge_conditions(*conditions)
+        sql &lt;&lt; &quot;WHERE #{merged_conditions} &quot; unless merged_conditions.blank?
+      end
 
-        # The optional scope argument is for the current &lt;tt&gt;:find&lt;/tt&gt; scope.
-        def add_joins!(sql, joins, scope = :auto)
-          scope = scope(:find) if :auto == scope
-          merged_joins = scope &amp;&amp; scope[:joins] &amp;&amp; joins ? merge_joins(scope[:joins], joins) : (joins || scope &amp;&amp; scope[:joins])
-          case merged_joins
-          when Symbol, Hash, Array
-            join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
-            sql &lt;&lt; &quot; #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} &quot;
-          when String
-            sql &lt;&lt; &quot; #{merged_joins} &quot;
-          end
+      def type_condition(table_alias=nil)
+        quoted_table_alias = self.connection.quote_table_name(table_alias || table_name)
+        quoted_inheritance_column = connection.quote_column_name(inheritance_column)
+        type_condition = subclasses.inject(&quot;#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' &quot;) do |condition, subclass|
+          condition &lt;&lt; &quot;OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' &quot;
         end
 
-        # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed.
-        # The optional scope argument is for the current &lt;tt&gt;:find&lt;/tt&gt; scope.
-        def add_conditions!(sql, conditions, scope = :auto)
-          scope = scope(:find) if :auto == scope
-          conditions = [conditions]
-          conditions &lt;&lt; scope[:conditions] if scope
-          conditions &lt;&lt; type_condition if finder_needs_type_condition?
-          merged_conditions = merge_conditions(*conditions)
-          sql &lt;&lt; &quot;WHERE #{merged_conditions} &quot; unless merged_conditions.blank?
-        end
-
-        def type_condition(table_alias=nil)
-          quoted_table_alias = self.connection.quote_table_name(table_alias || table_name)
-          quoted_inheritance_column = connection.quote_column_name(inheritance_column)
-          type_condition = subclasses.inject(&quot;#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' &quot;) do |condition, subclass|
-            condition &lt;&lt; &quot;OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' &quot;
-          end
+        &quot; (#{type_condition}) &quot;
+      end
 
-          &quot; (#{type_condition}) &quot;
-        end
-
-        # Guesses the table name, but does not decorate it with prefix and suffix information.
-        def undecorated_table_name(class_name = base_class.name)
-          table_name = class_name.to_s.demodulize.underscore
-          table_name = table_name.pluralize if pluralize_table_names
-          table_name
-        end
-
-        # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into
-        # find(:first, :conditions =&gt; [&quot;user_name = ?&quot;, user_name]) and  find(:first, :conditions =&gt; [&quot;user_name = ? AND password = ?&quot;, user_name, password])
-        # respectively. Also works for find(:all) by using find_all_by_amount(50) that is turned into find(:all, :conditions =&gt; [&quot;amount = ?&quot;, 50]).
-        #
-        # It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
-        # is actually find_all_by_amount(amount, options).
-        #
-        # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
-        # or find_or_create_by_user_and_password(user, password).
-        #
-        # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
-        # attempts to use it do not run through method_missing.
-        def method_missing(method_id, *arguments)
-          if match = DynamicFinderMatch.match(method_id)
-            attribute_names = match.attribute_names
-            super unless all_attributes_exists?(attribute_names)
-            if match.finder?
-              finder = match.finder
-              bang = match.bang?
-              self.class_eval %{
+      # Guesses the table name, but does not decorate it with prefix and suffix information.
+      def undecorated_table_name(class_name = base_class.name)
+        table_name = class_name.to_s.demodulize.underscore
+        table_name = table_name.pluralize if pluralize_table_names
+        table_name
+      end
+
+      # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into
+      # find(:first, :conditions =&gt; [&quot;user_name = ?&quot;, user_name]) and  find(:first, :conditions =&gt; [&quot;user_name = ? AND password = ?&quot;, user_name, password])
+      # respectively. Also works for find(:all) by using find_all_by_amount(50) that is turned into find(:all, :conditions =&gt; [&quot;amount = ?&quot;, 50]).
+      #
+      # It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
+      # is actually find_all_by_amount(amount, options).
+      #
+      # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
+      # or find_or_create_by_user_and_password(user, password).
+      #
+      # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
+      # attempts to use it do not run through method_missing.
+      def method_missing(method_id, *arguments)
+        if match = DynamicFinderMatch.match(method_id)
+          attribute_names = match.attribute_names
+          super unless all_attributes_exists?(attribute_names)
+          if match.finder?
+            finder = match.finder
+            bang = match.bang?
+            self.class_eval %{
                 def self.#{method_id}(*args)
                   options = args.extract_options!
                   attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
@@ -1708,11 +1717,11 @@ module ActiveRecord #:nodoc:
                   end
                   #{'result || raise(RecordNotFound)' if bang}
                 end
-              }, __FILE__, __LINE__
-              send(method_id, *arguments)
-            elsif match.instantiator?
-              instantiator = match.instantiator
-              self.class_eval %{
+            }, __FILE__, __LINE__
+            send(method_id, *arguments)
+          elsif match.instantiator?
+            instantiator = match.instantiator
+            self.class_eval %{
                 def self.#{method_id}(*args)
                   guard_protected_attributes = false
 
@@ -1738,1090 +1747,1093 @@ module ActiveRecord #:nodoc:
                     record
                   end
                 end
-              }, __FILE__, __LINE__
-              send(method_id, *arguments)
-            end
-          else
-            super
+            }, __FILE__, __LINE__
+            send(method_id, *arguments)
           end
+        else
+          super
         end
+      end
 
-        def construct_attributes_from_arguments(attribute_names, arguments)
-          attributes = {}
-          attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
-          attributes
-        end
+      def construct_attributes_from_arguments(attribute_names, arguments)
+        attributes = {}
+        attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
+        attributes
+      end
 
-        # Similar in purpose to +expand_hash_conditions_for_aggregates+.
-        def expand_attribute_names_for_aggregates(attribute_names)
-          expanded_attribute_names = []
-          attribute_names.each do |attribute_name|
-            unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
-              aggregate_mapping(aggregation).each do |field_attr, aggregate_attr|
-                expanded_attribute_names &lt;&lt; field_attr
-              end
-            else
-              expanded_attribute_names &lt;&lt; attribute_name
+      # Similar in purpose to +expand_hash_conditions_for_aggregates+.
+      def expand_attribute_names_for_aggregates(attribute_names)
+        expanded_attribute_names = []
+        attribute_names.each do |attribute_name|
+          unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
+            aggregate_mapping(aggregation).each do |field_attr, aggregate_attr|
+              expanded_attribute_names &lt;&lt; field_attr
             end
+          else
+            expanded_attribute_names &lt;&lt; attribute_name
           end
-          expanded_attribute_names
         end
+        expanded_attribute_names
+      end
 
-        def all_attributes_exists?(attribute_names)
-          attribute_names = expand_attribute_names_for_aggregates(attribute_names)
-          attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
-        end
+      def all_attributes_exists?(attribute_names)
+        attribute_names = expand_attribute_names_for_aggregates(attribute_names)
+        attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
+      end
 
-        def attribute_condition(argument)
-          case argument
-            when nil   then &quot;IS ?&quot;
-            when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then &quot;IN (?)&quot;
-            when Range then &quot;BETWEEN ? AND ?&quot;
-            else            &quot;= ?&quot;
-          end
+      def attribute_condition(argument)
+        case argument
+        when nil   then &quot;IS ?&quot;
+        when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then &quot;IN (?)&quot;
+        when Range then &quot;BETWEEN ? AND ?&quot;
+        else            &quot;= ?&quot;
         end
+      end
 
-        # Interpret Array and Hash as conditions and anything else as an id.
-        def expand_id_conditions(id_or_conditions)
-          case id_or_conditions
-            when Array, Hash then id_or_conditions
-            else sanitize_sql(primary_key =&gt; id_or_conditions)
-          end
+      # Interpret Array and Hash as conditions and anything else as an id.
+      def expand_id_conditions(id_or_conditions)
+        case id_or_conditions
+        when Array, Hash then id_or_conditions
+        else sanitize_sql(primary_key =&gt; id_or_conditions)
         end
+      end
 
 
-        # Defines an &quot;attribute&quot; method (like +inheritance_column+ or
-        # +table_name+). A new (class) method will be created with the
-        # given name. If a value is specified, the new method will
-        # return that value (as a string). Otherwise, the given block
-        # will be used to compute the value of the method.
-        #
-        # The original method will be aliased, with the new name being
-        # prefixed with &quot;original_&quot;. This allows the new method to
-        # access the original value.
-        #
-        # Example:
-        #
-        #   class A &lt; ActiveRecord::Base
-        #     define_attr_method :primary_key, &quot;sysid&quot;
-        #     define_attr_method( :inheritance_column ) do
-        #       original_inheritance_column + &quot;_id&quot;
-        #     end
-        #   end
-        def define_attr_method(name, value=nil, &amp;block)
-          sing = class &lt;&lt; self; self; end
-          sing.send :alias_method, &quot;original_#{name}&quot;, name
-          if block_given?
-            sing.send :define_method, name, &amp;block
-          else
-            # use eval instead of a block to work around a memory leak in dev
-            # mode in fcgi
-            sing.class_eval &quot;def #{name}; #{value.to_s.inspect}; end&quot;
-          end
+      # Defines an &quot;attribute&quot; method (like +inheritance_column+ or
+      # +table_name+). A new (class) method will be created with the
+      # given name. If a value is specified, the new method will
+      # return that value (as a string). Otherwise, the given block
+      # will be used to compute the value of the method.
+      #
+      # The original method will be aliased, with the new name being
+      # prefixed with &quot;original_&quot;. This allows the new method to
+      # access the original value.
+      #
+      # Example:
+      #
+      #   class A &lt; ActiveRecord::Base
+      #     define_attr_method :primary_key, &quot;sysid&quot;
+      #     define_attr_method( :inheritance_column ) do
+      #       original_inheritance_column + &quot;_id&quot;
+      #     end
+      #   end
+      def define_attr_method(name, value=nil, &amp;block)
+        sing = class &lt;&lt; self; self; end
+        sing.send :alias_method, &quot;original_#{name}&quot;, name
+        if block_given?
+          sing.send :define_method, name, &amp;block
+        else
+          # use eval instead of a block to work around a memory leak in dev
+          # mode in fcgi
+          sing.class_eval &quot;def #{name}; #{value.to_s.inspect}; end&quot;
         end
+      end
 
       protected
-        # Scope parameters to method calls within the block.  Takes a hash of method_name =&gt; parameters hash.
-        # method_name may be &lt;tt&gt;:find&lt;/tt&gt; or &lt;tt&gt;:create&lt;/tt&gt;. &lt;tt&gt;:find&lt;/tt&gt; parameters may include the &lt;tt&gt;:conditions&lt;/tt&gt;, &lt;tt&gt;:joins&lt;/tt&gt;,
-        # &lt;tt&gt;:include&lt;/tt&gt;, &lt;tt&gt;:offset&lt;/tt&gt;, &lt;tt&gt;:limit&lt;/tt&gt;, and &lt;tt&gt;:readonly&lt;/tt&gt; options. &lt;tt&gt;:create&lt;/tt&gt; parameters are an attributes hash.
-        #
-        #   class Article &lt; ActiveRecord::Base
-        #     def self.create_with_scope
-        #       with_scope(:find =&gt; { :conditions =&gt; &quot;blog_id = 1&quot; }, :create =&gt; { :blog_id =&gt; 1 }) do
-        #         find(1) # =&gt; SELECT * from articles WHERE blog_id = 1 AND id = 1
-        #         a = create(1)
-        #         a.blog_id # =&gt; 1
-        #       end
-        #     end
-        #   end
-        #
-        # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
-        # &lt;tt&gt;:conditions&lt;/tt&gt; and &lt;tt&gt;:include&lt;/tt&gt; options in &lt;tt&gt;:find&lt;/tt&gt;, which are merged.
-        #
-        #   class Article &lt; ActiveRecord::Base
-        #     def self.find_with_scope
-        #       with_scope(:find =&gt; { :conditions =&gt; &quot;blog_id = 1&quot;, :limit =&gt; 1 }, :create =&gt; { :blog_id =&gt; 1 }) do
-        #         with_scope(:find =&gt; { :limit =&gt; 10 })
-        #           find(:all) # =&gt; SELECT * from articles WHERE blog_id = 1 LIMIT 10
-        #         end
-        #         with_scope(:find =&gt; { :conditions =&gt; &quot;author_id = 3&quot; })
-        #           find(:all) # =&gt; SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
-        #         end
-        #       end
-        #     end
-        #   end
-        #
-        # You can ignore any previous scopings by using the &lt;tt&gt;with_exclusive_scope&lt;/tt&gt; method.
-        #
-        #   class Article &lt; ActiveRecord::Base
-        #     def self.find_with_exclusive_scope
-        #       with_scope(:find =&gt; { :conditions =&gt; &quot;blog_id = 1&quot;, :limit =&gt; 1 }) do
-        #         with_exclusive_scope(:find =&gt; { :limit =&gt; 10 })
-        #           find(:all) # =&gt; SELECT * from articles LIMIT 10
-        #         end
-        #       end
-        #     end
-        #   end
-        def with_scope(method_scoping = {}, action = :merge, &amp;block)
-          method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
-
-          # Dup first and second level of hash (method and params).
-          method_scoping = method_scoping.inject({}) do |hash, (method, params)|
-            hash[method] = (params == true) ? params : params.dup
-            hash
-          end
-
-          method_scoping.assert_valid_keys([ :find, :create ])
-
-          if f = method_scoping[:find]
-            f.assert_valid_keys(VALID_FIND_OPTIONS)
-            set_readonly_option! f
-          end
-
-          # Merge scopings
-          if action == :merge &amp;&amp; current_scoped_methods
-            method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
-              case hash[method]
-                when Hash
-                  if method == :find
-                    (hash[method].keys + params.keys).uniq.each do |key|
-                      merge = hash[method][key] &amp;&amp; params[key] # merge if both scopes have the same key
-                      if key == :conditions &amp;&amp; merge
-                        hash[method][key] = merge_conditions(params[key], hash[method][key])
-                      elsif key == :include &amp;&amp; merge
-                        hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
-                      elsif key == :joins &amp;&amp; merge
-                        hash[method][key] = merge_joins(params[key], hash[method][key])
-                      else
-                        hash[method][key] = hash[method][key] || params[key]
-                      end
-                    end
+      # Scope parameters to method calls within the block.  Takes a hash of method_name =&gt; parameters hash.
+      # method_name may be &lt;tt&gt;:find&lt;/tt&gt; or &lt;tt&gt;:create&lt;/tt&gt;. &lt;tt&gt;:find&lt;/tt&gt; parameters may include the &lt;tt&gt;:conditions&lt;/tt&gt;, &lt;tt&gt;:joins&lt;/tt&gt;,
+      # &lt;tt&gt;:include&lt;/tt&gt;, &lt;tt&gt;:offset&lt;/tt&gt;, &lt;tt&gt;:limit&lt;/tt&gt;, and &lt;tt&gt;:readonly&lt;/tt&gt; options. &lt;tt&gt;:create&lt;/tt&gt; parameters are an attributes hash.
+      #
+      #   class Article &lt; ActiveRecord::Base
+      #     def self.create_with_scope
+      #       with_scope(:find =&gt; { :conditions =&gt; &quot;blog_id = 1&quot; }, :create =&gt; { :blog_id =&gt; 1 }) do
+      #         find(1) # =&gt; SELECT * from articles WHERE blog_id = 1 AND id = 1
+      #         a = create(1)
+      #         a.blog_id # =&gt; 1
+      #       end
+      #     end
+      #   end
+      #
+      # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
+      # &lt;tt&gt;:conditions&lt;/tt&gt; and &lt;tt&gt;:include&lt;/tt&gt; options in &lt;tt&gt;:find&lt;/tt&gt;, which are merged.
+      #
+      #   class Article &lt; ActiveRecord::Base
+      #     def self.find_with_scope
+      #       with_scope(:find =&gt; { :conditions =&gt; &quot;blog_id = 1&quot;, :limit =&gt; 1 }, :create =&gt; { :blog_id =&gt; 1 }) do
+      #         with_scope(:find =&gt; { :limit =&gt; 10 })
+      #           find(:all) # =&gt; SELECT * from articles WHERE blog_id = 1 LIMIT 10
+      #         end
+      #         with_scope(:find =&gt; { :conditions =&gt; &quot;author_id = 3&quot; })
+      #           find(:all) # =&gt; SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
+      #         end
+      #       end
+      #     end
+      #   end
+      #
+      # You can ignore any previous scopings by using the &lt;tt&gt;with_exclusive_scope&lt;/tt&gt; method.
+      #
+      #   class Article &lt; ActiveRecord::Base
+      #     def self.find_with_exclusive_scope
+      #       with_scope(:find =&gt; { :conditions =&gt; &quot;blog_id = 1&quot;, :limit =&gt; 1 }) do
+      #         with_exclusive_scope(:find =&gt; { :limit =&gt; 10 })
+      #           find(:all) # =&gt; SELECT * from articles LIMIT 10
+      #         end
+      #       end
+      #     end
+      #   end
+      def with_scope(method_scoping = {}, action = :merge, &amp;block)
+        method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
+
+        # Dup first and second level of hash (method and params).
+        method_scoping = method_scoping.inject({}) do |hash, (method, params)|
+          hash[method] = (params == true) ? params : params.dup
+          hash
+        end
+
+        method_scoping.assert_valid_keys([ :find, :create ])
+
+        if f = method_scoping[:find]
+          f.assert_valid_keys(VALID_FIND_OPTIONS)
+          set_readonly_option! f
+        end
+
+        # Merge scopings
+        if action == :merge &amp;&amp; current_scoped_methods
+          method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
+            case hash[method]
+            when Hash
+              if method == :find
+                (hash[method].keys + params.keys).uniq.each do |key|
+                  merge = hash[method][key] &amp;&amp; params[key] # merge if both scopes have the same key
+                  if key == :conditions &amp;&amp; merge
+                    hash[method][key] = merge_conditions(params[key], hash[method][key])
+                  elsif key == :include &amp;&amp; merge
+                    hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
+                  elsif key == :joins &amp;&amp; merge
+                    hash[method][key] = merge_joins(params[key], hash[method][key])
                   else
-                    hash[method] = params.merge(hash[method])
+                    hash[method][key] = hash[method][key] || params[key]
                   end
-                else
-                  hash[method] = params
+                end
+              else
+                hash[method] = params.merge(hash[method])
               end
-              hash
+            else
+              hash[method] = params
             end
+            hash
           end
+        end
 
-          self.scoped_methods &lt;&lt; method_scoping
+        self.scoped_methods &lt;&lt; method_scoping
 
-          begin
-            yield
-          ensure
-            self.scoped_methods.pop
-          end
+        begin
+          yield
+        ensure
+          self.scoped_methods.pop
         end
+      end
 
-        # Works like with_scope, but discards any nested properties.
-        def with_exclusive_scope(method_scoping = {}, &amp;block)
-          with_scope(method_scoping, :overwrite, &amp;block)
-        end
+      # Works like with_scope, but discards any nested properties.
+      def with_exclusive_scope(method_scoping = {}, &amp;block)
+        with_scope(method_scoping, :overwrite, &amp;block)
+      end
 
-        def subclasses #:nodoc:
-          @@subclasses[self] ||= []
-          @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
-        end
+      def subclasses #:nodoc:
+        @@subclasses[self] ||= []
+        @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
+      end
 
-        # Test whether the given method and optional key are scoped.
-        def scoped?(method, key = nil) #:nodoc:
-          if current_scoped_methods &amp;&amp; (scope = current_scoped_methods[method])
-            !key || scope.has_key?(key)
-          end
+      # Test whether the given method and optional key are scoped.
+      def scoped?(method, key = nil) #:nodoc:
+        if current_scoped_methods &amp;&amp; (scope = current_scoped_methods[method])
+          !key || scope.has_key?(key)
         end
+      end
 
-        # Retrieve the scope for the given method and optional key.
-        def scope(method, key = nil) #:nodoc:
-          if current_scoped_methods &amp;&amp; (scope = current_scoped_methods[method])
-            key ? scope[key] : scope
-          end
+      # Retrieve the scope for the given method and optional key.
+      def scope(method, key = nil) #:nodoc:
+        if current_scoped_methods &amp;&amp; (scope = current_scoped_methods[method])
+          key ? scope[key] : scope
         end
+      end
 
-        def scoped_methods #:nodoc:
-          scoped_methods = (Thread.current[:scoped_methods] ||= {})
-          scoped_methods[self] ||= []
-        end
+      def scoped_methods #:nodoc:
+        scoped_methods = (Thread.current[:scoped_methods] ||= {})
+        scoped_methods[self] ||= []
+      end
 
-        def current_scoped_methods #:nodoc:
-          scoped_methods.last
-        end
+      def current_scoped_methods #:nodoc:
+        scoped_methods.last
+      end
 
-        # Returns the class type of the record using the current module as a prefix. So descendents of
-        # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
-        def compute_type(type_name)
-          modularized_name = type_name_with_module(type_name)
-          silence_warnings do
-            begin
-              class_eval(modularized_name, __FILE__, __LINE__)
-            rescue NameError
-              class_eval(type_name, __FILE__, __LINE__)
-            end
+      # Returns the class type of the record using the current module as a prefix. So descendents of
+      # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
+      def compute_type(type_name)
+        modularized_name = type_name_with_module(type_name)
+        silence_warnings do
+          begin
+            class_eval(modularized_name, __FILE__, __LINE__)
+          rescue NameError
+            class_eval(type_name, __FILE__, __LINE__)
           end
         end
+      end
 
-        # Returns the class descending directly from Active Record in the inheritance hierarchy.
-        def class_of_active_record_descendant(klass)
-          if klass.superclass == Base || klass.superclass.abstract_class?
-            klass
-          elsif klass.superclass.nil?
-            raise ActiveRecordError, &quot;#{name} doesn't belong in a hierarchy descending from ActiveRecord&quot;
-          else
-            class_of_active_record_descendant(klass.superclass)
-          end
+      # Returns the class descending directly from Active Record in the inheritance hierarchy.
+      def class_of_active_record_descendant(klass)
+        if klass.superclass == Base || klass.superclass.abstract_class?
+          klass
+        elsif klass.superclass.nil?
+          raise ActiveRecordError, &quot;#{name} doesn't belong in a hierarchy descending from ActiveRecord&quot;
+        else
+          class_of_active_record_descendant(klass.superclass)
         end
+      end
 
-        # Returns the name of the class descending directly from Active Record in the inheritance hierarchy.
-        def class_name_of_active_record_descendant(klass) #:nodoc:
-          klass.base_class.name
-        end
+      # Returns the name of the class descending directly from Active Record in the inheritance hierarchy.
+      def class_name_of_active_record_descendant(klass) #:nodoc:
+        klass.base_class.name
+      end
 
-        # Accepts an array, hash, or string of SQL conditions and sanitizes
-        # them into a valid SQL fragment for a WHERE clause.
-        #   [&quot;name='%s' and group_id='%s'&quot;, &quot;foo'bar&quot;, 4]  returns  &quot;name='foo''bar' and group_id='4'&quot;
-        #   { :name =&gt; &quot;foo'bar&quot;, :group_id =&gt; 4 }  returns &quot;name='foo''bar' and group_id='4'&quot;
-        #   &quot;name='foo''bar' and group_id='4'&quot; returns &quot;name='foo''bar' and group_id='4'&quot;
-        def sanitize_sql_for_conditions(condition)
-          return nil if condition.blank?
+      # Accepts an array, hash, or string of SQL conditions and sanitizes
+      # them into a valid SQL fragment for a WHERE clause.
+      #   [&quot;name='%s' and group_id='%s'&quot;, &quot;foo'bar&quot;, 4]  returns  &quot;name='foo''bar' and group_id='4'&quot;
+      #   { :name =&gt; &quot;foo'bar&quot;, :group_id =&gt; 4 }  returns &quot;name='foo''bar' and group_id='4'&quot;
+      #   &quot;name='foo''bar' and group_id='4'&quot; returns &quot;name='foo''bar' and group_id='4'&quot;
+      def sanitize_sql_for_conditions(condition)
+        return nil if condition.blank?
 
-          case condition
-            when Array; sanitize_sql_array(condition)
-            when Hash;  sanitize_sql_hash_for_conditions(condition)
-            else        condition
-          end
+        case condition
+        when Array; sanitize_sql_array(condition)
+        when Hash;  sanitize_sql_hash_for_conditions(condition)
+        else        condition
         end
-        alias_method :sanitize_sql, :sanitize_sql_for_conditions
+      end
+      alias_method :sanitize_sql, :sanitize_sql_for_conditions
 
-        # Accepts an array, hash, or string of SQL conditions and sanitizes
-        # them into a valid SQL fragment for a SET clause.
-        #   { :name =&gt; nil, :group_id =&gt; 4 }  returns &quot;name = NULL , group_id='4'&quot;
-        def sanitize_sql_for_assignment(assignments)
-          case assignments
-            when Array; sanitize_sql_array(assignments)
-            when Hash;  sanitize_sql_hash_for_assignment(assignments)
-            else        assignments
-          end
+      # Accepts an array, hash, or string of SQL conditions and sanitizes
+      # them into a valid SQL fragment for a SET clause.
+      #   { :name =&gt; nil, :group_id =&gt; 4 }  returns &quot;name = NULL , group_id='4'&quot;
+      def sanitize_sql_for_assignment(assignments)
+        case assignments
+        when Array; sanitize_sql_array(assignments)
+        when Hash;  sanitize_sql_hash_for_assignment(assignments)
+        else        assignments
         end
+      end
 
-        def aggregate_mapping(reflection)
-          mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
-          mapping.first.is_a?(Array) ? mapping : [mapping]
-        end
-
-        # Accepts a hash of SQL conditions and replaces those attributes
-        # that correspond to a +composed_of+ relationship with their expanded
-        # aggregate attribute values.
-        # Given:
-        #     class Person &lt; ActiveRecord::Base
-        #       composed_of :address, :class_name =&gt; &quot;Address&quot;,
-        #         :mapping =&gt; [%w(address_street street), %w(address_city city)]
-        #     end
-        # Then:
-        #     { :address =&gt; Address.new(&quot;813 abc st.&quot;, &quot;chicago&quot;) }
-        #       # =&gt; { :address_street =&gt; &quot;813 abc st.&quot;, :address_city =&gt; &quot;chicago&quot; }
-        def expand_hash_conditions_for_aggregates(attrs)
-          expanded_attrs = {}
-          attrs.each do |attr, value|
-            unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
-              mapping = aggregate_mapping(aggregation)
-              mapping.each do |field_attr, aggregate_attr|
-                if mapping.size == 1 &amp;&amp; !value.respond_to?(aggregate_attr)
-                  expanded_attrs[field_attr] = value
-                else
-                  expanded_attrs[field_attr] = value.send(aggregate_attr)
-                end
+      def aggregate_mapping(reflection)
+        mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
+        mapping.first.is_a?(Array) ? mapping : [mapping]
+      end
+
+      # Accepts a hash of SQL conditions and replaces those attributes
+      # that correspond to a +composed_of+ relationship with their expanded
+      # aggregate attribute values.
+      # Given:
+      #     class Person &lt; ActiveRecord::Base
+      #       composed_of :address, :class_name =&gt; &quot;Address&quot;,
+      #         :mapping =&gt; [%w(address_street street), %w(address_city city)]
+      #     end
+      # Then:
+      #     { :address =&gt; Address.new(&quot;813 abc st.&quot;, &quot;chicago&quot;) }
+      #       # =&gt; { :address_street =&gt; &quot;813 abc st.&quot;, :address_city =&gt; &quot;chicago&quot; }
+      def expand_hash_conditions_for_aggregates(attrs)
+        expanded_attrs = {}
+        attrs.each do |attr, value|
+          unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
+            mapping = aggregate_mapping(aggregation)
+            mapping.each do |field_attr, aggregate_attr|
+              if mapping.size == 1 &amp;&amp; !value.respond_to?(aggregate_attr)
+                expanded_attrs[field_attr] = value
+              else
+                expanded_attrs[field_attr] = value.send(aggregate_attr)
               end
-            else
-              expanded_attrs[attr] = value
             end
+          else
+            expanded_attrs[attr] = value
           end
-          expanded_attrs
-        end
-
-        # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
-        #   { :name =&gt; &quot;foo'bar&quot;, :group_id =&gt; 4 }
-        #     # =&gt; &quot;name='foo''bar' and group_id= 4&quot;
-        #   { :status =&gt; nil, :group_id =&gt; [1,2,3] }
-        #     # =&gt; &quot;status IS NULL and group_id IN (1,2,3)&quot;
-        #   { :age =&gt; 13..18 }
-        #     # =&gt; &quot;age BETWEEN 13 AND 18&quot;
-        #   { 'other_records.id' =&gt; 7 }
-        #     # =&gt; &quot;`other_records`.`id` = 7&quot;
-        #   { :other_records =&gt; { :id =&gt; 7 } }
-        #     # =&gt; &quot;`other_records`.`id` = 7&quot;
-        # And for value objects on a composed_of relationship:
-        #   { :address =&gt; Address.new(&quot;123 abc st.&quot;, &quot;chicago&quot;) }
-        #     # =&gt; &quot;address_street='123 abc st.' and address_city='chicago'&quot;
-        def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name)
-          attrs = expand_hash_conditions_for_aggregates(attrs)
-
-          conditions = attrs.map do |attr, value|
-            unless value.is_a?(Hash)
-              attr = attr.to_s
-
-              # Extract table name from qualified attribute names.
-              if attr.include?('.')
-                table_name, attr = attr.split('.', 2)
-                table_name = connection.quote_table_name(table_name)
-              end
-
-              &quot;#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}&quot;
-            else
-              sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
+        end
+        expanded_attrs
+      end
+
+      # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
+      #   { :name =&gt; &quot;foo'bar&quot;, :group_id =&gt; 4 }
+      #     # =&gt; &quot;name='foo''bar' and group_id= 4&quot;
+      #   { :status =&gt; nil, :group_id =&gt; [1,2,3] }
+      #     # =&gt; &quot;status IS NULL and group_id IN (1,2,3)&quot;
+      #   { :age =&gt; 13..18 }
+      #     # =&gt; &quot;age BETWEEN 13 AND 18&quot;
+      #   { 'other_records.id' =&gt; 7 }
+      #     # =&gt; &quot;`other_records`.`id` = 7&quot;
+      #   { :other_records =&gt; { :id =&gt; 7 } }
+      #     # =&gt; &quot;`other_records`.`id` = 7&quot;
+      # And for value objects on a composed_of relationship:
+      #   { :address =&gt; Address.new(&quot;123 abc st.&quot;, &quot;chicago&quot;) }
+      #     # =&gt; &quot;address_street='123 abc st.' and address_city='chicago'&quot;
+      def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name)
+        attrs = expand_hash_conditions_for_aggregates(attrs)
+
+        conditions = attrs.map do |attr, value|
+          unless value.is_a?(Hash)
+            attr = attr.to_s
+
+            # Extract table name from qualified attribute names.
+            if attr.include?('.')
+              table_name, attr = attr.split('.', 2)
+              table_name = connection.quote_table_name(table_name)
             end
-          end.join(' AND ')
-
-          replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))
-        end
-        alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
-
-        # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
-        #   { :status =&gt; nil, :group_id =&gt; 1 }
-        #     # =&gt; &quot;status = NULL , group_id = 1&quot;
-        def sanitize_sql_hash_for_assignment(attrs)
-          attrs.map do |attr, value|
-            &quot;#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}&quot;
-          end.join(', ')
-        end
-
-        # Accepts an array of conditions.  The array has each value
-        # sanitized and interpolated into the SQL statement.
-        #   [&quot;name='%s' and group_id='%s'&quot;, &quot;foo'bar&quot;, 4]  returns  &quot;name='foo''bar' and group_id='4'&quot;
-        def sanitize_sql_array(ary)
-          statement, *values = ary
-          if values.first.is_a?(Hash) and statement =~ /:\w+/
-            replace_named_bind_variables(statement, values.first)
-          elsif statement.include?('?')
-            replace_bind_variables(statement, values)
+
+            &quot;#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}&quot;
           else
-            statement % values.collect { |value| connection.quote_string(value.to_s) }
+            sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
           end
+        end.join(' AND ')
+
+        replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))
+      end
+      alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
+
+      # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
+      #   { :status =&gt; nil, :group_id =&gt; 1 }
+      #     # =&gt; &quot;status = NULL , group_id = 1&quot;
+      def sanitize_sql_hash_for_assignment(attrs)
+        attrs.map do |attr, value|
+          &quot;#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}&quot;
+        end.join(', ')
+      end
+
+      # Accepts an array of conditions.  The array has each value
+      # sanitized and interpolated into the SQL statement.
+      #   [&quot;name='%s' and group_id='%s'&quot;, &quot;foo'bar&quot;, 4]  returns  &quot;name='foo''bar' and group_id='4'&quot;
+      def sanitize_sql_array(ary)
+        statement, *values = ary
+        if values.first.is_a?(Hash) and statement =~ /:\w+/
+          replace_named_bind_variables(statement, values.first)
+        elsif statement.include?('?')
+          replace_bind_variables(statement, values)
+        else
+          statement % values.collect { |value| connection.quote_string(value.to_s) }
         end
+      end
 
-        alias_method :sanitize_conditions, :sanitize_sql
+      alias_method :sanitize_conditions, :sanitize_sql
 
-        def replace_bind_variables(statement, values) #:nodoc:
-          raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
-          bound = values.dup
-          statement.gsub('?') { quote_bound_value(bound.shift) }
-        end
+      def replace_bind_variables(statement, values) #:nodoc:
+        raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
+        bound = values.dup
+        statement.gsub('?') { quote_bound_value(bound.shift) }
+      end
 
-        def replace_named_bind_variables(statement, bind_vars) #:nodoc:
-          statement.gsub(/(:?):([a-zA-Z]\w*)/) do
-            if $1 == ':' # skip postgresql casts
-              $&amp; # return the whole match
-            elsif bind_vars.include?(match = $2.to_sym)
-              quote_bound_value(bind_vars[match])
-            else
-              raise PreparedStatementInvalid, &quot;missing value for :#{match} in #{statement}&quot;
-            end
+      def replace_named_bind_variables(statement, bind_vars) #:nodoc:
+        statement.gsub(/(:?):([a-zA-Z]\w*)/) do
+          if $1 == ':' # skip postgresql casts
+            $&amp; # return the whole match
+          elsif bind_vars.include?(match = $2.to_sym)
+            quote_bound_value(bind_vars[match])
+          else
+            raise PreparedStatementInvalid, &quot;missing value for :#{match} in #{statement}&quot;
           end
         end
+      end
 
-        def expand_range_bind_variables(bind_vars) #:nodoc:
-          expanded = []
+      def expand_range_bind_variables(bind_vars) #:nodoc:
+        expanded = []
 
-          bind_vars.each do |var|
-            next if var.is_a?(Hash)
+        bind_vars.each do |var|
+          next if var.is_a?(Hash)
 
-            if var.is_a?(Range)
-              expanded &lt;&lt; var.first
-              expanded &lt;&lt; var.last
-            else
-              expanded &lt;&lt; var
-            end
+          if var.is_a?(Range)
+            expanded &lt;&lt; var.first
+            expanded &lt;&lt; var.last
+          else
+            expanded &lt;&lt; var
           end
-
-          expanded
         end
 
-        def quote_bound_value(value) #:nodoc:
-          if value.respond_to?(:map) &amp;&amp; !value.is_a?(String)
-            if value.respond_to?(:empty?) &amp;&amp; value.empty?
-              connection.quote(nil)
-            else
-              value.map { |v| connection.quote(v) }.join(',')
-            end
+        expanded
+      end
+
+      def quote_bound_value(value) #:nodoc:
+        if value.respond_to?(:map) &amp;&amp; !value.is_a?(String)
+          if value.respond_to?(:empty?) &amp;&amp; value.empty?
+            connection.quote(nil)
           else
-            connection.quote(value)
+            value.map { |v| connection.quote(v) }.join(',')
           end
+        else
+          connection.quote(value)
         end
+      end
 
-        def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
-          unless expected == provided
-            raise PreparedStatementInvalid, &quot;wrong number of bind variables (#{provided} for #{expected}) in: #{statement}&quot;
-          end
+      def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
+        unless expected == provided
+          raise PreparedStatementInvalid, &quot;wrong number of bind variables (#{provided} for #{expected}) in: #{statement}&quot;
         end
+      end
 
-        VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,
-                               :order, :select, :readonly, :group, :from, :lock ]
+      VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,
+        :order, :select, :readonly, :group, :from, :lock ]
 
-        def validate_find_options(options) #:nodoc:
-          options.assert_valid_keys(VALID_FIND_OPTIONS)
-        end
+      def validate_find_options(options) #:nodoc:
+        options.assert_valid_keys(VALID_FIND_OPTIONS)
+      end
 
-        def set_readonly_option!(options) #:nodoc:
-          # Inherit :readonly from finder scope if set.  Otherwise,
-          # if :joins is not blank then :readonly defaults to true.
-          unless options.has_key?(:readonly)
-            if scoped_readonly = scope(:find, :readonly)
-              options[:readonly] = scoped_readonly
-            elsif !options[:joins].blank? &amp;&amp; !options[:select]
-              options[:readonly] = true
-            end
+      def set_readonly_option!(options) #:nodoc:
+        # Inherit :readonly from finder scope if set.  Otherwise,
+        # if :joins is not blank then :readonly defaults to true.
+        unless options.has_key?(:readonly)
+          if scoped_readonly = scope(:find, :readonly)
+            options[:readonly] = scoped_readonly
+          elsif !options[:joins].blank? &amp;&amp; !options[:select]
+            options[:readonly] = true
           end
         end
+      end
 
-        def encode_quoted_value(value) #:nodoc:
-          quoted_value = connection.quote(value)
-          quoted_value = &quot;'#{quoted_value[1..-2].gsub(/\'/, &quot;\\\\'&quot;)}'&quot; if quoted_value.include?(&quot;\\\'&quot;) # (for ruby mode) &quot;
-          quoted_value
-        end
+      def encode_quoted_value(value) #:nodoc:
+        quoted_value = connection.quote(value)
+        quoted_value = &quot;'#{quoted_value[1..-2].gsub(/\'/, &quot;\\\\'&quot;)}'&quot; if quoted_value.include?(&quot;\\\'&quot;) # (for ruby mode) &quot;
+        quoted_value
+      end
     end
 
     public
-      # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
-      # attributes but not yet saved (pass a hash with key names matching the associated table column names).
-      # In both instances, valid attribute keys are determined by the column names of the associated table --
-      # hence you can't have attributes that aren't part of the table columns.
-      def initialize(attributes = nil)
-        @attributes = attributes_from_column_definition
-        @attributes_cache = {}
-        @new_record = true
-        ensure_proper_type
-        self.attributes = attributes unless attributes.nil?
-        self.class.send(:scope, :create).each { |att,value| self.send(&quot;#{att}=&quot;, value) } if self.class.send(:scoped?, :create)
-        result = yield self if block_given?
-        callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
-        result
-      end
+    # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
+    # attributes but not yet saved (pass a hash with key names matching the associated table column names).
+    # In both instances, valid attribute keys are determined by the column names of the associated table --
+    # hence you can't have attributes that aren't part of the table columns.
+    def initialize(attributes = nil)
+      @attributes = attributes_from_column_definition
+      @attributes_cache = {}
+      @new_record = true
+      ensure_proper_type
+      self.attributes = attributes unless attributes.nil?
+      self.class.send(:scope, :create).each { |att,value| self.send(&quot;#{att}=&quot;, value) } if self.class.send(:scoped?, :create)
+      result = yield self if block_given?
+      callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
+      result
+    end
 
-      # A model instance's primary key is always available as model.id
-      # whether you name it the default 'id' or set it to something else.
-      def id
-        attr_name = self.class.primary_key
-        column = column_for_attribute(attr_name)
+    # A model instance's primary key is always available as model.id
+    # whether you name it the default 'id' or set it to something else.
+    def id
+      attr_name = self.class.primary_key
+      column = column_for_attribute(attr_name)
 
-        self.class.send(:define_read_method, :id, attr_name, column)
-        # now that the method exists, call it
-        self.send attr_name.to_sym
+      self.class.send(:define_read_method, :id, attr_name, column)
+      # now that the method exists, call it
+      self.send attr_name.to_sym
 
-      end
+    end
 
-      # Enables Active Record objects to be used as URL parameters in Action Pack automatically.
-      def to_param
-        # We can't use alias_method here, because method 'id' optimizes itself on the fly.
-        (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
-      end
+    # Enables Active Record objects to be used as URL parameters in Action Pack automatically.
+    def to_param
+      # We can't use alias_method here, because method 'id' optimizes itself on the fly.
+      (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
+    end
 
-      # Returns a cache key that can be used to identify this record.
-      #
-      # ==== Examples
-      #
-      #   Product.new.cache_key     # =&gt; &quot;products/new&quot;
-      #   Product.find(5).cache_key # =&gt; &quot;products/5&quot; (updated_at not available)
-      #   Person.find(5).cache_key  # =&gt; &quot;people/5-20071224150000&quot; (updated_at available)
-      def cache_key
-        case
-        when new_record?
-          &quot;#{self.class.model_name.cache_key}/new&quot;
-        when timestamp = self[:updated_at]
-          &quot;#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}&quot;
-        else
-          &quot;#{self.class.model_name.cache_key}/#{id}&quot;
-        end
+    # Returns a cache key that can be used to identify this record.
+    #
+    # ==== Examples
+    #
+    #   Product.new.cache_key     # =&gt; &quot;products/new&quot;
+    #   Product.find(5).cache_key # =&gt; &quot;products/5&quot; (updated_at not available)
+    #   Person.find(5).cache_key  # =&gt; &quot;people/5-20071224150000&quot; (updated_at available)
+    def cache_key
+      case
+      when new_record?
+        &quot;#{self.class.model_name.cache_key}/new&quot;
+      when timestamp = self[:updated_at]
+        &quot;#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}&quot;
+      else
+        &quot;#{self.class.model_name.cache_key}/#{id}&quot;
       end
+    end
 
-      def id_before_type_cast #:nodoc:
-        read_attribute_before_type_cast(self.class.primary_key)
-      end
+    def id_before_type_cast #:nodoc:
+      read_attribute_before_type_cast(self.class.primary_key)
+    end
 
-      def quoted_id #:nodoc:
-        quote_value(id, column_for_attribute(self.class.primary_key))
-      end
+    def quoted_id #:nodoc:
+      quote_value(id, column_for_attribute(self.class.primary_key))
+    end
 
-      # Sets the primary ID.
-      def id=(value)
-        write_attribute(self.class.primary_key, value)
-      end
+    # Sets the primary ID.
+    def id=(value)
+      write_attribute(self.class.primary_key, value)
+    end
 
-      # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet.
-      def new_record?
-        defined?(@new_record) &amp;&amp; @new_record
-      end
+    # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet.
+    def new_record?
+      defined?(@new_record) &amp;&amp; @new_record
+    end
 
-      # :call-seq:
-      #   save(perform_validation = true)
-      #
-      # Saves the model.
-      #
-      # If the model is new a record gets created in the database, otherwise
-      # the existing record gets updated.
-      #
-      # If +perform_validation+ is true validations run. If any of them fail
-      # the action is cancelled and +save+ returns +false+. If the flag is
-      # false validations are bypassed altogether. See
-      # ActiveRecord::Validations for more information. 
-      #
-      # There's a series of callbacks associated with +save+. If any of the
-      # &lt;tt&gt;before_*&lt;/tt&gt; callbacks return +false+ the action is cancelled and
-      # +save+ returns +false+. See ActiveRecord::Callbacks for further
-      # details. 
-      def save
-        create_or_update
-      end
+    # :call-seq:
+    #   save(perform_validation = true)
+    #
+    # Saves the model.
+    #
+    # If the model is new a record gets created in the database, otherwise
+    # the existing record gets updated.
+    #
+    # If +perform_validation+ is true validations run. If any of them fail
+    # the action is cancelled and +save+ returns +false+. If the flag is
+    # false validations are bypassed altogether. See
+    # ActiveRecord::Validations for more information.
+    #
+    # There's a series of callbacks associated with +save+. If any of the
+    # &lt;tt&gt;before_*&lt;/tt&gt; callbacks return +false+ the action is cancelled and
+    # +save+ returns +false+. See ActiveRecord::Callbacks for further
+    # details.
+    def save
+      create_or_update
+    end
 
-      # Saves the model.
-      #
-      # If the model is new a record gets created in the database, otherwise
-      # the existing record gets updated.
-      #
-      # With &lt;tt&gt;save!&lt;/tt&gt; validations always run. If any of them fail
-      # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
-      # for more information. 
-      #
-      # There's a series of callbacks associated with &lt;tt&gt;save!&lt;/tt&gt;. If any of
-      # the &lt;tt&gt;before_*&lt;/tt&gt; callbacks return +false+ the action is cancelled
-      # and &lt;tt&gt;save!&lt;/tt&gt; raises ActiveRecord::RecordNotSaved. See
-      # ActiveRecord::Callbacks for further details. 
-      def save!
-        create_or_update || raise(RecordNotSaved)
-      end
+    # Saves the model.
+    #
+    # If the model is new a record gets created in the database, otherwise
+    # the existing record gets updated.
+    #
+    # With &lt;tt&gt;save!&lt;/tt&gt; validations always run. If any of them fail
+    # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
+    # for more information.
+    #
+    # There's a series of callbacks associated with &lt;tt&gt;save!&lt;/tt&gt;. If any of
+    # the &lt;tt&gt;before_*&lt;/tt&gt; callbacks return +false+ the action is cancelled
+    # and &lt;tt&gt;save!&lt;/tt&gt; raises ActiveRecord::RecordNotSaved. See
+    # ActiveRecord::Callbacks for further details.
+    def save!
+      create_or_update || raise(RecordNotSaved)
+    end
 
-      # Deletes the record in the database and freezes this instance to reflect that no changes should
-      # be made (since they can't be persisted).
-      def destroy
-        unless new_record?
-          connection.delete &lt;&lt;-end_sql, &quot;#{self.class.name} Destroy&quot;
+    # Deletes the record in the database and freezes this instance to reflect that no changes should
+    # be made (since they can't be persisted).
+    def destroy
+      unless new_record?
+        connection.delete &lt;&lt;-end_sql, &quot;#{self.class.name} Destroy&quot;
             DELETE FROM #{self.class.quoted_table_name}
             WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}
-          end_sql
-        end
-
-        freeze
-      end
-
-      # Returns a clone of the record that hasn't been assigned an id yet and
-      # is treated as a new record.  Note that this is a &quot;shallow&quot; clone:
-      # it copies the object's attributes only, not its associations.
-      # The extent of a &quot;deep&quot; clone is application-specific and is therefore
-      # left to the application to implement according to its need.
-      def clone
-        attrs = clone_attributes(:read_attribute_before_type_cast)
-        attrs.delete(self.class.primary_key)
-        record = self.class.new
-        record.send :instance_variable_set, '@attributes', attrs
-        record
+        end_sql
       end
 
-      # Returns an instance of the specified +klass+ with the attributes of the current record. This is mostly useful in relation to
-      # single-table inheritance structures where you want a subclass to appear as the superclass. This can be used along with record
-      # identification in Action Pack to allow, say, &lt;tt&gt;Client &lt; Company&lt;/tt&gt; to do something like render &lt;tt&gt;:partial =&gt; @client.becomes(Company)&lt;/tt&gt;
-      # to render that instance using the companies/company partial instead of clients/client.
-      #
-      # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
-      # instance will affect the other.
-      def becomes(klass)
-        returning klass.new do |became|
-          became.instance_variable_set(&quot;@attributes&quot;, @attributes)
-          became.instance_variable_set(&quot;@attributes_cache&quot;, @attributes_cache)
-          became.instance_variable_set(&quot;@new_record&quot;, new_record?)
-        end
-      end
+      freeze
+    end
 
-      # Updates a single attribute and saves the record without going through the normal validation procedure.
-      # This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
-      # in Base is replaced with this when the validations module is mixed in, which it is by default.
-      def update_attribute(name, value)
-        send(name.to_s + '=', value)
-        save(false)
-      end
+    # Returns a clone of the record that hasn't been assigned an id yet and
+    # is treated as a new record.  Note that this is a &quot;shallow&quot; clone:
+    # it copies the object's attributes only, not its associations.
+    # The extent of a &quot;deep&quot; clone is application-specific and is therefore
+    # left to the application to implement according to its need.
+    def clone
+      attrs = clone_attributes(:read_attribute_before_type_cast)
+      attrs.delete(self.class.primary_key)
+      record = self.class.new
+      record.send :instance_variable_set, '@attributes', attrs
+      record
+    end
 
-      # Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will
-      # fail and false will be returned.
-      def update_attributes(attributes)
-        self.attributes = attributes
-        save
+    # Returns an instance of the specified +klass+ with the attributes of the current record. This is mostly useful in relation to
+    # single-table inheritance structures where you want a subclass to appear as the superclass. This can be used along with record
+    # identification in Action Pack to allow, say, &lt;tt&gt;Client &lt; Company&lt;/tt&gt; to do something like render &lt;tt&gt;:partial =&gt; @client.becomes(Company)&lt;/tt&gt;
+    # to render that instance using the companies/company partial instead of clients/client.
+    #
+    # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
+    # instance will affect the other.
+    def becomes(klass)
+      returning klass.new do |became|
+        became.instance_variable_set(&quot;@attributes&quot;, @attributes)
+        became.instance_variable_set(&quot;@attributes_cache&quot;, @attributes_cache)
+        became.instance_variable_set(&quot;@new_record&quot;, new_record?)
       end
+    end
 
-      # Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid.
-      def update_attributes!(attributes)
-        self.attributes = attributes
-        save!
-      end
+    # Updates a single attribute and saves the record without going through the normal validation procedure.
+    # This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
+    # in Base is replaced with this when the validations module is mixed in, which it is by default.
+    def update_attribute(name, value)
+      send(name.to_s + '=', value)
+      save(false)
+    end
 
-      # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
-      # The increment is performed directly on the underlying attribute, no setter is invoked.
-      # Only makes sense for number-based attributes. Returns +self+.
-      def increment(attribute, by = 1)
-        self[attribute] ||= 0
-        self[attribute] += by
-        self
-      end
+    # Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will
+    # fail and false will be returned.
+    def update_attributes(attributes)
+      self.attributes = attributes
+      save
+    end
 
-      # Wrapper around +increment+ that saves the record. This method differs from
-      # its non-bang version in that it passes through the attribute setter.
-      # Saving is not subjected to validation checks. Returns +true+ if the
-      # record could be saved.
-      def increment!(attribute, by = 1)
-        increment(attribute, by).update_attribute(attribute, self[attribute])
-      end
+    # Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid.
+    def update_attributes!(attributes)
+      self.attributes = attributes
+      save!
+    end
 
-      # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
-      # The decrement is performed directly on the underlying attribute, no setter is invoked.
-      # Only makes sense for number-based attributes. Returns +self+.
-      def decrement(attribute, by = 1)
-        self[attribute] ||= 0
-        self[attribute] -= by
-        self
-      end
+    # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
+    # The increment is performed directly on the underlying attribute, no setter is invoked.
+    # Only makes sense for number-based attributes. Returns +self+.
+    def increment(attribute, by = 1)
+      self[attribute] ||= 0
+      self[attribute] += by
+      self
+    end
 
-      # Wrapper around +decrement+ that saves the record. This method differs from
-      # its non-bang version in that it passes through the attribute setter.
-      # Saving is not subjected to validation checks. Returns +true+ if the
-      # record could be saved.
-      def decrement!(attribute, by = 1)
-        decrement(attribute, by).update_attribute(attribute, self[attribute])
-      end
+    # Wrapper around +increment+ that saves the record. This method differs from
+    # its non-bang version in that it passes through the attribute setter.
+    # Saving is not subjected to validation checks. Returns +true+ if the
+    # record could be saved.
+    def increment!(attribute, by = 1)
+      increment(attribute, by).update_attribute(attribute, self[attribute])
+    end
 
-      # Assigns to +attribute+ the boolean opposite of &lt;tt&gt;attribute?&lt;/tt&gt;. So
-      # if the predicate returns +true+ the attribute will become +false+. This
-      # method toggles directly the underlying value without calling any setter.
-      # Returns +self+.
-      def toggle(attribute)
-        self[attribute] = !send(&quot;#{attribute}?&quot;)
-        self
-      end
+    # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
+    # The decrement is performed directly on the underlying attribute, no setter is invoked.
+    # Only makes sense for number-based attributes. Returns +self+.
+    def decrement(attribute, by = 1)
+      self[attribute] ||= 0
+      self[attribute] -= by
+      self
+    end
 
-      # Wrapper around +toggle+ that saves the record. This method differs from
-      # its non-bang version in that it passes through the attribute setter.
-      # Saving is not subjected to validation checks. Returns +true+ if the
-      # record could be saved.
-      def toggle!(attribute)
-        toggle(attribute).update_attribute(attribute, self[attribute])
-      end
-
-      # Reloads the attributes of this object from the database.
-      # The optional options argument is passed to find when reloading so you
-      # may do e.g. record.reload(:lock =&gt; true) to reload the same record with
-      # an exclusive row lock.
-      def reload(options = nil)
-        clear_aggregation_cache
-        clear_association_cache
-        @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
-        @attributes_cache = {}
-        self
-      end
+    # Wrapper around +decrement+ that saves the record. This method differs from
+    # its non-bang version in that it passes through the attribute setter.
+    # Saving is not subjected to validation checks. Returns +true+ if the
+    # record could be saved.
+    def decrement!(attribute, by = 1)
+      decrement(attribute, by).update_attribute(attribute, self[attribute])
+    end
 
-      # Returns the value of the attribute identified by &lt;tt&gt;attr_name&lt;/tt&gt; after it has been typecast (for example,
-      # &quot;2004-12-12&quot; in a data column is cast to a date object, like Date.new(2004, 12, 12)).
-      # (Alias for the protected read_attribute method).
-      def [](attr_name)
-        read_attribute(attr_name)
-      end
-
-      # Updates the attribute identified by &lt;tt&gt;attr_name&lt;/tt&gt; with the specified +value+.
-      # (Alias for the protected write_attribute method).
-      def []=(attr_name, value)
-        write_attribute(attr_name, value)
-      end
+    # Assigns to +attribute+ the boolean opposite of &lt;tt&gt;attribute?&lt;/tt&gt;. So
+    # if the predicate returns +true+ the attribute will become +false+. This
+    # method toggles directly the underlying value without calling any setter.
+    # Returns +self+.
+    def toggle(attribute)
+      self[attribute] = !send(&quot;#{attribute}?&quot;)
+      self
+    end
 
-      # Allows you to set all the attributes at once by passing in a hash with keys
-      # matching the attribute names (which again matches the column names). Sensitive attributes can be protected
-      # from this form of mass-assignment by using the +attr_protected+ macro. Or you can alternatively
-      # specify which attributes *can* be accessed with the +attr_accessible+ macro. Then all the
-      # attributes not included in that won't be allowed to be mass-assigned.
-      def attributes=(new_attributes, guard_protected_attributes = true)
-        return if new_attributes.nil?
-        attributes = new_attributes.dup
-        attributes.stringify_keys!
+    # Wrapper around +toggle+ that saves the record. This method differs from
+    # its non-bang version in that it passes through the attribute setter.
+    # Saving is not subjected to validation checks. Returns +true+ if the
+    # record could be saved.
+    def toggle!(attribute)
+      toggle(attribute).update_attribute(attribute, self[attribute])
+    end
 
-        multi_parameter_attributes = []
-        attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes
-
-        attributes.each do |k, v|
-          if k.include?(&quot;(&quot;)
-            multi_parameter_attributes &lt;&lt; [ k, v ]
-          else
-            respond_to?(:&quot;#{k}=&quot;) ? send(:&quot;#{k}=&quot;, v) : raise(UnknownAttributeError, &quot;unknown attribute: #{k}&quot;)
-          end
-        end
+    # Reloads the attributes of this object from the database.
+    # The optional options argument is passed to find when reloading so you
+    # may do e.g. record.reload(:lock =&gt; true) to reload the same record with
+    # an exclusive row lock.
+    def reload(options = nil)
+      clear_aggregation_cache
+      clear_association_cache
+      @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
+      @attributes_cache = {}
+      self
+    end
 
-        assign_multiparameter_attributes(multi_parameter_attributes)
-      end
+    # Returns the value of the attribute identified by &lt;tt&gt;attr_name&lt;/tt&gt; after it has been typecast (for example,
+    # &quot;2004-12-12&quot; in a data column is cast to a date object, like Date.new(2004, 12, 12)).
+    # (Alias for the protected read_attribute method).
+    def [](attr_name)
+      read_attribute(attr_name)
+    end
 
+    # Updates the attribute identified by &lt;tt&gt;attr_name&lt;/tt&gt; with the specified +value+.
+    # (Alias for the protected write_attribute method).
+    def []=(attr_name, value)
+      write_attribute(attr_name, value)
+    end
 
-      # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
-      def attributes
-        self.attribute_names.inject({}) do |attrs, name|
-          attrs[name] = read_attribute(name)
-          attrs
+    # Allows you to set all the attributes at once by passing in a hash with keys
+    # matching the attribute names (which again matches the column names). Sensitive attributes can be protected
+    # from this form of mass-assignment by using the +attr_protected+ macro. Or you can alternatively
+    # specify which attributes *can* be accessed with the +attr_accessible+ macro. Then all the
+    # attributes not included in that won't be allowed to be mass-assigned.
+    def attributes=(new_attributes, guard_protected_attributes = true)
+      return if new_attributes.nil?
+      attributes = new_attributes.dup
+      attributes.stringify_keys!
+
+      multi_parameter_attributes = []
+      attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes
+
+      attributes.each do |k, v|
+        if k.include?(&quot;(&quot;)
+          multi_parameter_attributes &lt;&lt; [ k, v ]
+        else
+          respond_to?(:&quot;#{k}=&quot;) ? send(:&quot;#{k}=&quot;, v) : raise(UnknownAttributeError, &quot;unknown attribute: #{k}&quot;)
         end
       end
 
-      # Returns a hash of attributes before typecasting and deserialization.
-      def attributes_before_type_cast
-        self.attribute_names.inject({}) do |attrs, name|
-          attrs[name] = read_attribute_before_type_cast(name)
-          attrs
-        end
-      end
+      assign_multiparameter_attributes(multi_parameter_attributes)
+    end
 
-      # Format attributes nicely for inspect.
-      def attribute_for_inspect(attr_name)
-        value = read_attribute(attr_name)
 
-        if value.is_a?(String) &amp;&amp; value.length &gt; 50
-          &quot;#{value[0..50]}...&quot;.inspect
-        elsif value.is_a?(Date) || value.is_a?(Time)
-          %(&quot;#{value.to_s(:db)}&quot;)
-        else
-          value.inspect
-        end
+    # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
+    def attributes
+      self.attribute_names.inject({}) do |attrs, name|
+        attrs[name] = read_attribute(name)
+        attrs
       end
+    end
 
-      # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
-      # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
-      def attribute_present?(attribute)
-        value = read_attribute(attribute)
-        !value.blank?
+    # Returns a hash of attributes before typecasting and deserialization.
+    def attributes_before_type_cast
+      self.attribute_names.inject({}) do |attrs, name|
+        attrs[name] = read_attribute_before_type_cast(name)
+        attrs
       end
+    end
 
-      # Returns true if the given attribute is in the attributes hash
-      def has_attribute?(attr_name)
-        @attributes.has_key?(attr_name.to_s)
-      end
+    # Format attributes nicely for inspect.
+    def attribute_for_inspect(attr_name)
+      value = read_attribute(attr_name)
 
-      # Returns an array of names for the attributes available on this object sorted alphabetically.
-      def attribute_names
-        @attributes.keys.sort
+      if value.is_a?(String) &amp;&amp; value.length &gt; 50
+        &quot;#{value[0..50]}...&quot;.inspect
+      elsif value.is_a?(Date) || value.is_a?(Time)
+        %(&quot;#{value.to_s(:db)}&quot;)
+      else
+        value.inspect
       end
+    end
 
-      # Returns the column object for the named attribute.
-      def column_for_attribute(name)
-        self.class.columns_hash[name.to_s]
-      end
+    # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
+    # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
+    def attribute_present?(attribute)
+      value = read_attribute(attribute)
+      !value.blank?
+    end
 
-      # Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
-      def ==(comparison_object)
-        comparison_object.equal?(self) ||
-          (comparison_object.instance_of?(self.class) &amp;&amp;
-            comparison_object.id == id &amp;&amp;
-            !comparison_object.new_record?)
-      end
+    # Returns true if the given attribute is in the attributes hash
+    def has_attribute?(attr_name)
+      @attributes.has_key?(attr_name.to_s)
+    end
 
-      # Delegates to ==
-      def eql?(comparison_object)
-        self == (comparison_object)
-      end
+    # Returns an array of names for the attributes available on this object sorted alphabetically.
+    def attribute_names
+      @attributes.keys.sort
+    end
 
-      # Delegates to id in order to allow two records of the same type and id to work with something like:
-      #   [ Person.find(1), Person.find(2), Person.find(3) ] &amp; [ Person.find(1), Person.find(4) ] # =&gt; [ Person.find(1) ]
-      def hash
-        id.hash
-      end
+    # Returns the column object for the named attribute.
+    def column_for_attribute(name)
+      self.class.columns_hash[name.to_s]
+    end
 
-      # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
-      def freeze
-        @attributes.freeze; self
-      end
+    # Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
+    def ==(comparison_object)
+      comparison_object.equal?(self) ||
+        (comparison_object.instance_of?(self.class) &amp;&amp;
+          comparison_object.id == id &amp;&amp;
+          !comparison_object.new_record?)
+    end
 
-      # Returns +true+ if the attributes hash has been frozen.
-      def frozen?
-        @attributes.frozen?
-      end
+    # Delegates to ==
+    def eql?(comparison_object)
+      self == (comparison_object)
+    end
 
-      # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
-      # attributes will be marked as read only since they cannot be saved.
-      def readonly?
-        defined?(@readonly) &amp;&amp; @readonly == true
-      end
+    # Delegates to id in order to allow two records of the same type and id to work with something like:
+    #   [ Person.find(1), Person.find(2), Person.find(3) ] &amp; [ Person.find(1), Person.find(4) ] # =&gt; [ Person.find(1) ]
+    def hash
+      id.hash
+    end
 
-      # Marks this record as read only.
-      def readonly!
-        @readonly = true
-      end
+    # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
+    def freeze
+      @attributes.freeze; self
+    end
 
-      # Returns the contents of the record as a nicely formatted string.
-      def inspect
-        attributes_as_nice_string = self.class.column_names.collect { |name|
-          if has_attribute?(name) || new_record?
-            &quot;#{name}: #{attribute_for_inspect(name)}&quot;
-          end
-        }.compact.join(&quot;, &quot;)
-        &quot;#&lt;#{self.class} #{attributes_as_nice_string}&gt;&quot;
-      end
+    # Returns +true+ if the attributes hash has been frozen.
+    def frozen?
+      @attributes.frozen?
+    end
+
+    # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
+    # attributes will be marked as read only since they cannot be saved.
+    def readonly?
+      defined?(@readonly) &amp;&amp; @readonly == true
+    end
+
+    # Marks this record as read only.
+    def readonly!
+      @readonly = true
+    end
+
+    # Returns the contents of the record as a nicely formatted string.
+    def inspect
+      attributes_as_nice_string = self.class.column_names.collect { |name|
+        if has_attribute?(name) || new_record?
+          &quot;#{name}: #{attribute_for_inspect(name)}&quot;
+        end
+      }.compact.join(&quot;, &quot;)
+      &quot;#&lt;#{self.class} #{attributes_as_nice_string}&gt;&quot;
+    end
 
     private
-      def create_or_update
-        raise ReadOnlyRecord if readonly?
-        result = new_record? ? create : update
-        result != false
-      end
-
-      # Updates the associated record with values matching those of the instance attributes.
-      # Returns the number of affected rows.
-      def update(attribute_names = @attributes.keys)
-        quoted_attributes = attributes_with_quotes(false, false, attribute_names)
-        return 0 if quoted_attributes.empty?
-        connection.update(
-          &quot;UPDATE #{self.class.quoted_table_name} &quot; +
+    def create_or_update
+      raise ReadOnlyRecord if readonly?
+      result = new_record? ? create : update
+      result != false
+    end
+
+    # Updates the associated record with values matching those of the instance attributes.
+    # Returns the number of affected rows.
+    def update(attribute_names = @attributes.keys)
+      quoted_attributes = attributes_with_quotes(false, false, attribute_names)
+      return 0 if quoted_attributes.empty?
+      connection.update(
+        &quot;UPDATE #{self.class.quoted_table_name} &quot; +
           &quot;SET #{quoted_comma_pair_list(connection, quoted_attributes)} &quot; +
           &quot;WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quote_value(id)}&quot;,
-          &quot;#{self.class.name} Update&quot;
-        )
-      end
+        &quot;#{self.class.name} Update&quot;
+      )
+    end
 
-      # Creates a record with values matching those of the instance attributes
-      # and returns its id.
-      def create
-        if self.id.nil? &amp;&amp; connection.prefetch_primary_key?(self.class.table_name)
-          self.id = connection.next_sequence_value(self.class.sequence_name)
-        end
+    # Creates a record with values matching those of the instance attributes
+    # and returns its id.
+    def create
+      if self.id.nil? &amp;&amp; connection.prefetch_primary_key?(self.class.table_name)
+        self.id = connection.next_sequence_value(self.class.sequence_name)
+      end
 
-        quoted_attributes = attributes_with_quotes
+      quoted_attributes = attributes_with_quotes
 
-        statement = if quoted_attributes.empty?
-          connection.empty_insert_statement(self.class.table_name)
-        else
-          &quot;INSERT INTO #{self.class.quoted_table_name} &quot; +
+      statement = if quoted_attributes.empty?
+        connection.empty_insert_statement(self.class.table_name)
+      else
+        &quot;INSERT INTO #{self.class.quoted_table_name} &quot; +
           &quot;(#{quoted_column_names.join(', ')}) &quot; +
           &quot;VALUES(#{quoted_attributes.values.join(', ')})&quot;
-        end
+      end
 
-        self.id = connection.insert(statement, &quot;#{self.class.name} Create&quot;,
-          self.class.primary_key, self.id, self.class.sequence_name)
+      self.id = connection.insert(statement, &quot;#{self.class.name} Create&quot;,
+        self.class.primary_key, self.id, self.class.sequence_name)
 
-        @new_record = false
-        id
-      end
+      @new_record = false
+      id
+    end
 
-      # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendent.
-      # Considering the hierarchy Reply &lt; Message &lt; ActiveRecord::Base, this makes it possible to do Reply.new without having to
-      # set &lt;tt&gt;Reply[Reply.inheritance_column] = &quot;Reply&quot;&lt;/tt&gt; yourself. No such attribute would be set for objects of the
-      # Message class in that example.
-      def ensure_proper_type
-        unless self.class.descends_from_active_record?
-          write_attribute(self.class.inheritance_column, self.class.sti_name)
-        end
+    # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendent.
+    # Considering the hierarchy Reply &lt; Message &lt; ActiveRecord::Base, this makes it possible to do Reply.new without having to
+    # set &lt;tt&gt;Reply[Reply.inheritance_column] = &quot;Reply&quot;&lt;/tt&gt; yourself. No such attribute would be set for objects of the
+    # Message class in that example.
+    def ensure_proper_type
+      unless self.class.descends_from_active_record?
+        write_attribute(self.class.inheritance_column, self.class.sti_name)
       end
+    end
 
-      def convert_number_column_value(value)
-        if value == false
-          0
-        elsif value == true
-          1
-        elsif value.is_a?(String) &amp;&amp; value.blank?
-          nil
-        else
-          value
-        end
+    def convert_number_column_value(value)
+      if value == false
+        0
+      elsif value == true
+        1
+      elsif value.is_a?(String) &amp;&amp; value.blank?
+        nil
+      else
+        value
       end
+    end
 
-      def remove_attributes_protected_from_mass_assignment(attributes)
-        safe_attributes =
-          if self.class.accessible_attributes.nil? &amp;&amp; self.class.protected_attributes.nil?
-            attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, &quot;&quot;)) }
-          elsif self.class.protected_attributes.nil?
-            attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, &quot;&quot;)) || attributes_protected_by_default.include?(key.gsub(/\(.+/, &quot;&quot;)) }
-          elsif self.class.accessible_attributes.nil?
-            attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,&quot;&quot;)) || attributes_protected_by_default.include?(key.gsub(/\(.+/, &quot;&quot;)) }
-          else
-            raise &quot;Declare either attr_protected or attr_accessible for #{self.class}, but not both.&quot;
-          end
-
-        removed_attributes = attributes.keys - safe_attributes.keys
-
-        if removed_attributes.any?
-          log_protected_attribute_removal(removed_attributes)
-        end
-
-        safe_attributes
+    def remove_attributes_protected_from_mass_assignment(attributes)
+      safe_attributes =
+        if self.class.accessible_attributes.nil? &amp;&amp; self.class.protected_attributes.nil?
+        attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, &quot;&quot;)) }
+      elsif self.class.protected_attributes.nil?
+        attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, &quot;&quot;)) || attributes_protected_by_default.include?(key.gsub(/\(.+/, &quot;&quot;)) }
+      elsif self.class.accessible_attributes.nil?
+        attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,&quot;&quot;)) || attributes_protected_by_default.include?(key.gsub(/\(.+/, &quot;&quot;)) }
+      else
+        raise &quot;Declare either attr_protected or attr_accessible for #{self.class}, but not both.&quot;
       end
 
-      # Removes attributes which have been marked as readonly.
-      def remove_readonly_attributes(attributes)
-        unless self.class.readonly_attributes.nil?
-          attributes.delete_if { |key, value| self.class.readonly_attributes.include?(key.gsub(/\(.+/,&quot;&quot;)) }
-        else
-          attributes
+      removed_attributes = attributes.keys - safe_attributes.keys
+
+      if removed_attributes.any?
+        log_protected_attribute_removal(removed_attributes)
+        if raise_on_illegal_mass_assignment
+          raise IllegalMassAssignmentError.new(&quot;Illegal mass assignment of #{removed_attributes.join(&quot;, &quot;)}&quot;)
         end
       end
 
-      def log_protected_attribute_removal(*attributes)
-        logger.debug &quot;WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}&quot;
-      end
+      safe_attributes
+    end
 
-      # The primary key and inheritance column can never be set by mass-assignment for security reasons.
-      def attributes_protected_by_default
-        default = [ self.class.primary_key, self.class.inheritance_column ]
-        default &lt;&lt; 'id' unless self.class.primary_key.eql? 'id'
-        default
+    # Removes attributes which have been marked as readonly.
+    def remove_readonly_attributes(attributes)
+      unless self.class.readonly_attributes.nil?
+        attributes.delete_if { |key, value| self.class.readonly_attributes.include?(key.gsub(/\(.+/,&quot;&quot;)) }
+      else
+        attributes
       end
+    end
 
-      # Returns a copy of the attributes hash where all the values have been safely quoted for use in
-      # an SQL statement.
-      def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
-        quoted = {}
-        connection = self.class.connection
-        attribute_names.each do |name|
-          if (column = column_for_attribute(name)) &amp;&amp; (include_primary_key || !column.primary)
-            value = read_attribute(name)
+    def log_protected_attribute_removal(*attributes)
+      logger.debug &quot;WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}&quot;
+    end
 
-            # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
-            if value &amp;&amp; self.class.serialized_attributes.has_key?(name) &amp;&amp; (value.acts_like?(:date) || value.acts_like?(:time))
-              value = value.to_yaml
-            end
+    # The primary key and inheritance column can never be set by mass-assignment for security reasons.
+    def attributes_protected_by_default
+      default = [ self.class.primary_key, self.class.inheritance_column ]
+      default &lt;&lt; 'id' unless self.class.primary_key.eql? 'id'
+      default
+    end
 
-            quoted[name] = connection.quote(value, column)
+    # Returns a copy of the attributes hash where all the values have been safely quoted for use in
+    # an SQL statement.
+    def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
+      quoted = {}
+      connection = self.class.connection
+      attribute_names.each do |name|
+        if (column = column_for_attribute(name)) &amp;&amp; (include_primary_key || !column.primary)
+          value = read_attribute(name)
+
+          # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
+          if value &amp;&amp; self.class.serialized_attributes.has_key?(name) &amp;&amp; (value.acts_like?(:date) || value.acts_like?(:time))
+            value = value.to_yaml
           end
+
+          quoted[name] = connection.quote(value, column)
         end
-        include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
       end
+      include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
+    end
 
-      # Quote strings appropriately for SQL statements.
-      def quote_value(value, column = nil)
-        self.class.connection.quote(value, column)
-      end
+    # Quote strings appropriately for SQL statements.
+    def quote_value(value, column = nil)
+      self.class.connection.quote(value, column)
+    end
 
-      # Interpolate custom SQL string in instance context.
-      # Optional record argument is meant for custom insert_sql.
-      def interpolate_sql(sql, record = nil)
-        instance_eval(&quot;%@#{sql.gsub('@', '\@')}@&quot;)
-      end
+    # Interpolate custom SQL string in instance context.
+    # Optional record argument is meant for custom insert_sql.
+    def interpolate_sql(sql, record = nil)
+      instance_eval(&quot;%@#{sql.gsub('@', '\@')}@&quot;)
+    end
 
-      # Initializes the attributes array with keys matching the columns from the linked table and
-      # the values matching the corresponding default value of that column, so
-      # that a new instance, or one populated from a passed-in Hash, still has all the attributes
-      # that instances loaded from the database would.
-      def attributes_from_column_definition
-        self.class.columns.inject({}) do |attributes, column|
-          attributes[column.name] = column.default unless column.name == self.class.primary_key
-          attributes
-        end
+    # Initializes the attributes array with keys matching the columns from the linked table and
+    # the values matching the corresponding default value of that column, so
+    # that a new instance, or one populated from a passed-in Hash, still has all the attributes
+    # that instances loaded from the database would.
+    def attributes_from_column_definition
+      self.class.columns.inject({}) do |attributes, column|
+        attributes[column.name] = column.default unless column.name == self.class.primary_key
+        attributes
       end
+    end
 
-      # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
-      # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
-      # So having the pairs written_on(1) = &quot;2004&quot;, written_on(2) = &quot;6&quot;, written_on(3) = &quot;24&quot;, will instantiate
-      # written_on (a date type) with Date.new(&quot;2004&quot;, &quot;6&quot;, &quot;24&quot;). You can also specify a typecast character in the
-      # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
-      # s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.
-      def assign_multiparameter_attributes(pairs)
-        execute_callstack_for_multiparameter_attributes(
-          extract_callstack_for_multiparameter_attributes(pairs)
-        )
-      end
+    # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
+    # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
+    # So having the pairs written_on(1) = &quot;2004&quot;, written_on(2) = &quot;6&quot;, written_on(3) = &quot;24&quot;, will instantiate
+    # written_on (a date type) with Date.new(&quot;2004&quot;, &quot;6&quot;, &quot;24&quot;). You can also specify a typecast character in the
+    # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
+    # s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.
+    def assign_multiparameter_attributes(pairs)
+      execute_callstack_for_multiparameter_attributes(
+        extract_callstack_for_multiparameter_attributes(pairs)
+      )
+    end
 
-      def instantiate_time_object(name, values)
-        if self.class.time_zone_aware_attributes &amp;&amp; !self.class.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
-          Time.zone.local(*values)
-        else
-          Time.time_with_datetime_fallback(@@default_timezone, *values)
-        end
+    def instantiate_time_object(name, values)
+      if self.class.time_zone_aware_attributes &amp;&amp; !self.class.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
+        Time.zone.local(*values)
+      else
+        Time.time_with_datetime_fallback(@@default_timezone, *values)
       end
+    end
 
-      def execute_callstack_for_multiparameter_attributes(callstack)
-        errors = []
-        callstack.each do |name, values|
-          klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
-          if values.empty?
-            send(name + &quot;=&quot;, nil)
-          else
-            begin
-              value = if Time == klass
-                instantiate_time_object(name, values)
-              elsif Date == klass
-                begin
-                  Date.new(*values)
-                rescue ArgumentError =&gt; ex # if Date.new raises an exception on an invalid date
-                  instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
-                end
-              else
-                klass.new(*values)
+    def execute_callstack_for_multiparameter_attributes(callstack)
+      errors = []
+      callstack.each do |name, values|
+        klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
+        if values.empty?
+          send(name + &quot;=&quot;, nil)
+        else
+          begin
+            value = if Time == klass
+              instantiate_time_object(name, values)
+            elsif Date == klass
+              begin
+                Date.new(*values)
+              rescue ArgumentError =&gt; ex # if Date.new raises an exception on an invalid date
+                instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
               end
-
-              send(name + &quot;=&quot;, value)
-            rescue =&gt; ex
-              errors &lt;&lt; AttributeAssignmentError.new(&quot;error on assignment #{values.inspect} to #{name}&quot;, ex, name)
+            else
+              klass.new(*values)
             end
+
+            send(name + &quot;=&quot;, value)
+          rescue =&gt; ex
+            errors &lt;&lt; AttributeAssignmentError.new(&quot;error on assignment #{values.inspect} to #{name}&quot;, ex, name)
           end
         end
-        unless errors.empty?
-          raise MultiparameterAssignmentErrors.new(errors), &quot;#{errors.size} error(s) on assignment of multiparameter attributes&quot;
-        end
       end
+      unless errors.empty?
+        raise MultiparameterAssignmentErrors.new(errors), &quot;#{errors.size} error(s) on assignment of multiparameter attributes&quot;
+      end
+    end
 
-      def extract_callstack_for_multiparameter_attributes(pairs)
-        attributes = { }
+    def extract_callstack_for_multiparameter_attributes(pairs)
+      attributes = { }
 
-        for pair in pairs
-          multiparameter_name, value = pair
-          attribute_name = multiparameter_name.split(&quot;(&quot;).first
-          attributes[attribute_name] = [] unless attributes.include?(attribute_name)
+      for pair in pairs
+        multiparameter_name, value = pair
+        attribute_name = multiparameter_name.split(&quot;(&quot;).first
+        attributes[attribute_name] = [] unless attributes.include?(attribute_name)
 
-          unless value.empty?
-            attributes[attribute_name] &lt;&lt;
-              [ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
-          end
+        unless value.empty?
+          attributes[attribute_name] &lt;&lt;
+            [ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
         end
-
-        attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
       end
 
-      def type_cast_attribute_value(multiparameter_name, value)
-        multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send(&quot;to_&quot; + $1) : value
-      end
+      attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
+    end
 
-      def find_parameter_position(multiparameter_name)
-        multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
-      end
+    def type_cast_attribute_value(multiparameter_name, value)
+      multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send(&quot;to_&quot; + $1) : value
+    end
 
-      # Returns a comma-separated pair list, like &quot;key1 = val1, key2 = val2&quot;.
-      def comma_pair_list(hash)
-        hash.inject([]) { |list, pair| list &lt;&lt; &quot;#{pair.first} = #{pair.last}&quot; }.join(&quot;, &quot;)
-      end
+    def find_parameter_position(multiparameter_name)
+      multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
+    end
 
-      def quoted_column_names(attributes = attributes_with_quotes)
-        connection = self.class.connection
-        attributes.keys.collect do |column_name|
-          connection.quote_column_name(column_name)
-        end
-      end
+    # Returns a comma-separated pair list, like &quot;key1 = val1, key2 = val2&quot;.
+    def comma_pair_list(hash)
+      hash.inject([]) { |list, pair| list &lt;&lt; &quot;#{pair.first} = #{pair.last}&quot; }.join(&quot;, &quot;)
+    end
 
-      def self.quoted_table_name
-        self.connection.quote_table_name(self.table_name)
+    def quoted_column_names(attributes = attributes_with_quotes)
+      connection = self.class.connection
+      attributes.keys.collect do |column_name|
+        connection.quote_column_name(column_name)
       end
+    end
 
-      def quote_columns(quoter, hash)
-        hash.inject({}) do |quoted, (name, value)|
-          quoted[quoter.quote_column_name(name)] = value
-          quoted
-        end
-      end
+    def self.quoted_table_name
+      self.connection.quote_table_name(self.table_name)
+    end
 
-      def quoted_comma_pair_list(quoter, hash)
-        comma_pair_list(quote_columns(quoter, hash))
+    def quote_columns(quoter, hash)
+      hash.inject({}) do |quoted, (name, value)|
+        quoted[quoter.quote_column_name(name)] = value
+        quoted
       end
+    end
 
-      def object_from_yaml(string)
-        return string unless string.is_a?(String)
-        YAML::load(string) rescue string
-      end
+    def quoted_comma_pair_list(quoter, hash)
+      comma_pair_list(quote_columns(quoter, hash))
+    end
 
-      def clone_attributes(reader_method = :read_attribute, attributes = {})
-        self.attribute_names.inject(attributes) do |attrs, name|
-          attrs[name] = clone_attribute_value(reader_method, name)
-          attrs
-        end
-      end
+    def object_from_yaml(string)
+      return string unless string.is_a?(String)
+      YAML::load(string) rescue string
+    end
 
-      def clone_attribute_value(reader_method, attribute_name)
-        value = send(reader_method, attribute_name)
-        value.duplicable? ? value.clone : value
-      rescue TypeError, NoMethodError
-        value
+    def clone_attributes(reader_method = :read_attribute, attributes = {})
+      self.attribute_names.inject(attributes) do |attrs, name|
+        attrs[name] = clone_attribute_value(reader_method, name)
+        attrs
       end
+    end
+
+    def clone_attribute_value(reader_method, attribute_name)
+      value = send(reader_method, attribute_name)
+      value.duplicable? ? value.clone : value
+    rescue TypeError, NoMethodError
+      value
+    end
   end
 end</diff>
      <filename>activerecord/lib/active_record/base.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>892dd736247b79b1e1d96fef461ec357f8c76b4f</id>
    </parent>
  </parents>
  <author>
    <name>Miles Georgi</name>
    <email>azimux@gmail.com</email>
  </author>
  <url>http://github.com/azimux/rails/commit/a306d4a71c82114d4332eb2dca9a4246a39284ce</url>
  <id>a306d4a71c82114d4332eb2dca9a4246a39284ce</id>
  <committed-date>2009-03-31T18:13:49-07:00</committed-date>
  <authored-date>2009-03-31T18:13:49-07:00</authored-date>
  <message>Added a config.active_record.raise_on_illegal_mass_assignment option
to not only log attributes removed by attr_accessible/attr_protected, but
to also raise ActiveRecord::IllegalMassAssignmentError when this occurs.</message>
  <tree>938df7d54932a0ebb50725951c3b04927e2059ff</tree>
  <committer>
    <name>Miles Georgi</name>
    <email>azimux@gmail.com</email>
  </committer>
</commit>
