= Foxy Fixtures Foxy Fixtures backports Rails 2.0's advanced YAML fixtures to 1.2.x. It provides: * Stable, autogenerated ID's * Label references for associations (belongs_to, has_one, has_many) * HABTM associations as inline lists * Autofilled timestamp columns * Fixture label interpolation * Support for YAML defaults == Stable, autogenerated ID's Here, have a monkey fixture: george: id: 1 name: George the Monkey reginald: id: 2 name: Reginald the Pirate Each of these fixtures has two unique identifiers: one for the database and one for the humans. Why don't we generate the primary key instead? Hashing each fixture's label yields a consistent ID: george: # generated id: 503576764 name: George the Monkey reginald: # generated id: 324201669 name: Reginald the Pirate We look at the fixture's model class, discovers the correct primary key, and generates it right before inserting the fixture into the database. The generated ID for a given label is constant, so we can discover any fixture's ID without loading anything, as long as we know the label. == Label references for associations (belongs_to, has_one, has_many) Specifying foreign keys in fixtures can be very fragile, not to mention difficult to read. Since we can figure out the ID of and fixture from its label, you can specify FK's by label instead of ID. === belongs_to Let's break out some more monkeys and pirates. ### in pirates.yml reginald: id: 1 name: Reginald the Pirate monkey_id: 1 ### in monkeys.yml george: id: 1 name: George the Monkey pirate_id: 1 Add a few more monkeys and pirates and break this into multiple files, and it gets pretty hard to keep track of what's going on. Let's use labels instead of ID's: ### in pirates.yml reginald: name: Reginald the Pirate monkey: george ### in monkeys.yml george: name: George the Monkey pirate: reginald Pow! All is made clear. We reflect on the fixture's model class, finds all the +belongs_to+ associations, and allows you to specify a target *label* for the *association* (monkey: george) rather than a target *id* for the *FK* (monkey_id: 1). === has_and_belongs_to_many Time to give our monkey some fruit. ### in monkeys.yml george: id: 1 name: George the Monkey pirate_id: 1 ### in fruits.yml apple: id: 1 name: apple orange: id: 2 name: orange grape: id: 3 name: grape ### in fruits_monkeys.yml apple_george: fruit_id: 1 monkey_id: 1 orange_george: fruit_id: 2 monkey_id: 1 grape_george: fruit_id: 3 monkey_id: 1 Let's make the HABTM fixture go away. ### in monkeys.yml george: name: George the Monkey pirate: reginald fruits: apple, orange, grape ### in fruits.yml apple: name: apple orange: name: orange grape: name: grape Zap! No more fruits_monkeys.yml file. We've specified the list of fruits on George's fixture, but we could've just as easily specified a list of monkeys on each fruit. As with +belongs_to+, we reflect on the fixture's model class and discovers the +has_and_belongs_to_many+ associations. == Autofilled timestamp columns If your table/model specifies any of ActiveRecord's standard timestamp columns (created_at, created_on, updated_at, updated_on), they will automatically be set to Time.now. If you've set specific values, they'll be left alone. == Fixture label interpolation The label of the current fixture is always available as a column value: geeksomnia: name: Geeksomnia's Account subdomain: $LABEL Also, sometimes (like when porting older join table fixtures) you'll need to be able to get ahold of the identifier for a given label. ERB to the rescue: george_reginald: monkey_id: <%= Fixtures.identify(:reginald) %> pirate_id: <%= Fixtures.identify(:george) %> == Support for YAML defaults You probably already know how to use YAML to set and reuse defaults in your +database.yml+ file,. You can use the same technique in your fixtures: DEFAULTS: &DEFAULTS created_on: <%= 3.weeks.ago.to_s(:db) %> first: name: Smurf <<: *DEFAULTS second: name: Fraggle <<: *DEFAULTS Any fixture labeled "DEFAULTS" is safely ignored. == Contact John Barnette (jbarnette@gmail.com)