activerecord 1.10.1 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +187 -19
 - data/RUNNING_UNIT_TESTS +11 -0
 - data/lib/active_record.rb +3 -1
 - data/lib/active_record/acts/list.rb +25 -14
 - data/lib/active_record/acts/nested_set.rb +4 -4
 - data/lib/active_record/acts/tree.rb +18 -1
 - data/lib/active_record/associations.rb +90 -17
 - data/lib/active_record/associations/association_collection.rb +44 -5
 - data/lib/active_record/associations/has_and_belongs_to_many_association.rb +17 -4
 - data/lib/active_record/associations/has_many_association.rb +13 -3
 - data/lib/active_record/associations/has_one_association.rb +19 -0
 - data/lib/active_record/base.rb +292 -268
 - data/lib/active_record/callbacks.rb +14 -14
 - data/lib/active_record/connection_adapters/abstract_adapter.rb +137 -75
 - data/lib/active_record/connection_adapters/db2_adapter.rb +10 -8
 - data/lib/active_record/connection_adapters/mysql_adapter.rb +91 -64
 - data/lib/active_record/connection_adapters/oci_adapter.rb +6 -6
 - data/lib/active_record/connection_adapters/postgresql_adapter.rb +113 -60
 - data/lib/active_record/connection_adapters/sqlite_adapter.rb +15 -12
 - data/lib/active_record/connection_adapters/sqlserver_adapter.rb +159 -132
 - data/lib/active_record/fixtures.rb +59 -12
 - data/lib/active_record/locking.rb +10 -9
 - data/lib/active_record/migration.rb +112 -5
 - data/lib/active_record/query_cache.rb +64 -0
 - data/lib/active_record/timestamp.rb +10 -8
 - data/lib/active_record/validations.rb +121 -26
 - data/rakefile +16 -10
 - data/test/aaa_create_tables_test.rb +26 -48
 - data/test/abstract_unit.rb +3 -0
 - data/test/aggregations_test.rb +19 -19
 - data/test/association_callbacks_test.rb +110 -0
 - data/test/associations_go_eager_test.rb +48 -14
 - data/test/associations_test.rb +344 -142
 - data/test/base_test.rb +150 -31
 - data/test/binary_test.rb +7 -0
 - data/test/callbacks_test.rb +24 -5
 - data/test/column_alias_test.rb +2 -2
 - data/test/connections/native_sqlserver_odbc/connection.rb +26 -0
 - data/test/deprecated_associations_test.rb +27 -28
 - data/test/deprecated_finder_test.rb +8 -9
 - data/test/finder_test.rb +52 -17
 - data/test/fixtures/author.rb +39 -0
 - data/test/fixtures/categories.yml +7 -0
 - data/test/fixtures/categories_posts.yml +8 -0
 - data/test/fixtures/category.rb +2 -0
 - data/test/fixtures/comment.rb +3 -1
 - data/test/fixtures/comments.yml +43 -1
 - data/test/fixtures/companies.yml +14 -0
 - data/test/fixtures/company.rb +1 -1
 - data/test/fixtures/computers.yml +2 -1
 - data/test/fixtures/db_definitions/db2.sql +7 -2
 - data/test/fixtures/db_definitions/mysql.drop.sql +2 -0
 - data/test/fixtures/db_definitions/mysql.sql +11 -6
 - data/test/fixtures/db_definitions/oci.sql +7 -2
 - data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
 - data/test/fixtures/db_definitions/postgresql.sql +8 -5
 - data/test/fixtures/db_definitions/sqlite.drop.sql +2 -0
 - data/test/fixtures/db_definitions/sqlite.sql +9 -4
 - data/test/fixtures/db_definitions/sqlserver.drop.sql +2 -0
 - data/test/fixtures/db_definitions/sqlserver.sql +12 -7
 - data/test/fixtures/developer.rb +8 -1
 - data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
 - data/test/fixtures/post.rb +8 -2
 - data/test/fixtures/posts.yml +21 -0
 - data/test/fixtures/project.rb +14 -1
 - data/test/fixtures/subscriber.rb +3 -0
 - data/test/fixtures_test.rb +14 -0
 - data/test/inheritance_test.rb +30 -22
 - data/test/lifecycle_test.rb +3 -4
 - data/test/locking_test.rb +2 -4
 - data/test/migration_test.rb +186 -0
 - data/test/mixin_nested_set_test.rb +19 -19
 - data/test/mixin_test.rb +88 -88
 - data/test/modules_test.rb +5 -10
 - data/test/multiple_db_test.rb +2 -0
 - data/test/pk_test.rb +8 -12
 - data/test/reflection_test.rb +8 -4
 - data/test/schema_test_postgresql.rb +63 -0
 - data/test/thread_safety_test.rb +4 -1
 - data/test/transactions_test.rb +9 -2
 - data/test/unconnected_test.rb +1 -0
 - data/test/validations_test.rb +151 -8
 - metadata +11 -5
 - data/test/migration_mysql.rb +0 -104
 
| 
         @@ -181,17 +181,17 @@ module ActiveRecord 
     | 
|
| 
       181 
181 
     | 
    
         | 
| 
       182 
182 
     | 
    
         
             
                    # Returns a set of itself and all of it's nested children
         
     | 
| 
       183 
183 
     | 
    
         
             
                    def full_set
         
     | 
| 
       184 
     | 
    
         
            -
                      self.class. 
     | 
| 
      
 184 
     | 
    
         
            +
                      self.class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} BETWEEN #{self[left_col_name]} and #{self[right_col_name]})" )
         
     | 
| 
       185 
185 
     | 
    
         
             
                    end
         
     | 
| 
       186 
186 
     | 
    
         | 
| 
       187 
187 
     | 
    
         
             
                    # Returns a set of all of it's children and nested children
         
     | 
| 
       188 
188 
     | 
    
         
             
                    def all_children
         
     | 
| 
       189 
     | 
    
         
            -
                      self.class. 
     | 
| 
      
 189 
     | 
    
         
            +
                      self.class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} > #{self[left_col_name]}) and (#{right_col_name} < #{self[right_col_name]})" )
         
     | 
| 
       190 
190 
     | 
    
         
             
                    end
         
     | 
| 
       191 
191 
     | 
    
         | 
| 
       192 
192 
     | 
    
         
             
                    # Returns a set of only this entries immediate children
         
     | 
| 
       193 
193 
     | 
    
         
             
                    def direct_children
         
     | 
| 
       194 
     | 
    
         
            -
                      self.class. 
     | 
