aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2011-03-02 21:24:56 +0000
committerJon Leighton <j@jonathanleighton.com>2011-03-04 09:30:27 +0000
commit735844db712c511dd8abf36a5279318fbc0ff9d0 (patch)
tree5fbd5d224ef85d8c878bf221db98b422c9345466 /activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
parent9a98c766e045aebc2ef6d5b716936b73407f095d (diff)
parentb171b9e73dcc6a89b1da652da61c5127fe605b51 (diff)
downloadrails-735844db712c511dd8abf36a5279318fbc0ff9d0.tar.gz
rails-735844db712c511dd8abf36a5279318fbc0ff9d0.tar.bz2
rails-735844db712c511dd8abf36a5279318fbc0ff9d0.zip
Merge branch 'master' into nested_has_many_through
Conflicts: activerecord/CHANGELOG activerecord/lib/active_record/association_preload.rb activerecord/lib/active_record/associations.rb activerecord/lib/active_record/associations/class_methods/join_dependency.rb activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb activerecord/lib/active_record/associations/has_many_association.rb activerecord/lib/active_record/associations/has_many_through_association.rb activerecord/lib/active_record/associations/has_one_association.rb activerecord/lib/active_record/associations/has_one_through_association.rb activerecord/lib/active_record/associations/through_association_scope.rb activerecord/lib/active_record/reflection.rb activerecord/test/cases/associations/has_many_through_associations_test.rb activerecord/test/cases/associations/has_one_through_associations_test.rb activerecord/test/cases/reflection_test.rb activerecord/test/cases/relations_test.rb activerecord/test/fixtures/memberships.yml activerecord/test/models/categorization.rb activerecord/test/models/category.rb activerecord/test/models/member.rb activerecord/test/models/reference.rb activerecord/test/models/tagging.rb
Diffstat (limited to 'activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb')
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb63
1 files changed, 63 insertions, 0 deletions
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
new file mode 100644
index 0000000000..e40b32826a
--- /dev/null
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -0,0 +1,63 @@
+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)
+ redefine_destroy
+ reflection
+ end
+
+ private
+
+ def redefine_destroy
+ # Don't use a before_destroy callback since users' before_destroy
+ # callbacks will be executed after the association is wiped out.
+ name = self.name
+ model.send(:include, Module.new {
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def destroy # def destroy
+ super # super
+ #{name}.clear # posts.clear
+ end # end
+ RUBY
+ })
+ 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)
+ )
+
+ if model.connection.supports_primary_key? && (model.connection.primary_key(reflection.options[:join_table]) rescue false)
+ raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection)
+ end
+ 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