在 Sinatra 中使用环境变量设置 database.yml

Set database.yml using environment variables in Sinatra

我在从事的项目中有一个非常奇怪的要求。

我将文件存储在某个位置 /etc/config/config.json,其中包含 HostPortUsernamePassword 等数据库信息,或者大致如下所示:

{ 
      "mysql-db": {
            "host": "172.17.0.27",
            "port": 3306,
            "password": "root",
            "username": "root"
        }
}

我正在基于 Sinatra 构建我的小型网络应用程序,并使用 sinatra-activerecord 来处理 MySql 数据库。

我的 database.yml 文件如下所示:

development:
  adapter: mysql2
  database: toopaste
  host: <%= ENV["MYSQL_DB_HOST"] %>
  port: <%= ENV["MYSQL_DB_PORT"] %>
  username: <%= ENV["MYSQL_DB_USERNAME"] %>
  password: <%= ENV["MYSQL_DB_PASSWORD"] %>

我在尝试什么?

我创建了一个类似 setup.rb 的可执行文件:

#! /usr/bin/env ruby

require 'json'

FILE_PATH = "/etc/atlantis/config/konfig.json"

data = JSON.parse(File.read(FILE_PATH))

system("export MYSQL_DB_HOST=#{data['mysql-db']['host']}")
system("export MYSQL_DB_PORT=#{data['mysql-db']['port']}")
system("export MYSQL_DB_USERNAME=#{data['mysql-db']['username']}")
system("export MYSQL_DB_PASSWORD=#{data['mysql-db']['password']}")

这不会设置 env 变量 MYSQL_DB_HOSTconfig/database.yml 文件使用的其他变量。

知道如何完成这样的工作吗?

我能想到的一种方法是“在读取 config.json 参数后动态生成整个 database.yml 文件。 但想知道是否有更好的解决方案。

我感觉 Sinatra 没有将环境变量处理到 database.yml 文件中。 Rails 确实...您可以这样做,但我认为这有点麻烦。我认为你必须将 YML 文件作为 ERB 模板或其他东西通过。

一些其他选项:

从您的 setup.rb 动态写入整个 database.yml 文件 - 虽然我不会这样做。负载正常形成。

或者,使用 Sinatra 配置从您的首选文件中设置数据库连接详细信息。 sinatra-activerecord 中的示例请阅读我。

set :database, {adapter: 'mysql', database: ENV['MY_SQL_DB_HOST']}

这对我来说似乎更干净。事实上,我会走得更远,使用 Sinatra 配置来完成整个事情(加载文件,并从那里获取参数)。这样代码变得更明确,并且将来更容易更改。也就是说,这只是我的头脑,所以你可能需要调整:

configure do
  FILE_PATH = "/etc/atlantis/config/konfig.json"
  data = JSON.parse(File.read(FILE_PATH))
  set :DB_PASSWORD = data['mysql-db']['password']
  # ETC...
  set :database, {  ... }
end

希望对您有所帮助。

我假设这个配置文件超出了您的应用程序的范围,否则您可以直接读取 json 文件。否则,您可以使用 JSON 的解析器并将其转换为 YAML。像这样的东西会起作用:

require 'json'
require 'yaml'

json = JSON.parse(File.read('./test.json'))
database = Hash.new
database[:development] = json
File.open("./test.yaml","w"){|h| h.write database.to_yaml }

您的 YAML 应如下所示。

$ cat test.yaml 
---
:development:
  mysql-db:
    host: 172.17.0.27
    port: 3306
    password: root
    username: root

我假设您可以将额外的参数添加到散列中以获得类似下面的配置。

config/production.yaml

database:
   adapter: mysql2
   host: localhost
   port: 3306
   database: myappdb
   username: myprodusername
   password: myprodpassword

config/development.yaml

database:
   adapter: mysql2
   host: localhost
   port: 3306
   database: myappdb_dev
   username: mydevuser
   password: mydevpassword

然后像这样将它们加载到您的应用程序中。

config.ru

require 'sinatra'
require 'yaml'

configure :production do
    @config = YAML.load_file("config/#{ENV["RACK_ENV"]}.yaml")
    #some other things that you do in prod
end
configure :development do
    @config = YAML.load_file("config/#{ENV["RACK_ENV"]}.yaml")
    #some other things that you only do in dev
end

正在启动应用程序

$ RACK_ENV=development puma (or whatever other server you use like thin)

or for prod

$ RACK_ENV=production puma (or whatever other server you use like thin)

system method创建一个新的子shell并在其中执行命令。当命令设置环境变量时,该环境变量在该子外壳 中设置 ,更改不会传播回父进程。这意味着没有设置这些环境变量。

要在当前进程中设置环境变量,直接访问ENV即可。所以不要 system("export MYSQL_DB_HOST=#{data['mysql-db']['host']}") 这样做:

ENV['MYSQL_DB_HOST'] = data['mysql-db']['host']

(其他设置也类似)。


这应该可以解决您的问题,但是由于您是直接从 json 配置文件中自己读取设置,因此可以说使用环境变量没有多大意义。直接从 Erb/Yaml 文件访问设置会更直接。理想情况下,您希望以某种方式将设置传递给 Erb 评估,但 Sinatra-ActiveRecord doesn’t appear to allow you to set the binding or context 以任何方式传递给 Erb 评估。另一种方法是对数据哈希使用全局变量:

# When reading the json, use a global instead,
# and pick out the 'mysql-db' key
$data = JSON.parse(File.read(FILE_PATH))['mysql-db']

database.yml中:

development:
  adapter: mysql2
  database: toopaste
  host: <%= $data['host'] %>
  port: <%= $data['port'] %>
  username: <%= $data['username'] %>
  password: <%= $data['password'] %>