View on GitHub

emersonprado.github.io

Start page
Português

Efficient Vagrantfiles – Part 3 – Code and data separation

Emerson Prado - 14/01/2020

In the second part of this series, we got rid of lots of repeated code (and open doors for typing mistakes) by using arrays, hashes, loops and conditionals. In this part, we'll take a break from Vagrantfiles and check out a file format that will be quite useful once we get back to them.

  1. TL;DR
  2. Problem - messing with code to each environment change
  3. Solution - each thing where it belongs
  4. Yaml
    1. Basics
    2. Nesting
    3. References
  5. Next step

TL;DR

If you already know about YAML files, you might skip this part of the articles, and get going with what I've summarized below.

Code and data mixed up in the Vagrantfile makes maintenance and versioning messy. We can make it easier and more organized by putting data in YAML files and load them into the Vagrantfile.

YAML files can store objects like strings, numbers, arrays and hashes. These last two can be nested. Some exaples to refresh your mind (read each one as a file):

  ---
  # Start with a line with 3 dashes, telling the OS it's an YAML file
  # It MUST be the FIRST line and THREE dashes. Exactly.
  # Hash signs indicate comments. Standalone or inline.
  # This file has a number
  3.14

  --- # Another file
  This file has this string

  --- # Array (items classes can vary)
  - One
  - -3.14
  - Blowin' in the Wind
  # Items MUST be prepended by ONE dash and ONE space. Exactly.

  --- # Hash (keys and values can vary)
  One: 1
  2: Two
  Multi-word key: Multi-word value
  "Key with colon: just quote it": Value
  # Keys MUST be prepended by NOTHING and appended by ONE colon. Exactly.
  # Vaues MUST be separated from keys by spaces.

  --- # Nesting from an array
  - # Outer array item 1 is middle array below
    - item_1_1
    - item_1_2
    - # More nesting - Middle array item 3 is inner array below
      - item_1_3_1
      - item_1_3_2
    - # Nesting hashes into arrays
      key_1_4_1: value_1_4_1
      key_1_4_2: value_1_4_2
  - # Outer array item 2
    ...

  --- # Nesting from a hash
  key_1: # Outer hash element 1 value is middle hash below
    key_1_1: value_1_1
    key_1_2: value_1_1
    key_1_3: # More nesting - Middle hash element 3 value is inner hash below
      key_1_3_1: value_1_3_1
      key_1_3_2: value_1_3_2
    key_1_4: # Nesting arrays into hashes
      - item_1_4_1
      - item_1_4_2
  key_2: # Outer hash element 2
    ...
  

For the Vagrantfile integration part, go to part 4 (coming soon).

Problem - messing with code for each environment change

Your environment will change. Period. You might have to test an application's new version, or debug something in it, or try it in more OS options, or update boxes, etc. That's one of the main reasons you're using Vagrant in first place. In the common Vagrant use, every change means editing your Vagrantfile. This means trouble because:

Solution - each thing where it belongs

If the problem is code and data mixed up, the solution is obvious: let's take them apart.

Since the Vagrantfile is intended to have code describing your environment, it's already the right place for code. So, the data should be somewhere else.

A good way to store data is, you bet, in files which format is intended to store data. Lucky we, there's one such format which is a close friend to Ruby.

Yaml

YAML is a structured file format which can store a Ruby object, which can be a single value, an array, a hash, or nested arrays/hashes, without much hassle (given you get familiar to the picky format).

So, placing data in YAML files makes it easy to load data into the Vagrantfile. Then, the Vagrantfile will be only code, and the YAML files, only data. Cheers!

Basics

The YAML file contents format depends primarily on the type of object it contains:

  1. Start with 3 dashes in the first line. This tells the OS it's an YAML file. Not quite required, but highly recommended.
  2. For a single value, just put it plainly in the additional line. It could be next to the dashes in the same line, but would look weird.
  3. For an array, put each item in a line, prepended by one dash and one space. I do mean one. Remember: YAML is picky!
  4. For a hash, put each key in the beginning of a line, followed by a colon, then space(s), and the value. I do mean the beginning, and also space(s) between the colon and the value. Remember: YAML is picky!
  5. Comments are indicated by a hash sign.

Examples (each one is a file)

  ---
  # A number
  3.14

  ---
  # A string
  Hello, world!

  ---
  # An array (items classes can vary)
  - One
  - -3.14
  - Blowin' in the Wind

  ---
  # A hash (keys and values classes can vary)
  One: 1
  2: Two
  Multi-word key: Multi-word value
  "Key with colon: just quote it": Value
  

Nesting

Just as arrays and hashes can be nested in Ruby objects, so can they in YAML files. Nesting is indicated by indentation, plus some special attention in "outer" objects:

  1. Start "outer" objects as normal, but omitting the contents - the array item or the hash value. That is:
    1. For an "outer" array, just put the dash
    2. For an "outer" hash, just put the key and the colon
  2. Indent "inner" objects with two spaces. I do mean two. Remember: YAML is picky!
  3. For additional nesting, do exactly the same, but indented with two additional spaces per depth level. I do mean two. Remember: YAML is picky!
  4. Never indent with tabs. Ever! Don't know if I already warned you, but YAML is picky!

Examples (each one is a file)

  ---
  # Nesting from an array
  - # Outer array item 1 is middle array below
    - item_1_1
    - item_1_2
    - # More nesting - Middle array item 3 is inner array below
      - item_3_1
      - item_3_2
    - # Nesting hashes into arrays
      key_1_4_1: value_1_4_1
      key_1_4_2: value_1_4_2
  - # Outer array item 2
    ...

  ---
  # Nesting from a hash
  key_1: # Outer hash element 1 value is middle hash below
    key_1_1: value_1_1
    key_1_2: value_1_1
    key_1_3: # More nesting - Middle hash element 3 value is inner hash below
      key_1_3_1: value_1_3_1
      key_1_3_2: value_1_3_2
    key_1_4: # Nesting arrays into hashes
      - item_1_4_1
      - item_1_4_2
  key_2: # Outer hash element 2
    ...

  ---
  # Is this trip really necessary?
  key_1:        # This element value is the array below
    -               # This item value is the hash below
      key_1_1_1:        # This element value is the array below
        - item_1_1_1_1
        - item_1_1_1_2
      key_1_1_2:
        - item_1_1_2_1
        - item_1_1_2_2
    -
      key_1_2_1: ...
  key_2:
    ...
  

References

For real YAML learning, check out:

Next step

Now, all we have to do is to take the huge hash from part 2, move it to an YAML file, then load the object in the Vagrantfile. On to part 4 (coming soon)!