Created
October 2, 2013 23:10
-
-
Save tenderlove/6801895 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md | |
index 08803f8..8ee38fc 100644 | |
--- a/activerecord/CHANGELOG.md | |
+++ b/activerecord/CHANGELOG.md | |
@@ -1,3 +1,6 @@ | |
+* `has_and_belongs_to_many` is now transparently implemented in terms of | |
+ `has_many :through`. Behavior should remain the same, if not, it is a bug. | |
+ | |
* `create_savepoint`, `rollback_to_savepoint` and `release_savepoint` accept | |
a savepoint name. | |
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb | |
index 33cbafc..74e2774 100644 | |
--- a/activerecord/lib/active_record/associations.rb | |
+++ b/activerecord/lib/active_record/associations.rb | |
@@ -73,12 +73,6 @@ module ActiveRecord | |
end | |
end | |
- class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc: | |
- def initialize(reflection) | |
- super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.") | |
- end | |
- end | |
- | |
class EagerLoadPolymorphicError < ActiveRecordError #:nodoc: | |
def initialize(reflection) | |
super("Can not eagerly load the polymorphic association #{reflection.name.inspect}") | |
@@ -114,7 +108,6 @@ module ActiveRecord | |
autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association' | |
autoload :BelongsToPolymorphicAssociation, 'active_record/associations/belongs_to_polymorphic_association' | |
- autoload :HasAndBelongsToManyAssociation, 'active_record/associations/has_and_belongs_to_many_association' | |
autoload :HasManyAssociation, 'active_record/associations/has_many_association' | |
autoload :HasManyThroughAssociation, 'active_record/associations/has_many_through_association' | |
autoload :HasOneAssociation, 'active_record/associations/has_one_association' | |
@@ -1560,8 +1553,39 @@ module ActiveRecord | |
# has_and_belongs_to_many :categories, join_table: "prods_cats" | |
# has_and_belongs_to_many :categories, -> { readonly } | |
def has_and_belongs_to_many(name, scope = nil, options = {}, &extension) | |
- reflection = Builder::HasAndBelongsToMany.build(self, name, scope, options, &extension) | |
- Reflection.add_reflection self, name, reflection | |
+ if scope.is_a?(Hash) | |
+ options = scope | |
+ scope = nil | |
+ end | |
+ | |
+ builder = Builder::HasAndBelongsToMany.new name, self, options | |
+ | |
+ join_model = builder.through_model | |
+ | |
+ middle_reflection = builder.middle_reflection join_model | |
+ | |
+ Builder::HasMany.define_callbacks self, middle_reflection | |
+ Reflection.add_reflection self, middle_reflection.name, middle_reflection | |
+ | |
+ include Module.new { | |
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1 | |
+ def destroy_associations | |
+ association(:#{middle_reflection.name}).delete_all(:delete_all) | |
+ association(:#{name}).reset | |
+ super | |
+ end | |
+ RUBY | |
+ } | |
+ | |
+ hm_options = {} | |
+ hm_options[:through] = middle_reflection.name | |
+ hm_options[:source] = join_model.right_reflection.name | |
+ | |
+ [:before_add, :after_add, :before_remove, :after_remove].each do |k| | |
+ hm_options[k] = options[k] if options.key? k | |
+ end | |
+ | |
+ has_many name, scope, hm_options, &extension | |
end | |
end | |
end | |
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb | |
index 67d24b3..04c36d5 100644 | |
--- a/activerecord/lib/active_record/associations/association.rb | |
+++ b/activerecord/lib/active_record/associations/association.rb | |
@@ -13,7 +13,6 @@ module ActiveRecord | |
# BelongsToAssociation | |
# BelongsToPolymorphicAssociation | |
# CollectionAssociation | |
- # HasAndBelongsToManyAssociation | |
# HasManyAssociation | |
# HasManyThroughAssociation + ThroughAssociation | |
class Association #:nodoc: | |
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb | |
index 8027acf..d862a5f 100644 | |
--- a/activerecord/lib/active_record/associations/association_scope.rb | |
+++ b/activerecord/lib/active_record/associations/association_scope.rb | |
@@ -44,18 +44,6 @@ module ActiveRecord | |
chain.each_with_index do |reflection, i| | |
table, foreign_table = tables.shift, tables.first | |
- if reflection.source_macro == :has_and_belongs_to_many | |
- join_table = tables.shift | |
- | |
- scope = scope.joins(join( | |
- join_table, | |
- table[reflection.association_primary_key]. | |
- eq(join_table[reflection.association_foreign_key]) | |
- )) | |
- | |
- table, foreign_table = join_table, tables.first | |
- end | |
- | |
if reflection.source_macro == :belongs_to | |
if reflection.options[:polymorphic] | |
key = reflection.association_primary_key(self.klass) | |
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb | |
index 6cf5523..a8cde62 100644 | |
--- a/activerecord/lib/active_record/associations/builder/association.rb | |
+++ b/activerecord/lib/active_record/associations/builder/association.rb | |
@@ -8,7 +8,6 @@ | |
# - HasOneAssociation | |
# - CollectionAssociation | |
# - HasManyAssociation | |
-# - HasAndBelongsToManyAssociation | |
module ActiveRecord::Associations::Builder | |
class Association #:nodoc: | |
@@ -37,6 +36,17 @@ module ActiveRecord::Associations::Builder | |
reflection | |
end | |
+ def self.create_builder(model, name, scope, options, &block) | |
+ raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol) | |
+ | |
+ if scope.is_a?(Hash) | |
+ options = scope | |
+ scope = nil | |
+ end | |
+ | |
+ new(name, scope, options, &block) | |
+ end | |
+ | |
def initialize(name, scope, options) | |
@name = name | |
@scope = scope | |
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 | |
index 50c9dbe..b4537c5 100644 | |
--- 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 | |
@@ -1,24 +1,121 @@ | |
module ActiveRecord::Associations::Builder | |
- class HasAndBelongsToMany < CollectionAssociation #:nodoc: | |
- def macro | |
- :has_and_belongs_to_many | |
+ class HasAndBelongsToMany # :nodoc: | |
+ class JoinTableResolver | |
+ KnownTable = Struct.new :join_table | |
+ | |
+ class KnownClass | |
+ def initialize(rhs_class, lhs_class_name) | |
+ @rhs_class = rhs_class | |
+ @lhs_class_name = lhs_class_name | |
+ @join_table = nil | |
+ end | |
+ | |
+ def join_table | |
+ @join_table ||= [@rhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_") | |
+ end | |
+ | |
+ private | |
+ def klass; @lhs_class_name.constantize; end | |
+ end | |
+ | |
+ def self.build(rhs_class, name, options) | |
+ if options[:join_table] | |
+ KnownTable.new options[:join_table] | |
+ else | |
+ class_name = options.fetch(:class_name) { | |
+ name.to_s.camelize.singularize | |
+ } | |
+ KnownClass.new rhs_class, class_name | |
+ end | |
+ end | |
end | |
- def valid_options | |
- super + [:join_table, :association_foreign_key] | |
+ attr_reader :lhs_model, :association_name, :options | |
+ | |
+ def initialize(association_name, lhs_model, options) | |
+ @association_name = association_name | |
+ @lhs_model = lhs_model | |
+ @options = options | |
end | |
- def self.define_callbacks(model, reflection) | |
- super | |
- name = reflection.name | |
- model.send(:include, Module.new { | |
- class_eval <<-RUBY, __FILE__, __LINE__ + 1 | |
- def destroy_associations | |
- association(:#{name}).delete_all | |
- super | |
- end | |
- RUBY | |
- }) | |
+ def through_model | |
+ habtm = JoinTableResolver.build lhs_model, association_name, options | |
+ | |
+ join_model = Class.new(ActiveRecord::Base) { | |
+ class << self; | |
+ attr_accessor :class_resolver | |
+ attr_accessor :name | |
+ attr_accessor :table_name_resolver | |
+ attr_accessor :left_reflection | |
+ attr_accessor :right_reflection | |
+ end | |
+ | |
+ def self.table_name | |
+ table_name_resolver.join_table | |
+ end | |
+ | |
+ def self.compute_type(class_name) | |
+ class_resolver.compute_type class_name | |
+ end | |
+ | |
+ def self.add_left_association(name, options) | |
+ belongs_to name, options | |
+ self.left_reflection = reflect_on_association(name) | |
+ end | |
+ | |
+ def self.add_right_association(name, options) | |
+ rhs_name = name.to_s.singularize.to_sym | |
+ belongs_to rhs_name, options | |
+ self.right_reflection = reflect_on_association(rhs_name) | |
+ end | |
+ | |
+ } | |
+ | |
+ join_model.name = "HABTM_#{association_name.to_s.camelize}" | |
+ join_model.table_name_resolver = habtm | |
+ join_model.class_resolver = lhs_model | |
+ | |
+ join_model.add_left_association :left_side, class: lhs_model | |
+ join_model.add_right_association association_name, belongs_to_options(options) | |
+ join_model | |
+ end | |
+ | |
+ def middle_reflection(join_model) | |
+ middle_name = [lhs_model.name.downcase.pluralize, | |
+ association_name].join('_').gsub(/::/, '_').to_sym | |
+ middle_options = middle_options join_model | |
+ hm_builder = HasMany.create_builder(lhs_model, | |
+ middle_name, | |
+ nil, | |
+ middle_options) | |
+ hm_builder.build lhs_model | |
+ end | |
+ | |
+ private | |
+ | |
+ def middle_options(join_model) | |
+ middle_options = {} | |
+ middle_options[:class] = join_model | |
+ middle_options[:source] = join_model.left_reflection.name | |
+ if options.key? :foreign_key | |
+ middle_options[:foreign_key] = options[:foreign_key] | |
+ end | |
+ middle_options | |
+ end | |
+ | |
+ def belongs_to_options(options) | |
+ rhs_options = {} | |
+ | |
+ if options.key? :class_name | |
+ rhs_options[:foreign_key] = options[:class_name].foreign_key | |
+ rhs_options[:class_name] = options[:class_name] | |
+ end | |
+ | |
+ if options.key? :association_foreign_key | |
+ rhs_options[:foreign_key] = options[:association_foreign_key] | |
+ end | |
+ | |
+ rhs_options | |
end | |
end | |
end | |
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb | |
index b9199f6..75f8990 100644 | |
--- a/activerecord/lib/active_record/associations/collection_association.rb | |
+++ b/activerecord/lib/active_record/associations/collection_association.rb | |
@@ -7,7 +7,6 @@ module ActiveRecord | |
# collections. See the class hierarchy in AssociationProxy. | |
# | |
# CollectionAssociation: | |
- # HasAndBelongsToManyAssociation => has_and_belongs_to_many | |
# HasManyAssociation => has_many | |
# HasManyThroughAssociation + ThroughAssociation => has_many :through | |
# | |
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb | |
deleted file mode 100644 | |
index b2e6c70..0000000 | |
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb | |
+++ /dev/null | |
@@ -1,56 +0,0 @@ | |
-module ActiveRecord | |
- # = Active Record Has And Belongs To Many Association | |
- module Associations | |
- class HasAndBelongsToManyAssociation < CollectionAssociation #:nodoc: | |
- attr_reader :join_table | |
- | |
- def initialize(owner, reflection) | |
- @join_table = Arel::Table.new(reflection.join_table) | |
- super | |
- end | |
- | |
- def insert_record(record, validate = true, raise = false) | |
- if record.new_record? | |
- if raise | |
- record.save!(:validate => validate) | |
- else | |
- return unless record.save(:validate => validate) | |
- end | |
- end | |
- | |
- stmt = join_table.compile_insert( | |
- join_table[reflection.foreign_key] => owner.id, | |
- join_table[reflection.association_foreign_key] => record.id | |
- ) | |
- | |
- owner.class.connection.insert stmt | |
- | |
- record | |
- end | |
- | |
- private | |
- | |
- def count_records | |
- load_target.size | |
- end | |
- | |
- def delete_records(records, method) | |
- relation = join_table | |
- condition = relation[reflection.foreign_key].eq(owner.id) | |
- | |
- unless records == :all | |
- condition = condition.and( | |
- relation[reflection.association_foreign_key] | |
- .in(records.map { |x| x.id }.compact) | |
- ) | |
- end | |
- | |
- owner.class.connection.delete(relation.where(condition).compile_delete) | |
- end | |
- | |
- def invertible_for?(record) | |
- false | |
- end | |
- end | |
- end | |
-end | |
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb | |
index 5aa17e5..fa212f3 100644 | |
--- a/activerecord/lib/active_record/associations/join_dependency.rb | |
+++ b/activerecord/lib/active_record/associations/join_dependency.rb | |
@@ -216,7 +216,7 @@ module ActiveRecord | |
else | |
association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil? | |
case macro | |
- when :has_many, :has_and_belongs_to_many | |
+ when :has_many | |
other = record.association(join_part.reflection.name) | |
other.loaded! | |
other.target.push(association) if association | |
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb | |
index 58fc00d..72e77a1 100644 | |
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb | |
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb | |
@@ -83,17 +83,6 @@ module ActiveRecord | |
when :belongs_to | |
key = reflection.association_primary_key | |
foreign_key = reflection.foreign_key | |
- when :has_and_belongs_to_many | |
- # Join the join table first... | |
- joins << join( | |
- table, | |
- table[reflection.foreign_key]. | |
- eq(foreign_table[reflection.active_record_primary_key])) | |
- | |
- foreign_table, table = table, tables.shift | |
- | |
- key = reflection.association_primary_key | |
- foreign_key = reflection.association_foreign_key | |
else | |
key = reflection.foreign_key | |
foreign_key = reflection.active_record_primary_key | |
diff --git a/activerecord/lib/active_record/associations/join_helper.rb b/activerecord/lib/active_record/associations/join_helper.rb | |
index 27b70ed..68c36d4 100644 | |
--- a/activerecord/lib/active_record/associations/join_helper.rb | |
+++ b/activerecord/lib/active_record/associations/join_helper.rb | |
@@ -16,13 +16,6 @@ module ActiveRecord | |
table_name_for(reflection), | |
table_alias_for(reflection, reflection != self.reflection) | |
) | |
- | |
- if reflection.source_macro == :has_and_belongs_to_many | |
- tables << alias_tracker.aliased_table_for( | |
- reflection.source_reflection.join_table, | |
- table_alias_for(reflection, true) | |
- ) | |
- end | |
end | |
tables | |
end | |
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb | |
index 127d0e2..713ff80 100644 | |
--- a/activerecord/lib/active_record/associations/preloader.rb | |
+++ b/activerecord/lib/active_record/associations/preloader.rb | |
@@ -42,7 +42,6 @@ module ActiveRecord | |
autoload :HasManyThrough, 'active_record/associations/preloader/has_many_through' | |
autoload :HasOne, 'active_record/associations/preloader/has_one' | |
autoload :HasOneThrough, 'active_record/associations/preloader/has_one_through' | |
- autoload :HasAndBelongsToMany, 'active_record/associations/preloader/has_and_belongs_to_many' | |
autoload :BelongsTo, 'active_record/associations/preloader/belongs_to' | |
end | |
@@ -205,8 +204,6 @@ module ActiveRecord | |
reflection.options[:through] ? HasManyThrough : HasMany | |
when :has_one | |
reflection.options[:through] ? HasOneThrough : HasOne | |
- when :has_and_belongs_to_many | |
- HasAndBelongsToMany | |
when :belongs_to | |
BelongsTo | |
end | |
diff --git a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb | |
deleted file mode 100644 | |
index b62ca6f..0000000 | |
--- a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb | |
+++ /dev/null | |
@@ -1,71 +0,0 @@ | |
-module ActiveRecord | |
- module Associations | |
- class Preloader | |
- class HasAndBelongsToMany < CollectionAssociation #:nodoc: | |
- attr_reader :join_table | |
- | |
- def initialize(klass, records, reflection, preload_options) | |
- super | |
- @join_table = Arel::Table.new(reflection.join_table).alias('t0') | |
- end | |
- | |
- # Unlike the other associations, we want to get a raw array of rows so that we can | |
- # access the aliased column on the join table | |
- def records_for(ids) | |
- scope = query_scope ids | |
- klass.connection.select_all(scope.arel, 'SQL', scope.bind_values) | |
- end | |
- | |
- def owner_key_name | |
- reflection.active_record_primary_key | |
- end | |
- | |
- def association_key_name | |
- 'ar_association_key_name' | |
- end | |
- | |
- def association_key | |
- join_table[reflection.foreign_key] | |
- end | |
- | |
- private | |
- | |
- # Once we have used the join table column (in super), we manually instantiate the | |
- # actual records, ensuring that we don't create more than one instances of the same | |
- # record | |
- def load_slices(slices) | |
- identity_map = {} | |
- caster = nil | |
- name = association_key_name | |
- | |
- records_to_keys = slices.flat_map { |slice| | |
- records = records_for(slice) | |
- caster ||= records.column_types.fetch(name, records.identity_type) | |
- records.map! { |row| | |
- record = identity_map[row[klass.primary_key]] ||= klass.instantiate(row) | |
- [record, caster.type_cast(row[name])] | |
- } | |
- } | |
- @preloaded_records = records_to_keys.map(&:first) | |
- | |
- records_to_keys | |
- end | |
- | |
- def build_scope | |
- super.joins(join).select(join_select) | |
- end | |
- | |
- def join_select | |
- association_key.as(Arel.sql(association_key_name)) | |
- end | |
- | |
- def join | |
- condition = table[reflection.association_primary_key].eq( | |
- join_table[reflection.association_foreign_key]) | |
- | |
- table.create_join(join_table, table.create_on(condition)) | |
- end | |
- end | |
- end | |
- end | |
-end | |
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb | |
index 9a26e5d..3bb3131 100644 | |
--- a/activerecord/lib/active_record/fixtures.rb | |
+++ b/activerecord/lib/active_record/fixtures.rb | |
@@ -639,8 +639,6 @@ module ActiveRecord | |
if association.options[:through] | |
add_join_records(rows, row, HasManyThroughProxy.new(association)) | |
end | |
- when :has_and_belongs_to_many | |
- add_join_records(rows, row, HABTMProxy.new(association)) | |
end | |
end | |
end | |
@@ -674,16 +672,6 @@ module ActiveRecord | |
end | |
end | |
- class HABTMProxy < ReflectionProxy # :nodoc: | |
- def rhs_key | |
- @association.association_foreign_key | |
- end | |
- | |
- def lhs_key | |
- @association.foreign_key | |
- end | |
- end | |
- | |
private | |
def primary_key_name | |
@primary_key_name ||= model_class && model_class.primary_key | |
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb | |
index 263ba92..8ddf5cf 100644 | |
--- a/activerecord/lib/active_record/reflection.rb | |
+++ b/activerecord/lib/active_record/reflection.rb | |
@@ -12,8 +12,6 @@ module ActiveRecord | |
def self.create(macro, name, scope, options, ar) | |
case macro | |
- when :has_and_belongs_to_many | |
- klass = AssociationReflection | |
when :has_many, :belongs_to, :has_one | |
klass = options[:through] ? ThroughReflection : AssociationReflection | |
when :composed_of | |
@@ -196,7 +194,7 @@ module ActiveRecord | |
def initialize(macro, name, scope, options, active_record) | |
super | |
- @collection = [:has_many, :has_and_belongs_to_many].include?(macro) | |
+ @collection = :has_many == macro | |
@automatic_inverse_of = nil | |
@type = options[:as] && "#{options[:as]}_type" | |
@foreign_type = options[:foreign_type] || "#{name}_type" | |
@@ -256,10 +254,6 @@ module ActiveRecord | |
def check_validity! | |
check_validity_of_inverse! | |
- | |
- if has_and_belongs_to_many? && association_foreign_key == foreign_key | |
- raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(self) | |
- end | |
end | |
def check_validity_of_inverse! | |
@@ -341,10 +335,6 @@ module ActiveRecord | |
macro == :belongs_to | |
end | |
- def has_and_belongs_to_many? | |
- macro == :has_and_belongs_to_many | |
- end | |
- | |
def association_class | |
case macro | |
when :belongs_to | |
@@ -353,8 +343,6 @@ module ActiveRecord | |
else | |
Associations::BelongsToAssociation | |
end | |
- when :has_and_belongs_to_many | |
- Associations::HasAndBelongsToManyAssociation | |
when :has_many | |
if options[:through] | |
Associations::HasManyThroughAssociation | |
@@ -604,7 +592,7 @@ module ActiveRecord | |
# A through association is nested if there would be more than one join table | |
def nested? | |
- chain.length > 2 || through_reflection.has_and_belongs_to_many? | |
+ chain.length > 2 | |
end | |
# We want to use the klass from this reflection, rather than just delegate straight to | |
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb | |
index 8d3d696..874ae77 100644 | |
--- a/activerecord/test/cases/associations/eager_test.rb | |
+++ b/activerecord/test/cases/associations/eager_test.rb | |
@@ -747,6 +747,8 @@ class EagerAssociationTest < ActiveRecord::TestCase | |
end | |
def test_eager_with_default_scope_as_block | |
+ # warm up the habtm cache | |
+ EagerDeveloperWithBlockDefaultScope.where(:name => 'David').first.projects | |
developer = EagerDeveloperWithBlockDefaultScope.where(:name => 'David').first | |
projects = Project.order(:id).to_a | |
assert_no_queries do | |
@@ -1136,6 +1138,10 @@ class EagerAssociationTest < ActiveRecord::TestCase | |
end | |
def test_deep_including_through_habtm | |
+ # warm up habtm cache | |
+ posts = Post.all.merge!(:includes => {:categories => :categorizations}, :order => "posts.id").to_a | |
+ posts[0].categories[0].categorizations.length | |
+ | |
posts = Post.all.merge!(:includes => {:categories => :categorizations}, :order => "posts.id").to_a | |
assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length } | |
assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length } | |
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb | |
index f77066e..be928ec 100644 | |
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb | |
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb | |
@@ -613,7 +613,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase | |
3, | |
Developer.references(:developers_projects_join).merge( | |
:includes => {:projects => :developers}, | |
- :where => 'developers_projects_join.joined_on IS NOT NULL' | |
+ :where => 'projects_developers_projects_join.joined_on IS NOT NULL' | |
).to_a.size | |
) | |
end | |
@@ -632,7 +632,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase | |
assert_equal( | |
3, | |
Developer.references(:developers_projects_join).merge( | |
- :includes => {:projects => :developers}, :where => 'developers_projects_join.joined_on IS NOT NULL', | |
+ :includes => {:projects => :developers}, :where => 'projects_developers_projects_join.joined_on IS NOT NULL', | |
:group => group.join(",") | |
).to_a.size | |
) | |
@@ -646,8 +646,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase | |
end | |
def test_find_scoped_grouped | |
- assert_equal 5, categories(:general).posts_grouped_by_title.size | |
- assert_equal 1, categories(:technology).posts_grouped_by_title.size | |
+ assert_equal 5, categories(:general).posts_grouped_by_title.to_a.size | |
+ assert_equal 1, categories(:technology).posts_grouped_by_title.to_a.size | |
end | |
def test_find_scoped_grouped_having | |
@@ -718,12 +718,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase | |
assert_equal project, developer.projects.first | |
end | |
- def test_self_referential_habtm_without_foreign_key_set_should_raise_exception | |
- assert_raise(ActiveRecord::HasAndBelongsToManyAssociationForeignKeyNeeded) { | |
- SelfMember.new.friends | |
- } | |
- end | |
- | |
def test_dynamic_find_should_respect_association_include | |
# SQL error in sort clause if :include is not included | |
# due to Unknown column 'authors.id' | |
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb | |
index cf3c078..95f49a3 100644 | |
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb | |
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb | |
@@ -214,7 +214,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase | |
end | |
def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection_preload | |
- authors = assert_queries(3) { Author.includes(:post_categories).to_a.sort_by(&:id) } | |
+ authors = assert_queries(4) { Author.includes(:post_categories).to_a.sort_by(&:id) } | |
general, cooking = categories(:general), categories(:cooking) | |
assert_no_queries do | |
@@ -242,7 +242,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase | |
end | |
def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload | |
- categories = assert_queries(3) { Category.includes(:post_comments).to_a.sort_by(&:id) } | |
+ categories = assert_queries(4) { Category.includes(:post_comments).to_a.sort_by(&:id) } | |
greetings, more = comments(:greetings), comments(:more_greetings) | |
assert_no_queries do | |
@@ -270,7 +270,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase | |
end | |
def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload | |
- authors = assert_queries(5) { Author.includes(:category_post_comments).to_a.sort_by(&:id) } | |
+ authors = assert_queries(6) { Author.includes(:category_post_comments).to_a.sort_by(&:id) } | |
greetings, more = comments(:greetings), comments(:more_greetings) | |
assert_no_queries do | |
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb | |
index 635278a..517d267 100644 | |
--- a/activerecord/test/cases/autosave_association_test.rb | |
+++ b/activerecord/test/cases/autosave_association_test.rb | |
@@ -1440,10 +1440,6 @@ class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCas | |
test "should generate validation methods for HABTM associations with :validate => true" do | |
assert_respond_to @pirate, :validate_associated_records_for_parrots | |
end | |
- | |
- test "should not generate validation methods for HABTM associations without :validate => true" do | |
- assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrots) | |
- end | |
end | |
class TestAutosaveAssociationWithTouch < ActiveRecord::TestCase | |
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb | |
index 9e5ffa0..f814947 100644 | |
--- a/activerecord/test/cases/relations_test.rb | |
+++ b/activerecord/test/cases/relations_test.rb | |
@@ -42,6 +42,11 @@ class RelationTest < ActiveRecord::TestCase | |
end | |
def test_two_scopes_with_includes_should_not_drop_any_include | |
+ # heat habtm cache | |
+ car = Car.incl_engines.incl_tyres.first | |
+ car.tyres.length | |
+ car.engines.length | |
+ | |
car = Car.incl_engines.incl_tyres.first | |
assert_no_queries { car.tyres.length } | |
assert_no_queries { car.engines.length } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment