Структуры по умолчанию, используемые в качестве места назначения для данных yaml в golang - PullRequest
0 голосов
/ 09 октября 2018

Я пытаюсь улучшить пользовательский опыт, который я поддерживаю.Основная цель - обеспечить разумные значения по умолчанию.Он использует yaml широко для конфигурации.

Базовую демонстрационную реализацию конфигурации можно найти здесь: https://github.com/unprofession-al/configuration/tree/bf5a89b3eee7338899b28c047f3795546ce3d2e6

General

Основная конфигурация выглядит следующим образом:

type Config map[string]ConfigSection

type ConfigSection struct {
    Input  InputConfig  `yaml:"input"`
    Output OutputConfig `yaml:"output"`
}

Config содержит группу ConfigSections.Это позволяет пользователю определять варианты конфигурации (скажем, например, prod, dev и testing) и использовать для этого YAML-якоря.

Части ConfigSection (Input и Output) будут определены в пакете, который использует конфигурацию.Каждая из этих частей предоставляет Defaults() и пользовательскую UnmarshalYAML() функцию.Также ConfigSection сам обеспечивает функцию UnmarshalYAML().Эта идея украдена у https://github.com/go-yaml/yaml/issues/165#issuecomment-255223956.

Вопрос

В data.go в репозитории определен некоторый тестовый вход, а также ожидаемый результат.Запуск тестов (go test -v) показывает:

  • Ничего не определено в ConfigSection (пример empty), по умолчанию не применяются.
  • Если часть (из * 1043)*) если поле данных не определено, эта часть не будет иметь значений по умолчанию.Часть «undefined» имеет значения по умолчанию (см. input, output).
  • Если обе части определены (как в разделе both), но не имеют полей данных, устанавливаются любые значения по умолчанию.

Я не вижу паттерна вообще и исчерпал идеи, почему это работает так и как получить ожидаемые результаты (например, пройти тест).

1 Ответ

0 голосов
/ 10 октября 2018

Хорошо, так что шаблон, который я не видел, был довольно очевиден: «самый глубокий лист» конфигурации перезаписывает все ниже, либо с заданными данными, либо с значениями по умолчанию go для пустых значений:

Это означает, чточто структура, подобная этой ...

[key_string]:
  input:
    listener: [string]
    static: [string]
  output:
    listener: [string]
    details:
      filter: [string]
      retention: [string]

... по умолчанию с данными ...

defaults:
  input:
    listener: 127.0.0.1:8910
    static: default
  output:
    listener: 127.0.0.1:8989
    details:
      filter: '*foo*'
      retention: 3h

... подается с yaml этой формы ..

empty:

both:
  input:
  output:

input: &input
  input:

input-modified-with-anchor:
  <<: *input
  input:
    static: NOTDEFAULT

input-modified-without-anchor:
  input:
    static: NOTDEFAULT

output: &output
  output:

output-modified-with-anchor:
  <<: *output
  output:
    details:
      filter: NOTDEFAULT

output-modified-without-anchor:
  output:
    details:
      filter: NOTDEFAULT

... получается как ...

both:
  input:
    listener: ""
    static: ""
  output:
    listener: ""
    details:
      filter: ""
      retention: ""
empty:
  input:
    listener: ""
    static: ""
  output:
    listener: ""
    details:
      filter: ""
      retention: ""
input:
  input:
    listener: ""
    static: ""
  output:
    listener: 127.0.0.1:8989
    details:
      filter: '*foo*'
      retention: 3h
input-modified-with-anchor:
  input:
    listener: 127.0.0.1:8910
    static: NOTDEFAULT
  output:
    listener: 127.0.0.1:8989
    details:
      filter: '*foo*'
      retention: 3h
input-modified-without-anchor:
  input:
    listener: 127.0.0.1:8910
    static: NOTDEFAULT
  output:
    listener: 127.0.0.1:8989
    details:
      filter: '*foo*'
      retention: 3h

Для моего случая использования это слишком сложное поведение, поэтому я пытаюсь использовать другой подход: я, если требуется, ввожу значение по умолчаниюconfig в yaml и поместите ссылку на его якорь в каждом разделе.Я чувствую, что это более прозрачно и воспроизводимо для конечного пользователя.Вот ужасный чертёж функции:

func injectYAML(data []byte) ([]byte, error) {
    // render a default section an add an anchor
    key := "injected_defaults"
    defaultData := Config{key: Defaults()}
    var defaultSection []byte
    defaultSection, _ = yaml.Marshal(defaultData)
    defaultSection = bytes.Replace(defaultSection, []byte(key+":"), []byte(key+": &"+key), 1)

    // get list of sections in input data
    c := Config{}
    err := yaml.Unmarshal(data, &c)
    if err != nil {
        return data, fmt.Errorf("Error while reading sections from yaml: %s", err.Error())
    }

    // remove "---" at beginning when present
    data = bytes.TrimLeft(data, "---")

    // add reference to default section to each section
    lines := bytes.Split(data, []byte("\n"))
    var updatedLines [][]byte
    for _, line := range lines {
        updatedLines = append(updatedLines, line)
        for section := range c {
            if bytes.HasPrefix(line, []byte(section+":")) {
                updatedLines = append(updatedLines, []byte("  <<: *"+key))
            }
        }
    }
    updatedData := bytes.Join(updatedLines, []byte("\n"))

    // compose injected yaml
    out := []byte("---\n")
    out = append(out, defaultSection...)
    out = append(out, updatedData...)
    return out, nil
}

Полный пример по адресу: https://github.com/unprofession-al/configuration/tree/7c2eb7da58b51f52b50f2a0fbac193c799c9eb08

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...