Changeset 2714
- Timestamp:
- 10/23/05 19:02:38 (3 years ago)
- Files:
-
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record/fixtures.rb (modified) (14 diffs)
- trunk/activerecord/test/column_alias_test.rb (modified) (1 diff)
- trunk/activerecord/test/conditions_scoping_test.rb (modified) (2 diffs)
- trunk/activerecord/test/finder_test.rb (modified) (1 diff)
- trunk/activerecord/test/fixtures_test.rb (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/CHANGELOG
r2713 r2714 1 1 *1.12.1* (October 19th, 2005) 2 3 * Keep closer tabs on dirty, loaded, and declared fixtures. #2404 [ryand-ruby@zenspider.com] 2 4 3 5 * Map Active Record time to SQL TIME. #2575, #2576 [Robby Russell <robby@planetargon.com>] trunk/activerecord/lib/active_record/fixtures.rb
r2650 r2714 2 2 require 'yaml' 3 3 require 'csv' 4 require 'set' 4 5 5 6 module YAML #:nodoc: … … 123 124 # 124 125 # By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here tho), we trigger 125 # the testing environment to automatically load the appropriate fixtures into the database before each test. 126 # the testing environment to automatically load the appropriate fixtures into the database before each test. 126 127 # To ensure consistent data, the environment deletes the fixtures before running the load. 127 128 # … … 149 150 # 150 151 # - to keep the fixture instance (@web_sites) available, but do not automatically 'find' each instance: 151 # self.use_instantiated_fixtures = :no_instances 152 # self.use_instantiated_fixtures = :no_instances 152 153 # 153 154 # Even if auto-instantiated fixtures are disabled, you can still access them … … 181 182 # = Transactional fixtures 182 183 # 183 # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case. 184 # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case. 184 185 # They can also turn off auto-instantiation of fixture data since the feature is costly and often unused. 185 186 # … … 187 188 # self.use_transactional_fixtures = true 188 189 # self.use_instantiated_fixtures = false 189 # 190 # 190 191 # fixtures :foos 191 # 192 # 192 193 # def test_godzilla 193 194 # assert !Foo.find(:all).empty? … … 195 196 # assert Foo.find(:all).empty? 196 197 # end 197 # 198 # 198 199 # def test_godzilla_aftermath 199 200 # assert !Foo.find(:all).empty? 200 201 # end 201 202 # end 202 # 203 # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures, 203 # 204 # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures, 204 205 # then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes. 205 206 # 206 # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide 207 # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide 207 208 # access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+) 208 209 # 209 # When *not* to use transactional fixtures: 210 # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit, 211 # particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify 212 # the results of your transaction until Active Record supports nested transactions or savepoints (in progress.) 213 # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM. 210 # When *not* to use transactional fixtures: 211 # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit, 212 # particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify 213 # the results of your transaction until Active Record supports nested transactions or savepoints (in progress.) 214 # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM. 214 215 # Use InnoDB, MaxDB, or NDB instead. 215 216 class Fixtures < YAML::Omap … … 231 232 ActiveRecord::Base.logger.level = old_logger_level 232 233 end 233 234 234 235 def self.instantiate_all_loaded_fixtures(object, load_instances=true) 235 all_loaded_fixtures.each do | table_name,fixtures|236 Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)237 end 238 end 239 236 all_loaded_fixtures.each do |fixtures| 237 Fixtures.instantiate_fixtures(object, fixtures.table_name, fixtures, load_instances) 238 end 239 end 240 240 241 cattr_accessor :all_loaded_fixtures 241 self.all_loaded_fixtures = {}242 self.all_loaded_fixtures = [] 242 243 243 244 def self.create_fixtures(fixtures_directory, *table_names) 245 table_names = table_names.flatten 244 246 connection = block_given? ? yield : ActiveRecord::Base.connection 245 old_logger_level = ActiveRecord::Base.logger.level 246 247 begin 248 ActiveRecord::Base.logger.level = Logger::ERROR 249 250 fixtures_map = {} 251 fixtures = table_names.flatten.map do |table_name| 252 fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, File.join(fixtures_directory, table_name.to_s)) 253 end 254 all_loaded_fixtures.merge! fixtures_map 255 256 247 248 ActiveRecord::Base.logger.silence do 249 fixtures = table_names.map do |table_name| 250 Fixtures.new(connection, File.split(table_name.to_s).last, File.join(fixtures_directory, table_name.to_s)) 251 end 252 257 253 connection.transaction do 258 fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures } 259 fixtures.each { |fixture| fixture.insert_fixtures } 260 end 261 262 # Cap primary key sequences to max(pk). 263 if connection.respond_to?(:reset_pk_sequence!) 264 table_names.flatten.each do |table_name| 265 connection.reset_pk_sequence!(table_name) 266 end 267 end 268 254 table_names.reverse.each do |table_name| 255 connection.delete "DELETE FROM #{table_name}" 256 end 257 258 fixtures.each { |f| f.insert_fixtures } 259 260 if connection.respond_to?(:reset_pk_sequence!) 261 table_names.flatten.each do |table_name| 262 connection.reset_pk_sequence!(table_name) 263 end 264 end 265 end 266 267 all_loaded_fixtures.concat fixtures 269 268 return fixtures.size > 1 ? fixtures : fixtures.first 270 ensure 271 ActiveRecord::Base.logger.level = old_logger_level 269 end 270 end 271 272 def self.delete_fixtures(table_names) 273 ActiveRecord::Base.silence do 274 connection = block_given? ? yield : ActiveRecord::Base.connection 275 276 connection.transaction do 277 table_names.reverse.each do |table_name| 278 connection.delete "DELETE FROM #{table_name}" 279 end 280 end 272 281 end 273 282 end … … 424 433 class TestCase #:nodoc: 425 434 cattr_accessor :fixture_path 435 cattr_accessor :dirty_fixture_table_names 436 cattr_accessor :loaded_fixture_table_names 426 437 class_inheritable_accessor :fixture_table_names 427 438 class_inheritable_accessor :use_transactional_fixtures … … 429 440 class_inheritable_accessor :pre_loaded_fixtures 430 441 442 self.dirty_fixture_table_names = [] 443 self.loaded_fixture_table_names = [] 431 444 self.fixture_table_names = [] 432 445 self.use_transactional_fixtures = false … … 434 447 self.pre_loaded_fixtures = false 435 448 436 @@already_loaded_fixtures = {} 437 438 def self.fixtures(*table_names) 439 table_names = table_names.flatten 440 self.fixture_table_names |= table_names 441 require_fixture_classes(table_names) 442 setup_fixture_accessors(table_names) 443 end 444 445 def self.require_fixture_classes(table_names=nil) 446 (table_names || fixture_table_names).each do |table_name| 447 begin 448 require Inflector.singularize(table_name.to_s) 449 rescue LoadError 450 # Let's hope the developer has included it himself 451 end 452 end 453 end 454 455 def self.setup_fixture_accessors(table_names=nil) 456 (table_names || fixture_table_names).each do |table_name| 457 table_name = table_name.to_s.tr('.','_') 458 define_method(table_name) do |fixture, *optionals| 459 force_reload = optionals.shift 460 @fixture_cache[table_name] ||= Hash.new 461 @fixture_cache[table_name][fixture] = nil if force_reload 462 @fixture_cache[table_name][fixture] ||= @loaded_fixtures[table_name][fixture.to_s].find 463 end 464 end 465 end 466 467 def self.uses_transaction(*methods) 468 @uses_transaction ||= [] 469 @uses_transaction.concat methods.map { |m| m.to_s } 470 end 471 472 def self.uses_transaction?(method) 473 @uses_transaction && @uses_transaction.include?(method.to_s) 449 class << self 450 def fixtures(*table_names) 451 table_names = table_names.flatten.map { |n| n.to_s } 452 self.fixture_table_names |= table_names 453 self.dirty_fixture_table_names |= table_names 454 455 require_fixture_classes(table_names) 456 setup_fixture_accessors(table_names) 457 end 458 459 def require_fixture_classes(table_names=nil) 460 (table_names || fixture_table_names).each do |table_name| 461 begin 462 require table_name.to_s.singularize 463 rescue LoadError 464 # Let's hope the developer has included it himself 465 end 466 end 467 end 468 469 def setup_fixture_accessors(table_names = nil) 470 (table_names || fixture_table_names).each do |table_name| 471 table_name = table_name.to_s.tr('.', '_') 472 class_eval <<-end_eval, __FILE__, __LINE__ 473 def #{table_name}(fixture, force_reload = false) 474 fixture = fixture.to_s 475 @fixture_cache ||= Hash.new { |h,k| h[k] = Hash.new } 476 if force_reload or @fixture_cache['#{table_name}'][fixture].nil? 477 @fixture_cache['#{table_name}'][fixture] = @loaded_fixtures['#{table_name}'][fixture].find 478 end 479 @fixture_cache['#{table_name}'][fixture] 480 end 481 end_eval 482 end 483 end 484 485 def uses_transaction(*methods) 486 @uses_transaction ||= Set.new 487 @uses_transaction += methods.flatten.map { |m| m.to_s } 488 end 489 490 def uses_transaction?(*methods) 491 @uses_transaction && methods.flatten.all? { |m| @uses_transaction.include?(m.to_s) } 492 end 474 493 end 475 494 476 495 def use_transactional_fixtures? 477 use_transactional_fixtures && 478 !self.class.uses_transaction?(method_name) 496 use_transactional_fixtures && !self.class.uses_transaction?(method_name) 479 497 end 480 498 481 499 def setup_with_fixtures 482 500 if pre_loaded_fixtures && !use_transactional_fixtures 483 raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures' 484 end 485 486 @fixture_cache = Hash.new 501 raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures' 502 end 487 503 488 504 # Load fixtures once and begin transaction. 489 505 if use_transactional_fixtures? 490 if @@already_loaded_fixtures[self.class] 491 @loaded_fixtures = @@already_loaded_fixtures[self.class] 492 else 493 load_fixtures 494 @@already_loaded_fixtures[self.class] = @loaded_fixtures 495 end 506 reload_fixtures! unless @loaded_fixtures and dirty_fixture_table_names.empty? 496 507 ActiveRecord::Base.lock_mutex 497 508 ActiveRecord::Base.connection.begin_db_transaction … … 499 510 # Load fixtures for every test. 500 511 else 501 @@already_loaded_fixtures[self.class] = nil502 load_fixtures512 reload_fixtures! 513 self.dirty_fixture_table_names |= loaded_fixture_table_names 503 514 end 504 515 … … 514 525 ActiveRecord::Base.connection.rollback_db_transaction 515 526 ActiveRecord::Base.unlock_mutex 527 end 528 unless dirty_fixture_table_names.empty? 529 Fixtures.delete_fixtures(dirty_fixture_table_names) 530 dirty_fixture_table_names.clear 516 531 end 517 532 end … … 541 556 542 557 private 543 def load_fixtures 544 @loaded_fixtures = {} 545 fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names) 546 unless fixtures.nil? 547 if fixtures.instance_of?(Fixtures) 548 @loaded_fixtures[fixtures.table_name] = fixtures 549 else 550 fixtures.each { |f| @loaded_fixtures[f.table_name] = f } 551 end 558 def reload_fixtures! 559 # Clear dirty fixtures and loaded fixtures which were not declared 560 # for this test case. 561 wipe = dirty_fixture_table_names + loaded_fixture_table_names - fixture_table_names 562 Fixtures.delete_fixtures(wipe) unless wipe.empty? 563 dirty_fixture_table_names.clear 564 loaded_fixture_table_names.clear 565 566 case fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names) 567 when Fixtures 568 loaded_fixture_table_names.push fixtures.table_name.to_s 569 @loaded_fixtures = { fixtures.table_name => fixtures } 570 when Enumerable 571 @loaded_fixtures = {} 572 loaded_fixture_table_names.concat fixtures.map { |f| 573 @loaded_fixtures[f.table_name] = f 574 f.table_name.to_s 575 } 552 576 end 553 577 end … … 560 584 raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty? 561 585 unless @@required_fixture_classes 562 self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys 586 names = Fixtures.all_loaded_fixtures.map { |fixtures| fixtures.table_name } 587 self.class.require_fixture_classes names 563 588 @@required_fixture_classes = true 564 589 end trunk/activerecord/test/column_alias_test.rb
r1457 r2714 3 3 4 4 class TestColumnAlias < Test::Unit::TestCase 5 fixtures :topics 6 7 QUERY = if 'OCI' == ActiveRecord::Base.connection.adapter_name 8 'SELECT id AS pk FROM topics WHERE ROWNUM < 2' 9 else 10 'SELECT id AS pk FROM topics' 11 end 5 12 6 13 def test_column_alias 7 topic = Topic.find(1) 8 if ActiveRecord::ConnectionAdapters.const_defined? :OracleAdapter 9 if ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::OracleAdapter) 10 records = topic.connection.select_all("SELECT id AS pk FROM topics WHERE ROWNUM < 2") 11 assert_equal("pk", records[0].keys[0]) 12 end 13 else 14 records = topic.connection.select_all("SELECT id AS pk FROM topics") 15 assert_equal("pk", records[0].keys[0]) 16 end 14 records = Topic.connection.select_all(QUERY) 15 assert_equal 'pk', records[0].keys[0] 17 16 end 18 19 17 end trunk/activerecord/test/conditions_scoping_test.rb
r2510 r2714 70 70 71 71 class HasAndBelongsToManyScopingTest< Test::Unit::TestCase 72 fixtures :posts, :categories 72 fixtures :posts, :categories, :categories_posts 73 73 74 74 def setup … … 86 86 assert_equal 2, @welcome.categories.find_all_by_type('Category').size 87 87 end 88 89 88 end 90 89 trunk/activerecord/test/finder_test.rb
r2705 r2714 7 7 8 8 class FinderTest < Test::Unit::TestCase 9 fixtures :companies, :topics, :entrants, :developers, : posts9 fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts 10 10 11 11 def test_find trunk/activerecord/test/fixtures_test.rb
r2681 r2714 87 87 assert_nil(secondRow["author_email_address"]) 88 88 ensure 89 Fixtures.all_loaded_fixtures.delete(topics) 89 90 ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil 90 91 end … … 260 261 261 262 def test_fixture_table_names 262 assert_equal ([:topics, :developers, :accounts], fixture_table_names)263 assert_equal %w(topics developers accounts), fixture_table_names 263 264 end 264 265 end … … 270 271 271 272 def test_fixture_table_names 272 assert_equal ([:topics, :developers, :accounts], fixture_table_names)273 assert_equal %w(topics developers accounts), fixture_table_names 273 274 end 274 275 end … … 290 291 end 291 292 end 293 294 295 class FixtureCleanup1Test < Test::Unit::TestCase 296 fixtures :topics 297 298 def test_isolation 299 assert_equal 0, Developer.count 300 assert_equal 2, Topic.count 301 end 302 end 303 304 class FixtureCleanup2Test < Test::Unit::TestCase 305 fixtures :developers 306 307 def test_isolation 308 assert_equal 0, Topic.count 309 assert_equal 10, Developer.count 310 end 311 end 312 313 class FixtureCleanup3Test < FixtureCleanup2Test 314 self.use_transactional_fixtures = false 315 316 def test_dirty_fixture_table_names 317 assert_equal %w(developers), dirty_fixture_table_names 318 assert_equal %w(developers), loaded_fixture_table_names 319 assert_equal %w(developers), fixture_table_names 320 end 321 end 322 323 class FixtureCleanup4Test < FixtureCleanup2Test 324 self.use_transactional_fixtures = true 325 326 def test_dirty_fixture_table_names 327 assert_equal [], dirty_fixture_table_names 328 assert_equal %w(developers), loaded_fixture_table_names 329 assert_equal %w(developers), fixture_table_names 330 end 331 end