aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2008-10-05 19:46:48 +0100
committerPratik Naik <pratiknaik@gmail.com>2008-10-05 19:46:48 +0100
commit6090513cfb8acb5554a6653a6f2cb87648585d41 (patch)
tree99bfd589a48153e33f19ae72baa6e98f5708a9b8 /activerecord
parent01159a6431bbc2dc7d7d95ce294c8567c954f39e (diff)
parent4df45d86097efbeabceecfe53d8ea2da9ccbb107 (diff)
downloadrails-6090513cfb8acb5554a6653a6f2cb87648585d41.tar.gz
rails-6090513cfb8acb5554a6653a6f2cb87648585d41.tar.bz2
rails-6090513cfb8acb5554a6653a6f2cb87648585d41.zip
Merge commit 'mainstream/master'
Conflicts: activerecord/lib/active_record/association_preload.rb
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/association_preload.rb32
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb11
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb4
-rwxr-xr-xactiverecord/lib/active_record/base.rb8
-rw-r--r--activerecord/lib/active_record/calculations.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb6
-rw-r--r--activerecord/lib/active_record/dynamic_finder_match.rb8
-rw-r--r--activerecord/lib/active_record/reflection.rb135
-rw-r--r--activerecord/test/cases/active_schema_test_mysql.rb5
-rw-r--r--activerecord/test/cases/associations/eager_test.rb16
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb1
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb17
-rw-r--r--activerecord/test/cases/calculations_test.rb6
-rw-r--r--activerecord/test/cases/finder_test.rb27
-rw-r--r--activerecord/test/cases/reflection_test.rb4
-rw-r--r--activerecord/test/cases/reload_models_test.rb20
-rw-r--r--activerecord/test/models/post.rb2
19 files changed, 239 insertions, 108 deletions
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index cef8cd8647..99e3973830 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -161,12 +161,13 @@ module ActiveRecord
# the objects' IDs to the relevant objects. Returns a 2-tuple
# <tt>(id_to_record_map, ids)</tt> where +id_to_record_map+ is the Hash,
# and +ids+ is an Array of record IDs.
- def construct_id_map(records)
+ def construct_id_map(records, primary_key=nil)
id_to_record_map = {}
ids = []
records.each do |record|
- ids << record.id
- mapped_records = (id_to_record_map[record.id.to_s] ||= [])
+ primary_key ||= record.class.primary_key
+ ids << record[primary_key]
+ mapped_records = (id_to_record_map[ids.last.to_s] ||= [])
mapped_records << record
end
ids.uniq!
@@ -180,7 +181,7 @@ module ActiveRecord
options = reflection.options
conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
- conditions << append_conditions(options, preload_options)
+ conditions << append_conditions(reflection, preload_options)
associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
:include => options[:include],
@@ -213,23 +214,24 @@ module ActiveRecord
end
def preload_has_many_association(records, reflection, preload_options={})
- id_to_record_map, ids = construct_id_map(records)
- records.each {|record| record.send(reflection.name).loaded}
options = reflection.options
+ primary_key_name = reflection.through_reflection_primary_key_name
+ id_to_record_map, ids = construct_id_map(records, primary_key_name)
+ records.each {|record| record.send(reflection.name).loaded}
+
if options[:through]
through_records = preload_through_records(records, reflection, options[:through])
through_reflection = reflections[options[:through]]
- through_primary_key = through_reflection.primary_key_name
unless through_records.empty?
source = reflection.source_reflection.name
- #add conditions from reflection!
- through_records.first.class.preload_associations(through_records, source, reflection.options)
+ through_records.first.class.preload_associations(through_records, source, options)
through_records.each do |through_record|
- add_preloaded_records_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
- reflection.name, through_record.send(source))
+ through_record_id = through_record[reflection.through_reflection_primary_key].to_s
+ add_preloaded_records_to_collection(id_to_record_map[through_record_id], reflection.name, through_record.send(source))
end
end
+
else
set_association_collection_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options),
reflection.primary_key_name)
@@ -317,7 +319,7 @@ module ActiveRecord
end
end
conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}"
- conditions << append_conditions(options, preload_options)
+ conditions << append_conditions(reflection, preload_options)
associated_records = klass.find(:all, :conditions => [conditions, ids],
:include => options[:include],
:select => options[:select],
@@ -338,7 +340,7 @@ module ActiveRecord
conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"
end
- conditions << append_conditions(options, preload_options)
+ conditions << append_conditions(reflection, preload_options)
reflection.klass.find(:all,
:select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
@@ -354,9 +356,9 @@ module ActiveRecord
instance_eval("%@#{sql.gsub('@', '\@')}@")
end
- def append_conditions(options, preload_options)
+ def append_conditions(reflection, preload_options)
sql = ""
- sql << " AND (#{interpolate_sql_for_preload(sanitize_sql(options[:conditions]))})" if options[:conditions]
+ sql << " AND (#{interpolate_sql_for_preload(reflection.sanitized_conditions)})" if reflection.sanitized_conditions
sql << " AND (#{sanitize_sql preload_options[:conditions]})" if preload_options[:conditions]
sql
end
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index acdcd14ec8..b617147f67 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -240,7 +240,7 @@ module ActiveRecord
# the kind of the class of the associated objects. Meant to be used as
# a sanity check when you are about to assign an associated record.
def raise_on_type_mismatch(record)
- unless record.is_a?(@reflection.klass)
+ unless record.is_a?(@reflection.klass) || record.is_a?(@reflection.class_name.constantize)
message = "#{@reflection.class_name}(##{@reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
raise ActiveRecord::AssociationTypeMismatch, message
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 3171665e19..a0bb3a45b0 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -32,6 +32,14 @@ module ActiveRecord
end
protected
+ def target_reflection_has_associated_record?
+ if @reflection.through_reflection.macro == :belongs_to && @owner[@reflection.through_reflection.primary_key_name].blank?
+ false
+ else
+ true
+ end
+ end
+
def construct_find_options!(options)
options[:select] = construct_select(options[:select])
options[:from] ||= construct_from
@@ -61,6 +69,7 @@ module ActiveRecord
end
def find_target
+ return [] unless target_reflection_has_associated_record?
@reflection.klass.find(:all,
:select => construct_select,
:conditions => construct_conditions,
@@ -102,6 +111,8 @@ module ActiveRecord
"#{as}_type" => reflection.klass.quote_value(
@owner.class.base_class.name.to_s,
reflection.klass.columns_hash["#{as}_type"]) }
+ elsif reflection.macro == :belongs_to
+ { reflection.klass.primary_key => @owner[reflection.primary_key_name] }
else
{ reflection.primary_key_name => owner_quoted_id }
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index e5486738f0..1c753524de 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -342,7 +342,9 @@ module ActiveRecord
method_name = method.to_s
if super
return true
- elsif self.private_methods.include?(method_name) && !include_private_methods
+ elsif !include_private_methods && super(method, true)
+ # If we're here than we haven't found among non-private methods
+ # but found among all methods. Which means that given method is private.
return false
elsif !self.class.generated_methods?
self.class.define_attribute_methods
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 6fb05b5551..6a1a3794a2 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1780,10 +1780,10 @@ module ActiveRecord #:nodoc:
#{'result = ' if bang}if options[:conditions]
with_scope(:find => finder_options) do
- ActiveSupport::Deprecation.silence { send(:#{finder}, options) }
+ find(:#{finder}, options)
end
else
- ActiveSupport::Deprecation.silence { send(:#{finder}, options.merge(finder_options)) }
+ find(:#{finder}, options.merge(finder_options))
end
#{'result || raise(RecordNotFound)' if bang}
end
@@ -1806,9 +1806,9 @@ module ActiveRecord #:nodoc:
options = { :conditions => find_attributes }
set_readonly_option!(options)
- record = find_initial(options)
+ record = find(:first, options)
- if record.nil?
+ if record.nil?
record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
#{'yield(record) if block_given?'}
#{'record.save' if instantiator == :create}
diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb
index 80992dd34b..5e33cf1bd4 100644
--- a/activerecord/lib/active_record/calculations.rb
+++ b/activerecord/lib/active_record/calculations.rb
@@ -217,7 +217,7 @@ module ActiveRecord
sql << " ORDER BY #{options[:order]} " if options[:order]
add_limit!(sql, options, scope)
- sql << ") AS #{aggregate_alias}_subquery" if use_workaround
+ sql << ") #{aggregate_alias}_subquery" if use_workaround
sql
end
@@ -285,11 +285,15 @@ module ActiveRecord
operation = operation.to_s.downcase
case operation
when 'count' then value.to_i
- when 'sum' then value =~ /\./ ? value.to_f : value.to_i
- when 'avg' then value && value.to_f
- else column ? column.type_cast(value) : value
+ when 'sum' then type_cast_using_column(value || '0', column)
+ when 'avg' then value && value.to_d
+ else type_cast_using_column(value, column)
end
end
+
+ def type_cast_using_column(value, column)
+ column ? column.type_cast(value) : value
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 10cae5d840..432c341e6c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -172,21 +172,24 @@ module ActiveRecord
# within the timeout period.
def checkout
# Checkout an available connection
- conn = @connection_mutex.synchronize do
- if @checked_out.size < @connections.size
- checkout_existing_connection
- elsif @connections.size < @size
- checkout_new_connection
- end
- end
- return conn if conn
-
- # No connections available; wait for one
@connection_mutex.synchronize do
- if @queue.wait(@timeout)
- checkout_existing_connection
- else
- raise ConnectionTimeoutError, "could not obtain a database connection within #{@timeout} seconds. The pool size is currently #{@size}, perhaps you need to increase it?"
+ loop do
+ conn = if @checked_out.size < @connections.size
+ checkout_existing_connection
+ elsif @connections.size < @size
+ checkout_new_connection
+ end
+ return conn if conn
+ # No connections available; wait for one
+ if @queue.wait(@timeout)
+ next
+ else
+ # try looting dead threads
+ clear_stale_cached_connections!
+ if @size == @checked_out.size
+ raise ConnectionTimeoutError, "could not obtain a database connection within #{@timeout} seconds. The pool size is currently #{@size}, perhaps you need to increase it?"
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index a26fd02b90..1e452ae88a 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -218,7 +218,7 @@ module ActiveRecord
s = column.class.string_to_binary(value).unpack("H*")[0]
"x'#{s}'"
elsif value.kind_of?(BigDecimal)
- "'#{value.to_s("F")}'"
+ value.to_s("F")
else
super
end
@@ -371,9 +371,9 @@ module ActiveRecord
end
end
- def recreate_database(name) #:nodoc:
+ def recreate_database(name, options = {}) #:nodoc:
drop_database(name)
- create_database(name)
+ create_database(name, options)
end
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb
index f4a5712981..8f9f05ce36 100644
--- a/activerecord/lib/active_record/dynamic_finder_match.rb
+++ b/activerecord/lib/active_record/dynamic_finder_match.rb
@@ -6,11 +6,11 @@ module ActiveRecord
end
def initialize(method)
- @finder = :find_initial
+ @finder = :first
case method.to_s
when /^find_(all_by|last_by|by)_([_a-zA-Z]\w*)$/
- @finder = :find_last if $1 == 'last_by'
- @finder = :find_every if $1 == 'all_by'
+ @finder = :last if $1 == 'last_by'
+ @finder = :all if $1 == 'all_by'
names = $2
when /^find_by_([_a-zA-Z]\w*)\!$/
@bang = true
@@ -31,7 +31,7 @@ module ActiveRecord
end
def instantiator?
- @finder == :find_initial && !@instantiator.nil?
+ @finder == :first && !@instantiator.nil?
end
def bang?
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index a1b498eceb..dbff4f24d6 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -13,14 +13,15 @@ module ActiveRecord
def create_reflection(macro, name, options, active_record)
case macro
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
- reflection = AssociationReflection.new(macro, name, options, active_record)
+ klass = options[:through] ? ThroughReflection : AssociationReflection
+ reflection = klass.new(macro, name, options, active_record)
when :composed_of
reflection = AggregateReflection.new(macro, name, options, active_record)
end
write_inheritable_hash :reflections, name => reflection
reflection
end
-
+
# Returns a hash containing all AssociationReflection objects for the current class
# Example:
#
@@ -30,7 +31,7 @@ module ActiveRecord
def reflections
read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
end
-
+
# Returns an array of AggregateReflection objects for all the aggregations in the class.
def reflect_on_all_aggregations
reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
@@ -116,6 +117,11 @@ module ActiveRecord
@sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
end
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
+ def belongs_to?
+ macro == :belongs_to
+ end
+
private
def derive_class_name
name.to_s.camelize
@@ -192,6 +198,52 @@ module ActiveRecord
end
end
+ def check_validity!
+ end
+
+ def through_reflection
+ false
+ end
+
+ def through_reflection_primary_key_name
+ end
+
+ def source_reflection
+ nil
+ end
+
+ private
+ def derive_class_name
+ class_name = name.to_s.camelize
+ class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
+ class_name
+ end
+
+ def derive_primary_key_name
+ if belongs_to?
+ "#{name}_id"
+ elsif options[:as]
+ "#{options[:as]}_id"
+ else
+ active_record.name.foreign_key
+ end
+ end
+ end
+
+ # Holds all the meta-data about a :through association as it was specified in the Active Record class.
+ class ThroughReflection < AssociationReflection #:nodoc:
+ # Gets the source of the through reflection. It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
+ # (The <tt>:tags</tt> association on Tagging below.)
+ #
+ # class Post < ActiveRecord::Base
+ # has_many :taggings
+ # has_many :tags, :through => :taggings
+ # end
+ #
+ def source_reflection
+ @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
+ end
+
# Returns the AssociationReflection object specified in the <tt>:through</tt> option
# of a HasManyThrough or HasOneThrough association. Example:
#
@@ -204,7 +256,7 @@ module ActiveRecord
# taggings_reflection = tags_reflection.through_reflection
#
def through_reflection
- @through_reflection ||= options[:through] ? active_record.reflect_on_association(options[:through]) : false
+ @through_reflection ||= active_record.reflect_on_association(options[:through])
end
# Gets an array of possible <tt>:through</tt> source reflection names:
@@ -215,63 +267,40 @@ module ActiveRecord
@source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
end
- # Gets the source of the through reflection. It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
- # (The <tt>:tags</tt> association on Tagging below.)
- #
- # class Post < ActiveRecord::Base
- # has_many :taggings
- # has_many :tags, :through => :taggings
- # end
- #
- def source_reflection
- return nil unless through_reflection
- @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
- end
-
def check_validity!
- if options[:through]
- if through_reflection.nil?
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
- end
-
- if source_reflection.nil?
- raise HasManyThroughSourceAssociationNotFoundError.new(self)
- end
+ if through_reflection.nil?
+ raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
+ end
- if options[:source_type] && source_reflection.options[:polymorphic].nil?
- raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
- end
-
- if source_reflection.options[:polymorphic] && options[:source_type].nil?
- raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
- end
-
- unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
- raise HasManyThroughSourceAssociationMacroError.new(self)
- end
+ if source_reflection.nil?
+ raise HasManyThroughSourceAssociationNotFoundError.new(self)
+ end
+
+ if options[:source_type] && source_reflection.options[:polymorphic].nil?
+ raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
end
+
+ if source_reflection.options[:polymorphic] && options[:source_type].nil?
+ raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
+ end
+
+ unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
+ raise HasManyThroughSourceAssociationMacroError.new(self)
+ end
+ end
+
+ def through_reflection_primary_key
+ through_reflection.belongs_to? ? through_reflection.klass.primary_key : through_reflection.primary_key_name
+ end
+
+ def through_reflection_primary_key_name
+ through_reflection.primary_key_name if through_reflection.belongs_to?
end
private
def derive_class_name
# get the class_name of the belongs_to association of the through reflection
- if through_reflection
- options[:source_type] || source_reflection.class_name
- else
- class_name = name.to_s.camelize
- class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
- class_name
- end
- end
-
- def derive_primary_key_name
- if macro == :belongs_to
- "#{name}_id"
- elsif options[:as]
- "#{options[:as]}_id"
- else
- active_record.name.foreign_key
- end
+ options[:source_type] || source_reflection.class_name
end
end
end
diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb
index 2a42dc3517..9aff538ce9 100644
--- a/activerecord/test/cases/active_schema_test_mysql.rb
+++ b/activerecord/test/cases/active_schema_test_mysql.rb
@@ -25,6 +25,11 @@ class ActiveSchemaTest < ActiveRecord::TestCase
assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
end
+
+ def test_recreate_mysql_database_with_encoding
+ create_database(:luca, {:charset => 'latin1'})
+ assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'})
+ end
end
def test_add_column
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index e78624a98d..7f42577ab0 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -116,6 +116,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal 2, posts.first.comments.size
end
+ def test_loading_from_an_association_that_has_a_hash_of_conditions
+ assert_nothing_raised do
+ Author.find(:all, :include => :hello_posts_with_hash_conditions)
+ end
+ assert !Author.find(authors(:david).id, :include => :hello_posts_with_hash_conditions).hello_posts.empty?
+ end
+
def test_loading_with_no_associations
assert_nil Post.find(posts(:authorless).id, :include => :author).author
end
@@ -268,6 +275,15 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
end
+ def test_eager_with_has_many_through_a_belongs_to_association
+ author = authors(:mary)
+ post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
+ author.author_favorites.create(:favorite_author_id => 1)
+ author.author_favorites.create(:favorite_author_id => 2)
+ posts_with_author_favorites = author.posts.find(:all, :include => :author_favorites)
+ assert_no_queries { posts_with_author_favorites.first.author_favorites.first.author_id }
+ end
+
def test_eager_with_has_many_through_an_sti_join_model
author = Author.find(:first, :include => :special_post_comments, :order => 'authors.id')
assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 315d77de07..1bc9c39c19 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1081,3 +1081,4 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
+
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 12cce98c26..a07f4bcbdd 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -5,7 +5,7 @@ require 'models/reader'
require 'models/comment'
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
- fixtures :posts, :readers, :people, :comments
+ fixtures :posts, :readers, :people, :comments, :authors
def test_associate_existing
assert_queries(2) { posts(:thinking);people(:david) }
@@ -229,4 +229,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
end
+
+ def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
+ author = authors(:mary)
+ post = Post.create!(:title => "TITLE", :body => "BODY")
+ assert_equal [], post.author_favorites
+ end
+
+ def test_has_many_association_through_a_belongs_to_association
+ author = authors(:mary)
+ post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
+ author.author_favorites.create(:favorite_author_id => 1)
+ author.author_favorites.create(:favorite_author_id => 2)
+ author.author_favorites.create(:favorite_author_id => 3)
+ assert_equal post.author.author_favorites, post.author_favorites
+ end
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 754fd58f35..0fa61500c0 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -18,8 +18,8 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_average_field
value = Account.average(:credit_limit)
- assert_kind_of Float, value
- assert_in_delta 53.0, value, 0.001
+ assert_kind_of BigDecimal, value
+ assert_equal BigDecimal.new('53.0'), value
end
def test_should_return_nil_as_average
@@ -273,7 +273,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_sum_expression
- assert_equal 636, Account.sum("2 * credit_limit")
+ assert_equal '636', Account.sum("2 * credit_limit")
end
def test_count_with_from_option
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 292b88edbc..853474916c 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -21,7 +21,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location")
assert_not_nil match
assert match.finder?
- assert_equal :find_initial, match.finder
+ assert_equal :first, match.finder
assert_equal %w(age sex location), match.attribute_names
end
@@ -30,7 +30,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
assert_not_nil match
assert match.finder?
assert match.bang?
- assert_equal :find_initial, match.finder
+ assert_equal :first, match.finder
assert_equal %w(age sex location), match.attribute_names
end
@@ -38,7 +38,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
match = ActiveRecord::DynamicFinderMatch.match("find_all_by_age_and_sex_and_location")
assert_not_nil match
assert match.finder?
- assert_equal :find_every, match.finder
+ assert_equal :all, match.finder
assert_equal %w(age sex location), match.attribute_names
end
@@ -47,7 +47,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
assert_not_nil match
assert !match.finder?
assert match.instantiator?
- assert_equal :find_initial, match.finder
+ assert_equal :first, match.finder
assert_equal :new, match.instantiator
assert_equal %w(age sex location), match.attribute_names
end
@@ -57,7 +57,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
assert_not_nil match
assert !match.finder?
assert match.instantiator?
- assert_equal :find_initial, match.finder
+ assert_equal :first, match.finder
assert_equal :create, match.instantiator
assert_equal %w(age sex location), match.attribute_names
end
@@ -500,6 +500,23 @@ class FinderTest < ActiveRecord::TestCase
assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
end
+ uses_mocha('test_dynamic_finder_should_go_through_the_find_class_method') do
+ def test_dynamic_finders_should_go_through_the_find_class_method
+ Topic.expects(:find).with(:first, :conditions => { :title => 'The First Topic!' })
+ Topic.find_by_title("The First Topic!")
+
+ Topic.expects(:find).with(:last, :conditions => { :title => 'The Last Topic!' })
+ Topic.find_last_by_title("The Last Topic!")
+
+ Topic.expects(:find).with(:all, :conditions => { :title => 'A Topic.' })
+ Topic.find_all_by_title("A Topic.")
+
+ Topic.expects(:find).with(:first, :conditions => { :title => 'Does not exist yet for sure!' }).times(2)
+ Topic.find_or_initialize_by_title('Does not exist yet for sure!')
+ Topic.find_or_create_by_title('Does not exist yet for sure!')
+ end
+ end
+
def test_find_by_one_attribute
assert_equal topics(:first), Topic.find_by_title("The First Topic")
assert_nil Topic.find_by_title("The First Topic!")
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index e339ef41ab..e0ed3e5886 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -170,6 +170,10 @@ class ReflectionTest < ActiveRecord::TestCase
assert_nothing_raised { Firm.reflections[:clients] == Object.new }
end
+ def test_has_many_through_reflection
+ assert_kind_of ActiveRecord::Reflection::ThroughReflection, Subscriber.reflect_on_association(:books)
+ end
+
private
def assert_reflection(klass, association, options)
assert reflection = klass.reflect_on_association(association)
diff --git a/activerecord/test/cases/reload_models_test.rb b/activerecord/test/cases/reload_models_test.rb
new file mode 100644
index 0000000000..411b5f6afa
--- /dev/null
+++ b/activerecord/test/cases/reload_models_test.rb
@@ -0,0 +1,20 @@
+require "cases/helper"
+require 'models/owner'
+require 'models/pet'
+
+class ReloadModelsTest < ActiveRecord::TestCase
+ def test_has_one_with_reload
+ pet = Pet.find_by_name('parrot')
+ pet.owner = Owner.find_by_name('ashley')
+
+ # Reload the class Owner, simulating auto-reloading of model classes in a
+ # development environment. Note that meanwhile the class Pet is not
+ # reloaded, simulating a class that is present in a plugin.
+ Object.class_eval { remove_const :Owner }
+ Kernel.load(File.expand_path(File.join(File.dirname(__FILE__), "../models/owner.rb")))
+
+ pet = Pet.find_by_name('parrot')
+ pet.owner = Owner.find_by_name('ashley')
+ assert_equal pet.owner, Owner.find_by_name('ashley')
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 3adbc0ce1f..6da37c31ff 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -22,6 +22,8 @@ class Post < ActiveRecord::Base
end
end
+ has_many :author_favorites, :through => :author
+
has_many :comments_with_interpolated_conditions, :class_name => 'Comment',
:conditions => ['#{"#{aliased_table_name}." rescue ""}body = ?', 'Thank you for the welcome']