Changeset 2730
- Timestamp:
- 10/25/05 18:14:09 (3 years ago)
- Files:
-
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record/base.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/fixtures.rb (modified) (15 diffs)
- trunk/activerecord/test/associations_go_eager_test.rb (modified) (1 diff)
- trunk/activerecord/test/conditions_scoping_test.rb (modified) (1 diff)
- trunk/activerecord/test/fixtures_test.rb (modified) (2 diffs)
- trunk/activerecord/test/pk_test.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/CHANGELOG
r2717 r2730 1 1 *SVN* 2 3 * Keep closer tabs on dirty, loaded, and declared fixtures. #2404 [ryand-ruby@zenspider.com]4 2 5 3 * Map Active Record time to SQL TIME. #2575, #2576 [Robby Russell <robby@planetargon.com>] trunk/activerecord/lib/active_record/base.rb
r2718 r2730 1114 1114 attr_name = self.class.primary_key 1115 1115 column = column_for_attribute(attr_name) 1116 raise ActiveRecordError, "No such primary key column #{attr_name} for table #{table_name}" if column.nil? 1116 1117 define_read_method(:id, attr_name, column) if self.class.generate_read_methods 1117 1118 (value = @attributes[attr_name]) && column.type_cast(value) trunk/activerecord/lib/active_record/fixtures.rb
r2719 r2730 2 2 require 'yaml' 3 3 require 'csv' 4 require 'set'5 4 6 5 module YAML #:nodoc: … … 124 123 # 125 124 # By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here tho), we trigger 126 # the testing environment to automatically load the appropriate fixtures into the database before each test. 125 # the testing environment to automatically load the appropriate fixtures into the database before each test. 127 126 # To ensure consistent data, the environment deletes the fixtures before running the load. 128 127 # … … 150 149 # 151 150 # - to keep the fixture instance (@web_sites) available, but do not automatically 'find' each instance: 152 # self.use_instantiated_fixtures = :no_instances 151 # self.use_instantiated_fixtures = :no_instances 153 152 # 154 153 # Even if auto-instantiated fixtures are disabled, you can still access them … … 182 181 # = Transactional fixtures 183 182 # 184 # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case. 183 # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case. 185 184 # They can also turn off auto-instantiation of fixture data since the feature is costly and often unused. 186 185 # … … 188 187 # self.use_transactional_fixtures = true 189 188 # self.use_instantiated_fixtures = false 190 # 189 # 191 190 # fixtures :foos 192 # 191 # 193 192 # def test_godzilla 194 193 # assert !Foo.find(:all).empty? … … 196 195 # assert Foo.find(:all).empty? 197 196 # end 198 # 197 # 199 198 # def test_godzilla_aftermath 200 199 # assert !Foo.find(:all).empty? 201 200 # end 202 201 # end 203 # 204 # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures, 202 # 203 # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures, 205 204 # 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. 206 205 # 207 # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide 206 # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide 208 207 # access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+) 209 208 # 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. 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. 215 214 # Use InnoDB, MaxDB, or NDB instead. 216 215 class Fixtures < YAML::Omap … … 231 230 232 231 def self.instantiate_all_loaded_fixtures(object, load_instances=true) 233 all_loaded_fixtures.each do | fixtures|234 Fixtures.instantiate_fixtures(object, fixtures.table_name, fixtures, load_instances)235 end 236 end 237 232 all_loaded_fixtures.each do |table_name, fixtures| 233 Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances) 234 end 235 end 236 238 237 cattr_accessor :all_loaded_fixtures 239 self.all_loaded_fixtures = []238 self.all_loaded_fixtures = {} 240 239 241 240 def self.create_fixtures(fixtures_directory, *table_names) 242 table_names = table_names.flatten 241 table_names = table_names.flatten.map { |n| n.to_s } 243 242 connection = block_given? ? yield : ActiveRecord::Base.connection 244 243 245 244 ActiveRecord::Base.logger.silence do 245 fixtures_map = {} 246 246 fixtures = table_names.map do |table_name| 247 Fixtures.new(connection, File.split(table_name.to_s).last, File.join(fixtures_directory, table_name.to_s)) 248 end 247 fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, File.join(fixtures_directory, table_name.to_s)) 248 end 249 all_loaded_fixtures.merge! fixtures_map 249 250 250 251 connection.transaction do 251 table_names.reverse.each do |table_name| 252 connection.delete "DELETE FROM #{table_name}" 253 end 254 255 fixtures.each { |f| f.insert_fixtures } 256 252 fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures } 253 fixtures.each { |fixture| fixture.insert_fixtures } 254 255 # Cap primary key sequences to max(pk). 257 256 if connection.respond_to?(:reset_pk_sequence!) 258 table_names. flatten.each do |table_name|257 table_names.each do |table_name| 259 258 connection.reset_pk_sequence!(table_name) 260 259 end … … 262 261 end 263 262 264 all_loaded_fixtures.concat fixtures265 263 return fixtures.size > 1 ? fixtures : fixtures.first 266 end267 end268 269 def self.delete_fixtures(table_names)270 ActiveRecord::Base.silence do271 connection = block_given? ? yield : ActiveRecord::Base.connection272 273 connection.transaction do274 table_names.reverse.each do |table_name|275 connection.delete "DELETE FROM #{table_name}"276 end277 end278 264 end 279 265 end … … 430 416 class TestCase #:nodoc: 431 417 cattr_accessor :fixture_path 432 cattr_accessor :dirty_fixture_table_names433 cattr_accessor :loaded_fixture_table_names434 418 class_inheritable_accessor :fixture_table_names 435 419 class_inheritable_accessor :use_transactional_fixtures … … 437 421 class_inheritable_accessor :pre_loaded_fixtures 438 422 439 self.dirty_fixture_table_names = []440 self.loaded_fixture_table_names = []441 423 self.fixture_table_names = [] 442 424 self.use_transactional_fixtures = false … … 444 426 self.pre_loaded_fixtures = false 445 427 446 class << self 447 def fixtures(*table_names) 448 table_names = table_names.flatten.map { |n| n.to_s } 449 self.fixture_table_names |= table_names 450 self.dirty_fixture_table_names |= table_names 451 452 require_fixture_classes(table_names) 453 setup_fixture_accessors(table_names) 454 end 455 456 def require_fixture_classes(table_names=nil) 457 (table_names || fixture_table_names).each do |table_name| 458 begin 459 require table_name.to_s.singularize 460 rescue LoadError 461 # Let's hope the developer has included it himself 462 end 463 end 464 end 465 466 def setup_fixture_accessors(table_names = nil) 467 (table_names || fixture_table_names).each do |table_name| 468 table_name = table_name.to_s.tr('.', '_') 469 class_eval <<-end_eval, __FILE__, __LINE__ 470 def #{table_name}(fixture, force_reload = false) 471 fixture = fixture.to_s 472 @fixture_cache ||= Hash.new { |h,k| h[k] = Hash.new } 473 if force_reload or @fixture_cache['#{table_name}'][fixture].nil? 474 @fixture_cache['#{table_name}'][fixture] = @loaded_fixtures['#{table_name}'][fixture].find 475 end 476 @fixture_cache['#{table_name}'][fixture] 477 end 478 end_eval 479 end 480 end 481 482 def uses_transaction(*methods) 483 @uses_transaction ||= Set.new 484 @uses_transaction += methods.flatten.map { |m| m.to_s } 485 end 486 487 def uses_transaction?(*methods) 488 @uses_transaction && methods.flatten.all? { |m| @uses_transaction.include?(m.to_s) } 489 end 428 @@already_loaded_fixtures = {} 429 430 def self.fixtures(*table_names) 431 table_names = table_names.flatten.map { |n| n.to_s } 432 self.fixture_table_names |= table_names 433 require_fixture_classes(table_names) 434 setup_fixture_accessors(table_names) 435 end 436 437 def self.require_fixture_classes(table_names=nil) 438 (table_names || fixture_table_names).each do |table_name| 439 begin 440 require Inflector.singularize(table_name.to_s) 441 rescue LoadError 442 # Let's hope the developer has included it himself 443 end 444 end 445 end 446 447 def self.setup_fixture_accessors(table_names=nil) 448 (table_names || fixture_table_names).each do |table_name| 449 table_name = table_name.to_s.tr('.','_') 450 define_method(table_name) do |fixture, *optionals| 451 force_reload = optionals.shift 452 @fixture_cache[table_name] ||= Hash.new 453 @fixture_cache[table_name][fixture] = nil if force_reload 454 @fixture_cache[table_name][fixture] ||= @loaded_fixtures[table_name][fixture.to_s].find 455 end 456 end 457 end 458 459 def self.uses_transaction(*methods) 460 @uses_transaction ||= [] 461 @uses_transaction.concat methods.map { |m| m.to_s } 462 end 463 464 def self.uses_transaction?(method) 465 @uses_transaction && @uses_transaction.include?(method.to_s) 490 466 end 491 467 492 468 def use_transactional_fixtures? 493 use_transactional_fixtures && !self.class.uses_transaction?(method_name) 469 use_transactional_fixtures && 470 !self.class.uses_transaction?(method_name) 494 471 end 495 472 496 473 def setup_with_fixtures 497 474 if pre_loaded_fixtures && !use_transactional_fixtures 498 raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures' 499 end 475 raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures' 476 end 477 478 @fixture_cache = Hash.new 500 479 501 480 # Load fixtures once and begin transaction. 502 481 if use_transactional_fixtures? 503 reload_fixtures! unless @loaded_fixtures and dirty_fixture_table_names.empty? 482 if @@already_loaded_fixtures[self.class] 483 @loaded_fixtures = @@already_loaded_fixtures[self.class] 484 else 485 load_fixtures 486 @@already_loaded_fixtures[self.class] = @loaded_fixtures 487 end 504 488 ActiveRecord::Base.lock_mutex 505 489 ActiveRecord::Base.connection.begin_db_transaction … … 507 491 # Load fixtures for every test. 508 492 else 509 reload_fixtures!510 self.dirty_fixture_table_names |= loaded_fixture_table_names493 @@already_loaded_fixtures[self.class] = nil 494 load_fixtures 511 495 end 512 496 … … 522 506 ActiveRecord::Base.connection.rollback_db_transaction 523 507 ActiveRecord::Base.unlock_mutex 524 end525 unless dirty_fixture_table_names.empty?526 Fixtures.delete_fixtures(dirty_fixture_table_names)527 dirty_fixture_table_names.clear528 508 end 529 509 end … … 553 533 554 534 private 555 def reload_fixtures! 556 # Clear dirty fixtures and loaded fixtures which were not declared 557 # for this test case. 558 wipe = dirty_fixture_table_names | loaded_fixture_table_names - fixture_table_names 559 Fixtures.delete_fixtures(wipe) unless wipe.empty? 560 dirty_fixture_table_names.clear 561 loaded_fixture_table_names.clear 562 563 case fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names) 564 when Fixtures 565 loaded_fixture_table_names.push fixtures.table_name.to_s 566 @loaded_fixtures = { fixtures.table_name => fixtures } 567 when Enumerable 568 @loaded_fixtures = {} 569 loaded_fixture_table_names.concat fixtures.map { |f| 570 @loaded_fixtures[f.table_name] = f 571 f.table_name.to_s 572 } 535 def load_fixtures 536 @loaded_fixtures = {} 537 fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names) 538 unless fixtures.nil? 539 if fixtures.instance_of?(Fixtures) 540 @loaded_fixtures[fixtures.table_name] = fixtures 573 541 else 574 @loaded_fixtures = {} 542 fixtures.each { |f| @loaded_fixtures[f.table_name] = f } 543 end 575 544 end 576 545 end … … 583 552 raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty? 584 553 unless @@required_fixture_classes 585 names = Fixtures.all_loaded_fixtures.map { |fixtures| fixtures.table_name } 586 self.class.require_fixture_classes names 554 self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys 587 555 @@required_fixture_classes = true 588 556 end trunk/activerecord/test/associations_go_eager_test.rb
r2675 r2730 97 97 98 98 def test_eager_with_has_many_and_limit 99 posts = Post.find(:all, : include => [ :author, :comments ], :limit => 2)99 posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2) 100 100 assert_equal 2, posts.size 101 101 assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size } trunk/activerecord/test/conditions_scoping_test.rb
r2714 r2730 86 86 assert_equal 2, @welcome.categories.find_all_by_type('Category').size 87 87 end 88 88 89 end 89 90 trunk/activerecord/test/fixtures_test.rb
r2719 r2730 87 87 assert_nil(secondRow["author_email_address"]) 88 88 ensure 89 Fixtures.all_loaded_fixtures.delete(topics)90 89 ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil 91 90 end … … 291 290 end 292 291 end 293 294 295 class FixtureCleanup1Test < Test::Unit::TestCase296 fixtures :topics297 298 def test_isolation299 assert_equal 0, Developer.count300 assert_equal 2, Topic.count301 end302 end303 304 class FixtureCleanup2Test < Test::Unit::TestCase305 fixtures :developers306 307 def test_isolation308 assert_equal 0, Topic.count309 assert_equal 10, Developer.count310 end311 end312 313 class FixtureCleanup3Test < FixtureCleanup2Test314 self.use_transactional_fixtures = false315 316 def test_dirty_fixture_table_names317 assert_equal %w(developers), dirty_fixture_table_names318 assert_equal %w(developers), loaded_fixture_table_names319 assert_equal %w(developers), fixture_table_names320 end321 end322 323 class FixtureCleanup4Test < FixtureCleanup2Test324 self.use_transactional_fixtures = true325 326 def test_dirty_fixture_table_names327 assert_equal [], dirty_fixture_table_names328 assert_equal %w(developers), loaded_fixture_table_names329 assert_equal %w(developers), fixture_table_names330 end331 end332 333 class FixtureCleanup5Test < FixtureCleanup3Test334 self.use_instantiated_fixtures = false335 336 def test_dirty_fixture_table_names337 assert_equal %w(developers), dirty_fixture_table_names338 assert_equal %w(developers), loaded_fixture_table_names339 assert_equal %w(developers), fixture_table_names340 end341 end342 343 class FixtureCleanup6Test < FixtureCleanup4Test344 self.use_instantiated_fixtures = true345 346 def test_dirty_fixture_table_names347 assert_equal [], dirty_fixture_table_names348 assert_equal %w(developers), loaded_fixture_table_names349 assert_equal %w(developers), fixture_table_names350 end351 end352 353 class FixtureCleanup7Test < Test::Unit::TestCase354 self.use_transactional_fixtures = false355 self.use_instantiated_fixtures = true356 357 def test_dirty_fixture_table_names358 assert_equal [], dirty_fixture_table_names359 assert_equal [], loaded_fixture_table_names360 assert_equal [], fixture_table_names361 end362 363 def test_isolation364 assert_equal 0, Topic.count365 assert_equal 0, Developer.count366 end367 end368 369 class FixtureCleanup8Test < FixtureCleanup7Test370 self.use_transactional_fixtures = true371 self.use_instantiated_fixtures = true372 endtrunk/activerecord/test/pk_test.rb
r2718 r2730 25 25 26 26 def test_customized_primary_key_auto_assigns_on_save 27 Keyboard.delete_all 27 28 keyboard = Keyboard.new(:name => 'HHKB') 28 29 assert_nothing_raised { keyboard.save! }