diff options
Diffstat (limited to 'activerecord')
30 files changed, 412 insertions, 39 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 700e11ff94..143de81925 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,8 @@ -*Rails 3.2.0 (unreleased)* +Wed Sep 7 15:25:02 2011 Aaron Patterson <aaron@tenderlovemaking.com> + + * lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache + keys are per process id. + * lib/active_record/connection_adapters/sqlite_adapter.rb: ditto * Support bulk change_table in mysql2 adapter, as well as the mysql one. [Jon Leighton] diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index adb6af7165..58c9648ce8 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -20,6 +20,10 @@ module ActiveRecord private + def find_target? + !loaded? && foreign_key_present? && klass + end + def update_counters(record) counter_cache_name = reflection.counter_cache_column diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index 81172179e0..b347a94978 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -16,7 +16,7 @@ module ActiveRecord chain[1..-1].each do |reflection| scope = scope.merge( reflection.klass.scoped.with_default_scope. - except(:select, :create_with, :includes) + except(:select, :create_with, :includes, :preload, :joins, :eager_load) ) end scope 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 61994d4a47..20863e73aa 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -421,7 +421,7 @@ module ActiveRecord # can be used as an argument for establish_connection, for easily # re-establishing the connection. def remove_connection(klass) - pool = @connection_pools[klass.name] + pool = @connection_pools.delete(klass.name) return nil unless pool pool.automatic_reconnect = false diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index 7312e34f01..c08c0263b9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -126,7 +126,7 @@ module ActiveRecord end def connection_pool - connection_handler.retrieve_connection_pool(self) + connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished end def retrieve_connection diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index 3de850ec9e..f93c7cd74a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -102,10 +102,13 @@ module ActiveRecord def quoted_date(value) if value.acts_like?(:time) zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal - value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value - else - value - end.to_s(:db) + + if value.respond_to?(zone_conversion_method) + value = value.send(zone_conversion_method) + end + end + + value.to_s(:db) 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 e9bdcc2104..a1824fe396 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -1,4 +1,5 @@ require 'active_record/connection_adapters/abstract_mysql_adapter' +require 'active_record/connection_adapters/statement_pool' require 'active_support/core_ext/hash/keys' gem 'mysql', '~> 2.8.1' @@ -90,9 +91,42 @@ module ActiveRecord ADAPTER_NAME = 'MySQL' + class StatementPool < ConnectionAdapters::StatementPool + def initialize(connection, max = 1000) + super + @cache = Hash.new { |h,pid| h[pid] = {} } + end + + def each(&block); cache.each(&block); end + def key?(key); cache.key?(key); end + def [](key); cache[key]; end + def length; cache.length; end + def delete(key); cache.delete(key); end + + def []=(sql, key) + while @max <= cache.size + cache.shift.last[:stmt].close + end + cache[sql] = key + end + + def clear + cache.values.each do |hash| + hash[:stmt].close + end + cache.clear + end + + private + def cache + @cache[$$] + end + end + def initialize(connection, logger, connection_options, config) super - @statements = {} + @statements = StatementPool.new(@connection, + config.fetch(:statement_limit) { 1000 }) @client_encoding = nil connect end @@ -187,9 +221,6 @@ module ActiveRecord # Clears the prepared statements cache. def clear_cache! - @statements.values.each do |cache| - cache[:stmt].close - end @statements.clear end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index ba4a6c7a78..5402918b1d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -1,5 +1,6 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_support/core_ext/object/blank' +require 'active_record/connection_adapters/statement_pool' # Make sure we're using pg high enough for PGResult#values gem 'pg', '~> 0.11' @@ -246,6 +247,47 @@ module ActiveRecord true end + class StatementPool < ConnectionAdapters::StatementPool + def initialize(connection, max) + super + @counter = 0 + @cache = Hash.new { |h,pid| h[pid] = {} } + end + + def each(&block); cache.each(&block); end + def key?(key); cache.key?(key); end + def [](key); cache[key]; end + def length; cache.length; end + + def next_key + "a#{@counter + 1}" + end + + def []=(sql, key) + while @max <= cache.size + dealloc(cache.shift.last) + end + @counter += 1 + cache[sql] = key + end + + def clear + cache.each_value do |stmt_key| + dealloc stmt_key + end + cache.clear + end + + private + def cache + @cache[$$] + end + + def dealloc(key) + @connection.query "DEALLOCATE #{key}" + end + end + # Initializes and connects a PostgreSQL adapter. def initialize(connection, logger, connection_parameters, config) super(connection, logger) @@ -254,9 +296,10 @@ module ActiveRecord # @local_tz is initialized as nil to avoid warnings when connect tries to use it @local_tz = nil @table_alias_length = nil - @statements = {} connect + @statements = StatementPool.new @connection, + config.fetch(:statement_limit) { 1000 } if postgresql_version < 80200 raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!" @@ -271,9 +314,6 @@ module ActiveRecord # Clears the prepared statements cache. def clear_cache! - @statements.each_value do |value| - @connection.query "DEALLOCATE #{value}" - end @statements.clear end @@ -683,12 +723,12 @@ module ActiveRecord binds << [nil, schema] if schema exec_query(<<-SQL, 'SCHEMA', binds).rows.first[0].to_i > 0 - SELECT COUNT(*) - FROM pg_class c - LEFT JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE c.relkind in ('v','r') - AND c.relname = $1 - AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'} + SELECT COUNT(*) + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relkind in ('v','r') + AND c.relname = $1 + AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'} SQL end @@ -996,7 +1036,7 @@ module ActiveRecord def exec_cache(sql, binds) unless @statements.key? sql - nextkey = "a#{@statements.length + 1}" + nextkey = @statements.next_key @connection.prepare nextkey, sql @statements[sql] = nextkey end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index a90c675bf6..1932a849ee 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -1,4 +1,5 @@ require 'active_record/connection_adapters/abstract_adapter' +require 'active_record/connection_adapters/statement_pool' require 'active_support/core_ext/string/encoding' module ActiveRecord @@ -48,9 +49,45 @@ module ActiveRecord end end + class StatementPool < ConnectionAdapters::StatementPool + def initialize(connection, max) + super + @cache = Hash.new { |h,pid| h[pid] = {} } + end + + def each(&block); cache.each(&block); end + def key?(key); cache.key?(key); end + def [](key); cache[key]; end + def length; cache.length; end + + def []=(sql, key) + while @max <= cache.size + dealloc(cache.shift.last[:stmt]) + end + cache[sql] = key + end + + def clear + cache.values.each do |hash| + dealloc hash[:stmt] + end + cache.clear + end + + private + def cache + @cache[$$] + end + + def dealloc(stmt) + stmt.close unless stmt.closed? + end + end + def initialize(connection, logger, config) super(connection, logger) - @statements = {} + @statements = StatementPool.new(@connection, + config.fetch(:statement_limit) { 1000 }) @config = config end @@ -107,10 +144,6 @@ module ActiveRecord # Clears the prepared statements cache. def clear_cache! - @statements.values.map { |hash| hash[:stmt] }.each { |stmt| - stmt.close unless stmt.closed? - } - @statements.clear end diff --git a/activerecord/lib/active_record/connection_adapters/statement_pool.rb b/activerecord/lib/active_record/connection_adapters/statement_pool.rb new file mode 100644 index 0000000000..c6b1bc8b5b --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/statement_pool.rb @@ -0,0 +1,40 @@ +module ActiveRecord + module ConnectionAdapters + class StatementPool + include Enumerable + + def initialize(connection, max = 1000) + @connection = connection + @max = max + end + + def each + raise NotImplementedError + end + + def key?(key) + raise NotImplementedError + end + + def [](key) + raise NotImplementedError + end + + def length + raise NotImplementedError + end + + def []=(sql, key) + raise NotImplementedError + end + + def clear + raise NotImplementedError + end + + def delete(key) + raise NotImplementedError + end + end + end +end diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 2eeff7e36f..ffee5a081a 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -71,7 +71,7 @@ module ActiveRecord end def invert_rename_index(args) - [:rename_index, args.reverse] + [:rename_index, [args.first] + args.last(2).reverse] end def invert_rename_column(args) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index a2324039cf..89179779e3 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -226,7 +226,7 @@ module ActiveRecord if options[:counter_cache] == true "#{active_record.name.demodulize.underscore.pluralize}_count" elsif options[:counter_cache] - options[:counter_cache] + options[:counter_cache].to_s end end diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index ec1176e3dd..2fd89882ff 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -62,7 +62,7 @@ module ActiveRecord start = options.delete(:start).to_i batch_size = options.delete(:batch_size) || 1000 - relation = relation.except(:order).order(batch_order).limit(batch_size) + relation = relation.reorder(batch_order).limit(batch_size) records = relation.where(table[primary_key].gteq(start)).all while records.any? diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 73368aed18..7eeb3dde70 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -114,7 +114,7 @@ module ActiveRecord def first(*args) if args.any? if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash)) - to_a.first(*args) + limit(*args).to_a else apply_finder_options(args.first).first end @@ -134,7 +134,11 @@ module ActiveRecord def last(*args) if args.any? if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash)) - to_a.last(*args) + if order_values.empty? && reorder_value.nil? + order("#{primary_key} DESC").limit(*args).reverse + else + to_a.last(*args) + end else apply_finder_options(args.first).last end @@ -180,7 +184,9 @@ module ActiveRecord # Person.exists?(:name => "David") # Person.exists?(['name LIKE ?', "%#{query}%"]) # Person.exists? - def exists?(id = nil) + def exists?(id = false) + return false if id.nil? + id = id.id if ActiveRecord::Base === id join_dependency = construct_join_dependency_for_association_find diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb new file mode 100644 index 0000000000..83de90f179 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb @@ -0,0 +1,23 @@ +require 'cases/helper' + +module ActiveRecord::ConnectionAdapters + class MysqlAdapter + class StatementPoolTest < ActiveRecord::TestCase + def test_cache_is_per_pid + return skip('must support fork') unless Process.respond_to?(:fork) + + cache = StatementPool.new nil, 10 + cache['foo'] = 'bar' + assert_equal 'bar', cache['foo'] + + pid = fork { + lookup = cache['foo']; + exit!(!lookup) + } + + Process.waitpid pid + assert $?.success?, 'process should exit successfully' + end + end + end +end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 4c6d865d59..76c73e9dfa 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -91,6 +91,12 @@ class SchemaTest < ActiveRecord::TestCase end end + def test_table_exists_quoted_table + with_schema_search_path(SCHEMA_NAME) do + assert(@connection.table_exists?('"things.table"'), "table should exist") + end + end + def test_with_schema_prefixed_table_name assert_nothing_raised do assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}") diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb new file mode 100644 index 0000000000..a82c6f67d6 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb @@ -0,0 +1,23 @@ +require 'cases/helper' + +module ActiveRecord::ConnectionAdapters + class PostgreSQLAdapter < AbstractAdapter + class StatementPoolTest < ActiveRecord::TestCase + def test_cache_is_per_pid + return skip('must support fork') unless Process.respond_to?(:fork) + + cache = StatementPool.new nil, 10 + cache['foo'] = 'bar' + assert_equal 'bar', cache['foo'] + + pid = fork { + lookup = cache['foo']; + exit!(!lookup) + } + + Process.waitpid pid + assert $?.success?, 'process should exit successfully' + end + end + end +end diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb new file mode 100644 index 0000000000..ae272e2c4b --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb @@ -0,0 +1,24 @@ +require 'cases/helper' + +module ActiveRecord::ConnectionAdapters + class SQLiteAdapter + class StatementPoolTest < ActiveRecord::TestCase + def test_cache_is_per_pid + return skip('must support fork') unless Process.respond_to?(:fork) + + cache = StatementPool.new nil, 10 + cache['foo'] = 'bar' + assert_equal 'bar', cache['foo'] + + pid = fork { + lookup = cache['foo']; + exit!(!lookup) + } + + Process.waitpid pid + assert $?.success?, 'process should exit successfully' + end + end + end +end + diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 818902beb5..866a3cca10 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -352,6 +352,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal members(:groucho), sponsor.sponsorable end + def test_dont_find_target_when_foreign_key_is_null + tagging = taggings(:thinking_general) + queries = assert_sql { tagging.super_tag } + assert_equal 0, queries.length + end + def test_field_name_same_as_foreign_key computer = Computer.find(1) assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # ' diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb index 490fc5177e..8dc1423375 100644 --- a/activerecord/test/cases/associations/extension_test.rb +++ b/activerecord/test/cases/associations/extension_test.rb @@ -36,6 +36,11 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase end def test_marshalling_extensions + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + david = developers(:david) assert_equal projects(:action_controller), david.projects.find_most_recent @@ -46,6 +51,11 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase end def test_marshalling_named_extensions + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + david = developers(:david) assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index a2764f3e3b..1e59931963 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -17,6 +17,7 @@ require 'models/invoice' require 'models/line_item' require 'models/car' require 'models/bulb' +require 'models/engine' class HasManyAssociationsTestForCountWithFinderSql < ActiveRecord::TestCase class Invoice < ActiveRecord::Base @@ -850,6 +851,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end end + def test_clearing_updates_counter_cache_when_inverse_counter_cache_is_a_symbol_with_dependent_destroy + car = Car.first + car.engines.create! + + assert_difference 'car.reload.engines_count', -1 do + car.engines.clear + end + end + def test_clearing_a_dependent_association_collection firm = companies(:first_firm) client_id = firm.dependent_clients_of_firm.first.id diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 80c6e41169..530f5212a2 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -356,6 +356,17 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase assert_equal categories(:general), members(:groucho).club_category end + def test_joins_and_includes_from_through_models_not_included_in_association + prev_default_scope = Club.default_scopes + + [:includes, :preload, :joins, :eager_load].each do |q| + Club.default_scopes = [Club.send(q, :category)] + assert_equal categories(:general), members(:groucho).reload.club_category + end + ensure + Club.default_scopes = prev_default_scope + end + def test_has_one_through_has_one_through_with_belongs_to_source_reflection_preload members = assert_queries(4) { Member.includes(:club_category).to_a.sort_by(&:id) } general = categories(:general) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 8b95eb958b..87f5b5ee81 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1835,6 +1835,11 @@ class BasicsTest < ActiveRecord::TestCase end def test_marshal_round_trip + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + expected = posts(:welcome) marshalled = Marshal.dump(expected) actual = Marshal.load(marshalled) @@ -1843,6 +1848,11 @@ class BasicsTest < ActiveRecord::TestCase end def test_marshal_new_record_round_trip + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + marshalled = Marshal.dump(Post.new) post = Marshal.load(marshalled) @@ -1850,6 +1860,11 @@ class BasicsTest < ActiveRecord::TestCase end def test_marshalling_with_associations + if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7" + return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \ + "to be a Ruby bug.") + end + post = Post.new post.comments.build diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index a35baee4ed..660098b9ad 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -113,7 +113,27 @@ class EachTest < ActiveRecord::TestCase batch.map! { not_a_post } end end + end + def test_find_in_batches_should_ignore_the_order_default_scope + # First post is with title scope + first_post = PostWithDefaultScope.first + posts = [] + PostWithDefaultScope.find_in_batches do |batch| + posts.concat(batch) + end + # posts.first will be ordered using id only. Title order scope should not apply here + assert_not_equal first_post, posts.first + assert_equal posts(:welcome), posts.first + end + + def test_find_in_batches_should_not_ignore_the_default_scope_if_it_is_other_then_order + special_posts_ids = SpecialPostWithDefaultScope.all.map(&:id).sort + posts = [] + SpecialPostWithDefaultScope.find_in_batches do |batch| + posts.concat(batch) + end + assert_equal special_posts_ids, posts.map(&:id) end end diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index abf317768f..bd0d161838 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -6,7 +6,12 @@ module ActiveRecord def setup @handler = ConnectionHandler.new @handler.establish_connection 'america', Base.connection_pool.spec - @klass = Struct.new(:name).new('america') + @klass = Class.new do + def self.name; 'america'; end + end + @subklass = Class.new(@klass) do + def self.name; 'north america'; end + end end def test_retrieve_connection @@ -28,6 +33,20 @@ module ActiveRecord def test_retrieve_connection_pool assert_not_nil @handler.retrieve_connection_pool(@klass) end + + def test_retrieve_connection_pool_uses_superclass_when_no_subclass_connection + assert_not_nil @handler.retrieve_connection_pool(@subklass) + end + + def test_retrieve_connection_pool_uses_superclass_pool_after_subclass_establish_and_remove + @handler.establish_connection 'north america', Base.connection_pool.spec + assert_not_same @handler.retrieve_connection_pool(@klass), + @handler.retrieve_connection_pool(@subklass) + + @handler.remove_connection @subklass + assert_same @handler.retrieve_connection_pool(@klass), + @handler.retrieve_connection_pool(@subklass) + end end end end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 5dc5f99582..3088ab012f 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -48,6 +48,15 @@ class FinderTest < ActiveRecord::TestCase assert Topic.exists? end + # exists? should handle nil for id's that come from URLs and always return false + # (example: Topic.exists?(params[:id])) where params[:id] is nil + def test_exists_with_nil_arg + assert !Topic.exists?(nil) + assert Topic.exists? + assert !Topic.first.replies.exists?(nil) + assert Topic.first.replies.exists? + end + def test_does_not_exist_with_empty_table_and_no_args_given Topic.delete_all assert !Topic.exists? @@ -243,6 +252,32 @@ class FinderTest < ActiveRecord::TestCase end end + def test_first_and_last_with_integer_should_use_sql_limit + assert_sql(/LIMIT 2|ROWNUM <= 2/) { Topic.first(2).entries } + assert_sql(/LIMIT 5|ROWNUM <= 5/) { Topic.last(5).entries } + end + + def test_last_with_integer_and_order_should_keep_the_order + assert_equal Topic.order("title").to_a.last(2), Topic.order("title").last(2) + end + + def test_last_with_integer_and_order_should_not_use_sql_limit + query = assert_sql { Topic.order("title").last(5).entries } + assert_equal 1, query.length + assert_no_match(/LIMIT/, query.first) + end + + def test_last_with_integer_and_reorder_should_not_use_sql_limit + query = assert_sql { Topic.reorder("title").last(5).entries } + assert_equal 1, query.length + assert_no_match(/LIMIT/, query.first) + end + + def test_first_and_last_with_integer_should_return_an_array + assert_kind_of Array, Topic.first(5) + assert_kind_of Array, Topic.last(5) + end + def test_unexisting_record_exception_handling assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1).parent diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb index 36007255fa..d108b456f0 100644 --- a/activerecord/test/cases/migration/command_recorder_test.rb +++ b/activerecord/test/cases/migration/command_recorder_test.rb @@ -104,9 +104,9 @@ module ActiveRecord end def test_invert_rename_index - @recorder.record :rename_index, [:old, :new] + @recorder.record :rename_index, [:table, :old, :new] rename = @recorder.inverse.first - assert_equal [:rename_index, [:new, :old]], rename + assert_equal [:rename_index, [:table, :new, :old]], rename end def test_invert_add_timestamps diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index ed0240cada..4a09a87322 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -182,7 +182,7 @@ class NamedScopeTest < ActiveRecord::TestCase def test_first_and_last_should_allow_integers_for_limit assert_equal Topic.base.first(2), Topic.base.to_a.first(2) - assert_equal Topic.base.last(2), Topic.base.to_a.last(2) + assert_equal Topic.base.last(2), Topic.base.order("id").to_a.last(2) end def test_first_and_last_should_not_use_query_when_results_are_loaded diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb index 76f20b1061..b9c2e8ec9a 100644 --- a/activerecord/test/models/car.rb +++ b/activerecord/test/models/car.rb @@ -8,7 +8,7 @@ class Car < ActiveRecord::Base has_one :frickinawesome_bulb, :class_name => "Bulb", :conditions => { :frickinawesome => true } has_many :tyres - has_many :engines + has_many :engines, :dependent => :destroy has_many :wheels, :as => :wheelable scope :incl_tyres, includes(:tyres) diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index affa37b02d..198a963cbc 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -171,4 +171,14 @@ class PostWithDefaultInclude < ActiveRecord::Base self.table_name = 'posts' default_scope includes(:comments) has_many :comments, :foreign_key => :post_id +end + +class PostWithDefaultScope < ActiveRecord::Base + self.table_name = 'posts' + default_scope :order => :title +end + +class SpecialPostWithDefaultScope < ActiveRecord::Base + self.table_name = 'posts' + default_scope where(:id => [1, 5,6]) end
\ No newline at end of file |