diff options
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/activerecord.gemspec | 2 | ||||
-rw-r--r-- | activerecord/examples/performance.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/base.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb | 35 | ||||
-rw-r--r-- | activerecord/lib/active_record/persistence.rb | 11 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/query_methods.rb | 40 | ||||
-rw-r--r-- | activerecord/test/cases/associations/cascaded_eager_loading_test.rb | 7 | ||||
-rw-r--r-- | activerecord/test/cases/base_test.rb | 331 | ||||
-rw-r--r-- | activerecord/test/cases/connection_management_test.rb | 25 | ||||
-rw-r--r-- | activerecord/test/cases/connection_pool_test.rb | 50 | ||||
-rw-r--r-- | activerecord/test/cases/persistence_test.rb | 358 |
12 files changed, 465 insertions, 400 deletions
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 5aea992801..ce10404feb 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -24,5 +24,5 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) s.add_dependency('arel', '~> 0.4.0') - s.add_dependency('tzinfo', '~> 0.3.16') + s.add_dependency('tzinfo', '~> 0.3.22') end diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb index f7d358337c..a985cfcb66 100644 --- a/activerecord/examples/performance.rb +++ b/activerecord/examples/performance.rb @@ -58,7 +58,7 @@ end sqlfile = File.expand_path("../performance.sql", __FILE__) if File.exists?(sqlfile) - mysql_bin = %w[mysql mysql5].select { |bin| `which #{bin}`.length > 0 } + mysql_bin = %w[mysql mysql5].detect { |bin| `which #{bin}`.length > 0 } `#{mysql_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} < #{sqlfile}` else puts 'Generating data...' diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 9fe8d54f02..d67df64f59 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1761,7 +1761,7 @@ module ActiveRecord def graft(*associations) associations.each do |association| join_associations.detect {|a| association == a} || - build(association.reflection.name, association.find_parent_in(self), association.join_class) + build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_class) end self end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 400a0adbcf..19ccf75b00 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1471,7 +1471,7 @@ MSG # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false) # user.is_admin? # => true def attributes=(new_attributes, guard_protected_attributes = true) - return unless new_attributes.is_a? Hash + return unless new_attributes.is_a?(Hash) attributes = new_attributes.stringify_keys multi_parameter_attributes = [] 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 c2d79a421d..9d0251dda3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -161,8 +161,13 @@ module ActiveRecord # Return any checked-out connections back to the pool by threads that # are no longer alive. def clear_stale_cached_connections! - remove_stale_cached_threads!(@reserved_connections) do |name, conn| - checkin conn + keys = @reserved_connections.keys - Thread.list.find_all { |t| + t.alive? + }.map { |thread| thread.object_id } + + keys.each do |key| + checkin @reserved_connections[key] + @reserved_connections.delete(key) end end @@ -232,20 +237,6 @@ module ActiveRecord Thread.current.object_id end - # Remove stale threads from the cache. - def remove_stale_cached_threads!(cache, &block) - keys = Set.new(cache.keys) - - Thread.list.each do |thread| - keys.delete(thread.object_id) if thread.alive? - end - keys.each do |key| - next unless cache.has_key?(key) - block.call(key, cache[key]) - cache.delete(key) - end - end - def checkout_new_connection c = new_connection @connections << c @@ -290,14 +281,12 @@ module ActiveRecord # ActiveRecord::Base.connection_handler. Active Record models use this to # determine that connection pool that they should use. class ConnectionHandler + attr_reader :connection_pools + def initialize(pools = {}) @connection_pools = pools end - def connection_pools - @connection_pools ||= {} - end - def establish_connection(name, spec) @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec) end @@ -345,9 +334,11 @@ module ActiveRecord # re-establishing the connection. def remove_connection(klass) pool = @connection_pools[klass.name] + return nil unless pool + @connection_pools.delete_if { |key, value| value == pool } - pool.disconnect! if pool - pool.spec.config if pool + pool.disconnect! + pool.spec.config end def retrieve_connection_pool(klass) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 7ec443ccc7..68f5be63d6 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -102,8 +102,15 @@ module ActiveRecord became end - # Updates a single attribute and saves the record without going through the normal validation procedure - # or callbacks. This is especially useful for boolean flags on existing records. + # Updates a single attribute and saves the record. + # This is especially useful for boolean flags on existing records. Also note that + # + # * validation is skipped + # * No callbacks are invoked + # * updated_at/updated_on column is updated if that column is available + # * does not work on associations + # * does not work on attr_accessor attributes. The attribute that is being updated must be column name. + # def update_attribute(name, value) changes = record_update_timestamps || {} diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4692271266..0593897fa5 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -11,84 +11,84 @@ module ActiveRecord def includes(*args) args.reject! { |a| a.blank? } - clone.tap { |r| r.includes_values += args if args.present? } + clone.tap {|r| r.includes_values += args if args.present? } end def eager_load(*args) - clone.tap { |r| r.eager_load_values += args if args.present? } + clone.tap {|r| r.eager_load_values += args if args.present? } end def preload(*args) - clone.tap { |r| r.preload_values += args if args.present? } + clone.tap {|r| r.preload_values += args if args.present? } end def select(*args) if block_given? - to_a.select { |*block_args| yield(*block_args) } + to_a.select {|*block_args| yield(*block_args) } else - clone.tap { |r| r.select_values += args if args.present? } + clone.tap {|r| r.select_values += args if args.present? } end end def group(*args) - clone.tap { |r| r.group_values += args if args.present? } + clone.tap {|r| r.group_values += args if args.present? } end def order(*args) - clone.tap { |r| r.order_values += args if args.present? } + clone.tap {|r| r.order_values += args if args.present? } end def reorder(*args) - clone.tap { |r| r.order_values = args if args.present? } + clone.tap {|r| r.order_values = args if args.present? } end def joins(*args) args.flatten! - clone.tap { |r| r.joins_values += args if args.present? } + clone.tap {|r| r.joins_values += args if args.present? } end def where(*args) value = build_where(*args) - clone.tap { |r| r.where_values += Array.wrap(value) if value.present? } + clone.tap {|r| r.where_values += Array.wrap(value) if value.present? } end def having(*args) value = build_where(*args) - clone.tap { |r| r.having_values += Array.wrap(value) if value.present? } + clone.tap {|r| r.having_values += Array.wrap(value) if value.present? } end def limit(value = true) - clone.tap { |r| r.limit_value = value } + clone.tap {|r| r.limit_value = value } end def offset(value = true) - clone.tap { |r| r.offset_value = value } + clone.tap {|r| r.offset_value = value } end def lock(locks = true) case locks when String, TrueClass, NilClass - clone.tap { |r| r.lock_value = locks || true } + clone.tap {|r| r.lock_value = locks || true } else - clone.tap { |r| r.lock_value = false } + clone.tap {|r| r.lock_value = false } end end def readonly(value = true) - clone.tap { |r| r.readonly_value = value } + clone.tap {|r| r.readonly_value = value } end def create_with(value = true) - clone.tap { |r| r.create_with_value = value } + clone.tap {|r| r.create_with_value = value } end def from(value = true) - clone.tap { |r| r.from_value = value } + clone.tap {|r| r.from_value = value } end def extending(*modules, &block) modules << Module.new(&block) if block_given? - clone.tap { |r| r.send(:apply_modules, *modules) } + clone.tap {|r| r.send(:apply_modules, *modules) } end def reverse_order @@ -230,7 +230,7 @@ module ActiveRecord @implicit_readonly = false # TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array. # Before this change we were passing to ARel the last element only, and ARel is capable of handling an array - if selects.all? { |s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) + if selects.all? {|s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) arel.project(*selects) else arel.project(selects.last) diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 9c5dcc2ad9..f5d59c9a43 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -46,6 +46,13 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').order('people.id').first end + def test_eager_association_loading_with_join_for_count + authors = Author.joins(:special_posts).includes([:posts, :categorizations]) + + assert_nothing_raised { authors.count } + assert_queries(3) { authors.all } + end + def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id") assert_equal 2, authors.size diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index a4cf5120e1..831dd446ad 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -63,9 +63,7 @@ class BasicsTest < ActiveRecord::TestCase def test_set_attributes_without_hash topic = Topic.new - assert_nothing_raised do - topic.attributes = '' - end + assert_nothing_raised { topic.attributes = '' } end def test_integers_as_nil @@ -163,48 +161,6 @@ class BasicsTest < ActiveRecord::TestCase assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes end - def test_create - topic = Topic.new - topic.title = "New Topic" - topic.save - topic_reloaded = Topic.find(topic.id) - assert_equal("New Topic", topic_reloaded.title) - end - - def test_save! - topic = Topic.new(:title => "New Topic") - assert topic.save! - - reply = WrongReply.new - assert_raise(ActiveRecord::RecordInvalid) { reply.save! } - end - - def test_save_null_string_attributes - topic = Topic.find(1) - topic.attributes = { "title" => "null", "author_name" => "null" } - topic.save! - topic.reload - assert_equal("null", topic.title) - assert_equal("null", topic.author_name) - end - - def test_save_nil_string_attributes - topic = Topic.find(1) - topic.title = nil - topic.save! - topic.reload - assert_nil topic.title - end - - def test_save_for_record_with_only_primary_key - minimalistic = Minimalistic.new - assert_nothing_raised { minimalistic.save } - end - - def test_save_for_record_with_only_primary_key_that_is_provided - assert_nothing_raised { Minimalistic.create!(:id => 2) } - end - def test_hashes_not_mangled new_topic = { :title => "New Topic" } new_topic_values = { :title => "AnotherTopic" } @@ -216,78 +172,12 @@ class BasicsTest < ActiveRecord::TestCase assert_equal new_topic_values[:title], topic.title end - def test_create_many - topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) - assert_equal 2, topics.size - assert_equal "first", topics.first.title - end - - def test_create_columns_not_equal_attributes - topic = Topic.new - topic.title = 'Another New Topic' - topic.send :write_attribute, 'does_not_exist', 'test' - assert_nothing_raised { topic.save } - end - def test_create_through_factory topic = Topic.create("title" => "New Topic") topicReloaded = Topic.find(topic.id) assert_equal(topic, topicReloaded) end - def test_create_through_factory_with_block - topic = Topic.create("title" => "New Topic") do |t| - t.author_name = "David" - end - topicReloaded = Topic.find(topic.id) - assert_equal("New Topic", topic.title) - assert_equal("David", topic.author_name) - end - - def test_create_many_through_factory_with_block - topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t| - t.author_name = "David" - end - assert_equal 2, topics.size - topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id) - assert_equal "first", topic1.title - assert_equal "David", topic1.author_name - assert_equal "second", topic2.title - assert_equal "David", topic2.author_name - end - - def test_update - topic = Topic.new - topic.title = "Another New Topic" - topic.written_on = "2003-12-12 23:23:00" - topic.save - topicReloaded = Topic.find(topic.id) - assert_equal("Another New Topic", topicReloaded.title) - - topicReloaded.title = "Updated topic" - topicReloaded.save - - topicReloadedAgain = Topic.find(topic.id) - - assert_equal("Updated topic", topicReloadedAgain.title) - end - - def test_update_columns_not_equal_attributes - topic = Topic.new - topic.title = "Still another topic" - topic.save - - topicReloaded = Topic.find(topic.id) - topicReloaded.title = "A New Topic" - topicReloaded.send :write_attribute, 'does_not_exist', 'test' - assert_nothing_raised { topicReloaded.save } - end - - def test_update_for_record_with_only_primary_key - minimalistic = minimalistics(:first) - assert_nothing_raised { minimalistic.save } - end - def test_write_attribute topic = Topic.new topic.send(:write_attribute, :title, "Still another topic") @@ -395,7 +285,6 @@ class BasicsTest < ActiveRecord::TestCase assert !object.int_value? end - def test_non_attribute_access_and_assignment topic = Topic.new assert !topic.respond_to?("mumbo") @@ -499,29 +388,6 @@ class BasicsTest < ActiveRecord::TestCase assert topic.instance_variable_get("@custom_approved") end - def test_delete - topic = Topic.find(1) - assert_equal topic, topic.delete, 'topic.delete did not return self' - assert topic.frozen?, 'topic not frozen after delete' - assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } - end - - def test_delete_doesnt_run_callbacks - Topic.find(1).delete - assert_not_nil Topic.find(2) - end - - def test_destroy - topic = Topic.find(1) - assert_equal topic, topic.destroy, 'topic.destroy did not return self' - assert topic.frozen?, 'topic not frozen after destroy' - assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } - end - - def test_record_not_found_exception - assert_raise(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) } - end - def test_initialize_with_attributes topic = Topic.new({ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23" @@ -690,33 +556,6 @@ class BasicsTest < ActiveRecord::TestCase assert Topic.find(2).approved? end - def test_update_all - assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'") - assert_equal "bulk updated!", Topic.find(1).content - assert_equal "bulk updated!", Topic.find(2).content - - assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!']) - assert_equal "bulk updated again!", Topic.find(1).content - assert_equal "bulk updated again!", Topic.find(2).content - - assert_equal Topic.count, Topic.update_all(['content = ?', nil]) - assert_nil Topic.find(1).content - end - - def test_update_all_with_hash - assert_not_nil Topic.find(1).last_read - assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil) - assert_equal "bulk updated with hash!", Topic.find(1).content - assert_equal "bulk updated with hash!", Topic.find(2).content - assert_nil Topic.find(1).last_read - assert_nil Topic.find(2).last_read - end - - def test_update_all_with_non_standard_table_name - assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1]) - assert_equal 0, WarehouseThing.find(1).value - end - if current_adapter?(:MysqlAdapter) def test_update_all_with_order_and_limit assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC') @@ -863,119 +702,7 @@ class BasicsTest < ActiveRecord::TestCase assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ] end - def test_delete_new_record - client = Client.new - client.delete - assert client.frozen? - end - - def test_delete_record_with_associations - client = Client.find(3) - client.delete - assert client.frozen? - assert_kind_of Firm, client.firm - assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } - end - - def test_destroy_new_record - client = Client.new - client.destroy - assert client.frozen? - end - - def test_destroy_record_with_associations - client = Client.find(3) - client.destroy - assert client.frozen? - assert_kind_of Firm, client.firm - assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } - end - - def test_update_attribute - assert !Topic.find(1).approved? - Topic.find(1).update_attribute("approved", true) - assert Topic.find(1).approved? - - Topic.find(1).update_attribute(:approved, false) - assert !Topic.find(1).approved? - end - def test_update_attribute_with_one_changed_and_one_updated - t = Topic.order('id').limit(1).first - title, author_name = t.title, t.author_name - t.author_name = 'John' - t.update_attribute(:title, 'super_title') - assert_equal 'John', t.author_name - assert_equal 'super_title', t.title - assert t.changed?, "topic should have changed" - assert t.author_name_changed?, "author_name should have changed" - assert !t.title_changed?, "title should not have changed" - assert_nil t.title_change, 'title change should be nil' - assert_equal ['author_name'], t.changed - - t.reload - assert_equal 'David', t.author_name - assert_equal 'super_title', t.title - end - - def test_update_attribute_with_one_updated - t = Topic.first - title = t.title - t.update_attribute(:title, 'super_title') - assert_equal 'super_title', t.title - assert !t.changed?, "topic should not have changed" - assert !t.title_changed?, "title should not have changed" - assert_nil t.title_change, 'title change should be nil' - - t.reload - assert_equal 'super_title', t.title - end - - def test_update_attribute_for_udpated_at_on - developer = Developer.find(1) - updated_at = developer.updated_at - developer.update_attribute(:salary, 80001) - assert_not_equal updated_at, developer.updated_at - developer.reload - assert_not_equal updated_at, developer.updated_at - end - - def test_update_attributes - topic = Topic.find(1) - assert !topic.approved? - assert_equal "The First Topic", topic.title - - topic.update_attributes("approved" => true, "title" => "The First Topic Updated") - topic.reload - assert topic.approved? - assert_equal "The First Topic Updated", topic.title - - topic.update_attributes(:approved => false, :title => "The First Topic") - topic.reload - assert !topic.approved? - assert_equal "The First Topic", topic.title - end - - def test_update_attributes! - Reply.validates_presence_of(:title) - reply = Reply.find(2) - assert_equal "The Second Topic of the day", reply.title - assert_equal "Have a nice day", reply.content - - reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening") - reply.reload - assert_equal "The Second Topic of the day updated", reply.title - assert_equal "Have a nice evening", reply.content - - reply.update_attributes!(:title => "The Second Topic of the day", :content => "Have a nice day") - reply.reload - assert_equal "The Second Topic of the day", reply.title - assert_equal "Have a nice day", reply.content - - assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") } - ensure - Reply.reset_callbacks(:validate) - end def test_readonly_attributes assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes @@ -1236,35 +963,6 @@ class BasicsTest < ActiveRecord::TestCase assert_equal false, Topic.find(1).new_record? end - def test_destroyed_returns_boolean - developer = Developer.first - assert_equal false, developer.destroyed? - developer.destroy - assert_equal true, developer.destroyed? - - developer = Developer.last - assert_equal false, developer.destroyed? - developer.delete - assert_equal true, developer.destroyed? - end - - def test_persisted_returns_boolean - developer = Developer.new(:name => "Jose") - assert_equal false, developer.persisted? - developer.save! - assert_equal true, developer.persisted? - - developer = Developer.first - assert_equal true, developer.persisted? - developer.destroy - assert_equal false, developer.persisted? - - developer = Developer.last - assert_equal true, developer.persisted? - developer.delete - assert_equal false, developer.persisted? - end - def test_clone topic = Topic.find(1) cloned_topic = nil @@ -1607,24 +1305,6 @@ class BasicsTest < ActiveRecord::TestCase end end - def test_class_level_destroy - should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") - Topic.find(1).replies << should_be_destroyed_reply - - Topic.destroy(1) - assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } - assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) } - end - - def test_class_level_delete - should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") - Topic.find(1).replies << should_be_destroyed_reply - - Topic.delete(1) - assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } - assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) } - end - def test_increment_attribute assert_equal 50, accounts(:signals37).credit_limit accounts(:signals37).increment! :credit_limit @@ -2237,15 +1917,6 @@ class BasicsTest < ActiveRecord::TestCase ActiveRecord::Base.logger = original_logger end - def test_create_with_custom_timestamps - custom_datetime = 1.hour.ago.beginning_of_day - - %w(created_at created_on updated_at updated_on).each do |attribute| - parrot = LiveParrot.create(:name => "colombian", attribute => custom_datetime) - assert_equal custom_datetime, parrot[attribute] - end - end - def test_dup assert !Minimalistic.new.freeze.dup.frozen? end diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb new file mode 100644 index 0000000000..c535119972 --- /dev/null +++ b/activerecord/test/cases/connection_management_test.rb @@ -0,0 +1,25 @@ +require "cases/helper" + +class ConnectionManagementTest < ActiveRecord::TestCase + def setup + @env = {} + @app = stub('App') + @management = ActiveRecord::ConnectionAdapters::ConnectionManagement.new(@app) + + @connections_cleared = false + ActiveRecord::Base.stubs(:clear_active_connections!).with { @connections_cleared = true } + end + + test "clears active connections after each call" do + @app.expects(:call).with(@env) + @management.call(@env) + assert @connections_cleared + end + + test "doesn't clear active connections when running in a test case" do + @env['rack.test'] = true + @app.expects(:call).with(@env) + @management.call(@env) + assert !@connections_cleared + end +end diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index cc9b2a45f4..82b3c36ed2 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -1,25 +1,31 @@ require "cases/helper" -class ConnectionManagementTest < ActiveRecord::TestCase - def setup - @env = {} - @app = stub('App') - @management = ActiveRecord::ConnectionAdapters::ConnectionManagement.new(@app) - - @connections_cleared = false - ActiveRecord::Base.stubs(:clear_active_connections!).with { @connections_cleared = true } - end - - test "clears active connections after each call" do - @app.expects(:call).with(@env) - @management.call(@env) - assert @connections_cleared - end - - test "doesn't clear active connections when running in a test case" do - @env['rack.test'] = true - @app.expects(:call).with(@env) - @management.call(@env) - assert !@connections_cleared +module ActiveRecord + module ConnectionAdapters + class ConnectionPoolTest < ActiveRecord::TestCase + def test_clear_stale_cached_connections! + pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec + + threads = [ + Thread.new { pool.connection }, + Thread.new { pool.connection }] + + threads.map { |t| t.join } + + pool.extend Module.new { + attr_accessor :checkins + def checkin conn + @checkins << conn + conn.object_id + end + } + pool.checkins = [] + + cleared_threads = pool.clear_stale_cached_connections! + assert((cleared_threads - threads.map { |x| x.object_id }).empty?, + "threads should have been removed") + assert_equal pool.checkins.length, threads.length + end + end end -end
\ No newline at end of file +end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb new file mode 100644 index 0000000000..4ea5df0945 --- /dev/null +++ b/activerecord/test/cases/persistence_test.rb @@ -0,0 +1,358 @@ +require "cases/helper" +require 'models/post' +require 'models/author' +require 'models/topic' +require 'models/reply' +require 'models/category' +require 'models/company' +require 'models/customer' +require 'models/developer' +require 'models/project' +require 'models/default' +require 'models/auto_id' +require 'models/column_name' +require 'models/subscriber' +require 'models/keyboard' +require 'models/comment' +require 'models/minimalistic' +require 'models/warehouse_thing' +require 'models/parrot' +require 'models/loose_person' +require 'rexml/document' +require 'active_support/core_ext/exception' + +class PersistencesTest < ActiveRecord::TestCase + + fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts + + def test_create + topic = Topic.new + topic.title = "New Topic" + topic.save + topic_reloaded = Topic.find(topic.id) + assert_equal("New Topic", topic_reloaded.title) + end + + def test_save! + topic = Topic.new(:title => "New Topic") + assert topic.save! + + reply = WrongReply.new + assert_raise(ActiveRecord::RecordInvalid) { reply.save! } + end + + def test_save_null_string_attributes + topic = Topic.find(1) + topic.attributes = { "title" => "null", "author_name" => "null" } + topic.save! + topic.reload + assert_equal("null", topic.title) + assert_equal("null", topic.author_name) + end + + def test_save_nil_string_attributes + topic = Topic.find(1) + topic.title = nil + topic.save! + topic.reload + assert_nil topic.title + end + + def test_save_for_record_with_only_primary_key + minimalistic = Minimalistic.new + assert_nothing_raised { minimalistic.save } + end + + def test_save_for_record_with_only_primary_key_that_is_provided + assert_nothing_raised { Minimalistic.create!(:id => 2) } + end + + def test_create_many + topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) + assert_equal 2, topics.size + assert_equal "first", topics.first.title + end + + def test_create_columns_not_equal_attributes + topic = Topic.new + topic.title = 'Another New Topic' + topic.send :write_attribute, 'does_not_exist', 'test' + assert_nothing_raised { topic.save } + end + + def test_create_through_factory_with_block + topic = Topic.create("title" => "New Topic") do |t| + t.author_name = "David" + end + topicReloaded = Topic.find(topic.id) + assert_equal("New Topic", topic.title) + assert_equal("David", topic.author_name) + end + + def test_create_many_through_factory_with_block + topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t| + t.author_name = "David" + end + assert_equal 2, topics.size + topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id) + assert_equal "first", topic1.title + assert_equal "David", topic1.author_name + assert_equal "second", topic2.title + assert_equal "David", topic2.author_name + end + + def test_update + topic = Topic.new + topic.title = "Another New Topic" + topic.written_on = "2003-12-12 23:23:00" + topic.save + topicReloaded = Topic.find(topic.id) + assert_equal("Another New Topic", topicReloaded.title) + + topicReloaded.title = "Updated topic" + topicReloaded.save + + topicReloadedAgain = Topic.find(topic.id) + + assert_equal("Updated topic", topicReloadedAgain.title) + end + + def test_update_columns_not_equal_attributes + topic = Topic.new + topic.title = "Still another topic" + topic.save + + topicReloaded = Topic.find(topic.id) + topicReloaded.title = "A New Topic" + topicReloaded.send :write_attribute, 'does_not_exist', 'test' + assert_nothing_raised { topicReloaded.save } + end + + def test_update_for_record_with_only_primary_key + minimalistic = minimalistics(:first) + assert_nothing_raised { minimalistic.save } + end + + def test_delete + topic = Topic.find(1) + assert_equal topic, topic.delete, 'topic.delete did not return self' + assert topic.frozen?, 'topic not frozen after delete' + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } + end + + def test_delete_doesnt_run_callbacks + Topic.find(1).delete + assert_not_nil Topic.find(2) + end + + def test_destroy + topic = Topic.find(1) + assert_equal topic, topic.destroy, 'topic.destroy did not return self' + assert topic.frozen?, 'topic not frozen after destroy' + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } + end + + def test_record_not_found_exception + assert_raise(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) } + end + + def test_update_all + assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'") + assert_equal "bulk updated!", Topic.find(1).content + assert_equal "bulk updated!", Topic.find(2).content + + assert_equal Topic.count, Topic.update_all(['content = ?', 'bulk updated again!']) + assert_equal "bulk updated again!", Topic.find(1).content + assert_equal "bulk updated again!", Topic.find(2).content + + assert_equal Topic.count, Topic.update_all(['content = ?', nil]) + assert_nil Topic.find(1).content + end + + def test_update_all_with_hash + assert_not_nil Topic.find(1).last_read + assert_equal Topic.count, Topic.update_all(:content => 'bulk updated with hash!', :last_read => nil) + assert_equal "bulk updated with hash!", Topic.find(1).content + assert_equal "bulk updated with hash!", Topic.find(2).content + assert_nil Topic.find(1).last_read + assert_nil Topic.find(2).last_read + end + + def test_update_all_with_non_standard_table_name + assert_equal 1, WarehouseThing.update_all(['value = ?', 0], ['id = ?', 1]) + assert_equal 0, WarehouseThing.find(1).value + end + + def test_delete_new_record + client = Client.new + client.delete + assert client.frozen? + end + + def test_delete_record_with_associations + client = Client.find(3) + client.delete + assert client.frozen? + assert_kind_of Firm, client.firm + assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } + end + + def test_destroy_new_record + client = Client.new + client.destroy + assert client.frozen? + end + + def test_destroy_record_with_associations + client = Client.find(3) + client.destroy + assert client.frozen? + assert_kind_of Firm, client.firm + assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } + end + + def test_update_attribute + assert !Topic.find(1).approved? + Topic.find(1).update_attribute("approved", true) + assert Topic.find(1).approved? + + Topic.find(1).update_attribute(:approved, false) + assert !Topic.find(1).approved? + end + + def test_update_attribute_with_one_changed_and_one_updated + t = Topic.order('id').limit(1).first + title, author_name = t.title, t.author_name + t.author_name = 'John' + t.update_attribute(:title, 'super_title') + assert_equal 'John', t.author_name + assert_equal 'super_title', t.title + assert t.changed?, "topic should have changed" + assert t.author_name_changed?, "author_name should have changed" + assert !t.title_changed?, "title should not have changed" + assert_nil t.title_change, 'title change should be nil' + assert_equal ['author_name'], t.changed + + t.reload + assert_equal 'David', t.author_name + assert_equal 'super_title', t.title + end + + def test_update_attribute_with_one_updated + t = Topic.first + title = t.title + t.update_attribute(:title, 'super_title') + assert_equal 'super_title', t.title + assert !t.changed?, "topic should not have changed" + assert !t.title_changed?, "title should not have changed" + assert_nil t.title_change, 'title change should be nil' + + t.reload + assert_equal 'super_title', t.title + end + + def test_update_attribute_for_udpated_at_on + developer = Developer.find(1) + prev_month = Time.now.prev_month + developer.update_attribute(:updated_at, prev_month) + assert_equal prev_month, developer.updated_at + developer.update_attribute(:salary, 80001) + assert_not_equal prev_month, developer.updated_at + developer.reload + assert_not_equal prev_month, developer.updated_at + end + + def test_update_attributes + topic = Topic.find(1) + assert !topic.approved? + assert_equal "The First Topic", topic.title + + topic.update_attributes("approved" => true, "title" => "The First Topic Updated") + topic.reload + assert topic.approved? + assert_equal "The First Topic Updated", topic.title + + topic.update_attributes(:approved => false, :title => "The First Topic") + topic.reload + assert !topic.approved? + assert_equal "The First Topic", topic.title + end + + def test_update_attributes! + Reply.validates_presence_of(:title) + reply = Reply.find(2) + assert_equal "The Second Topic of the day", reply.title + assert_equal "Have a nice day", reply.content + + reply.update_attributes!("title" => "The Second Topic of the day updated", "content" => "Have a nice evening") + reply.reload + assert_equal "The Second Topic of the day updated", reply.title + assert_equal "Have a nice evening", reply.content + + reply.update_attributes!(:title => "The Second Topic of the day", :content => "Have a nice day") + reply.reload + assert_equal "The Second Topic of the day", reply.title + assert_equal "Have a nice day", reply.content + + assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") } + ensure + Reply.reset_callbacks(:validate) + end + + def test_destroyed_returns_boolean + developer = Developer.first + assert_equal false, developer.destroyed? + developer.destroy + assert_equal true, developer.destroyed? + + developer = Developer.last + assert_equal false, developer.destroyed? + developer.delete + assert_equal true, developer.destroyed? + end + + def test_persisted_returns_boolean + developer = Developer.new(:name => "Jose") + assert_equal false, developer.persisted? + developer.save! + assert_equal true, developer.persisted? + + developer = Developer.first + assert_equal true, developer.persisted? + developer.destroy + assert_equal false, developer.persisted? + + developer = Developer.last + assert_equal true, developer.persisted? + developer.delete + assert_equal false, developer.persisted? + end + + def test_class_level_destroy + should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") + Topic.find(1).replies << should_be_destroyed_reply + + Topic.destroy(1) + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } + assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) } + end + + def test_class_level_delete + should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") + Topic.find(1).replies << should_be_destroyed_reply + + Topic.delete(1) + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } + assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) } + end + + def test_create_with_custom_timestamps + custom_datetime = 1.hour.ago.beginning_of_day + + %w(created_at created_on updated_at updated_on).each do |attribute| + parrot = LiveParrot.create(:name => "colombian", attribute => custom_datetime) + assert_equal custom_datetime, parrot[attribute] + end + end + +end |