diff options
author | Pratik Naik <pratiknaik@gmail.com> | 2008-10-05 19:46:48 +0100 |
---|---|---|
committer | Pratik Naik <pratiknaik@gmail.com> | 2008-10-05 19:46:48 +0100 |
commit | 6090513cfb8acb5554a6653a6f2cb87648585d41 (patch) | |
tree | 99bfd589a48153e33f19ae72baa6e98f5708a9b8 /activerecord | |
parent | 01159a6431bbc2dc7d7d95ce294c8567c954f39e (diff) | |
parent | 4df45d86097efbeabceecfe53d8ea2da9ccbb107 (diff) | |
download | rails-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')
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'] |