realestate-com-au/soya

语言: Ruby

git: https://github.com/realestate-com-au/soya

YAML / JSON文件工具包
YAML/JSON file toolkit
README.md (中文)

黄豆

YAML / JSON文件工具包

介绍

Soya是一个旨在以灵活的方式使用YAML和JSON文件的程序,我希望它对配置有用 管理。

执照

版权所有(c)2015 REA-Group

特此授予任何获得者的免费许可 该软件及相关文档文件的副本( “软件”),无限制地处理软件,包括 无限制使用,复制,修改,合并,发布的权利, 分发,再许可和/或出售本软件的副本,以及 允许软件所在的人员这样做,但须遵守 以下条件:

上述版权声明和本许可声明应为 包含在本软件的所有副本或实质部分中。

该软件“按原样”提供,不提供任何形式的担保, 明示或暗示,包括但不限于保证 适销性,适用于特定用途和 不侵权。在任何情况下,作者或版权持有人都不应该 对于任何索赔,损害或其他责任均不承担任何责任 合同,侵权或其他方式,由此产生或与之相关的 使用软件或软件中的使用或其他交易。

系统要求

  • Ruby 1.9.3(或更高版本)。请参阅:https://www.ruby-lang.org
  • Rubygem safe_yaml 1.0.4(或更高版本)。请参阅:https://github.com/dtao/safe_yaml

执照

此代码的版权归MIT许可证所有。

安装

$ gem install safe_yaml -v 1.0.4
$ gem install soya
$ soya -h

教程与示例

如何查看使用信息?

$ soya -h
Usage: soya [OPTION]... [FILE]...

    -C, --canonical                  Output in a canonical order.
    -s, --[no-]strict                Strict mode (error on duplicate keys)
    -f, --from=format                Input/from file format [json, yaml (default)]
    -t, --to=format                  Output/to file format [json, yaml (default)]

    -D, --define=PATH=EXPRESSION ... Define an element
    -c, --copy=DEST=SRC ...          Copy a source subtree path to a destination path
    -e, --extract=PATH               Output the subtree under PATH
    -d, --delete=PATH ...            Deletes the subtree under PATH
    -i, --insert=PATH                Insert the entire tree under PATH

    -v, --verbose                    Verbose mode
    -V, --version                    Display version
    -h, --help                       Display help

Processing order: merge, definition, copying, extraction, deletion, insertion
PATH is a dot-delimited list of keys.

$ soya -V
soya 0.9.2

我们来创建一个示例文件

$ echo '{"brand":"Hyundai","model":"i30","year":2014,"engine":{"type":"Nu","capacity":1.8},"options":{"camera":true,"gps":false},"service_history":[{"datetime":"2014-07-10T03:12:51Z","cost":0.0,"odometer":2952},{"datetime":"2015-04-21T23:41:03Z","cost":199.95,"odometer":10937}]}' > car.json

JSON到YAML的转换

从标准输入读取。

$ cat car.json | soya -f json -t yaml | tee car.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

YAML转JSON转换

从文件名读取

$ soya -f yaml -t json car.yaml
{
  "brand": "Hyundai",
  "model": "i30",
  "year": 2014,
  "engine": {
    "type": "Nu",
    "capacity": 1.8
  },
  "options": {
    "camera": true,
    "gps": false
  },
  "service_history": [
    {
      "datetime": "2014-07-10T03:12:51Z",
      "cost": 0.0,
      "odometer": 2952
    },
    {
      "datetime": "2015-04-21T23:41:03Z",
      "cost": 199.95,
      "odometer": 10937
    }
  ]
}

