syntax yaml transclusion

syntax - cómo hacer referencia a una "configuración" de YAML desde otro lugar en el mismo archivo YAML?



transclusion (7)

Creé una biblioteca, disponible en Packagist, que realiza esta función: https://packagist.org/packages/grasmash/yaml-expander

Ejemplo de archivo YAML:

type: book book: title: Dune author: Frank Herbert copyright: ${book.author} 1965 protaganist: ${characters.0.name} media: - hardcover characters: - name: Paul Atreides occupation: Kwisatz Haderach aliases: - Usul - Muad''Dib - The Preacher - name: Duncan Idaho occupation: Swordmaster summary: ${book.title} by ${book.author} product-name: ${${type}.title}

Ejemplo de lógica:

// Parse a yaml string directly, expanding internal property references. $yaml_string = file_get_contents("dune.yml"); $expanded = /Grasmash/YamlExpander/Expander::parse($yaml_string); print_r($expanded);

Matriz resultante:

array ( ''type'' => ''book'', ''book'' => array ( ''title'' => ''Dune'', ''author'' => ''Frank Herbert'', ''copyright'' => ''Frank Herbert 1965'', ''protaganist'' => ''Paul Atreides'', ''media'' => array ( 0 => ''hardcover'', ), ), ''characters'' => array ( 0 => array ( ''name'' => ''Paul Atreides'', ''occupation'' => ''Kwisatz Haderach'', ''aliases'' => array ( 0 => ''Usul'', 1 => ''Muad/'Dib'', 2 => ''The Preacher'', ), ), 1 => array ( ''name'' => ''Duncan Idaho'', ''occupation'' => ''Swordmaster'', ), ), ''summary'' => ''Dune by Frank Herbert'', );

Tengo el siguiente YAML:

paths: patha: /path/to/root/a pathb: /path/to/root/b pathc: /path/to/root/c

¿Cómo puedo "normalizar" esto, eliminando /path/to/root/ desde las tres rutas, y tenerlo como su propia configuración, algo así como:

paths: root: /path/to/root/ patha: *root* + a pathb: *root* + b pathc: *root* + c

Obviamente eso no es válido, lo inventé. ¿Cuál es la sintaxis real? Se puede hacer?


En algunos idiomas, puede usar una biblioteca alternativa. Por ejemplo, tampax es una implementación de variables de manejo YAML:

const tampax = require(''tampax''); const yamlString = ` dude: name: Arthur weapon: favorite: Excalibur useless: knife sentence: "{{dude.name}} use {{weapon.favorite}}. The goal is {{goal}}."`; const r = tampax.yamlParseString(yamlString, { goal: ''to kill Mordred'' }); console.log(r.sentence); // output : "Arthur use Excalibur. The goal is to kill Mordred."


No creo que sea posible. Puede reutilizar "nodo" pero no parte de él.

bill-to: &id001 given : Chris family : Dumars ship-to: *id001

Este es un YAML perfectamente válido y los campos given y la family se reutilizan en el ship-to bloque. Puede reutilizar un nodo escalar de la misma manera, pero no hay forma de que pueda cambiar lo que está dentro y agregar la última parte de una ruta desde dentro de YAML.

Si la repetición te molesta tanto, te sugiero que hagas que tu aplicación conozca root propiedad root y la agregue a cada ruta que se ve como relativa, no absoluta.


Otra forma de ver esto es simplemente usar otro campo.

paths: root_path: &root val: /path/to/root/ patha: &a root_path: *root rel_path: a pathb: &b root_path: *root rel_path: b pathc: &c root_path: *root rel_path: c


Que su ejemplo no sea válido es solo porque eligió un carácter reservado para comenzar sus escalares. Si reemplaza * con algún otro carácter no reservado (tiendo a usar caracteres que no sean ASCII para eso, ya que rara vez se usan como parte de alguna especificación), termina con YAML perfectamente legal:

paths: root: /path/to/root/ patha: ♦root♦ + a pathb: ♦root♦ + b pathc: ♦root♦ + c

Esto se cargará en la representación estándar para asignaciones en el lenguaje que utiliza su analizador sintáctico y no expande mágicamente nada.
Para hacer eso, use un tipo de objeto localmente predeterminado como en el siguiente programa de Python:

# coding: utf-8 from __future__ import print_function import ruamel.yaml as yaml class Paths: def __init__(self): self.d = {} def __repr__(self): return repr(self.d).replace(''ordereddict'', ''Paths'') @staticmethod def __yaml_in__(loader, data): result = Paths() loader.construct_mapping(data, result.d) return result @staticmethod def __yaml_out__(dumper, self): return dumper.represent_mapping(''!Paths'', self.d) def __getitem__(self, key): res = self.d[key] return self.expand(res) def expand(self, res): try: before, rest = res.split(u''♦'', 1) kw, rest = rest.split(u''♦ +'', 1) rest = rest.lstrip() # strip any spaces after "+" # the lookup will throw the correct keyerror if kw is not found # recursive call expand() on the tail if there are multiple # parts to replace return before + self.d[kw] + self.expand(rest) except ValueError: return res yaml_str = """/ paths: !Paths root: /path/to/root/ patha: ♦root♦ + a pathb: ♦root♦ + b pathc: ♦root♦ + c """ loader = yaml.RoundTripLoader loader.add_constructor(''!Paths'', Paths.__yaml_in__) paths = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)[''paths''] for k in [''root'', ''pathc'']: print(u''{} -> {}''.format(k, paths[k]))

que se imprimirá:

root -> /path/to/root/ pathc -> /path/to/root/c

La expansión se realiza sobre la marcha y maneja definiciones anidadas, pero debe tener cuidado al no invocar recursión infinita.

Al especificar el volcador, puede volcar el YAML original de los datos cargados, debido a la expansión sobre la marcha:

dumper = yaml.RoundTripDumper dumper.add_representer(Paths, Paths.__yaml_out__) print(yaml.dump(paths, Dumper=dumper, allow_unicode=True))

esto cambiará el orden de las claves de mapeo. Si eso es un problema, debe hacer self.d a CommentedMap (importado de ruamel.yaml.comments.py )


Sí, usando etiquetas personalizadas Ejemplo en Python, haciendo que la etiqueta !join Join se una a las cadenas en una matriz:

import yaml ## define custom tag handler def join(loader, node): seq = loader.construct_sequence(node) return ''''.join([str(i) for i in seq]) ## register the tag handler yaml.add_constructor(''!join'', join) ## using your sample data yaml.load(""" paths: root: &BASE /path/to/root/ patha: !join [*BASE, a] pathb: !join [*BASE, b] pathc: !join [*BASE, c] """)

Lo que resulta en:

{ ''paths'': { ''patha'': ''/path/to/root/a'', ''pathb'': ''/path/to/root/b'', ''pathc'': ''/path/to/root/c'', ''root'': ''/path/to/root/'' } }

La matriz de argumentos para !join puede tener cualquier cantidad de elementos de cualquier tipo de datos, siempre que se puedan convertir en cadena, así que !join [*a, "/", *b, "/", *c] no lo que esperarías


Definición YML:

dir:   default: /home/data/in/   proj1: ${dir.default}p1   proj2: ${dir.default}p2   proj3: ${dir.default}p3

En algún lugar de la hoja de tomillo

<p th:utext=''${@environment.getProperty("dir.default")}'' /> <p th:utext=''${@environment.getProperty("dir.proj1")}'' />

Salida: / home / data / in / / home / data / in / p1