| 
      
 194 
     | 
    
         
            +
                      self.class.find(:all, :conditions => "#{scope_condition} and #{parent_column} = #{self.id}")
         
     | 
| 
       195 
195 
     | 
    
         
             
                    end
         
     | 
| 
       196 
196 
     | 
    
         | 
| 
       197 
197 
     | 
    
         
             
                    # Prunes a branch off of the tree, shifting all of the elements on the right
         
     | 
| 
         @@ -209,4 +209,4 @@ module ActiveRecord 
     | 
|
| 
       209 
209 
     | 
    
         
             
                  end
         
     | 
| 
       210 
210 
     | 
    
         
             
                end
         
     | 
| 
       211 
211 
     | 
    
         
             
              end
         
     | 
| 
       212 
     | 
    
         
            -
            end
         
     | 
| 
      
 212 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -35,9 +35,26 @@ module ActiveRecord 
     | 
|
| 
       35 
35 
     | 
    
         
             
                    def acts_as_tree(options = {})
         
     | 
| 
       36 
36 
     | 
    
         
             
                      configuration = { :foreign_key => "parent_id", :order => nil, :counter_cache => nil }
         
     | 
| 
       37 
37 
     | 
    
         
             
                      configuration.update(options) if options.is_a?(Hash)
         
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       39 
39 
     | 
    
         
             
                      belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
         
     | 
| 
       40 
40 
     | 
    
         
             
                      has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => true
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                      module_eval <<-END
         
     | 
| 
      
 43 
     | 
    
         
            +
                        def self.roots
         
     | 
| 
      
 44 
     | 
    
         
            +
                          self.find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => "#{configuration[:order]}")
         
     | 
| 
      
 45 
     | 
    
         
            +
                        end
         
     | 
| 
      
 46 
     | 
    
         
            +
                        def self.root
         
     | 
| 
      
 47 
     | 
    
         
            +
                          self.find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => "#{configuration[:order]}")
         
     | 
| 
      
 48 
     | 
    
         
            +
                        end
         
     | 
| 
      
 49 
     | 
    
         
            +
                      END
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                      define_method(:siblings) do
         
     | 
| 
      
 52 
     | 
    
         
            +
                        if parent
         
     | 
| 
      
 53 
     | 
    
         
            +
                          self.class.find(:all, :conditions => [ "#{configuration[:foreign_key]} = ?", parent.id ], :order => configuration[:order])
         
     | 
| 
      
 54 
     | 
    
         
            +
                        else
         
     | 
| 
      
 55 
     | 
    
         
            +
                          self.class.roots
         
     | 
| 
      
 56 
     | 
    
         
            +
                        end
         
     | 
| 
      
 57 
     | 
    
         
            +
                      end
         
     | 
| 
       41 
58 
     | 
    
         
             
                    end
         
     | 
| 
       42 
59 
     | 
    
         
             
                  end
         
     | 
| 
       43 
60 
     | 
    
         
             
                end
         
     | 
| 
         @@ -17,7 +17,7 @@ module ActiveRecord 
     | 
|
| 
       17 
17 
     | 
    
         
             
                def clear_association_cache #:nodoc:
         
     | 
| 
       18 
18 
     | 
    
         
             
                  self.class.reflect_on_all_associations.to_a.each do |assoc|
         
     | 
| 
       19 
19 
     | 
    
         
             
                    instance_variable_set "@#{assoc.name}", nil
         
     | 
| 
       20 
     | 
    
         
            -
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end unless self.new_record?
         
     | 
| 
       21 
21 
     | 
    
         
             
                end
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
                # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like 
         
     | 
| 
         @@ -43,7 +43,7 @@ module ActiveRecord 
     | 
|
| 
       43 
43 
     | 
    
         
             
                #
         
     | 
| 
       44 
44 
     | 
    
         
             
                # == Example
         
     | 
| 
       45 
45 
     | 
    
         
             
                #
         
     | 
| 
       46 
     | 
    
         
            -
                # link 
     | 
| 
      
 46 
     | 
    
         
            +
                # link:files/examples/associations.png
         
     | 
| 
       47 
47 
     | 
    
         
             
                #
         
     | 
| 
       48 
48 
     | 
    
         
             
                # == Is it belongs_to or has_one?
         
     | 
| 
       49 
49 
     | 
    
         
             
                #
         
     | 
| 
         @@ -96,6 +96,30 @@ module ActiveRecord 
     | 
|
| 
       96 
96 
     | 
    
         
             
                # * You can add an object to a collection without automatically saving it by using the #collection.build method (documented below).
         
     | 
| 
       97 
97 
     | 
    
         
             
                # * All unsaved (new_record? == true) members of the collection are automatically saved when the parent is saved.
         
     | 
| 
       98 
98 
     | 
    
         
             
                #
         
     | 
| 
      
 99 
     | 
    
         
            +
                # === Association callbacks
         
     | 
| 
      
 100 
     | 
    
         
            +
                #
         
     | 
| 
      
 101 
     | 
    
         
            +
                # Similiar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get
         
     | 
| 
      
 102 
     | 
    
         
            +
                # trigged when you add an object to or removing an object from a association collection. Example:
         
     | 
| 
      
 103 
     | 
    
         
            +
                #
         
     | 
| 
      
 104 
     | 
    
         
            +
                #   class Project
         
     | 
| 
      
 105 
     | 
    
         
            +
                #     has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
         
     | 
| 
      
 106 
     | 
    
         
            +
                #
         
     | 
| 
      
 107 
     | 
    
         
            +
                #     def evaluate_velocity(developer)
         
     | 
| 
      
 108 
     | 
    
         
            +
                #       ...
         
     | 
| 
      
 109 
     | 
    
         
            +
                #     end
         
     | 
| 
      
 110 
     | 
    
         
            +
                #   end 
         
     | 
| 
      
 111 
     | 
    
         
            +
                #
         
     | 
| 
      
 112 
     | 
    
         
            +
                # It's possible to stack callbacks by passing them as an array. Example:
         
     | 
| 
      
 113 
     | 
    
         
            +
                # 
         
     | 
| 
      
 114 
     | 
    
         
            +
                #   class Project
         
     | 
| 
      
 115 
     | 
    
         
            +
                #     has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
         
     | 
| 
      
 116 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 117 
     | 
    
         
            +
                #
         
     | 
| 
      
 118 
     | 
    
         
            +
                # Possible callbacks are: before_add, after_add, before_remove and after_remove.
         
     | 
| 
      
 119 
     | 
    
         
            +
                #
         
     | 
| 
      
 120 
     | 
    
         
            +
                # Should any of the before_add callbacks throw an exception, the object does not get added to the collection. Same with
         
     | 
