States / Aliases

Sometimes, lots and lots of state declarations with many different state definitions can clog up the component code and make it harder to understand and reason about all the different states a component might be in. Aliases help manage that: you can assign certain state definitions an alias and later use that alias in multiple state declarations.

Aliases must go above all other state manager declarations and don't require a special marker, but you can still put "alias" or "aliases" array element before defining them - for readability:

this.states = [
  "aliases", {
    // define your aliases inside this object
  },
  // ... other state manager declarations
];

Alias definitions follow pretty much the same rules as state definitions. Alias definition object keys are alias names, and values are state definitions that will replace your aliases when they're used later on in actual state declarations. Let's try and write something using state aliases:

this.states = [
  "aliases", {
    can_drink: { country: "United States", age: { more_than: 20 }},
  }
  "display", {},
  ["can_drink", "can_buy_alcohol"]
];

Here we defined an alias for a state that basically says "You're over 21 and from the United States". We then used this alias to display a special message to our site visitors that they can buy alcohol (the message, evidently, is contained inside the "can_buy_alcohol" entity). However, this only covers the United States, but what about other countries, that might have different policies? We can use the technique demonstrated earlier and define an alias with an "OR statement" by using an array of state definitions:

this.states = [
  "aliases", {
    "Can drink": [
      { country: "United States", age: { more_than: 20 }},
      { country: { not: "United States" }, age: { more_than: 17 }}
    ]
  }
  "display", {},
  ["Can drink", "can_buy_alcohol"]
];

Notice that we didn't have to change anything in the corresponding declaration for our DisplayStateManager - the same alias is being used there. But the alias definition itself has now changed to account for other countries and their drinking age policies.

Combining aliases

The true power of aliases is in the ability to combine them reducing unnecessary repetition of declarations and increasing code reuse. To combine two or more aliases, we just need to use the + (plus) character. Whitespace around that +character is ignored, so don't worry about writing it a bit nicer with spaces). Here's how you do it:

this.states = [
  "aliases", {
    "Can drink":      { country: "United States", age: { more_than: 20 }},
    "Can smoke weed": { states: ["CA", "WA", "CO"]}
  }
  "display", {},
  ["Can smoke weed + Can drink", "can_have_fun"]
];

The resulting state definition will, internally, look like this:
{ country: "United States", age: { more_than: 20 }, states: ["CA", "WA", "CO"] }

So this is nice - we've got ourselves stricter rules for cases when you want to smoke weed AND drink. That's useful, but what happens if instead of one state definition per alias we have a set of state definitions (our logical OR) such as here:
this.states = [
  "aliases", {
    "Can legally have sex": [
      { country: "United States", age: { more_than: 17 }, is_hamster: false },
      { country: "Dictatorstan",  age: { more_than: 15 }, married: true     }
    ],
    "Can smoke weed":
      { country: "United States", states: ["CA", "WA", "CO"], age: { more_than: 20 }}
  }
  "display", {},
  ["Can smoke weed + Can legally have sex", "party_time"]
];

The declaration on line 11 would expand the combined aliases into the following state definition:
[
  { country: "United States", states: ["CA", "WA", "CO"], age: { more_than: 20 }, is_hamster: false },
  { country: "Dictatorstan",  states: ["CA", "WA", "CO"], age: { more_than: 15 }, married: true }
]

We've got ourselves in a pretty weird situation, haven't we? What this says is that you can party in Dictatorstan, but only if you're married and if Dictatorstan has any of the three of the listed American states and you're living in one of them - which is never going to be that case! It also says that you can party in the United States, but you can't be a hamster. Apparently hamsters can smoke weed, but sex is off limits. So what happened here?

The alias combining mechanism took the first alias state definition - "Can smoke weed" and then merged it with each of the state definitions in "Can legally have sex", which resulted in an array of two elements presented above. Because "Can legally have sex" followed second it had the priority to replace values for the attributes of the same name it encountered in the "Can smoke weed" definition. You can also tell that the second state definition in the resulting array will never actually match because Dictatorstan doesn't have american states. Which is fine, but also highlights how combining aliases can be abused and lead to unexpected results. Let's see what happens of we reverse the order in which aliases were mixed:
this.states = [
  // ...
  ["Can legally have sex + Can smoke weed", "party_time"]
];

This alias combination will result in the following set of state definitions:
[
  { country: "United States",  states: ["CA", "WA", "CO"], age: { more_than: 20 }, is_hamster: false },
  { country: "United States",  states: ["CA", "WA", "CO"], age: { more_than: 20 }, married: true }
]

Now it looks like you can only party in the United States (but not in Dictatorstan!), however if you're a hamster, you are now allowed to do it - granted that you're married. Moral of the story: combining aliases is powerful, but do it responsibly!