require'active_support/core_ext/module/attribute_accessors'# This is the parent Association class which defines the variables# used by all associations.## The hierarchy is defined as follows:# Association# - SingularAssociation# - BelongsToAssociation# - HasOneAssociation# - CollectionAssociation# - HasManyAssociationmoduleActiveRecord::Associations::BuilderclassAssociation#:nodoc:class<<selfattr_accessor:extensions# TODO: This class accessor is needed to make activerecord-deprecated_finders work.# We can move it to a constant in 5.0.attr_accessor:valid_optionsendself.extensions=[]self.valid_options=[:class_name,:class,:foreign_key,:validate]attr_reader:name,:scope,:optionsdefself.build(model,name,scope,options,&block)ifmodel.dangerous_attribute_method?(name)raiseArgumentError,"You tried to define an association named #{name} on the model #{model.name}, but "\"this will conflict with a method #{name} already defined by Active Record. "\"Please choose a different association name."endbuilder=create_buildermodel,name,scope,options,&blockreflection=builder.build(model)define_accessorsmodel,reflectiondefine_callbacksmodel,reflectiondefine_validationsmodel,reflectionbuilder.define_extensionsmodelreflectionenddefself.create_builder(model,name,scope,options,&block)raiseArgumentError,"association names must be a Symbol"unlessname.kind_of?(Symbol)new(model,name,scope,options,&block)enddefinitialize(model,name,scope,options)# TODO: Move this to create_builder as soon we drop support to activerecord-deprecated_finders.ifscope.is_a?(Hash)options=scopescope=nilend# TODO: Remove this model argument as soon we drop support to activerecord-deprecated_finders.@name=name@scope=scope@options=optionsvalidate_optionsifscope&&scope.arity==0@scope=proc{instance_exec(&scope)}endenddefbuild(model)ActiveRecord::Reflection.create(macro,name,scope,options,model)enddefmacroraiseNotImplementedErrorenddefvalid_optionsAssociation.valid_options+Association.extensions.flat_map(&:valid_options)enddefvalidate_optionsoptions.assert_valid_keys(valid_options)enddefdefine_extensions(model)enddefself.define_callbacks(model,reflection)ifdependent=reflection.options[:dependent]check_dependent_options(dependent)add_destroy_callbacks(model,reflection)endAssociation.extensions.eachdo|extension|extension.buildmodel,reflectionendend# Defines the setter and getter methods for the association# class Post < ActiveRecord::Base# has_many :comments# end## Post.first.comments and Post.first.comments= methods are defined by this method...defself.define_accessors(model,reflection)mixin=model.generated_association_methodsname=reflection.namedefine_readers(mixin,name)define_writers(mixin,name)enddefself.define_readers(mixin,name)mixin.class_eval<<-CODE,__FILE__,__LINE__+1
def #{name}(*args)
association(:#{name}).reader(*args)
end
CODEenddefself.define_writers(mixin,name)mixin.class_eval<<-CODE,__FILE__,__LINE__+1
def #{name}=(value)
association(:#{name}).writer(value)
end
CODEenddefself.define_validations(model,reflection)# noopenddefself.valid_dependent_optionsraiseNotImplementedErrorendprivatedefself.check_dependent_options(dependent)unlessvalid_dependent_options.include?dependentraiseArgumentError,"The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"endenddefself.add_destroy_callbacks(model,reflection)name=reflection.namemodel.before_destroylambda{|o|o.association(name).handle_dependency}endendend