| 
      
 121 
     | 
    
         
            +
                # the before_remove callbacks, if an exception is thrown the object doesn't get removed.
         
     | 
| 
      
 122 
     | 
    
         
            +
                #
         
     | 
| 
       99 
123 
     | 
    
         
             
                # == Caching
         
     | 
| 
       100 
124 
     | 
    
         
             
                #
         
     | 
| 
       101 
125 
     | 
    
         
             
                # All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically
         
     | 
| 
         @@ -150,7 +174,8 @@ module ActiveRecord 
     | 
|
| 
       150 
174 
     | 
    
         
             
                # in both conditions and orders. So :order => "posts.id DESC" will work while :order => "id DESC" will not. This may require that
         
     | 
| 
       151 
175 
     | 
    
         
             
                # you alter the :order and :conditions on the association definitions themselves.
         
     | 
| 
       152 
176 
     | 
    
         
             
                #
         
     | 
| 
       153 
     | 
    
         
            -
                # It's currently not possible to use eager loading on multiple associations from the same table.
         
     | 
| 
      
 177 
     | 
    
         
            +
                # It's currently not possible to use eager loading on multiple associations from the same table. Eager loading will also not pull
         
     | 
| 
      
 178 
     | 
    
         
            +
                # additional attributes on join tables, so "rich associations" with has_and_belongs_to_many is not a good fit for eager loading.
         
     | 
| 
       154 
179 
     | 
    
         
             
                #
         
     | 
| 
       155 
180 
     | 
    
         
             
                # == Modules
         
     | 
| 
       156 
181 
     | 
    
         
             
                #
         
     | 
| 
         @@ -199,6 +224,8 @@ module ActiveRecord 
     | 
|
| 
       199 
224 
     | 
    
         
             
                  # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
         
     | 
| 
       200 
225 
     | 
    
         
             
                  # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by setting their foreign keys to NULL.  
         
     | 
| 
       201 
226 
     | 
    
         
             
                  #   This will also destroy the objects if they're declared as belongs_to and dependent on this model.
         
     | 
| 
      
 227 
     | 
    
         
            +
                  # * <tt>collection=objects</tt> - replaces the collections content by deleting and adding objects as appropriate.
         
     | 
| 
      
 228 
     | 
    
         
            +
                  # * <tt>collection_singular_ids=ids</tt> - replace the collection by the objects identified by the primary keys in +ids+
         
     | 
| 
       202 
229 
     | 
    
         
             
                  # * <tt>collection.clear</tt> - removes every object from the collection. This does not destroy the objects.
         
     | 
| 
       203 
230 
     | 
    
         
             
                  # * <tt>collection.empty?</tt> - returns true if there are no associated objects.
         
     | 
| 
       204 
231 
     | 
    
         
             
                  # * <tt>collection.size</tt> - returns the number of associated objects.
         
     | 
| 
         @@ -214,12 +241,14 @@ module ActiveRecord 
     | 
|
| 
       214 
241 
     | 
    
         
             
                  # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => "firm_id = #{id}"</tt>)
         
     | 
| 
       215 
242 
     | 
    
         
             
                  # * <tt>Firm#clients<<</tt>
         
     | 
| 
       216 
243 
     | 
    
         
             
                  # * <tt>Firm#clients.delete</tt>
         
     | 
| 
      
 244 
     | 
    
         
            +
                  # * <tt>Firm#clients=</tt>
         
     | 
| 
      
 245 
     | 
    
         
            +
                  # * <tt>Firm#client_ids=</tt>
         
     | 
| 
       217 
246 
     | 
    
         
             
                  # * <tt>Firm#clients.clear</tt>
         
     | 
| 
       218 
247 
     | 
    
         
             
                  # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
         
     | 
| 
       219 
248 
     | 
    
         
             
                  # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
         
     | 
| 
       220 
249 
     | 
    
         
             
                  # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
         
     | 
| 
       221 
250 
     | 
    
         
             
                  # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
         
     | 
| 
       222 
     | 
    
         
            -
                  # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new(" 
     | 
| 
      
 251 
     | 
    
         
            +
                  # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
         
     | 
| 
       223 
252 
     | 
    
         
             
                  # The declaration can also include an options hash to specialize the behavior of the association.
         
     | 
| 
       224 
253 
     | 
    
         
             
                  # 
         
     | 
| 
       225 
254 
     | 
    
         
             
                  # Options are:
         
     | 
| 
         @@ -254,7 +283,8 @@ module ActiveRecord 
     | 
|
| 
       254 
283 
     | 
    
         
             
                  #       'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
         
     | 
| 
       255 
284 
     | 
    
         
             
                  #       'ORDER BY p.first_name'
         
     | 
| 
       256 
285 
     | 
    
         
             
                  def has_many(association_id, options = {})
         
     | 
