草庐IT

go - 默认结构用作 golang 中 yaml 数据的目的地

coder 2024-07-09 原文

我尝试改善我维护的 cli 的用户体验。一个主要目标是提供合理的默认值。它广泛使用 yaml 进行配置。

可在此处找到配置的基本演示实现:https://github.com/unprofession-al/configuration/tree/bf5a89b3eee7338899b28c047f3795546ce3d2e6

一般

主要配置如下所示:

type Config map[string]ConfigSection

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

Config 包含一堆 ConfigSections。这允许用户定义配置的变体(例如,proddevtesting)并使用 YAML Achors 来执行此操作。

ConfigSection 的部分(InputOutput)将在使用配置的包中定义。此部分的每个部分都提供了一个 Defaults() 和一个自定义的 UnmarshalYAML() 函数。 ConfigSection 本身也提供了一个 UnmarshalYAML() 函数。这个想法是从https://github.com/go-yaml/yaml/issues/165#issuecomment-255223956偷来的.

问题

在 repo 的 data.go 中定义了一些测试输入以及预期输出。运行测试 (go test -v) 显示:

  • 在 ConfigSection 中没有定义任何内容( 示例)没有应用默认值。
  • 如果定义了没有数据字段的部分(ConfigSection),则该部分将没有默认值。 “未定义”部分有默认值(参见inputoutput)。
  • 如果两个部分都已定义(如 both 部分)但没有数据字段,则设置任何默认值。

我完全看不到任何模式,也想不出为什么会这样工作以及如何获得预期结果(例如让测试通过)。

最佳答案

好的,所以我没有看到的模式非常明显:配置的“最深叶”覆盖了下面的所有内容,无论是使用给定数据还是使用空值的 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

对于我的用例,这是一个过于复杂的行为,因此我尝试了一种不同的方法:如果需要,我将默认配置注入(inject)到 yaml 中,并在每个部分中引用其 anchor 。我觉得这对最终用户来说更加透明和可重现。这是函数的丑陋草稿:

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

关于go - 默认结构用作 golang 中 yaml 数据的目的地,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52719604/

有关go - 默认结构用作 golang 中 yaml 数据的目的地的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

  4. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  5. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  6. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  7. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  8. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  9. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  10. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

随机推荐