Ticket #9981: foxy_fixtures.diff
| File foxy_fixtures.diff, 20.1 kB (added by jbarnette, 1 year ago) |
|---|
-
activerecord/test/fixtures_test.rb
old new 7 7 require 'fixtures/joke' 8 8 require 'fixtures/course' 9 9 require 'fixtures/category' 10 require 'fixtures/parrot' 11 require 'fixtures/pirate' 12 require 'fixtures/treasure' 10 13 11 14 class FixturesTest < Test::Unit::TestCase 12 15 self.use_instantiated_fixtures = true … … 446 449 assert_equal 'Welcome to the weblog', posts(:welcome).title 447 450 end 448 451 end 452 453 class FoxyFixturesTest < Test::Unit::TestCase 454 fixtures :parrots, :parrots_pirates, :pirates, :treasures 455 456 def test_identifies_strings 457 assert_equal(Fixtures.identify("foo"), Fixtures.identify("foo")) 458 assert_not_equal(Fixtures.identify("foo"), Fixtures.identify("FOO")) 459 end 460 461 def test_identifies_symbols 462 assert_equal(Fixtures.identify(:foo), Fixtures.identify(:foo)) 463 end 464 465 TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on) 466 467 def test_populates_timestamp_columns 468 TIMESTAMP_COLUMNS.each do |property| 469 assert_not_nil(parrots(:george).send(property), "should set #{property}") 470 end 471 end 472 473 def test_populates_all_columns_with_the_same_time 474 last = nil 475 476 TIMESTAMP_COLUMNS.each do |property| 477 current = parrots(:george).send(property) 478 last ||= current 479 480 assert_equal(last, current) 481 last = current 482 end 483 end 484 485 def test_only_populates_columns_that_exist 486 assert_not_nil(pirates(:blackbeard).created_on) 487 assert_not_nil(pirates(:blackbeard).updated_on) 488 end 489 490 def test_preserves_existing_fixture_data 491 assert_equal(2.weeks.ago.to_date, pirates(:redbeard).created_on.to_date) 492 assert_equal(2.weeks.ago.to_date, pirates(:redbeard).updated_on.to_date) 493 end 494 495 def test_generates_unique_ids 496 assert_not_nil(parrots(:george).id) 497 assert_not_equal(parrots(:george).id, parrots(:louis).id) 498 end 499 500 def test_resolves_belongs_to_symbols 501 assert_equal(parrots(:george), pirates(:blackbeard).parrot) 502 end 503 504 def test_supports_join_tables 505 assert(pirates(:blackbeard).parrots.include?(parrots(:george))) 506 assert(pirates(:blackbeard).parrots.include?(parrots(:louis))) 507 assert(parrots(:george).pirates.include?(pirates(:blackbeard))) 508 end 509 510 def test_supports_inline_habtm 511 assert(parrots(:george).treasures.include?(treasures(:diamond))) 512 assert(parrots(:george).treasures.include?(treasures(:sapphire))) 513 assert(!parrots(:george).treasures.include?(treasures(:ruby))) 514 end 515 516 def test_supports_yaml_arrays 517 assert(parrots(:louis).treasures.include?(treasures(:diamond))) 518 assert(parrots(:louis).treasures.include?(treasures(:sapphire))) 519 end 520 521 def test_strips_DEFAULTS_key 522 assert_raise(StandardError) { parrots(:DEFAULTS) } 523 524 # this lets us do YAML defaults and not have an extra fixture entry 525 %w(sapphire ruby).each { |t| assert(parrots(:davey).treasures.include?(treasures(t))) } 526 end 527 528 def test_supports_label_interpolation 529 assert_equal("frederick", parrots(:frederick).name) 530 end 531 end -
activerecord/test/fixtures/treasure.rb
old new 1 class Treasure < ActiveRecord::Base 2 has_and_belongs_to_many :parrots 3 end -
activerecord/test/fixtures/treasures.yml
old new 1 diamond: 2 name: $LABEL 3 4 sapphire: 5 name: $LABEL 6 7 ruby: 8 name: $LABEL -
activerecord/test/fixtures/pirate.rb
old new 1 class Pirate < ActiveRecord::Base 2 belongs_to :parrot 3 has_and_belongs_to_many :parrots 4 end -
activerecord/test/fixtures/pirates.yml
old new 1 blackbeard: 2 catchphrase: "Yar." 3 parrot: george 4 5 redbeard: 6 catchphrase: "Avast!" 7 parrot: louis 8 created_on: <%= 2.weeks.ago.to_s(:db) %> 9 updated_on: <%= 2.weeks.ago.to_s(:db) %> -
activerecord/test/fixtures/parrots.yml
old new 1 george: 2 name: "Curious George" 3 treasures: diamond, sapphire 4 5 louis: 6 name: "King Louis" 7 treasures: [diamond, sapphire] 8 9 frederick: 10 name: $LABEL 11 12 DEFAULTS: &DEFAULTS 13 treasures: sapphire, ruby 14 15 davey: 16 <<: *DEFAULTS -
activerecord/test/fixtures/db_definitions/schema.rb
old new 295 295 t.column :city, :string, :null => false 296 296 t.column :type, :string 297 297 end 298 299 create_table :parrots, :force => true do |t| 300 t.column :name, :string 301 t.column :created_at, :datetime 302 t.column :created_on, :datetime 303 t.column :updated_at, :datetime 304 t.column :updated_on, :datetime 305 end 306 307 create_table :pirates, :force => true do |t| 308 t.column :catchphrase, :string 309 t.column :parrot_id, :integer 310 t.column :created_on, :datetime 311 t.column :updated_on, :datetime 312 end 313 314 create_table :parrots_pirates, :id => false, :force => true do |t| 315 t.column :parrot_id, :integer 316 t.column :pirate_id, :integer 317 end 318 319 create_table :treasures, :force => true do |t| 320 t.column :name, :string 321 end 322 323 create_table :parrots_treasures, :id => false, :force => true do |t| 324 t.column :parrot_id, :integer 325 t.column :treasure_id, :integer 326 end 298 327 end -
activerecord/test/fixtures/parrots_pirates.yml
old new 1 george_blackbeard: 2 parrot_id: <%= Fixtures.identify(:george) %> 3 pirate_id: <%= Fixtures.identify(:blackbeard) %> 4 5 louis_blackbeard: 6 parrot_id: <%= Fixtures.identify(:louis) %> 7 pirate_id: <%= Fixtures.identify(:blackbeard) %> -
activerecord/test/fixtures/parrot.rb
old new 1 class Parrot < ActiveRecord::Base 2 has_and_belongs_to_many :pirates 3 has_and_belongs_to_many :treasures 4 end -
activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
old new 70 70 name 71 71 end 72 72 73 # REFERENTIAL INTEGRITY ==================================== 74 75 # Override to turn off referential integrity while executing +&block+ 76 def disable_referential_integrity(&block) 77 yield 78 end 79 73 80 # CONNECTION MANAGEMENT ==================================== 74 81 75 82 # Is this connection active and ready to perform queries? -
activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
old new 364 364 end 365 365 end 366 366 367 # REFERENTIAL INTEGRITY ==================================== 367 368 369 def disable_referential_integrity(&block) #:nodoc: 370 execute(tables.collect { |name| "ALTER TABLE #{name} DISABLE TRIGGER ALL" }.join(";")) 371 372 begin 373 yield 374 ensure 375 execute(tables.collect { |name| "ALTER TABLE #{name} ENABLE TRIGGER ALL" }.join(";")) 376 end 377 end 378 368 379 # DATABASE STATEMENTS ====================================== 369 380 370 381 # Executes a SELECT query and returns an array of rows. Each row is an -
activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
old new 224 224 "0" 225 225 end 226 226 227 # REFERENTIAL INTEGRITY ==================================== 227 228 229 def disable_referential_integrity(&block) #:nodoc: 230 old = select_value("SELECT @@FOREIGN_KEY_CHECKS") 231 232 begin 233 update("SET FOREIGN_KEY_CHECKS = 0") 234 yield 235 ensure 236 update("SET FOREIGN_KEY_CHECKS = #{old}") 237 end 238 end 239 228 240 # CONNECTION MANAGEMENT ==================================== 229 241 230 242 def active? -
activerecord/lib/active_record/fixtures.rb
old new 215 215 # the results of your transaction until Active Record supports nested transactions or savepoints (in progress.) 216 216 # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM. 217 217 # Use InnoDB, MaxDB, or NDB instead. 218 # 219 # = Advanced YAML Fixtures 220 # 221 # YAML fixtures that don't specify an ID get some extra features: 222 # 223 # * Stable, autogenerated ID's 224 # * Label references for associations (belongs_to, has_one, has_many) 225 # * HABTM associations as inline lists 226 # * Autofilled timestamp columns 227 # * Fixture label interpolation 228 # * Support for YAML defaults 229 # 230 # == Stable, autogenerated ID's 231 # 232 # Here, have a monkey fixture: 233 # 234 # george: 235 # id: 1 236 # name: George the Monkey 237 # 238 # reginald: 239 # id: 2 240 # name: Reginald the Pirate 241 # 242 # Each of these fixtures has two unique identifiers: one for the database 243 # and one for the humans. Why don't we generate the primary key instead? 244 # Hashing each fixture's label yields a consistent ID: 245 # 246 # george: # generated id: 503576764 247 # name: George the Monkey 248 # 249 # reginald: # generated id: 324201669 250 # name: Reginald the Pirate 251 # 252 # ActiveRecord looks at the fixture's model class, discovers the correct 253 # primary key, and generates it right before inserting the fixture 254 # into the database. 255 # 256 # The generated ID for a given label is constant, so we can discover 257 # any fixture's ID without loading anything, as long as we know the label. 258 # 259 # == Label references for associations (belongs_to, has_one, has_many) 260 # 261 # Specifying foreign keys in fixtures can be very fragile, not to 262 # mention difficult to read. Since ActiveRecord can figure out the ID of 263 # and fixture from its label, you can specify FK's by label instead of ID. 264 # 265 # === belongs_to 266 # 267 # Let's break out some more monkeys and pirates. 268 # 269 # ### in pirates.yml 270 # 271 # reginald: 272 # id: 1 273 # name: Reginald the Pirate 274 # monkey_id: 1 275 # 276 # ### in monkeys.yml 277 # 278 # george: 279 # id: 1 280 # name: George the Monkey 281 # pirate_id: 1 282 # 283 # Add a few more monkeys and pirates and break this into multiple files, 284 # and it gets pretty hard to keep track of what's going on. Let's 285 # use labels instead of ID's: 286 # 287 # ### in pirates.yml 288 # 289 # reginald: 290 # name: Reginald the Pirate 291 # monkey: george 292 # 293 # ### in monkeys.yml 294 # 295 # george: 296 # name: George the Monkey 297 # pirate: reginald 298 # 299 # Pow! All is made clear. ActiveRecord reflects on the fixture's model class, 300 # finds all the +belongs_to+ associations, and allows you to specify 301 # a target *label* for the *association* (monkey: george) rather than 302 # a target *id* for the *FK* (monkey_id: 1). 303 # 304 # === has_and_belongs_to_many 305 # 306 # Time to give our monkey some fruit. 307 # 308 # ### in monkeys.yml 309 # 310 # george: 311 # id: 1 312 # name: George the Monkey 313 # pirate_id: 1 314 # 315 # ### in fruits.yml 316 # 317 # apple: 318 # id: 1 319 # name: apple 320 # 321 # orange: 322 # id: 2 323 # name: orange 324 # 325 # grape: 326 # id: 3 327 # name: grape 328 # 329 # ### in fruits_monkeys.yml 330 # 331 # apple_george: 332 # fruit_id: 1 333 # monkey_id: 1 334 # 335 # orange_george: 336 # fruit_id: 2 337 # monkey_id: 1 338 # 339 # grape_george: 340 # fruit_id: 3 341 # monkey_id: 1 342 # 343 # Let's make the HABTM fixture go away. 344 # 345 # ### in monkeys.yml 346 # 347 # george: 348 # name: George the Monkey 349 # pirate: reginald 350 # fruits: apple, orange, grape 351 # 352 # ### in fruits.yml 353 # 354 # apple: 355 # name: apple 356 # 357 # orange: 358 # name: orange 359 # 360 # grape: 361 # name: grape 362 # 363 # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits 364 # on George's fixture, but we could've just as easily specified a list 365 # of monkeys on each fruit. As with +belongs_to+, ActiveRecord reflects on 366 # the fixture's model class and discovers the +has_and_belongs_to_many+ 367 # associations. 368 # 369 # == Autofilled timestamp columns 370 # 371 # If your table/model specifies any of ActiveRecord's 372 # standard timestamp columns (created_at, created_on, updated_at, updated_on), 373 # they will automatically be set to Time.now. 374 # 375 # If you've set specific values, they'll be left alone. 376 # 377 # == Fixture label interpolation 378 # 379 # The label of the current fixture is always available as a column value: 380 # 381 # geeksomnia: 382 # name: Geeksomnia's Account 383 # subdomain: $LABEL 384 # 385 # Also, sometimes (like when porting older join table fixtures) you'll need 386 # to be able to get ahold of the identifier for a given label. ERB 387 # to the rescue: 388 # 389 # george_reginald: 390 # monkey_id: <%= Fixtures.identify(:reginald) %> 391 # pirate_id: <%= Fixtures.identify(:george) %> 392 # 393 # == Support for YAML defaults 394 # 395 # You probably already know how to use YAML to set and reuse defaults in 396 # your +database.yml+ file,. You can use the same technique in your fixtures: 397 # 398 # DEFAULTS: &DEFAULTS 399 # created_on: <%= 3.weeks.ago.to_s(:db) %> 400 # 401 # first: 402 # name: Smurf 403 # <<: *DEFAULTS 404 # 405 # second: 406 # name: Fraggle 407 # <<: *DEFAULTS 408 # 409 # Any fixture labeled "DEFAULTS" is safely ignored. 410 218 411 class Fixtures < YAML::Omap 219 412 DEFAULT_FILTER_RE = /\.ya?ml$/ 220 413 … … 279 472 280 473 unless table_names_to_fetch.empty? 281 474 ActiveRecord::Base.silence do 282 fixtures_map = {} 475 connection.disable_referential_integrity do 476 fixtures_map = {} 283 477 284 fixtures = table_names_to_fetch.map do |table_name|285 fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s))286 end478 fixtures = table_names_to_fetch.map do |table_name| 479 fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s)) 480 end 287 481 288 all_loaded_fixtures.update(fixtures_map)482 all_loaded_fixtures.update(fixtures_map) 289 483 290 connection.transaction(Thread.current['open_transactions'].to_i == 0) do291 fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }292 fixtures.each { |fixture| fixture.insert_fixtures }484 connection.transaction(Thread.current['open_transactions'].to_i == 0) do 485 fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures } 486 fixtures.each { |fixture| fixture.insert_fixtures } 293 487 294 # Cap primary key sequences to max(pk). 295 if connection.respond_to?(:reset_pk_sequence!) 296 table_names.each do |table_name| 297 connection.reset_pk_sequence!(table_name) 488 # Cap primary key sequences to max(pk). 489 if connection.respond_to?(:reset_pk_sequence!) 490 table_names.each do |table_name| 491 connection.reset_pk_sequence!(table_name) 492 end 298 493 end 299 494 end 495 496 cache_fixtures(connection, fixtures) 300 497 end 301 302 cache_fixtures(connection, fixtures)303 498 end 304 499 end 305 500 cached_fixtures(connection, table_names) 306 501 end 502 503 # Returns a consistent identifier for +label+. This will always 504 # be a positive integer, and will always be the same for a given 505 # label, assuming the same OS, platform, and version of Ruby. 506 def self.identify(label) 507 label.to_s.hash.abs 508 end 307 509 308 510 attr_reader :table_name 309 511 … … 322 524 end 323 525 324 526 def insert_fixtures 325 values.each do |fixture| 527 now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now 528 now = now.to_s(:db) 529 530 # allow a standard key to be used for doing defaults in YAML 531 delete(assoc("DEFAULTS")) 532 533 # track any join tables we need to insert later 534 habtm_fixtures = Hash.new do |h, habtm| 535 h[habtm] = HabtmFixtures.new(@connection, habtm.options[:join_table], nil, nil) 536 end 537 538 each do |label, fixture| 539 row = fixture.to_hash 540 541 if model_class && model_class < ActiveRecord::Base && !row[primary_key_name] 542 # fill in timestamp columns if they aren't specified 543 timestamp_column_names.each do |name| 544 row[name] = now unless row.key?(name) 545 end 546 547 # interpolate the fixture label 548 row.each do |key, value| 549 row[key] = label if value == "$LABEL" 550 end 551 552 # generate a primary key 553 row[primary_key_name] = Fixtures.identify(label) 554 555 model_class.reflect_on_all_associations.each do |association| 556 case association.macro 557 when :belongs_to 558 if value = row.delete(association.name.to_s) 559 fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s 560 row[fk_name] = Fixtures.identify(value) 561 end 562 when :has_and_belongs_to_many 563 if (targets = row.delete(association.name.to_s)) 564 targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/) 565 join_fixtures = habtm_fixtures[association] 566 567 targets.each do |target| 568 join_fixtures["#{label}_#{target}"] = Fixture.new( 569 { association.primary_key_name => Fixtures.identify(label), 570 association.association_foreign_key => Fixtures.identify(target) }, nil) 571 end 572 end 573 end 574 end 575 end 576 326 577 @connection.insert_fixture(fixture, @table_name) 327 578 end 579 580 # insert any HABTM join tables we discovered 581 habtm_fixtures.values.each do |fixture| 582 fixture.delete_existing_fixtures 583 fixture.insert_fixtures 584 end 328 585 end 329 586 330 587 private 588 class HabtmFixtures < ::Fixtures #:nodoc: 589 def read_fixture_files; end 590 end 591 592 def model_class 593 @model_class ||= @class_name.is_a?(Class) ? 594 @class_name : @class_name.constantize rescue nil 595 end 596 597 def primary_key_name 598 @primary_key_name ||= model_class && model_class.primary_key 599 end 600 601 def timestamp_column_names 602 @timestamp_column_names ||= %w(created_at created_on updated_at updated_on).select do |name| 603 column_names.include?(name) 604 end 605 end 606 607 def column_names 608 @column_names ||= @connection.columns(@table_name).collect(&:name) 609 end 610 331 611 def read_fixture_files 332 612 if File.file?(yaml_file_path) 333 613 read_yaml_fixture_files