| 
       257 
     | 
    
         
            -
                    validate_options([ :foreign_key, :class_name, :exclusively_dependent, :dependent, :conditions, :order, :finder_sql, :counter_sql 
     | 
| 
      
 286 
     | 
    
         
            +
                    validate_options([ :foreign_key, :class_name, :exclusively_dependent, :dependent, :conditions, :order, :finder_sql, :counter_sql, 
         
     | 
| 
      
 287 
     | 
    
         
            +
            													 :before_add, :after_add, :before_remove, :after_remove ], options.keys)
         
     | 
| 
       258 
288 
     | 
    
         
             
                    association_name, association_class_name, association_class_primary_key_name =
         
     | 
| 
       259 
289 
     | 
    
         
             
                          associate_identification(association_id, options[:class_name], options[:foreign_key])
         
     | 
| 
       260 
290 
     | 
    
         | 
| 
         @@ -271,7 +301,8 @@ module ActiveRecord 
     | 
|
| 
       271 
301 
     | 
    
         
             
                    end
         
     | 
| 
       272 
302 
     | 
    
         | 
| 
       273 
303 
     | 
    
         
             
                    add_multiple_associated_save_callbacks(association_name)
         
     | 
| 
       274 
     | 
    
         
            -
             
     | 
| 
      
 304 
     | 
    
         
            +
            				add_association_callbacks(association_name, options)
         
     | 
| 
      
 305 
     | 
    
         
            +
            				
         
     | 
| 
       275 
306 
     | 
    
         
             
                    collection_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, HasManyAssociation)
         
     | 
| 
       276 
307 
     | 
    
         | 
| 
       277 
308 
     | 
    
         
             
                    # deprecated api
         
     | 
| 
         @@ -299,7 +330,7 @@ module ActiveRecord 
     | 
|
| 
       299 
330 
     | 
    
         
             
                  #   with +attributes+ and linked to this object through a foreign key and that has already been saved (if it passed the validation).
         
     | 
| 
       300 
331 
     | 
    
         
             
                  #
         
     | 
| 
       301 
332 
     | 
    
         
             
                  # Example: An Account class declares <tt>has_one :beneficiary</tt>, which will add:
         
     | 
| 
       302 
     | 
    
         
            -
                  # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary. 
     | 
| 
      
 333 
     | 
    
         
            +
                  # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.find(:first, :conditions => "account_id = #{id}")</tt>)
         
     | 
| 
       303 
334 
     | 
    
         
             
                  # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
         
     | 
| 
       304 
335 
     | 
    
         
             
                  # * <tt>Account#beneficiary.nil?</tt>
         
     | 
| 
       305 
336 
     | 
    
         
             
                  # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
         
     | 
| 
         @@ -371,8 +402,8 @@ module ActiveRecord 
     | 
|
| 
       371 
402 
     | 
    
         
             
                  # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
         
     | 
| 
       372 
403 
     | 
    
         
             
                  # * <tt>Post#author?</tt> (similar to <tt>post.author == some_author</tt>)
         
     | 
| 
       373 
404 
     | 
    
         
             
                  # * <tt>Post#author.nil?</tt>
         
     | 
| 
       374 
     | 
    
         
            -
                  # * <tt>Post#build_author</tt> (similar to <tt>Author.new 
     | 
| 
       375 
     | 
    
         
            -
                  # * <tt>Post#create_author</tt> (similar to <tt> 
     | 
| 
      
 405 
     | 
    
         
            +
                  # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
         
     | 
| 
      
 406 
     | 
    
         
            +
                  # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
         
     | 
| 
       376 
407 
     | 
    
         
             
                  # The declaration can also include an options hash to specialize the behavior of the association.
         
     | 
| 
       377 
408 
     | 
    
         
             
                  # 
         
     | 
| 
       378 
409 
     | 
    
         
             
                  # Options are:
         
     | 
| 
         @@ -447,7 +478,6 @@ module ActiveRecord 
     | 
|
| 
       447 
478 
     | 
    
         
             
                  # that you want available on retrieval. Note that any fields in the join table will override matching field names
         
     | 
| 
       448 
479 
     | 
    
         
             
                  # in the two joined tables. As a consequence, having an "id" field in the join table usually has the undesirable
         
     | 
| 
       449 
480 
     | 
    
         
             
                  # result of clobbering the "id" fields in either of the other two tables.
         
     | 
| 
       450 
     | 
    
         
            -
                  # 
         
     | 
| 
       451 
481 
     | 
    
         
             
                  #
         
     | 
| 
       452 
482 
     | 
    
         
             
                  # Adds the following methods for retrieval and query.
         
     | 
| 
       453 
483 
     | 
    
         
             
                  # +collection+ is replaced with the symbol passed as the first argument, so 
         
     | 
| 
         @@ -462,6 +492,8 @@ module ActiveRecord 
     | 
|
| 
       462 
492 
     | 
    
         
             
                  #   (collection.concat_with_attributes is an alias to this method).
         
     | 
| 
       463 
493 
     | 
    
         
             
                  # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by removing their associations from the join table.  
         
     | 
| 
       464 
494 
     | 
    
         
             
                  #   This does not destroy the objects.
         
     | 
| 
      
 495 
     | 
    
         
            +
                  # * <tt>collection=objects</tt> - replaces the collections content by deleting and adding objects as appropriate.
         
     | 
| 
      
 496 
     | 
    
         
            +
                  # * <tt>collection_singular_ids=ids</tt> - replace the collection by the objects identified by the primary keys in +ids+
         
     | 
| 
       465 
497 
     | 
    
         
             
                  # * <tt>collection.clear</tt> - removes every object from the collection. This does not destroy the objects.
         
     | 
| 
       466 
498 
     | 
    
         
             
                  # * <tt>collection.empty?</tt> - returns true if there are no associated objects.
         
     | 
| 
       467 
499 
     | 
    
         
             
                  # * <tt>collection.size</tt> - returns the number of associated objects.
         
     | 
| 
         @@ -473,6 +505,8 @@ module ActiveRecord 
     | 
|
| 
       473 
505 
     | 
    
         
             
                  # * <tt>Developer#projects<<</tt>
         
     | 
| 
       474 
506 
     | 
    
         
             
                  # * <tt>Developer#projects.push_with_attributes</tt>
         
     | 
| 
       475 
507 
     | 
    
         
             
                  # * <tt>Developer#projects.delete</tt>
         
     | 
| 
      
 508 
     | 
    
         
            +
                  # * <tt>Developer#projects=</tt>
         
     | 
| 
      
 509 
     | 
    
         
            +
                  # * <tt>Developer#project_ids=</tt>
         
     | 
| 
       476 
510 
     | 
    
         
             
                  # * <tt>Developer#projects.clear</tt>
         
     | 
| 
       477 
511 
     | 
    
         
             
                  # * <tt>Developer#projects.empty?</tt>
         
     | 
| 
       478 
512 
     | 
    
         
             
                  # * <tt>Developer#projects.size</tt>
         
     | 
| 
         @@ -506,9 +540,12 @@ module ActiveRecord 
     | 
|
| 
       506 
540 
     | 
    
         
             
                  #   has_and_belongs_to_many :projects
         
     | 
| 
       507 
541 
     | 
    
         
             
                  #   has_and_belongs_to_many :nations, :class_name => "Country"
         
     | 
| 
       508 
542 
     | 
    
         
             
                  #   has_and_belongs_to_many :categories, :join_table => "prods_cats"
         
     | 
| 
      
 543 
     | 
    
         
            +
                  #   has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql => 
         
     | 
| 
      
 544 
     | 
    
         
            +
                  #   'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
         
     | 
| 
       509 
545 
     | 
    
         
             
                  def has_and_belongs_to_many(association_id, options = {})
         
     | 
| 
       510 
546 
     | 
    
         
             
                    validate_options([ :class_name, :table_name, :foreign_key, :association_foreign_key, :conditions,
         
     | 
| 
       511 
     | 
    
         
            -
                                       :join_table, :finder_sql, :delete_sql, :insert_sql, :order, :uniq  
     | 
| 
      
 547 
     | 
    
         
            +
                                       :join_table, :finder_sql, :delete_sql, :insert_sql, :order, :uniq, :before_add, :after_add, 
         
     | 
| 
      
 548 
     | 
    
         
            +
                                       :before_remove, :after_remove ], options.keys)
         
     | 
| 
       512 
549 
     | 
    
         
             
                    association_name, association_class_name, association_class_primary_key_name =
         
     | 
| 
       513 
550 
     | 
    
         
             
                          associate_identification(association_id, options[:class_name], options[:foreign_key])
         
     | 
| 
       514 
551 
     | 
    
         | 
| 
         @@ -522,6 +559,7 @@ module ActiveRecord 
     | 
|
| 
       522 
559 
     | 
    
         | 
| 
       523 
560 
     | 
    
         
             
                    before_destroy_sql = "DELETE FROM #{options[:join_table]} WHERE #{association_class_primary_key_name} = \\\#{self.quoted_id}"
         
     | 
| 
       524 
561 
     | 
    
         
             
                    module_eval(%{before_destroy "self.connection.delete(%{#{before_destroy_sql}})"}) # "
         
     | 
| 
      
 562 
     | 
    
         
            +
                    add_association_callbacks(association_name, options)
         
     | 
| 
       525 
563 
     | 
    
         | 
| 
       526 
564 
     | 
    
         
             
                    # deprecated api
         
     | 
| 
       527 
565 
     | 
    
         
             
                    deprecated_collection_count_method(association_name)
         
     | 
| 
         @@ -631,6 +669,10 @@ module ActiveRecord 
     | 
|
| 
       631 
669 
     | 
    
         
             
                        association.replace(new_value)
         
     | 
| 
       632 
670 
     | 
    
         
             
                        association
         
     | 
| 
       633 
671 
     | 
    
         
             
                      end
         
     | 
| 
      
 672 
     | 
    
         
            +
             
     | 
| 
      
 673 
     | 
    
         
            +
                      define_method("#{Inflector.singularize(association_name)}_ids=") do |new_value|
         
     | 
| 
      
 674 
     | 
    
         
            +
                        send("#{association_name}=", association_class_name.constantize.find(new_value))
         
     | 
| 
      
 675 
     | 
    
         
            +
                      end
         
     | 
| 
       634 
676 
     | 
    
         
             
                    end
         
     | 
| 
       635 
677 
     | 
    
         | 
| 
       636 
678 
     | 
    
         
             
                    def require_association_class(class_name)
         
     | 
| 
         @@ -656,7 +698,7 @@ module ActiveRecord 
     | 
|
| 
       656 
698 
     | 
    
         
             
                      end
         
     | 
| 
       657 
699 
     | 
    
         | 
| 
       658 
700 
     | 
    
         
             
                      module_eval do
         
     | 
| 
       659 
     | 
    
         
            -
                         
     | 
| 
      
 701 
     | 
    
         
            +
                        after_callback = <<-end_eval
         
     | 
| 
       660 
702 
     | 
    
         
             
                          association = instance_variable_get("@#{association_name}")
         
     | 
| 
       661 
703 
     | 
    
         
             
                          if association.respond_to?(:loaded?)
         
     | 
| 
       662 
704 
     | 
    
         
             
                            if @new_record_before_save
         
     | 
| 
         @@ -668,13 +710,18 @@ module ActiveRecord 
     | 
|
| 
       668 
710 
     | 
    
         
             
                            association.send(:construct_sql)   # reconstruct the SQL queries now that we know the owner's id
         
     | 
| 
       669 
711 
     | 
    
         
             
                          end
         
     | 
| 
       670 
712 
     | 
    
         
             
                        end_eval
         
     | 
| 
      
 713 
     | 
    
         
            +
             
     | 
| 
      
 714 
     | 
    
         
            +
                        # Doesn't use after_save as that would save associations added in after_create/after_update twice
         
     | 
| 
      
 715 
     | 
    
         
            +
                        after_create(after_callback)
         
     | 
| 
      
 716 
     | 
    
         
            +
                        after_update(after_callback)
         
     | 
| 
       671 
717 
     | 
    
         
             
                      end
         
     | 
| 
       672 
718 
     | 
    
         
             
                    end
         
     | 
| 
       673 
719 
     | 
    
         | 
| 
       674 
720 
     | 
    
         
             
                    def association_constructor_method(constructor, association_name, association_class_name, association_class_primary_key_name, options, association_proxy_class)
         
     | 
| 
       675 
721 
     | 
    
         
             
                      define_method("#{constructor}_#{association_name}") do |*params|
         
     | 
| 
       676 
     | 
    
         
            -
                        attributees 
     | 
| 
       677 
     | 
    
         
            -
                         
     | 
| 
      
 722 
     | 
    
         
            +
                        attributees      = params.first unless params.empty?
         
     | 
| 
      
 723 
     | 
    
         
            +
                        replace_existing = params[1].nil? ? true : params[1]
         
     | 
| 
      
 724 
     | 
    
         
            +
                        association      = instance_variable_get("@#{association_name}")
         
     | 
| 
       678 
725 
     | 
    
         | 
| 
       679 
726 
     | 
    
         
             
                        if association.nil?
         
     | 
| 
       680 
727 
     | 
    
         
             
                          association = association_proxy_class.new(self,
         
     | 
| 
         @@ -683,7 +730,11 @@ module ActiveRecord 
     | 
|
| 
       683 
730 
     | 
    
         
             
                          instance_variable_set("@#{association_name}", association)
         
     | 
| 
       684 
731 
     | 
    
         
             
                        end
         
     | 
| 
       685 
732 
     | 
    
         | 
| 
       686 
     | 
    
         
            -
                         
     | 
| 
      
 733 
     | 
    
         
            +
                        if association_proxy_class == HasOneAssociation
         
     | 
| 
      
 734 
     | 
    
         
            +
                          association.send(constructor, attributees, replace_existing)
         
     | 
| 
      
 735 
     | 
    
         
            +
                        else
         
     | 
| 
      
 736 
     | 
    
         
            +
                          association.send(constructor, attributees)
         
     | 
| 
      
 737 
     | 
    
         
            +
                        end
         
     | 
| 
       687 
738 
     | 
    
         
             
                      end
         
     | 
| 
       688 
739 
     | 
    
         
             
                    end
         
     | 
| 
       689 
740 
     | 
    
         | 
| 
         @@ -715,7 +766,7 @@ module ActiveRecord 
     | 
|
| 
       715 
766 
     | 
    
         
             
                              next unless row[primary_key_table[reflection.table_name]]
         
     | 
| 
       716 
767 
     | 
    
         | 
| 
       717 
768 
     | 
    
         
             
                              record.send(
         
     | 
| 
       718 
     | 
    
         
            -
                                "#{reflection.name} 
     | 
| 
      
 769 
     | 
    
         
            +
                                "set_#{reflection.name}_target", 
         
     | 
| 
       719 
770 
     | 
    
         
             
                                reflection.klass.send(:instantiate, extract_record(schema_abbreviations, reflection.table_name, row))
         
     | 
| 
       720 
771 
     | 
    
         
             
                              )
         
     | 
| 
       721 
772 
     | 
    
         
             
                          end
         
     | 
| 
         @@ -770,11 +821,21 @@ module ActiveRecord 
     | 
|
| 
       770 
821 
     | 
    
         
             
                      sql << reflections.collect { |reflection| association_join(reflection) }.to_s
         
     | 
| 
       771 
822 
     | 
    
         
             
                      sql << "#{options[:joins]} " if options[:joins]
         
     | 
| 
       772 
823 
     | 
    
         
             
                      add_conditions!(sql, options[:conditions])
         
     | 
| 
      
 824 
     | 
    
         
            +
                      add_sti_conditions!(sql, reflections)
         
     | 
| 
       773 
825 
     | 
    
         
             
                      sql << "ORDER BY #{options[:order]} " if options[:order]
         
     | 
| 
       774 
826 
     | 
    
         | 
| 
       775 
827 
     | 
    
         
             
                      return sanitize_sql(sql)
         
     | 
| 
       776 
828 
     | 
    
         
             
                    end
         
     | 
| 
       777 
829 
     | 
    
         | 
| 
      
 830 
     | 
    
         
            +
                    def add_sti_conditions!(sql, reflections)
         
     | 
| 
      
 831 
     | 
    
         
            +
                      sti_sql = ""
         
     | 
| 
      
 832 
     | 
    
         
            +
                      reflections.each do |reflection|
         
     | 
| 
      
 833 
     | 
    
         
            +
                        sti_sql << " AND #{reflection.klass.send(:type_condition)}" unless reflection.klass.descends_from_active_record?
         
     | 
| 
      
 834 
     | 
    
         
            +
                      end
         
     | 
| 
      
 835 
     | 
    
         
            +
                      sti_sql.sub!(/AND/, "WHERE") unless sql =~ /where/i
         
     | 
| 
      
 836 
     | 
    
         
            +
                      sql << sti_sql
         
     | 
| 
      
 837 
     | 
    
         
            +
                    end
         
     | 
| 
      
 838 
     | 
    
         
            +
             
     | 
| 
       778 
839 
     | 
    
         
             
                    def column_aliases(schema_abbreviations)
         
     | 
| 
       779 
840 
     | 
    
         
             
                      schema_abbreviations.collect { |cn, tc| "#{tc.join(".")} AS #{cn}" }.join(", ")
         
     | 
| 
       780 
841 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -786,7 +847,7 @@ module ActiveRecord 
     | 
|
| 
       786 
847 
     | 
    
         
             
                          "#{reflection.options[:join_table]}.#{reflection.options[:foreign_key] || table_name.classify.foreign_key} = " +
         
     | 
| 
       787 
848 
     | 
    
         
             
                          "#{table_name}.#{primary_key} " +
         
     | 
| 
       788 
849 
     | 
    
         
             
                          " LEFT OUTER JOIN #{reflection.klass.table_name} ON " +
         
     | 
| 
       789 
     | 
    
         
            -
                          "#{reflection.options[:join_table]}.#{reflection.options[: 
     | 
| 
      
 850 
     | 
    
         
            +
                          "#{reflection.options[:join_table]}.#{reflection.options[:association_foreign_key] || reflection.klass.table_name.classify.foreign_key} = " +
         
     | 
| 
       790 
851 
     | 
    
         
             
                          "#{reflection.klass.table_name}.#{reflection.klass.primary_key} "
         
     | 
| 
       791 
852 
     | 
    
         
             
                        when :has_many, :has_one
         
     | 
| 
       792 
853 
     | 
    
         
             
                          " LEFT OUTER JOIN #{reflection.klass.table_name} ON " +
         
     | 
| 
         @@ -801,6 +862,18 @@ module ActiveRecord 
     | 
|
| 
       801 
862 
     | 
    
         
             
                      end          
         
     | 
| 
       802 
863 
     | 
    
         
             
                    end
         
     | 
| 
       803 
864 
     | 
    
         | 
| 
      
 865 
     | 
    
         
            +
                    def add_association_callbacks(association_name, options)
         
     | 
| 
      
 866 
     | 
    
         
            +
                    	callbacks = %w(before_add after_add before_remove after_remove)
         
     | 
| 
      
 867 
     | 
    
         
            +
                    	callbacks.each do |callback_name|
         
     | 
| 
      
 868 
     | 
    
         
            +
                    	  full_callback_name = "#{callback_name.to_s}_for_#{association_name.to_s}"
         
     | 
| 
      
 869 
     | 
    
         
            +
                    	  defined_callbacks = options[callback_name.to_sym]
         
     | 
| 
      
 870 
     | 
    
         
            +
                    	  if options.has_key?(callback_name.to_sym)
         
     | 
| 
      
 871 
     | 
    
         
            +
                    	    callback_array = defined_callbacks.kind_of?(Array) ? defined_callbacks : [defined_callbacks]
         
     | 
| 
      
 872 
     | 
    
         
            +
                    	    class_inheritable_reader full_callback_name.to_sym
         
     | 
| 
      
 873 
     | 
    
         
            +
                          write_inheritable_array(full_callback_name.to_sym, callback_array)
         
     | 
| 
      
 874 
     | 
    
         
            +
                    	  end
         
     | 
| 
      
 875 
     | 
    
         
            +
                    	end
         
     | 
| 
      
 876 
     | 
    
         
            +
                    end
         
     | 
| 
       804 
877 
     | 
    
         | 
| 
       805 
878 
     | 
    
         
             
                    def extract_record(schema_abbreviations, table_name, row)
         
     | 
| 
       806 
879 
     | 
    
         
             
                      record = {}
         
     | 
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module ActiveRecord
         
     | 
| 
       2 
4 
     | 
    
         
             
              module Associations
         
     | 
| 
       3 
5 
     | 
    
         
             
                class AssociationCollection < AssociationProxy #:nodoc:
         
     | 
| 
         @@ -19,11 +21,13 @@ module ActiveRecord 
     | 
|
| 
       19 
21 
     | 
    
         
             
                    @owner.transaction do
         
     | 
| 
       20 
22 
     | 
    
         
             
                      flatten_deeper(records).each do |record|
         
     | 
| 
       21 
23 
     | 
    
         
             
                        raise_on_type_mismatch(record)
         
     | 
| 
      
 24 
     | 
    
         
            +
                        callback(:before_add, record)
         
     | 
| 
       22 
25 
     | 
    
         
             
                        result &&= insert_record(record) unless @owner.new_record?
         
     | 
| 
       23 
26 
     | 
    
         
             
                        @target << record
         
     | 
| 
      
 27 
     | 
    
         
            +
                        callback(:after_add, record)
         
     | 
| 
       24 
28 
     | 
    
         
             
                      end
         
     | 
| 
       25 
29 
     | 
    
         
             
                    end
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 30 
     | 
    
         
            +
            				
         
     | 
| 
       27 
31 
     | 
    
         
             
                    result and self
         
     | 
| 
       28 
32 
     | 
    
         
             
                  end
         
     | 
| 
       29 
33 
     | 
    
         | 
| 
         @@ -38,8 +42,12 @@ module ActiveRecord 
     | 
|
| 
       38 
42 
     | 
    
         
             
                    return if records.empty?
         
     | 
| 
       39 
43 
     | 
    
         | 
| 
       40 
44 
     | 
    
         
             
                    @owner.transaction do
         
     | 
| 
      
 45 
     | 
    
         
            +
                      records.each { |record| callback(:before_remove, record) }
         
     | 
| 
       41 
46 
     | 
    
         
             
                      delete_records(records)
         
     | 
| 
       42 
     | 
    
         
            -
                      records.each  
     | 
| 
      
 47 
     | 
    
         
            +
                      records.each do |record|
         
     | 
| 
      
 48 
     | 
    
         
            +
                        @target.delete(record)
         
     | 
| 
      
 49 
     | 
    
         
            +
                        callback(:after_remove, record)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      end
         
     | 
| 
       43 
51 
     | 
    
         
             
                    end
         
     | 
| 
       44 
52 
     | 
    
         
             
                  end
         
     | 
| 
       45 
53 
     | 
    
         | 
| 
         @@ -83,11 +91,19 @@ module ActiveRecord 
     | 
|
| 
       83 
91 
     | 
    
         
             
                    collection.inject([]) { |uniq_records, record| uniq_records << record unless uniq_records.include?(record); uniq_records }
         
     | 
| 
       84 
92 
     | 
    
         
             
                  end
         
     | 
| 
       85 
93 
     | 
    
         | 
| 
      
 94 
     | 
    
         
            +
                  # Replace this collection with +other_array+
         
     | 
| 
      
 95 
     | 
    
         
            +
                  # This will perform a diff and delete/add only records that have changed.
         
     | 
| 
       86 
96 
     | 
    
         
             
                  def replace(other_array)
         
     | 
| 
       87 
     | 
    
         
            -
                    other_array.each{ |val| raise_on_type_mismatch(val) }
         
     | 
| 
      
 97 
     | 
    
         
            +
                    other_array.each { |val| raise_on_type_mismatch(val) }
         
     | 
| 
       88 
98 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
                     
     | 
| 
       90 
     | 
    
         
            -
                     
     | 
| 
      
 99 
     | 
    
         
            +
                    load_target
         
     | 
| 
      
 100 
     | 
    
         
            +
                    other   = other_array.size < 100 ? other_array : other_array.to_set
         
     | 
| 
      
 101 
     | 
    
         
            +
                    current = @target.size < 100 ? @target : @target.to_set
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                    @owner.transaction do
         
     | 
| 
      
 104 
     | 
    
         
            +
                      delete(@target.select { |v| !other.include?(v) })
         
     | 
| 
      
 105 
     | 
    
         
            +
                      concat(other_array.select { |v| !current.include?(v) })
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
       91 
107 
     | 
    
         
             
                  end
         
     | 
| 
       92 
108 
     | 
    
         | 
| 
       93 
109 
     | 
    
         
             
                  private
         
     | 
| 
         @@ -103,6 +119,29 @@ module ActiveRecord 
     | 
|
| 
       103 
119 
     | 
    
         
             
                    def flatten_deeper(array)
         
     | 
| 
       104 
120 
     | 
    
         
             
                      array.collect { |element| element.respond_to?(:flatten) ? element.flatten : element }.flatten
         
     | 
| 
       105 
121 
     | 
    
         
             
                    end
         
     | 
| 
      
 122 
     | 
    
         
            +
                    
         
     | 
| 
      
 123 
     | 
    
         
            +
                    def callback(method, record)
         
     | 
| 
      
 124 
     | 
    
         
            +
                      callbacks_for(method).each do |callback|
         
     | 
| 
      
 125 
     | 
    
         
            +
                        case callback
         
     | 
| 
      
 126 
     | 
    
         
            +
                          when Symbol
         
     | 
| 
      
 127 
     | 
    
         
            +
                            @owner.send(callback, record)
         
     | 
| 
      
 128 
     | 
    
         
            +
                          when Proc, Method
         
     | 
| 
      
 129 
     | 
    
         
            +
                            callback.call(@owner, record)
         
     | 
| 
      
 130 
     | 
    
         
            +
                          else
         
     | 
| 
      
 131 
     | 
    
         
            +
                            if callback.respond_to?(method)
         
     | 
| 
      
 132 
     | 
    
         
            +
                              callback.send(method, @owner, record)
         
     | 
| 
      
 133 
     | 
    
         
            +
                            else
         
     | 
| 
      
 134 
     | 
    
         
            +
                              raise ActiveRecordError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method."
         
     | 
| 
      
 135 
     | 
    
         
            +
                            end
         
     | 
| 
      
 136 
     | 
    
         
            +
                        end
         
     | 
| 
      
 137 
     | 
    
         
            +
                      end
         
     | 
| 
      
 138 
     | 
    
         
            +
                    end
         
     | 
| 
      
 139 
     | 
    
         
            +
                    
         
     | 
| 
      
 140 
     | 
    
         
            +
                    def callbacks_for(callback_name)
         
     | 
| 
      
 141 
     | 
    
         
            +
                      full_callback_name = "#{callback_name.to_s}_for_#{@association_name.to_s}"
         
     | 
| 
      
 142 
     | 
    
         
            +
                      @owner.class.read_inheritable_attribute(full_callback_name.to_sym) or []
         
     | 
| 
      
 143 
     | 
    
         
            +
                    end
         
     | 
| 
      
 144 
     | 
    
         
            +
                    
         
     | 
| 
       106 
145 
     | 
    
         
             
                end
         
     | 
| 
       107 
146 
     | 
    
         
             
              end
         
     | 
| 
       108 
147 
     | 
    
         
             
            end
         
     | 
| 
         @@ -66,7 +66,7 @@ module ActiveRecord 
     | 
|
| 
       66 
66 
     | 
    
         
             
                    # Otherwise, construct a query.
         
     | 
| 
       67 
67 
     | 
    
         
             
                    else
         
     | 
| 
       68 
68 
     | 
    
         
             
                      ids_list = ids.map { |id| @owner.send(:quote, id) }.join(',')
         
     | 
| 
       69 
     | 
    
         
            -
                      records = find_target(@finder_sql.sub(/(ORDER BY|$)/, "AND j.#{@association_foreign_key} IN (#{ids_list}) \\1"))
         
     | 
| 
      
 69 
     | 
    
         
            +
                      records = find_target(@finder_sql.sub(/(ORDER BY|$)/, " AND j.#{@association_foreign_key} IN (#{ids_list}) \\1"))
         
     | 
| 
       70 
70 
     | 
    
         
             
                      if records.size == ids.size
         
     | 
| 
       71 
71 
     | 
    
         
             
                        if ids.size == 1 and !expects_array
         
     | 
| 
       72 
72 
     | 
    
         
             
                          records.first
         
     | 
| 
         @@ -82,8 +82,10 @@ module ActiveRecord 
     | 
|
| 
       82 
82 
     | 
    
         
             
                  def push_with_attributes(record, join_attributes = {})
         
     | 
| 
       83 
83 
     | 
    
         
             
                    raise_on_type_mismatch(record)
         
     | 
| 
       84 
84 
     | 
    
         
             
                    join_attributes.each { |key, value| record[key.to_s] = value }
         
     | 
| 
      
 85 
     | 
    
         
            +
                    callback(:before_add, record)
         
     | 
| 
       85 
86 
     | 
    
         
             
                    insert_record(record) unless @owner.new_record?
         
     | 
| 
       86 
87 
     | 
    
         
             
                    @target << record
         
     | 
| 
      
 88 
     | 
    
         
            +
                    callback(:after_add, record)
         
     | 
| 
       87 
89 
     | 
    
         
             
                    self
         
     | 
| 
       88 
90 
     | 
    
         
             
                  end
         
     | 
| 
       89 
91 
     | 
    
         | 
| 
         @@ -104,7 +106,9 @@ module ActiveRecord 
     | 
|
| 
       104 
106 
     | 
    
         
             
                    end
         
     | 
| 
       105 
107 
     | 
    
         | 
| 
       106 
108 
     | 
    
         
             
                    def insert_record(record)
         
     | 
| 
       107 
     | 
    
         
            -
                       
     | 
| 
      
 109 
     | 
    
         
            +
                      if record.new_record?
         
     | 
| 
      
 110 
     | 
    
         
            +
                        return false unless record.save
         
     | 
| 
      
 111 
     | 
    
         
            +
                      end
         
     | 
| 
       108 
112 
     | 
    
         | 
| 
       109 
113 
     | 
    
         
             
                      if @options[:insert_sql]
         
     | 
| 
       110 
114 
     | 
    
         
             
                        @owner.connection.execute(interpolate_sql(@options[:insert_sql], record))
         
     | 
| 
         @@ -136,7 +140,7 @@ module ActiveRecord 
     | 
|
| 
       136 
140 
     | 
    
         | 
| 
       137 
141 
     | 
    
         
             
                    def delete_records(records)
         
     | 
| 
       138 
142 
     | 
    
         
             
                      if sql = @options[:delete_sql]
         
     | 
| 
       139 
     | 
    
         
            -
                        records.each { |record| @owner.connection.execute(sql) }
         
     | 
| 
      
 143 
     | 
    
         
            +
                        records.each { |record| @owner.connection.execute(interpolate_sql(sql, record)) }
         
     | 
| 
       140 
144 
     | 
    
         
             
                      else
         
     | 
| 
       141 
145 
     | 
    
         
             
                        ids = quoted_record_ids(records)
         
     | 
| 
       142 
146 
     | 
    
         
             
                        sql = "DELETE FROM #{@join_table} WHERE #{@association_class_primary_key_name} = #{@owner.quoted_id} AND #{@association_foreign_key} IN (#{ids})"
         
     | 
| 
         @@ -145,7 +149,7 @@ module ActiveRecord 
     | 
|
| 
       145 
149 
     | 
    
         
             
                    end
         
     | 
| 
       146 
150 
     | 
    
         | 
| 
       147 
151 
     | 
    
         
             
                    def construct_sql
         
     | 
| 
       148 
     | 
    
         
            -
                      interpolate_sql_options!(@options, :finder_sql 
     | 
| 
      
 152 
     | 
    
         
            +
                      interpolate_sql_options!(@options, :finder_sql)
         
     | 
| 
       149 
153 
     | 
    
         | 
| 
       150 
154 
     | 
    
         
             
                      if @options[:finder_sql]
         
     | 
| 
       151 
155 
     | 
    
         
             
                        @finder_sql = @options[:finder_sql]
         
     | 
| 
         @@ -156,6 +160,15 @@ module ActiveRecord 
     | 
|
| 
       156 
160 
     | 
    
         
             
                          "j.#{@association_class_primary_key_name} = #{@owner.quoted_id} "
         
     | 
| 
       157 
161 
     | 
    
         | 
| 
       158 
162 
     | 
    
         
             
                        @finder_sql << " AND #{interpolate_sql(@options[:conditions])}" if @options[:conditions]
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                        unless @association_class.descends_from_active_record?
         
     | 
| 
      
 165 
     | 
    
         
            +
                          type_condition = @association_class.send(:subclasses).inject("t.#{@association_class.inheritance_column} = '#{@association_class.name.demodulize}' ") do |condition, subclass| 
         
     | 
| 
      
 166 
     | 
    
         
            +
                            condition << "OR t.#{@association_class.inheritance_column} = '#{subclass.name.demodulize}' "
         
     | 
| 
      
 167 
     | 
    
         
            +
                          end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                          @finder_sql << " AND (#{type_condition})"
         
     | 
| 
      
 170 
     | 
    
         
            +
                        end
         
     | 
| 
      
 171 
     | 
    
         
            +
            	
         
     | 
| 
       159 
172 
     | 
    
         
             
                        @finder_sql << " ORDER BY #{@order}" if @order
         
     | 
| 
       160 
173 
     | 
    
         
             
                      end
         
     | 
| 
       161 
174 
     | 
    
         
             
                    end
         
     |