Multiple YAML defaults in Rails fixtures

You know that you can use YAML defaults in your fixtures to extract common attributes values and make your fixtures more readable.

DEFAULTS: &DEFAULTS
  created_on: <%= 3.weeks.ago.to_s(:db) %>

first:
  name: Smurf
  <<: *DEFAULTS

second:
  name: Fraggle
  <<: *DEFAULTS

What if you want to use more than one set of defaults?

The old way - DEFAULTS

The Rails example is a bit confusing because it uses "DEFAULTS" for both the label and the anchor/alias, but here's how to handle multiple defaults:

DEFAULTS: &decisions
  icon: 🧠
  name: Decisions

DEFAULTS: &help
  icon: πŸ‘‹
  name: Help

founders_decisions:
  team: founders
  <<: *decisions

founders_help_requests:
  team: founders
  <<: *help

To understand how this works, you need to know two things.

The Rails docs tell us that it doesn't matter how many "DEFAULTS" label we have: they won't overwrite themselves even though they use the same label.

Any fixture labeled β€œDEFAULTS” is safely ignored

The YAML spec explains how aliasing works

Repeated nodes (objects) are first identified by an anchor (marked with the ampersand - β€œ&”), and are then aliased (referenced with an asterisk - β€œ*”) thereafter.

Edge Rails way - fixture:ignore

There's another way to do that on Edge Rails. You can specify fixtures that should be ignored and that you can therefore use as defaults.

We can rewrite the example above like this:

_fixture:
  ignore:
    - decisions
    - help

decisions: &decisions
  icon: 🧠
  name: Decisions

help: &help
  icon: πŸ‘‹
  name: Help

founders_decisions:
  team: founders
  <<: *decisions

founders_help_requests:
  team: founders
  <<: *help

Which one should I use?

If you're using the Rails 6.0.3.2 or below, you don't have a choice anyway. But even then, while the edge option is interesting because the anchor/alias mirrors the label, I'll stick with the "old way" for two reasons:

  1. it's less code (you don't have to write the _fixture block)
  2. the DEFAULTS label is easy to spot and explicit. You could use DECISION_DEFAULTS: &decisions instead of decisions: &decisions but the label isn't really useful and, again, you're writing more code than if it's just DEFAULTS: &decisions