【问题标题】:YAML.load_file Cannot Read ENV Variables CorrectlyYAML.load_file 无法正确读取 ENV 变量
【发布时间】:2021-04-09 12:19:40
【问题描述】:

我已经设置了一个辅助数据库 YAML 文件,如下所示:

# config/remote_database.yml

default: &default
  adapter: postgresql
  encoding: unicode
  pool: 5

development:
  <<: *default
  database: 'cexplorer'
  username: <%= ENV['HOST_PG_DATABASE_USERNAME'] %>
  password: <%= ENV['HOST_PG_DATABASE_PASSWORD'] %>
  host: <%= ENV['HOST_PG_DATABASE_IP'] %>
  port: 5432

此 YAML 文件是从创建变量以用于establish_connection 的初始化程序中读取的:

# config/initializers/remote_database.rb

REMOTE_DB = YAML.load_file(File.join(Rails.root, "config", "remote_database.yml"))[Rails.env.to_s]

当我使用byebug 检查 REMOTE_DB 的外观时,我看到 ENV 变量是使用占位符而不是变量本身读取的,即使变量可用。

# app/models/remote_record.rb

class RemoteRecord < ActiveRecord::Base
  byebug
  establish_connection REMOTE_DB
end
[1, 5] in /Users/sergio/Documents/github/swan-to-db-sync-backend/app/models/db_sync_record.rb
   1: class RemoteRecord < ActiveRecord::Base
   2:   byebug
=> 3:   establish_connection DB_SYNC_DB
   4:   # REMOTE_DB is defined in `config/initializers/db_sync_database.rb`
   5: end
(byebug) REMOTE_DB
{"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "database"=>"cexplorer", "username"=>"<%= ENV['HOST_PG_DATABASE_USERNAME'] %>", "password"=>"<%= ENV['HOST_PG_DATABASE_PASSWORD'] %>", "host"=>"<%= ENV['HOST_PG_DATABASE_IP'] %>", "port"=>"5432"}
(byebug) ENV['HOST_PG_DATABASE_USERNAME']
"postgres"
(byebug) 

如何在 REMOTE_DB 中获取实际的 ENV 变量值?

【问题讨论】:

    标签: ruby-on-rails ruby environment-variables yaml


    【解决方案1】:

    Pure YAML 对 ERB 语法一无所知,并且在加载文件时不会自动解释它。

    这令人困惑,因为在 Ruby on Rails 中,这似乎适用于所有 YAML 配置文件。但它只起作用,因为 Ruby 在内部做了一些魔法。

    当您想要加载 YAML 文件并动态解释该文件中的 ERB 时,您需要将 YAML 加载代码更改为如下内容:

    file = File.read(Rails.root.join("config", "remote_database.yml"))
    yaml = ERB.new(file).result
    
    REMOTE_DB = YAML.load(yaml)[Rails.env.to_s]
    

    【讨论】:

    • 虽然这确实回答了这个问题,但它的一个非常明显的例子是不必要地重新发明轮子。
    【解决方案2】:

    Ruby YAML 模块不通过 ERB 传递文件。这实际上是 ActionSupport::ConfigurationFile. 的 Rails 特定功能

    # frozen_string_literal: true
    
    module ActiveSupport
      # Reads a YAML configuration file, evaluating any ERB, then
      # parsing the resulting YAML.
      #
      # Warns in case of YAML confusing characters, like invisible
      # non-breaking spaces.
      class ConfigurationFile # :nodoc:
        class FormatError < StandardError; end
    
        def initialize(content_path)
          @content_path = content_path.to_s
          @content = read content_path
        end
    
        def self.parse(content_path, **options)
          new(content_path).parse(**options)
        end
    
        def parse(context: nil, **options)
          YAML.load(render(context), **options) || {}
        rescue Psych::SyntaxError => error
          raise "YAML syntax error occurred while parsing #{@content_path}. " \
                "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
                "Error: #{error.message}"
        end
    
        private
          def read(content_path)
            require "yaml"
            require "erb"
    
            File.read(content_path).tap do |content|
              if content.include?("\u00A0")
                warn "#{content_path} contains invisible non-breaking spaces, you may want to remove those"
              end
            end
          end
    
          def render(context)
            erb = ERB.new(@content).tap { |e| e.filename = @content_path }
            context ? erb.result(context) : erb.result
          end
      end
    end
    

    所以你可以解析一个配置文件:

    @config = ActiveSupport::ConfigurationFile.parse(path_to_file)
    

    ActiveRecord 实际上还有一些技巧,它将 YAML 配置与 ENV['DATABASE_URL'] 合并。

    但是你实际上并不需要它。在 Rails 6+ 中你实际上可以define multiple databases per environment:

    development:
      primary:
        database: my_development_database
        adapter: mysql
      remote:
        database: my_primary_database
        host: somehost
        adapter: mysql
    

    在任何以前的版本中,您都可以创建更多环境,例如临时环境或使用ENV['DATABASE_URL']supplement/override the YAML configuration

    【讨论】:

    • 由于this issue,根据文档的多个数据库对我不起作用
    • 您仍然可以在单个 database.env 文件中使用额外的根级别密钥,然后使用 Rails.configuration.database_configuration[:foo] 获取该配置,其中 :foo 是您选择调用根密钥的任何内容。跨度>
    猜你喜欢
    • 2017-03-20
    • 2021-08-12
    • 2020-10-02
    • 1970-01-01
    • 2019-01-29
    • 2013-03-07
    • 2022-01-08
    • 1970-01-01
    • 2021-10-11
    相关资源
    最近更新 更多