diff options
Diffstat (limited to 'hocon.html.markdown')
| -rw-r--r-- | hocon.html.markdown | 574 | 
1 files changed, 574 insertions, 0 deletions
| diff --git a/hocon.html.markdown b/hocon.html.markdown new file mode 100644 index 00000000..67ea30a2 --- /dev/null +++ b/hocon.html.markdown @@ -0,0 +1,574 @@ +--- +language: hocon +filename: learnhocon.conf +contributors: +- [TehBrian, 'https://tehbrian.xyz'] +--- + +Human-Optimized Configuration Object Notation, or HOCON, is a configuration and +data serialization format designed to be easily editable by humans. + +It's a superset of JSON, meaning that any valid JSON is valid HOCON, but it +differs in being much less pedantic and opinionated. With its flexible yet +easily determinable syntax, resulting configuration files are often much less +noisy than some other formats. + +Additionally, its support for comments makes it much better suited for +user-facing configurations than JSON. + +```hocon +// Comments can either look like this, +# or they can look like this. +// Anything after // or # is a comment. + +################## +### THE BASICS ### +################## + +# Everything in HOCON is either a key, a value, or a separator. +# : and = are separators. They separate the key from the value. +key: value +another_key = another_value + +# You can use either separator with or without whitespace on either side. +colon1:value +colon2: value +colon3 : value +equals1=value +equals2= value +equals3 = value +# As you'll see, HOCON is very nonrestrictive regarding its syntax style. + +# HOCON also isn't opinionated on how keys look. +THIS_IS_A_VALID_KEY: value +this-is-also-a-valid-key: value +keys can have spaces too: value +or even numbers like 12345: value +"you can even quote keys if you'd like!": value + +# A key, followed by any separator, and then finally a value, is called a field. +this_entire_line_is: a field + +################### +### VALUE TYPES ### +################### + +# The types that a value can be are string, number, object, array, boolean, and +# null. Every value type except for array and object are called simple values. + +## SIMPLE VALUES ## + +quoted_string: "I like quoting my strings." +unquoted_string: I don't like quoting my strings. +# Special characters that cannot be used in unquoted strings are: +# $ " { } [ ] : = , + # ` ^ ? ! @ * & +# Unquoted strings do not support any kind of escaping. If using one of those +# special characters is desired, use a quoted string. +multi-line_string: """ +  This entire thing is a string! +  One giant, multi-line string. +  You can put 'single' and "double" quotes without it being invalid. +""" + +number: 123 +negative: -123 +fraction: 3.1415926536 +scientific_notation: 1.2e6 // same as 1.2 * (10^6) + +boolean: true # or false +empty: null + +## ARRAYS ## + +# Arrays hold lists of values. +# Values in arrays can be separated with commas.. +array: [ 1, 2, 3, 4, 5 ] +fibonacci: [1,1,2,3,5,8,13] +multiples_of_5: [5, 10, 15, 20,] # Notice the trailing comma. That's okay here. +# or newlines.. +friends: [ +  "Brian" +  "Sophie" +  "Maya" +  "Sabina" +] +# or both! +ingredients: [ +  "Egg", +  "Sugar", +  "Oil", +  "Flour", # Notice the trailing comma. That's okay here too. +] +# Once again, HOCON has a very loose syntax. Use whichever style you prefer. +no newline before or after bracket: ["This" +  "is" +  "an" +  "array!"] + +# Just like any other value, arrays can hold other arrays. +array in array: [ [1, 2, 3], ["a", "b", "c"] ] +array in array in array: [ [ [1, 2], [8, 9] ], [ ["a", "b" ], ["y", "z"] ] ] + +## OBJECTS ## + +# Objects hold fields. +# Just like arrays, fields in objects can be separated with commas.. +object: { key: value, another_key: another_value } +server_connection: {ip: "127.0.0.1", port: 80} +first: {letter: a, number: 1,} # Notice the trailing comma. +# or newlines.. +power_grid: { +  max_capacity: 15000 +  current_power: 1200 +} +# or both! +food_colors: { +  carrot: orange, +  pear: green, +  apple: red, +  plum: purple, +  banana: yellow, # Trailing commas are okay here too! +} + +# Arrays can hold objects just like any other value type. +coworkers: [ +  { +    name: Jeff +    age: 27 +  }, +  { +    name: Henry +    age: 35 +  }, +  { +    name: Timmy +    age: 12 +  } +] + +# The field separator may be omitted if the key is followed by { +no_separator { +  key: value +  speed_of_light: very fast +  ten: 10 + +  # Objects can go inside other objects just like any other value. +  another_object { +    twenty: 20 +    speed_of_sound: also pretty fast +  } +} + +# In fact, the entirety of any HOCON document is an actually just an object. +# That object is called the root object. The only difference between it and any +# other object is that the curly brackets at the top and bottom of the document +# may be omitted. + +# This means that HOCON documents can be formatted in the same way that +# regular objects can be formatted, including separating fields with commas +# rather than with newlines. + +# Additionally, while the entirety of a HOCON document can be and is usually an +# object, it can also be an array. If it is an array, the opening and closing +# brackets at the top and bottom of the document must be explicitly written. + +###################### +### DUPLICATE KEYS ### +###################### + +is_happy: false +# If there is a duplicate key, the new value overrides the previous value. +is_happy: true +online_users: [Jacob, Mike] +# Same with arrays. +online_users: [Jacob, Mike, Henry] + +# For objects, it's a bit different. +my_car: { +  color: blue +  speed: 9001 +  passengers: null + +  engine: { +    running: true +    temperature: 137 +  } +} +# If there is a duplicate key and both values are objects, +# then the objects are merged. +my_car: { +  // These fields are added to the old, previous object. +  nickname: "My Favorite Car" +  type: 2-door sedan + +  // Since the value of this duplicate key is NOT an object, +  // it simply overrides the previous value. +  speed: 60 +  // Same with arrays. They override, not merge. +  passengers: ["Nate", "Ty"] + +  // This object is recursively merged with the other object. +  engine: { +    // These two fields are added to the previous object. +    type: gas +    oil_level: 10 +    // This field overrides the previous value. +    temperature: 179 +  } +} + +# Object merging is done two at a time. That is to say, the first two objects +# merge into one, then that object merges with the next object, and so on. + +# Because of this, if you set a field with an object value to a non-object value +# and then back to an object value, the new object will completely override any +# previous value. + +// Null, a non-object value, completely overrides the object value. +my_car: null + +// Then, this object completely overrides null. +my_car: { +  nickname: "My New Car" +  type: 4-door minivan +  color: gray +  speed: 90 +  passengers: ["Ayden", "Liz"] +} + +########################### +### VALUE CONCATENATION ### +########################### + +## SIMPLE VALUE CONCATENATION ## + +# Simple values (all value types except objects and arrays) separated by +# whitespace are concatenated into a single string. The whitespace between +# values is preserved. +number_concatenation: 1 2 3 12.5 -3 2e5 // same as: "1 2 3 12.5 -3 2e5" +boolean_concat: true false true // "true false true" +null_concat: null null null // "null null null" +mixed_concat: 1 true null // "1 true null" + +# String value concatenation can appear anywhere that a quoted string can. +number_concat_in_array: [1 2, 3 4, 5 6] // same as: ["1 2", "3 4", "5 6"] + +# In fact, unquoted strings are actually just string value concatenations. +unquoted_string_concat: his name is jeff // same as: "his name is jeff" + +# Going further, even keys that are unquoted strings are actually just string +# value concatenations. +this is a key: value // the KEY is the same as: "this is a key" +# The following field is identical to the field above. +"this is a key": value + +# Quoted strings can also be concatenated. This will be useful later, +# when we cover substitutions. +quoted_string_concat: "her"" name" "is ""jenna" // same as: "her name is jenna" +# Notice that the whitespace (or lack thereof) between values is preserved. + +## ARRAY CONCATENATION ## + +# Arrays separated by whitespace are merged into a single array. +array_concat: [1, 2, 3] [4, 5, 6] // same as: [1, 2, 3, 4, 5, 6] + +# Arrays cannot be concatenated with a non-array value. +//array_concat: true [false] results in an error +//array_concat: 1 [2] results in an error + +## OBJECT CONCATENATION ## + +# Objects separated by whitespace are merged into a single object. +# The merge functionality is identical to that of duplicate key object merging. +lamp: {on: true} {color: tan} // same as: {on: true, color: tan} + +# Similarly to arrays, objects cannot be concatenated with a non-object value. +//object_concat: true {on: false} results in an error +//object_concat: 1 {number: 2} results in an error + +######################## +### PATH EXPRESSIONS ### +######################## + +# Path expressions are used to write out a path through the object graph. +# Think of it as navigating through objects to a specific field. +# Each object to traverse through is called an element, and each element is +# separated with a period. + +country: { +  city: { +    neighborhood: { +      house: { +        name: "My House" +        address: 123 Example Dr. +      } +    } +  } +} +# For example, the path to the address of the house could be written as: +# country.city.neighborhood.house.address +# Country, city, neighborhood, house, and address are all elements. + +# Path expressions are used in two places: substitutions (which will be +# covered in a moment), and as keys. +# That's right: keys themselves can also be path expressions. +foo: { +  bar: { +    baz: { +      number: 12 +    } +  } +} +# Rather than tediously specifying each object, a path expression can be used. +# The following field represents the same object found above. +foo.bar.baz.number: 12 + +# Fields and objects specified with path expressions are merged in the same way +# that any object is usually merged. +foo.bar.baz.bool: true +// the object foo's value is: foo { bar { baz { number: 12, bool: true } } } + +##################### +### SUBSTITUTIONS ### +##################### + +# Substitutions refer to a specific value from some path expression. +# They're only allowed in values, not keys or nested inside other substitutions. + +me: { +  favorite_animal: parrots +  favorite_food: cookies +} +# The syntax for a substitution is either ${path_expression} or +# ${?path_expression}. The latter syntax will be discussed in a moment. +my_fav_animal: ${me.favorite_animal} +my_fav_food: ${me.favorite_food} + +# Substitutions are not parsed inside quoted strings. To get around this, +# either use an unquoted string or value concatenation. +animal_announcement: My favorite animal is ${my_fav_animal} +// the value is: My favorite animal is parrots +food_announcement: "My favorite food is "${my_fav_food}"!" +// the value is: "My favorite food is cookies!" + +# Substitutions are parsed last in the document. Because of this, you can +# reference a key that hasn't been defined yet. +color_announcement: "My favorite color is" ${my_fav_color}"!" +// the value is: "My favorite color is blue!" +my_fav_color: blue + +# Another effect of substitutions being parsed last is that substitutions will +# always use the latest, as in last, value assigned in the entire document, +# which includes merged objects. +color: green +their_favorite_color: ${color} // the value is: orange +color: orange + +random_object: { +  number: 12 +} +the_number: ${random_object.number} // the value is: 15 +random_object: { +  number: 15 +} + +############################### +### UNDEFINED SUBSTITUTIONS ### +############################### + +# A substitution using the ${path_expression} syntax with an undefined path +# expression, meaning a path expression that does not point to a defined value, +# is invalid and will therefore generate an error. +//${does.not.exist} will throw an error + +# However, an undefined substitution using the ${?path_expression} syntax +# has different behavior depending on what it is the value of. +request: { +  # If it is the value of a field, then the field will not be created. +  response: ${?does.not.exist} // this field won't be created and does not exist +  type: HTTP +} + +request: { +  # Additionally, if it would have overridden a previous value, then the +  # previous value remains unchanged. +  type: ${?does.not.exist} // request.type is still HTTP +} + +# If it is a value in an array, then it is simply not added. +values: [ 172, "Brian", ${?does.not.exist}, null, true, ] +// the value is: [ 172, "Brian", null, true ] + +# If it is part of simple value concatenation, it acts as an empty string. +final_string: "String One"${?does.not.exist}"String Two" +// the value is: "String OneString Two" + +# If it is part of array concatenation, it acts as an empty array. +final_array: [ 1, 2, 3 ] ${?does.not.exist} [ 7, 8, 9 ] +// the value is: [ 1, 2, 3, 7, 8, 9 ] + +# If it is part of object concatenation, it acts as an empty object. +final_array: { a: 1 } ${?does.not.exist} { c: 3 } +// the value is: { a: 1, c: 3 } + +###################################### +### SELF-REFERENTIAL SUBSTITUTIONS ### +###################################### + +# Substitutions normally "look forward" and use the final value defined in the +# document. However, in cases when this would create a cycle, the substitution +# looks only backwards. + +# A field which contains a substitution that points to itself or points to +# other fields that eventually point back to itself is called a +# self-referential field. +letters: "a b c" // the value is: "a b c" +letters: ${letters}" d" // "a b c d" +letters: ${letters}" e" // "a b c d e" + +PATH: [/bin] // the value is: [/bin] +PATH: ${PATH} [/usr/bin] // [/bin, /usr/bin] +PATH: ${PATH} [/usr/local/bin] // [/bin, /usr/bin, /usr/local/bin] + +x: "x" // the value is: "x" +y: ${x}"y" // "xy" +x: ${y}"z" // "xyz" + +########################## +### += FIELD SEPARATOR ### +########################## + +# In addition to : and =, there actually exists another separator: += +# A field separated with += acts as a self-referential array concatenation. +# In short, it appends an element to a previously defined array. + +a: [1] +b: [1] +# This field: +a += 2 // the value is: [1, 2] +# functions the same as: +b: ${?b} [2] // the value is: [1, 2] + +USERS: [/usr/luke] // the value is: [/usr/luke] +USERS += /usr/devon // [/usr/luke, /usr/devon] +USERS += /usr/michael // [/usr/luke, /usr/devon, /usr/michael] + +# Since += only appends elements to a previously existing array, if the previous +# value was not an array, an error will be generated. +OTHER_USERS: /usr/luke +//OTHER_USERS += /usr/devon results in an error + +# Notice that the underlying substitution syntax used is ${?path}, not ${path}. +# Recall that, using the ${?} syntax, an undefined substitution in array +# concatenation acts as an empty array. Because of this, it is perfectly +# acceptable if the field that is being set is initially undefined. +//z: [] not necessary +z += 3 // the value is: [3] +z += 4 // the value is: [3, 4] + +NEW_USERS += /usr/sandra // the value is: [/usr/sandra] +NEW_USERS += /usr/kennedy // [/usr/sandra, /usr/kennedy] +NEW_USERS += /usr/robin // [/usr/sandra, /usr/kennedy, /usr/robin] + +################ +### INCLUDES ### +################ + +# Includes allow you to "import" one HOCON document into another. + +# An include statement consists of the unquoted string "include" followed by +# whitespace and then a resource name, which is one of the following: +# - a single quoted string which is heuristically interpreted as a URL, +#   filename, or a Java classpath resource. +# - url(), file(), or classpath(), with the parentheses surrounding a quoted +#   string which is either a URL, filename, or classpath resource respectively. +# - required(), with the parentheses surrounding one of the above. +include "https://example.com/config.conf" +include "/foo/bar/config.conf" +include "config.conf" + +include url("https://example.com/config.conf") +include file("/foo/bar/config.conf") +include classpath("config.conf") + +# If the included file does not exist, it will be silently ignored and act as if +# it were an empty object. However, if it is wrapped around required(), then +# parsing will explicitly error if the file cannot be resolved. +//include required("doesnt_exist.conf") will error +//include required(url("https://example.com/doesnt_exist.conf")) will error +//include required(file("doesnt_exist.conf")) will error +//include required(classpath("doesnt_exist.conf")) will error + +# The file specified by the include statement is called the included file, and +# the file which contains the include statement is called the including file. + +# Including a file functions as if you directly replaced the include statement, +# wherever it may be, with the contents of the included file's root object. + +# An included file must have an object as its root value and not an array. +# If the included file has an array as its root value, then it is invalid and +# an error will be generated. + +# Pretend that the following is in a file called user_config.conf: +username: RandomUser1337 +auto_login: true +color_theme: dark +screensaver: { +  image: usr/images/screensaver.jpg +  turn_on_after: 1m +} + +# And then we include that file. +include file("user_config.conf") + +# We can now reference values from that file! +path_to_user_screensaver: ${screensaver.image} // +greeting: "Welcome, "${username}"!" // the value is: "Welcome, RandomUser1337!" + +# Duplicate keys override as they normally do. +status: "Auto Login: "${auto_login} // the value is: "Auto Login: true" +auto_login: false +status: "Auto Login: "${auto_login} // the value is: "Auto Login: false" + +# Object merging is also the same as usual. +screensaver: { +  // This gets added to the screensaver object. +  enable_during_day: false +  // This overrides the previous value. +  turn_on_after: 30s +} + +# Include statements can appear in place of a field. Anywhere that a field +# could appear, an include statement could appear as well. + +# Pretend that the following is in a file called server_settings.conf: +max_connections: 10 +url: example.com +port: 80 +admin_page: { +  username: admin +  password: pass12345 +} + +# And then we include that file nested inside another object. +websites: { +  my_epic_website: { +    include file("server_settings.conf") +  } +} + +# Now, we can reference the contents of server_settings.conf as if they +# had been written directly into the object my_epic_website. +server_port: ${websites.my_epic_website.port} + +the_password: "The password is: "${websites.my_epic_website.admin_page.password} +// the value is: The password is: pass12345 + +max_conn: "Max Connections: "${websites.my_epic_website.max_connections} +// the value is: Max Connections: 10 +``` + +### More Resources + ++ [Official HOCON Specification](https://github.com/lightbend/config/blob/master/HOCON.md) ++ [HOCON Playground](https://hocon-playground.herokuapp.com) | 
