has_and_belongs_to_many.rb 1.8 KB
Newer Older
1 2 3 4 5 6 7 8 9
module ActiveRecord::Associations::Builder
  class HasAndBelongsToMany < CollectionAssociation #:nodoc:
    self.macro = :has_and_belongs_to_many

    self.valid_options += [:join_table, :association_foreign_key, :delete_sql, :insert_sql]

    def build
      reflection = super
      check_validity(reflection)
10
      define_destroy_hook
11 12 13 14 15
      reflection
    end

    private

16
      def define_destroy_hook
17
        name = self.name
18 19 20 21
        mixin.send(:define_method, :destroy_associations) do
          association(name).delete_all
          super()
        end
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
      end

      # TODO: These checks should probably be moved into the Reflection, and we should not be
      #       redefining the options[:join_table] value - instead we should define a
      #       reflection.join_table method.
      def check_validity(reflection)
        if reflection.association_foreign_key == reflection.foreign_key
          raise ActiveRecord::HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
        end

        reflection.options[:join_table] ||= join_table_name(
          model.send(:undecorated_table_name, model.to_s),
          model.send(:undecorated_table_name, reflection.class_name)
        )
      end

      # Generates a join table name from two provided table names.
      # The names in the join table names end up in lexicographic order.
      #
      #   join_table_name("members", "clubs")         # => "clubs_members"
      #   join_table_name("members", "special_clubs") # => "members_special_clubs"
      def join_table_name(first_table_name, second_table_name)
        if first_table_name < second_table_name
          join_table = "#{first_table_name}_#{second_table_name}"
        else
          join_table = "#{second_table_name}_#{first_table_name}"
        end

        model.table_name_prefix + join_table + model.table_name_suffix
      end
  end
end