YAML是默认的输入和输出格式。因此,“-f”和“-t”都是可选的。此外,因为YAML-1.2是一个 严格的JSON超集(参见:https://en.wikipedia.org/wiki/JSON#YAML),“ - f”仅用于你想要明确或你的 想要确保无法加载YAML文件,因为它应该是一个JSON文件。

合并多个文件

你知道吗。我想将汽车细节(不可变)与汽车的服务历史(定期更改)分开。 让我们分开它以便于维护。

$ cat car_base.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
$ cat car_service_history.yaml
---
brand: test brand remove before commit
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

但是在使用它时,我想将它合并回来:

$ soya car_base.yaml car_service_history.yaml
---
brand: test brand remove before commit
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

哎呀,我在上面的例子中犯了一个错误。我两次加入“品牌”钥匙。请注意,合并发生在同一个 命令加载文件。

合并多个文件(严格模式)

在存在冲突键的情况下,我可能想要一个错误(以及非零返回码)而不是覆盖 默默。

$ cat car_service_history.yaml | soya -s car_base.yaml -
soya: duplicate key: brand
$ echo $?
1
$ grep -v '^brand: ' car_service_history.yaml | soya -s car_base.yaml -
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

该程序还遵循标准的Unix约定,例如:

  • 标准输入的输入(默认情况下);常规输出到标准输出和警告/错误到标准错误。
  • 返回代码在成功时为零,在错误时为非零。
  • 如果提供了单个输入文件名(或单个定义表达式),则程序将不会尝试从标准输入读取。
  • “ - ”(双击)选项会停止进一步的命令行参数处理(允许您使用以破折号开头的文件名)。
  • 文件名“ - ”(破折号)表示从标准输入读取。
  • 如果你真的想用“ - ”作为文件名,那么要么先用“./”或文件名的绝对路径。
  • 您可以根据需要指定任意数量的输入文件名(取决于操作系统限制)。

这是一个基本的例子,其中“大豆”的行为就像“猫”。

$ cat car.yaml | soya
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

## See doc directory for the sample files
$ cat third.yaml | soya first.yaml second.yaml - fourth.yaml fifth.yaml
---
first: 1
common: 5
second: 2
third: 3
fourth: 4
fifth: 5

册封

虽然你永远不应该依赖于哈希(map / associative-array)中元素的排序。有时它会有所帮助 为了比较多次运行,使哈希以确定性顺序出现。大豆支持分类 哈希键。在将来(以及大豆版本之间),排序顺序可能会发生变化,但它应始终保持确定性 特定大豆版。

$ soya car.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

$ soya -C car.yaml
---
brand: Hyundai
engine:
  capacity: 1.8
  type: Nu
model: i30
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937
year: 2014

定义

我还有一个GPS和婴儿座椅,我可以按需安装/卸载,所以我真的不想把它放到一个文件中。 ID 而是在命令行上设置它(以及一堆其他定义)。 (为什么是的,我给我的汽车数字类似的名称/版本,不 大家?)

$ soya -Doptions.gps=true -Doptions.baby_seat=2 '-Dname="123"' -Dversion="'1.2'" -Dodometer=1.36312e4 '-Dcups=[1,"two"]'
---
options:
  gps: true
  baby_seat: 2
name: '123'
version: '1.2'
odometer: 13631.2
cups:
- 1
- two

请注意,“true”值和数字是正确自动检测的(同样适用于“null”和“false”)。字符串值可以 引用(使用单引号或双引号)强制将其解释为字符串。只要确保你的报价没有 由命令shell使用(例如“bash”)。另请注意,浮点数可以使用科学计数法定义。

您还可以拥有多个定义,它们可以包含类似JSON语法的数组/对象。尽管如此,目前还有 表达式语法的局限性。但如果你想要一个可以涵盖所有角落案例的解决方案,那么你最好不要写作 原始JSON。将此视为90%的解决方案,具有更高的可读性。

仿形

您还可以将整个子树复制到另一个路径。在下面的情况中,“选项”子树被复制到“extra.addons”。注意 随后对“选项”的修改不会影响“extra.addons”。您还可以拥有多个复制选项。

$ soya -c extra.addons=options car.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937
extra:
  addons:
    camera: true
    gps: false

萃取

如果您只对部分输出(甚至单个值)感兴趣,则可以只输出该子树/值。你也可以 提取数组的元素(索引从零开始)。请注意,这个(以及更多)可以通过非常酷的工具(更好)完成 “jq”(见:https://stedolan.github.io/jq/)。我也被告知JMESPath(参见:http://jmespath.org/)很酷 我自己从未使用过它。

$ soya -e engine car.yaml
---
type: Nu
capacity: 1.8
$ soya -t json -e service_history.[0] car.yaml
{
  "datetime": "2014-07-10T03:12:51Z",
  "cost": 0.0,
  "odometer": 2952
}
$ soya -t json -e service_history.[1].cost car.yaml
199.95

插入

您还可以将输出移动到特定路径下方。

$ soya -i lee.alice car.yaml
---
lee:
  alice:
    brand: Hyundai
    model: i30
    year: 2014
    engine:
      type: Nu
      capacity: 1.8
    options:
      camera: true
      gps: false
    service_history:
    - datetime: '2014-07-10T03:12:51Z'
      cost: 0.0
      odometer: 2952
    - datetime: '2015-04-21T23:41:03Z'
      cost: 199.95
      odometer: 10937

删除

您还可以删除特定路径下的子树。与提取一样,也支持数组。你也可以有多个 删除选项。

$ soya -dservice_history car.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false

汉堡 - 用最大量

上述技术相互独立地互操作。唯一的主要信息是评估顺序:

  1. 文件按提供的顺序加载并合并。
  2. 评估定义。
  3. 执行复制。
  4. 提取发生。
  5. 随后删除任何内容。
  6. 插入发生在最后。

让我从汽车世界转移到配置MailPile的虚构示例(请参阅:https://www.mailpile.is/) 使用Jenkins(请参阅:https://www.jenkins-ci.org/)到数据中心时使用名为shipper的虚构工具进行部署。看到 “doc”目录下的示例文件。是的,这个例子只是有点人为,因此不必要地复杂化。 这就是汉堡与众多的意思 - bonappétit。

$ BUILD_NUMBER=123
$ env=production
$ epoch=1
$ soya -c "tmp=datacentre.${env}" -e "tmp" -i datacentre "all_datacentre_config.yaml" > "${env}.yaml"
$ soya -i "application.mailpile.environment" "mailpile_config_${env}.yaml" > mailpile_config.yaml
$ soya -s "${env}.yaml" mailpile.yaml mailpile_config.yaml > shipper.yaml
$ soya -f yaml -t json -Dapplication.mailpile.version="'${epoch}.${BUILD_NUMBER}'" shipper.yaml > shipper.json
$ shipper -c shipper.json deploy

这个工具是在考虑配置管理的情况下编写的。它旨在鼓励类似Unix的管道处理配置 文件和模板。我没有就如何做到这一点提供任何最佳实践。也就是说,我提供了机制 没有规定(甚至支持)任何政策(参见:https://en.wikipedia.org/wiki/Separation_of_mechanism_and_policy)。

历史与哲学

我最初想编写一个在YAML和JSON之间转换的程序。这是因为我希望使用YAML作为我的 AWS-CloudFormation模板语言(由于YAML支持评论),但我仍然希望能够轻松使用JSON 来自在线搜索结果的模板。

随着时间的推移,问题空间扩大了。我应该将它分成多个程序,毕竟这是Unix方式。但 管理六个脚本似乎是一个痛苦的后遗症。并且各种功能似乎是对每个功能的补充 其他相当不错。

复制功能的背景是我希望利用YAML规范中的锚点/引用。 但由于缺乏JSON支持,我将其作为复制动作。这是该计划中的一种模式。虽然它支持两者 JSON和YAML,它在决定什么是有效/无效时从根本上迎合最低公分母(即JSON)(例如 数据类型)。毫无疑问,我应该有一个程序可以在JSON / YAML和不同的程序之间进行转换。 做其他一切(仅限YAML)。但是这个当前的程序现在很好地解决了我的问题,尽管我接受了巧克力 贿赂。

YAML是一个复杂的规范。而且我很确定这个程序更加模糊不清。那就是说,我怀疑 我已经涵盖了99%的常见用例。本着“更糟,更好”的精神(见: https://www.dreamsongs.com/RiseOfWorseIsBetter.html),我想我现在可以解决那些未解决的问题。

本文使用googletrans自动翻译,仅供参考, 原文来自github.com

en_README.md

Soya

YAML/JSON file toolkit

Introduction

Soya is a program designed to work with YAML and JSON files in a flexible manner that I hope will be useful for configuration
management.

License

Copyright (c) 2015 REA-Group

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

System Requirements

License

This code is copyrighted under the MIT license.

Installation

$ gem install safe_yaml -v 1.0.4
$ gem install soya
$ soya -h

Tutorial with examples

How do I view the usage information?

$ soya -h
Usage: soya [OPTION]... [FILE]...

    -C, --canonical                  Output in a canonical order.
    -s, --[no-]strict                Strict mode (error on duplicate keys)
    -f, --from=format                Input/from file format [json, yaml (default)]
    -t, --to=format                  Output/to file format [json, yaml (default)]

    -D, --define=PATH=EXPRESSION ... Define an element
    -c, --copy=DEST=SRC ...          Copy a source subtree path to a destination path
    -e, --extract=PATH               Output the subtree under PATH
    -d, --delete=PATH ...            Deletes the subtree under PATH
    -i, --insert=PATH                Insert the entire tree under PATH

    -v, --verbose                    Verbose mode
    -V, --version                    Display version
    -h, --help                       Display help

Processing order: merge, definition, copying, extraction, deletion, insertion
PATH is a dot-delimited list of keys.

$ soya -V
soya 0.9.2

Let's create a sample file

$ echo '{"brand":"Hyundai","model":"i30","year":2014,"engine":{"type":"Nu","capacity":1.8},"options":{"camera":true,"gps":false},"service_history":[{"datetime":"2014-07-10T03:12:51Z","cost":0.0,"odometer":2952},{"datetime":"2015-04-21T23:41:03Z","cost":199.95,"odometer":10937}]}' > car.json

JSON to YAML conversion

Reading from standard input.

$ cat car.json | soya -f json -t yaml | tee car.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

YAML to JSON conversion

Reading from filename

$ soya -f yaml -t json car.yaml
{
  "brand": "Hyundai",
  "model": "i30",
  "year": 2014,
  "engine": {
    "type": "Nu",
    "capacity": 1.8
  },
  "options": {
    "camera": true,
    "gps": false
  },
  "service_history": [
    {
      "datetime": "2014-07-10T03:12:51Z",
      "cost": 0.0,
      "odometer": 2952
    },
    {
      "datetime": "2015-04-21T23:41:03Z",
      "cost": 199.95,
      "odometer": 10937
    }
  ]
}

YAML is the default input and output format. Therefore, both "-f" and "-t" are optional. Furthermore, because YAML-1.2 is a
strict superset of JSON (See: https://en.wikipedia.org/wiki/JSON#YAML), "-f" is only used if you want to be explicit or you
want to ensure that a YAML file cannot be loaded because it's supposed to be a JSON file.

Merging multiple files

You know what. I'd like to separate the car details (which is immutable) from the car's service history (which changes regularly).
Let's split it for ease of maintainance.

$ cat car_base.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
$ cat car_service_history.yaml
---
brand: test brand remove before commit
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

But when using it, I want to merge it back:

$ soya car_base.yaml car_service_history.yaml
---
brand: test brand remove before commit
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

Whoops, I made a mistake in the above example. I included the "brand" key twice. Notice that the merging occurs in the same
order that the files are loaded.

Merging multiple files (strict mode)

In the circumstance that there are clashing keys, I might want an error (along with a non-zero return code) instead of overwriting
silently.

$ cat car_service_history.yaml | soya -s car_base.yaml -
soya: duplicate key: brand
$ echo $?
1
$ grep -v '^brand: ' car_service_history.yaml | soya -s car_base.yaml -
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

This program also follows standard Unix conventions such as:

  • Input from standard-input (by default); regular-output to standard-output and warnings/errors to standard-error.
  • Return code is zero on success and non-zero on errors.
  • If even a single input filename (or a single definition expression) is provided, then the program won't try to read from standard-input.
  • An option of "--" (double-dash) stops further command-line argument processing (which lets you use filenames that start with a dash).
  • The filename of "-" (dash) means read from standard-input.
  • If you really want to use "-" as a filename, then either prepend it with "./" or the filename's absolute path.
  • You can specify as many input filenames as you want (dependent on OS limits).

Here's a basic example where "soya" acts like "cat".

$ cat car.yaml | soya
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

## See doc directory for the sample files
$ cat third.yaml | soya first.yaml second.yaml - fourth.yaml fifth.yaml
---
first: 1
common: 5
second: 2
third: 3
fourth: 4
fifth: 5

Canonisation

While you should never depend on the ordering of elements within a hash (map/associative-array). There are times where it's helpful
to have the hash come out in a deterministic ordering for the purposes of comparing multiple runs. Soya supports the sorting of
hash keys. In the future (and between soya versions), the sort order might change, but it should always remain deterministic for a
specific soya version.

$ soya car.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937

$ soya -C car.yaml
---
brand: Hyundai
engine:
  capacity: 1.8
  type: Nu
model: i30
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937
year: 2014

Definitions

I've also got a GPS and baby-seats which I can install/uninstall on-demand, so I don't really want to put it into a file. I'd
rather set it (and a bunch of other definitions) on the command-line. (Why yes, I give my cars numeric-like names/versions, doesn't
everyone?)

$ soya -Doptions.gps=true -Doptions.baby_seat=2 '-Dname="123"' -Dversion="'1.2'" -Dodometer=1.36312e4 '-Dcups=[1,"two"]'
---
options:
  gps: true
  baby_seat: 2
name: '123'
version: '1.2'
odometer: 13631.2
cups:
- 1
- two

Note that the "true" value and number are correctly auto-detected (likewise for "null" and "false"). And string values can
be quoted (using either single or double quotes) to force it to be interpreted as a string. Just make sure that your quotes aren't
consumed by your command-shell (eg "bash"). Also note that floating-point numbers can be defined using scientific notation.

You can also have multiple definitions and they can contain arrays/objects in JSON-like syntax. Nonetheless, there are currently
limitations of the expression-syntax. But if you want a solution that can cover all corner cases, then you're better off writing
raw JSON. Think of this as a 90% solution that is significantly more readable.

Copying

You can also copy an entire subtree to another path. In the case below, the "options" subtree is copied to "extra.addons". Note
that subsequent modifications to "options" will not affect "extra.addons". You can also have multiple copy options.

$ soya -c extra.addons=options car.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false
service_history:
- datetime: '2014-07-10T03:12:51Z'
  cost: 0.0
  odometer: 2952
- datetime: '2015-04-21T23:41:03Z'
  cost: 199.95
  odometer: 10937
extra:
  addons:
    camera: true
    gps: false

Extraction

If you're only interested in part of the output (or even a single value), you can output just that subtree/value. You can also
extract an element of an array (indices begin from zero). Note that this (and much more) can be (better) done by the very cool tool
"jq" (see: https://stedolan.github.io/jq/). I've also been told that JMESPath (see: http://jmespath.org/) is cool, though
I've never used it myself.

$ soya -e engine car.yaml
---
type: Nu
capacity: 1.8
$ soya -t json -e service_history.[0] car.yaml
{
  "datetime": "2014-07-10T03:12:51Z",
  "cost": 0.0,
  "odometer": 2952
}
$ soya -t json -e service_history.[1].cost car.yaml
199.95

Insertion

You can also move the output to beneath a specific path.

$ soya -i lee.alice car.yaml
---
lee:
  alice:
    brand: Hyundai
    model: i30
    year: 2014
    engine:
      type: Nu
      capacity: 1.8
    options:
      camera: true
      gps: false
    service_history:
    - datetime: '2014-07-10T03:12:51Z'
      cost: 0.0
      odometer: 2952
    - datetime: '2015-04-21T23:41:03Z'
      cost: 199.95
      odometer: 10937

Deletion

You can also remove the subtree beneath a specific path. Like extraction, arrays are also supported. You can also have multiple
delete options.

$ soya -dservice_history car.yaml
---
brand: Hyundai
model: i30
year: 2014
engine:
  type: Nu
  capacity: 1.8
options:
  camera: true
  gps: false

Burger-with-the-lot

The above techniques interoperate pretty independently. The only major piece of information is the order of evaluation:

  1. Files are loaded in the order provided and merged.
  2. Definitions are evaluated.
  3. Copying is performed.
  4. Extraction occurs.
  5. Followed by any deletion.
  6. With insertion occurring last.

Let me move from the world of cars onto a fictional example of configuring MailPile (see: https://www.mailpile.is/) for
deployment using a fictional tool called shipper when using Jenkins (see: https://www.jenkins-ci.org/) into a data-centre. See
sample files under the "doc" directory. Yes, this example is just a tad contrived and is therefore unnecessarily complicated.
That's what a Burger-with-the-Lot means -- bon appétit.

$ BUILD_NUMBER=123
$ env=production
$ epoch=1
$ soya -c "tmp=datacentre.${env}" -e "tmp" -i datacentre "all_datacentre_config.yaml" > "${env}.yaml"
$ soya -i "application.mailpile.environment" "mailpile_config_${env}.yaml" > mailpile_config.yaml
$ soya -s "${env}.yaml" mailpile.yaml mailpile_config.yaml > shipper.yaml
$ soya -f yaml -t json -Dapplication.mailpile.version="'${epoch}.${BUILD_NUMBER}'" shipper.yaml > shipper.json
$ shipper -c shipper.json deploy

This tool was written with configuration management in mind. It aims to encourage the Unix-like pipe-processing of configuration
files and templates. I've refrain from providing any best-practice on how this should be done. That is, I've provided the mechanism
without dictating (or even favouring) any policy (see: https://en.wikipedia.org/wiki/Separation_of_mechanism_and_policy).

History and Philosophy

I initially wanted to write a program that converted between YAML and JSON. That was due to my desire to use YAML as my
AWS-CloudFormation template language (due to YAML's support of comments), but I still wanted to be able to easily use JSON
templates from online search results.

Over time, the problem space expanded. I should possibly split this into multiple programs, that's the Unix-way after all. But
managing half-a-dozen scripts seems like it'd be a pain-in-the-posterior. And the various functionality seems to complement each
other pretty well.

The background of the copying functionality was my desire to take advantage of anchors/references within the YAML specification.
But given the lack of JSON support, I made it a copy action instead. This is a pattern in this program. While it supports both
JSON and YAML, it fundamentally caters to the lowest-common-denominator (which is JSON) when deciding what's valid/invalid (eg
data-types). It's undoubtedly a sign that I should have one program that translates between JSON/YAML and a different one which
does everything else (with YAML only). But this current program solves my problem pretty well right now, though I accept chocolate
bribes.

YAML is a complex specification. And I'm pretty sure that there are bugs in more obscure uses of this program. That said, I suspect
I've covered 99% of the common use cases. And in the spirit of "Worse-is-Better" (see:
https://www.dreamsongs.com/RiseOfWorseIsBetter.html), I think I can leave those corner cases unresolved for now.