518
votes

I am trying to write a YAML dictionary for internationalisation of a Rails project. I am a little confused though, as in some files I see strings in double-quotes and in some without. A few points to consider:

  • example 1 - all strings use double quotes;
  • example 2 - no strings (except the last two) use quotes;
  • the YAML cookbook says: Enclosing strings in double quotes allows you to use escaping to represent ASCII and Unicode characters. Does this mean I need to use double quotes only when I want to escape some characters? If yes - why do they use double quotes everywhere in the first example - only for the sake of unity / stylistic reasons?
  • the last two lines of example 2 use ! - the non-specific tag, while the last two lines of the first example don't - and they both work.

My question is: what are the rules for using the different types of quotes in YAML?

Could it be said that:

  • in general, you don't need quotes;
  • if you want to escape characters use double quotes;
  • use ! with single quotes, when... ?!?
4
Second link is not working anymore, I suggest to put your examples into the question.heroin

4 Answers

712
votes

After a brief review of the YAML cookbook cited in the question and some testing, here's my interpretation:

  • In general, you don't need quotes.
  • Use quotes to force a string, e.g. if your key or value is 10 but you want it to return a String and not a Fixnum, write '10' or "10".
  • Use quotes if your value includes special characters, (e.g. :, {, }, [, ], ,, &, *, #, ?, |, -, <, >, =, !, %, @, \).
  • Single quotes let you put almost any character in your string, and won't try to parse escape codes. '\n' would be returned as the string \n.
  • Double quotes parse escape codes. "\n" would be returned as a line feed character.
  • The exclamation mark introduces a method, e.g. !ruby/sym to return a Ruby symbol.

Seems to me that the best approach would be to not use quotes unless you have to, and then to use single quotes unless you specifically want to process escape codes.

Update

"Yes" and "No" should be enclosed in quotes (single or double) or else they will be interpreted as TrueClass and FalseClass values:

en:
  yesno:
    'yes': 'Yes'
    'no': 'No'
6
votes

While Mark's answer nicely summarizes when the quotes are needed according to the YAML language rules, I think what many of the developers/administrators are asking themselves, when working with strings in YAML, is "what should be my rule of thumb for handling the stings?"

It may sound subjective, but the number of rules you have to remember, if you want to use the quotes only when they are really needed as per the language spec, is somewhat excessive for such a simple thing as specifying one of the most common datatypes. Don't get me wrong, you will eventually remember them when working with YAML regularly, but what if you use it occasionally, and you didn't develop automatism for writing YAML? Do you really want to spend time remembering all the rules just to specify the string correctly?

The whole point of the "rule of thumb" is to save the cognitive resource and to handle a common task without thinking about it. Our "CPU" time can arguably be used for something more useful then handling the strings correctly.

From this - pure practical - perspective, I think the best rule of thumb is to single quote the strings. The rationale behind it:

  • Single quoted strings work for all scenarios, except when you need to use escape sequences.
  • The only special character you have to handle within single-quoted string is the single quote itself.

These are just 2 rules to remember for some occasional YAML user, minimizing the cognitive effort.

5
votes

The answer marked as correct one, is massively misleading. (Although it explains the yaml one encounters in the wild)

Strings in yaml only need quotation if (the beginning of) the value can be misinterpreted as a data type or the value contains a ":" (because it could get misinterpreted as key).

For example

foo: '{{ bar }}'

needs quotes, because it can be misinterpreted as datatype dict, but

foo: barbaz{{ bam }}

does not, since it does not begin with a critical char. Next,

foo: '123'

needs quotes, because it can be misinterpreted as datatype int, but

foo: bar1baz234
bar: 123baz

Does not, because it can not be misinterpreted as int

foo: 'yes'

needs quotes, because it can be misinterpreted as datatype bool

foo: "bar:baz:bam"

needs quotes, because the value can be misinterpreted as key.

These are just examples. Using yamllint helps avoiding to start values with a wrong token

foo@bar:/tmp$ yamllint test.yaml 
test.yaml
  3:4       error    syntax error: found character '@' that cannot start any token (syntax)

and is a must, if working productively with yaml.

Quoting all strings as some suggest, is like using brackets in python. It is bad practice, harms readability and throws away the beautiful feature of not having to quote strings. Please read the docs, before spreading fake knowledge.

2
votes

I had this concern when working on a Rails application with Docker.

My most preferred approach is to generally not use quotes. This includes not using quotes for:

  • variables like ${RAILS_ENV}
  • values separated by a colon (:) like postgres-log:/var/log/postgresql
  • other strings values

I, however, use double-quotes for integer values that need to be converted to strings like:

  • docker-compose version like version: "3.8"
  • port numbers like "8080:8080"
  • image "traefik:v2.2.1"

However, for special cases like booleans, floats, integers, and other cases, where using double-quotes for the entry values could be interpreted as strings, please do not use double-quotes.

Here's a sample docker-compose.yml file to explain this concept:

version: "3"

services:
  traefik:
    image: "traefik:v2.2.1"
    command:
      - --api.insecure=true # Don't do that in production
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

That's all.

I hope this helps