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

Changeset 8481

Show
Ignore:
Timestamp:
12/22/07 11:26:03 (1 year ago)
Author:
bitsweat
Message:

Ruby 1.9 compat: fix warnings, shadowed block vars, and unitialized instance vars

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activerecord/lib/active_record/associations.rb

    r8456 r8481  
    2020    end 
    2121  end 
    22    
     22 
    2323  class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc: 
    2424    def initialize(owner_class_name, reflection, source_reflection) 
     
    2626    end 
    2727  end 
    28    
     28 
    2929  class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc: 
    3030    def initialize(reflection) 
     
    7373    end 
    7474 
    75     # Clears out the association cache  
     75    # Clears out the association cache 
    7676    def clear_association_cache #:nodoc: 
    7777      self.class.reflect_on_all_associations.to_a.each do |assoc| 
     
    7979      end unless self.new_record? 
    8080    end 
    81      
    82     # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like  
    83     # "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are  
    84     # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own <tt>attr*</tt>  
     81 
     82    # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like 
     83    # "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are 
     84    # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own <tt>attr*</tt> 
    8585    # methods. Example: 
    8686    # 
    8787    #   class Project < ActiveRecord::Base 
    8888    #     belongs_to              :portfolio 
    89     #     has_one                 :project_manager  
     89    #     has_one                 :project_manager 
    9090    #     has_many                :milestones 
    9191    #     has_and_belongs_to_many :categories 
     
    118118    #   #create_other(attributes={})      |     X      |              |    X 
    119119    #   #other.create!(attributes={})     |            |              |    X 
    120     #   #other.nil?                       |     X      |      X       |     
     120    #   #other.nil?                       |     X      |      X       | 
    121121    # 
    122122    # ===Collection associations (one-to-many / many-to-many) 
    123123    #                                     |       |          | has_many 
    124     #   generated methods                 | habtm | has_many | :through   
     124    #   generated methods                 | habtm | has_many | :through 
    125125    #   ----------------------------------+-------+----------+---------- 
    126126    #   #others                           |   X   |    X     |    X 
    127     #   #others=(other,other,...)         |   X   |    X     |     
     127    #   #others=(other,other,...)         |   X   |    X     | 
    128128    #   #other_ids                        |   X   |    X     |    X 
    129     #   #other_ids=(id,id,...)            |   X   |    X     |     
     129    #   #other_ids=(id,id,...)            |   X   |    X     | 
    130130    #   #others<<                         |   X   |    X     |    X 
    131131    #   #others.push                      |   X   |    X     |    X 
    132132    #   #others.concat                    |   X   |    X     |    X 
    133133    #   #others.build(attributes={})      |   X   |    X     |    X 
    134     #   #others.create(attributes={})     |   X   |    X     |     
     134    #   #others.create(attributes={})     |   X   |    X     | 
    135135    #   #others.create!(attributes={})    |   X   |    X     |    X 
    136136    #   #others.size                      |   X   |    X     |    X 
     
    139139    #   #others.sum(args*,&block)         |   X   |    X     |    X 
    140140    #   #others.empty?                    |   X   |    X     |    X 
    141     #   #others.clear                     |   X   |    X     |     
     141    #   #others.clear                     |   X   |    X     | 
    142142    #   #others.delete(other,other,...)   |   X   |    X     |    X 
    143     #   #others.delete_all                |   X   |    X     |     
     143    #   #others.delete_all                |   X   |    X     | 
    144144    #   #others.destroy_all               |   X   |    X     |    X 
    145145    #   #others.find(*args)               |   X   |    X     |    X 
    146     #   #others.find_first                |   X   |          |     
    147     #   #others.uniq                      |   X   |    X     |     
     146    #   #others.find_first                |   X   |          | 
     147    #   #others.uniq                      |   X   |    X     | 
    148148    #   #others.reset                     |   X   |    X     |    X 
    149149    # 
    150150    # == Cardinality and associations 
    151     #  
     151    # 
    152152    # ActiveRecord associations can be used to describe relations with one-to-one, one-to-many 
    153153    # and many-to-many cardinality. Each model uses an association to describe its role in 
     
    208208    # 
    209209    # Choosing which way to build a many-to-many relationship is not always simple. 
    210     # If you need to work with the relationship model as its own entity,  
     210    # If you need to work with the relationship model as its own entity, 
    211211    # use <tt>has_many :through</tt>. Use +has_and_belongs_to_many+ when working with legacy schemas or when 
    212212    # you never work directly with the relationship itself. 
     
    254254    #   is cancelled. 
    255255    # * If you wish to assign an object to a +has_one+ association without saving it, use the <tt>#association.build</tt> method (documented below). 
    256     # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It  
     256    # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It 
    257257    #   does not save the parent either. 
    258258    # 
     
    276276    #       ... 
    277277    #     end 
    278     #   end  
     278    #   end 
    279279    # 
    280280    # It's possible to stack callbacks by passing them as an array. Example: 
    281     #  
     281    # 
    282282    #   class Project 
    283283    #     has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}] 
     
    335335    # Some extensions can only be made to work with knowledge of the association proxy's internals. 
    336336    # Extensions can access relevant state using accessors on the association proxy: 
    337     #  
     337    # 
    338338    # * +proxy_owner+ - Returns the object the association is part of. 
    339339    # * +proxy_reflection+ - Returns the reflection object that describes the association. 
     
    341341    # 
    342342    # === Association Join Models 
    343     #  
     343    # 
    344344    # Has Many associations can be configured with the <tt>:through</tt> option to use an explicit join model to retrieve the data.  This 
    345345    # operates similarly to a +has_and_belongs_to_many+ association.  The advantage is that you're able to add validations, 
    346346    # callbacks, and extra attributes on the join model.  Consider the following schema: 
    347     #  
     347    # 
    348348    #   class Author < ActiveRecord::Base 
    349349    #     has_many :authorships 
    350350    #     has_many :books, :through => :authorships 
    351351    #   end 
    352     #  
     352    # 
    353353    #   class Authorship < ActiveRecord::Base 
    354354    #     belongs_to :author 
    355355    #     belongs_to :book 
    356356    #   end 
    357     #  
     357    # 
    358358    #   @author = Author.find :first 
    359359    #   @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to. 
    360360    #   @author.books                              # selects all books by using the Authorship join model 
    361     #  
     361    # 
    362362    # You can also go through a +has_many+ association on the join model: 
    363     #  
     363    # 
    364364    #   class Firm < ActiveRecord::Base 
    365365    #     has_many   :clients 
    366366    #     has_many   :invoices, :through => :clients 
    367367    #   end 
    368     #    
     368    # 
    369369    #   class Client < ActiveRecord::Base 
    370370    #     belongs_to :firm 
    371371    #     has_many   :invoices 
    372372    #   end 
    373     #    
     373    # 
    374374    #   class Invoice < ActiveRecord::Base 
    375375    #     belongs_to :client 
     
    381381    # 
    382382    # === Polymorphic Associations 
    383     #  
    384     # Polymorphic associations on models are not restricted on what types of models they can be associated with.  Rather, they  
     383    # 
     384    # Polymorphic associations on models are not restricted on what types of models they can be associated with.  Rather, they 
    385385    # specify an interface that a +has_many+ association must adhere to. 
    386     #  
     386    # 
    387387    #   class Asset < ActiveRecord::Base 
    388388    #     belongs_to :attachable, :polymorphic => true 
    389389    #   end 
    390     #  
     390    # 
    391391    #   class Post < ActiveRecord::Base 
    392392    #     has_many :assets, :as => :attachable         # The :as option specifies the polymorphic interface to use. 
     
    394394    # 
    395395    #   @asset.attachable = @post 
    396     #  
     396    # 
    397397    # This works by using a type column in addition to a foreign key to specify the associated record.  In the Asset example, you'd need 
    398398    # an +attachable_id+ integer column and an +attachable_type+ string column. 
    399399    # 
    400400    # Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order 
    401     # for the associations to work as expected, ensure that you store the base model for the STI models in the  
     401    # for the associations to work as expected, ensure that you store the base model for the STI models in the 
    402402    # type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts 
    403403    # and member posts that use the posts table for STI. In this case, there must be a +type+ column in the posts table. 
     
    405405    #   class Asset < ActiveRecord::Base 
    406406    #     belongs_to :attachable, :polymorphic => true 
    407     #      
     407    # 
    408408    #     def attachable_type=(sType) 
    409409    #        super(sType.to_s.classify.constantize.base_class.to_s) 
    410410    #     end 
    411411    #   end 
    412     #  
     412    # 
    413413    #   class Post < ActiveRecord::Base 
    414414    #     # because we store "Post" in attachable_type now :dependent => :destroy will work 
     
    425425    # 
    426426    # All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically 
    427     # instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without  
     427    # instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without 
    428428    # worrying too much about performance at the first go. Example: 
    429429    # 
     
    451451    #     puts "Written by:      " + post.author.name 
    452452    #     puts "Last comment on: " + post.comments.first.created_on 
    453     #   end  
     453    #   end 
    454454    # 
    455455    # To iterate over these one hundred posts, we'll generate 201 database queries. Let's first just optimize it for retrieving the author: 
     
    476476    # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no 
    477477    # catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above. 
    478     #  
     478    # 
    479479    # Since the eager loading pulls from multiple tables, you'll have to disambiguate any column references in both conditions and orders. So 
    480480    # <tt>:order => "posts.id DESC"</tt> will work while <tt>:order => "id DESC"</tt> will not. Because eager loading generates the +SELECT+ statement too, the 
     
    484484    # as there is currently not any way to disambiguate them. Eager loading will not pull additional attributes on join tables, so "rich 
    485485    # associations" with +has_and_belongs_to_many+ are not a good fit for eager loading. 
    486     #  
     486    # 
    487487    # When eager loaded, conditions are interpolated in the context of the model class, not the model instance.  Conditions are lazily interpolated 
    488488    # before the actual model exists. 
    489     #  
     489    # 
    490490    # == Table Aliasing 
    491491    # 
     
    493493    # the standard table name is used.  The second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.  Indexes are appended 
    494494    # for any more successive uses of the table name. 
    495     #  
     495    # 
    496496    #   Post.find :all, :include => :comments 
    497497    #   # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... 
     
    500500    #   Post.find :all, :include => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name 
    501501    #   # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... LEFT OUTER JOIN comments special_comments_posts 
    502     #  
     502    # 
    503503    # Acts as tree example: 
    504     #  
     504    # 
    505505    #   TreeMixin.find :all, :include => :children 
    506506    #   # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ... 
    507507    #   TreeMixin.find :all, :include => {:children => :parent} # using cascading eager includes 
    508     #   # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...  
     508    #   # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ... 
    509509    #                               LEFT OUTER JOIN parents_mixins ... 
    510     #   TreeMixin.find :all, :include => {:children => {:parent => :children}}  
    511     #   # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...  
    512     #                               LEFT OUTER JOIN parents_mixins ...  
     510    #   TreeMixin.find :all, :include => {:children => {:parent => :children}} 
     511    #   # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ... 
     512    #                               LEFT OUTER JOIN parents_mixins ... 
    513513    #                               LEFT OUTER JOIN mixins childrens_mixins_2 
    514     #  
     514    # 
    515515    # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix: 
    516     #  
     516    # 
    517517    #   Post.find :all, :include => :categories 
    518518    #   # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ... 
     
    524524    #                              LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories 
    525525    #                              LEFT OUTER JOIN categories_posts categories_posts_join LEFT OUTER JOIN categories categories_posts 
    526     #  
     526    # 
    527527    # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table names will take precedence over the eager associations: 
    528     #  
     528    # 
    529529    #   Post.find :all, :include => :comments, :joins => "inner join comments ..." 
    530530    #   # => SELECT ... FROM posts LEFT OUTER JOIN comments_posts ON ... INNER JOIN comments ... 
    531531    #   Post.find :all, :include => [:comments, :special_comments], :joins => "inner join comments ..." 
    532     #   # => SELECT ... FROM posts LEFT OUTER JOIN comments comments_posts ON ...  
     532    #   # => SELECT ... FROM posts LEFT OUTER JOIN comments comments_posts ON ... 
    533533    #                              LEFT OUTER JOIN comments special_comments_posts ... 
    534534    #                              INNER JOIN comments ... 
    535     #  
     535    # 
    536536    # Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database. 
    537     #  
     537    # 
    538538    # == Modules 
    539539    # 
     
    576576    module ClassMethods 
    577577      # Adds the following methods for retrieval and query of collections of associated objects: 
    578       # +collection+ is replaced with the symbol passed as the first argument, so  
     578      # +collection+ is replaced with the symbol passed as the first argument, so 
    579579      # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>. 
    580580      # * <tt>collection(force_reload = false)</tt> - returns an array of all the associated objects. 
    581581      #   An empty array is returned if none are found. 
    582582      # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by setting their foreign keys to the collection's primary key. 
    583       # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by setting their foreign keys to NULL.   
     583      # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by setting their foreign keys to NULL. 
    584584      #   This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model. 
    585585      # * <tt>collection=objects</tt> - replaces the collections content by deleting and adding objects as appropriate. 
     
    593593      # * <tt>collection.find</tt> - finds an associated object according to the same rules as Base.find. 
    594594      # * <tt>collection.build(attributes = {}, ...)</tt> - returns one or more new objects of the collection type that have been instantiated 
    595       #   with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an  
     595      #   with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an 
    596596      #   associated object already exists, not if it's +nil+! 
    597597      # * <tt>collection.create(attributes = {})</tt> - returns a new object of the collection type that has been instantiated 
     
    613613      # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>) 
    614614      # The declaration can also include an options hash to specialize the behavior of the association. 
    615       #  
     615      # 
    616616      # Options are: 
    617617      # * <tt>:class_name</tt>  - specify the class name of the association. Use it only if that name can't be inferred 
     
    638638      # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned. 
    639639      # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows. 
    640       # * <tt>:select</tt>: By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join  
     640      # * <tt>:select</tt>: By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join 
    641641      #   but not include the joined columns. 
    642642      # * <tt>:as</tt>: Specifies a polymorphic interface (See <tt>#belongs_to</tt>). 
    643       # * <tt>:through</tt>: Specifies a Join Model through which to perform the query.  Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>  
     643      # * <tt>:through</tt>: Specifies a Join Model through which to perform the query.  Options for <tt>:class_name</tt> and <tt>:foreign_key</tt> 
    644644      #   are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt> 
    645645      #   or <tt>has_many</tt> association on the join model. 
    646       # * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries.  Only use it if the name cannot be  
     646      # * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries.  Only use it if the name cannot be 
    647647      #   inferred from the association.  <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or 
    648648      #   <tt>:subscriber</tt> on +Subscription+, unless a <tt>:source</tt> is given. 
     
    670670 
    671671        if options[:through] 
    672           collection_reader_method(reflection, HasManyThroughAssociation) 
    673672          collection_accessor_methods(reflection, HasManyThroughAssociation, false) 
    674673        else 
     
    680679 
    681680      # Adds the following methods for retrieval and query of a single associated object: 
    682       # +association+ is replaced with the symbol passed as the first argument, so  
     681      # +association+ is replaced with the symbol passed as the first argument, so 
    683682      # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>. 
    684683      # * <tt>association(force_reload = false)</tt> - returns the associated object. +nil+ is returned if none is found. 
    685       # * <tt>association=(associate)</tt> - assigns the associate object, extracts the primary key, sets it as the foreign key,  
     684      # * <tt>association=(associate)</tt> - assigns the associate object, extracts the primary key, sets it as the foreign key, 
    686685      #   and saves the associate object. 
    687686      # * <tt>association.nil?</tt> - returns +true+ if there is no associated object. 
     
    700699      # 
    701700      # The declaration can also include an options hash to specialize the behavior of the association. 
    702       #  
     701      # 
    703702      # Options are: 
    704703      # * <tt>:class_name</tt>  - specify the class name of the association. Use it only if that name can't be inferred 
     
    727726        reflection = create_has_one_reflection(association_id, options) 
    728727 
     728        ivar = "@#{reflection.name}" 
     729 
    729730        module_eval do 
    730731          after_save <<-EOF 
    731             association = instance_variable_get("@#{reflection.name}") 
     732            association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}") 
     733 
    732734            if !association.nil? && (new_record? || association.new_record? || association["#{reflection.primary_key_name}"] != id) 
    733735              association["#{reflection.primary_key_name}"] = id 
     
    736738          EOF 
    737739        end 
    738        
     740 
    739741        association_accessor_methods(reflection, HasOneAssociation) 
    740742        association_constructor_method(:build,  reflection, HasOneAssociation) 
    741743        association_constructor_method(:create, reflection, HasOneAssociation) 
    742          
     744 
    743745        configure_dependency_for_has_one(reflection) 
    744746      end 
    745747 
    746748      # Adds the following methods for retrieval and query for a single associated object for which this object holds an id: 
    747       # +association+ is replaced with the symbol passed as the first argument, so  
     749      # +association+ is replaced with the symbol passed as the first argument, so 
    748750      # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>. 
    749751      # * <tt>association(force_reload = false)</tt> - returns the associated object. +nil+ is returned if none is found. 
     
    763765      # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>) 
    764766      # The declaration can also include an options hash to specialize the behavior of the association. 
    765       #  
     767      # 
    766768      # Options are: 
    767769      # * <tt>:class_name</tt>  - specify the class name of the association. Use it only if that name can't be inferred 
     
    775777      #   of the association with an +_id+ suffix. So a class that defines a +belongs_to :person+ association will use +person_id+ as the default +foreign_key+. 
    776778      #   Similarly, +belongs_to :favorite_person, :class_name => "Person"+ will use a foreign key of +favorite_person_id+. 
    777       # * <tt>:counter_cache</tt> - caches the number of belonging objects on the associate class through the use of +increment_counter+  
     779      # * <tt>:counter_cache</tt> - caches the number of belonging objects on the associate class through the use of +increment_counter+ 
    778780      #   and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's 
    779781      #   destroyed. This requires that a column named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging +Comment+ class) 
    780       #   is used on the associate class (such as a +Post+ class). You can also specify a custom counter cache column by providing  
     782      #   is used on the associate class (such as a +Post+ class). You can also specify a custom counter cache column by providing 
    781783      #   a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.) 
    782784      #   Note: Specifying a counter_cache will add it to that model's list of readonly attributes using #attr_readonly. 
    783785      # * <tt>:include</tt>  - specify second-order associations that should be eager loaded when this object is loaded. 
    784786      # * <tt>:polymorphic</tt> - specify this association is a polymorphic association by passing +true+. 
    785       #   Note: If you've enabled the counter cache, then you may want to add the counter cache attribute  
     787      #   Note: If you've enabled the counter cache, then you may want to add the counter cache attribute 
    786788      #   to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end). 
    787789      # 
     
    789791      #   belongs_to :firm, :foreign_key => "client_of" 
    790792      #   belongs_to :author, :class_name => "Person", :foreign_key => "author_id" 
    791       #   belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",  
     793      #   belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id", 
    792794      #              :conditions => 'discounts > #{payments_count}' 
    793795      #   belongs_to :attachable, :polymorphic => true 
    794796      def belongs_to(association_id, options = {}) 
    795797        reflection = create_belongs_to_reflection(association_id, options) 
    796          
     798 
     799        ivar = "@#{reflection.name}" 
     800 
    797801        if reflection.options[:polymorphic] 
    798802          association_accessor_methods(reflection, BelongsToPolymorphicAssociation) 
     
    800804          module_eval do 
    801805            before_save <<-EOF 
    802               association = instance_variable_get("@#{reflection.name}") 
     806              association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}") 
     807 
    803808              if association && association.target 
    804809                if association.new_record? 
    805810                  association.save(true) 
    806811                end 
    807                  
     812 
    808813                if association.updated? 
    809814                  self["#{reflection.primary_key_name}"] = association.id 
     
    820825          module_eval do 
    821826            before_save <<-EOF 
    822               association = instance_variable_get("@#{reflection.name}") 
    823               if !association.nil?  
     827              association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}") 
     828 
     829              if !association.nil? 
    824830                if association.new_record? 
    825831                  association.save(true) 
    826832                end 
    827                  
     833 
    828834                if association.updated? 
    829835                  self["#{reflection.primary_key_name}"] = association.id 
    830836                end 
    831               end             
     837              end 
    832838            EOF 
    833839          end 
     
    849855            " unless #{reflection.name}.nil?'" 
    850856          ) 
    851            
     857 
    852858          module_eval( 
    853859            "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)" 
     
    859865      # an option, it is guessed using the lexical order of the class names. So a join between +Developer+ and +Project+ 
    860866      # will give the default join table name of +developers_projects+ because "D" outranks "P".  Note that this precedence 
    861       # is calculated using the <tt><</tt> operator for <tt>String</tt>.  This means that if the strings are of different lengths,  
     867      # is calculated using the <tt><</tt> operator for <tt>String</tt>.  This means that if the strings are of different lengths, 
    862868      # and the strings are equal when compared up to the shortest length, then the longer string is considered of higher 
    863       # lexical precedence than the shorter one.  For example, one would expect the tables <tt>paper_boxes</tt> and <tt>papers</tt>  
     869      # lexical precedence than the shorter one.  For example, one would expect the tables <tt>paper_boxes</tt> and <tt>papers</tt> 
    864870      # to generate a join table name of <tt>papers_paper_boxes</tt> because of the length of the name <tt>paper_boxes</tt>, 
    865       # but it in fact generates a join table name of <tt>paper_boxes_papers</tt>.  Be aware of this caveat, and use the  
     871      # but it in fact generates a join table name of <tt>paper_boxes_papers</tt>.  Be aware of this caveat, and use the 
    866872      # custom <tt>join_table</tt> option if you need to. 
    867873      # 
     
    872878      # 
    873879      # Adds the following methods for retrieval and query: 
    874       # +collection+ is replaced with the symbol passed as the first argument, so  
     880      # +collection+ is replaced with the symbol passed as the first argument, so 
    875881      # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>. 
    876882      # * <tt>collection(force_reload = false)</tt> - returns an array of all the associated objects. 
    877883      #   An empty array is returned if none are found. 
    878       # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by creating associations in the join table  
     884      # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by creating associations in the join table 
    879885      #   (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method). 
    880       # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by removing their associations from the join table.   
     886      # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by removing their associations from the join table. 
    881887      #   This does not destroy the objects. 
    882888      # * <tt>collection=objects</tt> - replaces the collection's content by deleting and adding objects as appropriate. 
     
    907913      # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>) 
    908914      # The declaration may include an options hash to specialize the behavior of the association. 
    909       #  
     915      # 
    910916      # Options are: 
    911917      # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred 
    912       #   from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the  
     918      #   from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the 
    913919      #   +Project+ class, but if the real class name is +SuperProject+, you'll have to specify it with this option. 
    914920      # * <tt>:join_table</tt> - specify the name of the join table if the default based on lexical order isn't what you want. 
     
    927933      # * <tt>:uniq</tt> - if set to +true+, duplicate associated objects will be ignored by accessors and query methods 
    928934      # * <tt>:finder_sql</tt> - overwrite the default generated SQL statement used to fetch the association with a manual statement 
    929       # * <tt>:delete_sql</tt> - overwrite the default generated SQL statement used to remove links between the associated  
     935      # * <tt>:delete_sql</tt> - overwrite the default generated SQL statement used to remove links between the associated 
    930936      #   classes with a manual statement 
    931937      # * <tt>:insert_sql</tt> - overwrite the default generated SQL statement used to add links between the associated classes 
     
    944950      #   has_and_belongs_to_many :nations, :class_name => "Country" 
    945951      #   has_and_belongs_to_many :categories, :join_table => "prods_cats" 
    946       #   has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>  
     952      #   has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql => 
    947953      #   'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}' 
    948954      def has_and_belongs_to_many(association_id, options = {}, &extension) 
    949955        reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension) 
    950          
     956 
    951957        add_multiple_associated_save_callbacks(reflection.name) 
    952958        collection_accessor_methods(reflection, HasAndBelongsToManyAssociation) 
     
    982988          table_name_prefix + join_table + table_name_suffix 
    983989        end 
    984        
     990 
    985991        def association_accessor_methods(reflection, association_proxy_class) 
     992          ivar = "@#{reflection.name}" 
     993 
    986994          define_method(reflection.name) do |*params| 
    987995            force_reload = params.first unless params.empty? 
    988             association = instance_variable_get("@#{reflection.name}") 
     996 
     997            association = instance_variable_get(ivar) if instance_variable_defined?(ivar) 
    989998 
    990999            if association.nil? || force_reload 
     
    9921001              retval = association.reload 
    9931002              if retval.nil? and association_proxy_class == BelongsToAssociation 
    994                 instance_variable_set("@#{reflection.name}", nil) 
     1003                instance_variable_set(ivar, nil) 
    9951004                return nil 
    9961005              end 
    997               instance_variable_set("@#{reflection.name}", association) 
     1006              instance_variable_set(ivar, association) 
    9981007            end 
    9991008 
     
    10021011 
    10031012          define_method("#{reflection.name}=") do |new_value| 
    1004             association = instance_variable_get("@#{reflection.name}") 
     1013            association = instance_variable_get(ivar) if instance_variable_defined?(ivar) 
     1014 
    10051015            if association.nil? || association.target != new_value 
    10061016              association = association_proxy_class.new(self, reflection) 
     
    10091019            association.replace(new_value) 
    10101020 
    1011             unless new_value.nil? 
    1012               instance_variable_set("@#{reflection.name}", association) 
    1013             else 
    1014               instance_variable_set("@#{reflection.name}", nil) 
    1015             end 
     1021            instance_variable_set(ivar, new_value.nil? ? nil : association) 
    10161022          end 
    10171023 
     
    10201026            association = association_proxy_class.new(self, reflection) 
    10211027            association.target = target 
    1022             instance_variable_set("@#{reflection.name}", association) 
     1028            instance_variable_set(ivar, association) 
    10231029          end 
    10241030        end 
     
    10261032        def collection_reader_method(reflection, association_proxy_class) 
    10271033          define_method(reflection.name) do |*params| 
     1034            ivar = "@#{reflection.name}" 
     1035 
    10281036            force_reload = params.first unless params.empty? 
    1029             association = instance_variable_get("@#{reflection.name}"
     1037            association = instance_variable_get(ivar) if instance_variable_defined?(ivar
    10301038 
    10311039            unless association.respond_to?(:loaded?) 
    10321040              association = association_proxy_class.new(self, reflection) 
    1033               instance_variable_set("@#{reflection.name}", association) 
     1041              instance_variable_set(ivar, association) 
    10341042            end 
    10351043 
     
    10371045 
    10381046            association 
     1047          end 
     1048 
     1049          define_method("#{reflection.name.to_s.singularize}_ids") do 
     1050            send(reflection.name).map(&:id) 
    10391051          end 
    10401052        end 
     
    10431055          collection_reader_method(reflection, association_proxy_class) 
    10441056 
    1045           define_method("#{reflection.name}=") do |new_value| 
    1046             # Loads proxy class instance (defined in collection_reader_method) if not already loaded 
    1047             association = send(reflection.name)  
    1048             association.replace(new_value) 
    1049             association 
    1050           end 
    1051  
    1052           define_method("#{reflection.name.to_s.singularize}_ids") do 
    1053             send(reflection.name).map(&:id) 
    1054           end 
    1055  
    1056           define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| 
    1057             ids = (new_value || []).reject { |nid| nid.blank? } 
    1058             send("#{reflection.name}=", reflection.class_name.constantize.find(ids)) 
    1059           end if writer 
     1057          if writer 
     1058            define_method("#{reflection.name}=") do |new_value| 
     1059              # Loads proxy class instance (defined in collection_reader_method) if not already loaded 
     1060              association = send(reflection.name) 
     1061              association.replace(new_value) 
     1062              association 
     1063            end 
     1064 
     1065            define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| 
     1066              ids = (new_value || []).reject { |nid| nid.blank? } 
     1067              send("#{reflection.name}=", reflection.class_name.constantize.find(ids)) 
     1068            end 
     1069          end 
    10601070        end 
    10611071 
    10621072        def add_multiple_associated_save_callbacks(association_name) 
    10631073          method_name = "validate_associated_records_for_#{association_name}".to_sym 
     1074          ivar = "@#{association_name}" 
     1075 
    10641076          define_method(method_name) do 
    1065             association = instance_variable_get("@#{association_name}") 
     1077            association = instance_variable_get(ivar) if instance_variable_defined?(ivar) 
     1078 
    10661079            if association.respond_to?(:loaded?) 
    10671080              if new_record? 
     
    10791092 
    10801093          after_callback = <<-end_eval 
    1081             association = instance_variable_get("@#{association_name}") 
     1094            association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}") 
    10821095 
    10831096            records_to_save = if @new_record_before_save 
     
    10901103 
    10911104            records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank? 
    1092              
     1105 
    10931106            # reconstruct the SQL queries now that we know the owner's id 
    10941107            association.send(:construct_sql) if association.respond_to?(:construct_sql) 
     
    11021115        def association_constructor_method(constructor, reflection, association_proxy_class) 
    11031116          define_method("#{constructor}_#{reflection.name}") do |*params| 
     1117            ivar = "@#{reflection.name}" 
     1118 
    11041119            attributees      = params.first unless params.empty? 
    11051120            replace_existing = params[1].nil? ? true : params[1] 
    1106             association      = instance_variable_get("@#{reflection.name}"
     1121            association      = instance_variable_get(ivar) if instance_variable_defined?(ivar
    11071122 
    11081123            if association.nil? 
    11091124              association = association_proxy_class.new(self, reflection) 
    1110               instance_variable_set("@#{reflection.name}", association) 
     1125              instance_variable_set(ivar, association) 
    11111126            end 
    11121127 
     
    11181133          end 
    11191134        end 
    1120          
     1135 
    11211136        def find_with_associations(options = {}) 
    11221137          catch :invalid_query do 
     
    11741189            :as, :through, :source, :source_type, 
    11751190            :uniq, 
    1176             :finder_sql, :counter_sql,  
    1177             :before_add, :after_add, :before_remove, :after_remove,  
     1191            :finder_sql, :counter_sql, 
     1192            :before_add, :after_add, :before_remove, :after_remove, 
    11781193            :extend 
    11791194          ) 
    11801195 
    1181           options[:extend] = create_extension_modules(association_id, extension, options[:extend]) if block_given? 
     1196          options[:extend] = create_extension_modules(association_id, extension, options[:extend]) 
    11821197 
    11831198          create_reflection(:has_many, association_id, options, self) 
     
    11941209        def create_belongs_to_reflection(association_id, options) 
    11951210          options.assert_valid_keys( 
    1196             :class_name, :foreign_key, :foreign_type, :remote, :conditions, :order, :include, :dependent,  
     1211            :class_name, :foreign_key, :foreign_type, :remote, :conditions, :order, :include, :dependent, 
    11971212            :counter_cache, :extend, :polymorphic 
    11981213          ) 
    1199            
     1214 
    12001215          reflection = create_reflection(:belongs_to, association_id, options, self) 
    12011216 
     
    12061221          reflection 
    12071222        end 
    1208          
     1223 
    12091224        def create_has_and_belongs_to_many_reflection(association_id, options, &extension) 
    12101225          options.assert_valid_keys( 
    1211             :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,  
     1226            :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key, 
    12121227            :select, :conditions, :include, :order, :group, :limit, :offset, 
    1213             :uniq,  
     1228            :uniq, 
    12141229            :finder_sql, :delete_sql, :insert_sql, 
    1215             :before_add, :after_add, :before_remove, :after_remove,  
     1230            :before_add, :after_add, :before_remove, :after_remove, 
    12161231            :extend 
    12171232          ) 
    12181233 
    1219           options[:extend] = create_extension_modules(association_id, extension, options[:extend]) if block_given? 
     1234          options[:extend] = create_extension_modules(association_id, extension, options[:extend]) 
    12201235 
    12211236          reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self) 
    12221237 
    12231238          reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name)) 
    1224            
     1239 
    12251240          reflection 
    12261241        end 
    &hell