【问题标题】:Accessing environment variables in a YAML file for Ruby project (using ${ENVVAR} syntax)访问 Ruby 项目的 YAML 文件中的环境变量(使用 ${ENVVAR} 语法)
【发布时间】:2019-04-27 13:40:54
【问题描述】:

我正在构建一个使用 Ruby 测试 HTTP 服务的开源项目:https://github.com/Comcast/http-blackbox-test-tool

我希望能够在我的test-plan.yaml 文件中引用环境变量。我可以使用 ERB,但是我不想支持嵌入任何随机的 Ruby 代码,而且 ERB 语法对于非 Ruby 爱好者来说很奇怪,我只想使用常用的 Unix 样式 ${ENV_VAR} 语法访问环境变量。

例如

order-lunch-app-health:
  request:
    url: ${ORDER_APP_URL}
    headers: 
      content-type: 'application/text'
    method: get
  expectedResponse:
    statusCode: 200
    maxRetryCount: 5

我为 Ruby 找到的所有示例都使用 ERB。有没有人对处理这个问题的最佳方法提出建议?我愿意使用其他工具来预处理 YAML,然后将其发送到 Ruby 应用程序。

【问题讨论】:

标签: ruby yaml


【解决方案1】:

我相信这样的事情在大多数情况下应该可以工作:

require 'yaml'

def load_yaml(file)
  content = File.read file
  content.gsub! /\${([^}]+)}/ do
    ENV[$1]
  end

  YAML.load content
end

p load_yaml 'sample.yml'

与我最初的答案相反,这既简单又能很好地处理未定义的 ENV 变量。

试试这个 YAML:

# sample.yml
path: ${PATH}
home: ${HOME}
error: ${NO_SUCH_VAR}

原始答案(留在这里供参考)

有几种方法可以做到这一点。如果您想让您的用户使用${VAR} 语法,那么也许一种方法是首先将这些变量转换为Ruby 字符串替换格式%{VAR},然后一起评估所有环境变量。

这是一个粗略的概念证明:

require 'yaml'

# Transform environments to a hash of { symbol: value }
env_hash = ENV.to_h.transform_keys(&:to_sym)

# Load the file and convert ${ANYTHING} to %{ANYTHING}
content = File.read 'sample.yml'
content.gsub! /\${([^}]+)}/, "%{\\1}"

# Use Ruby string substitution to replace %{VARS}
content %= env_hash

# Done
yaml = YAML.load content
p yaml

例如将它与 sample.yml 一起使用:

# sample.yml
path: ${PATH}
home: ${HOME}

当然有很多方法可以改进。

【讨论】:

  • 你的意思是file而不是load_yaml函数中的sample.yml
【解决方案2】:

预处理很简单,我建议您使用 YAML 加载器/转储器 基于解决方案,因为替换可能需要引号 替换标量。 (例如,您替换字符串 true,如果那样的话 未引用,则生成的 YAML 将被读取为布尔值)。

假设您的“来源”在input.yaml 和您的环境中。多变的 ORDER_APP_URL 设置为 https://some.site/and/url。以及以下 expand.py中的脚本:

import sys
import os
from pathlib import Path
import ruamel.yaml

def substenv(d, env):
    if isinstance(d, dict):
        for k, v in d.items():
            if isinstance(v, str) and '${' in v:
                d[k] = v.replace('${', '{').format(**env)
            else:
                substenv(v, env)
    elif isinstance(d, list):
        for idx, item in enumerate(d):
            if isinstance(v, str) and '${' in v:
                d[idx] = item.replace('${', '{').format(**env)
            else:
                substenv(item, env)


yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
data = yaml.load(Path(sys.argv[1]))

substenv(data, os.environ)
yaml.dump(data, Path(sys.argv[2]))

你可以这样做:

python expand.py input.yaml output.yaml

output.yaml:

order-lunch-app-health:
  request:
    url: https://some.site/and/url
    headers:
      content-type: 'application/text'
    method: get
  expectedResponse:
    statusCode: 200
    maxRetryCount: 5

请注意,“应用程序/文本”周围的虚假引号将被保留,任何 cmets 也会保留 在原始文件中。

替代 URL 周围的引号不是必需的,但如果是的话,就会添加。

substenv 例程递归遍历加载的数据,并进行替换,即使替换是在中间标量中,并且如果在一个标量中有多个替换。你可以“收紧”测试:

        if isinstance(v, str) and '${' in v:

如果这会匹配从 YAML 加载的太多字符串。

【讨论】:

    猜你喜欢
    • 2017-10-08
    • 1970-01-01
    • 2021-12-07
    • 2018-04-28
    • 2012-01-30
    • 2015-12-10
    • 1970-01-01
    • 2018-06-26
    • 1970-01-01
    相关资源
    最近更新 更多