fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます
ElixirのDBラッパーである「Ecto.Repo」は、Rails同様、DB設定をURL形式で記述できます
これを応用することで、環境変数でもDB設定が行え、本番環境での設定はもちろん、テスト環境やステージング環境でのDB設定切替が容易になります(ただし、configで設定するため、アプリ再起動が必要です)
内容が、面白かったり、役に立ったら、「いいね」よろしくお願いします
configの「url」でDB設定を書く
通常の設定(Phoenixで自動生成された場合も含む)は、以下のように記述されます
(なお、「adapter」が未記載ですが、Phoenix 1.4からは、configからRepoモジュール内に移動しています)
…
# Configure your database
config :sample_db, SampleDb.Repo,
username: "postgres",
password: "password",
database: "sample_db_dev",
hostname: "localhost",
port: 5432,
timeout: 1000,
pool_size: 10
上記と同じ設定を「url」で書くと、以下のようになります
…
# Configure your database
config :sample_db, SampleDb.Repo,
url: "ecto://postgres:password@localhost:5432/sample_db_dev?timeout=1000&pool_size=10"
書式は、以下の通りです
ecto://【ID】:【パスワード】@【ホスト名】:【ポート番号】/【DB名】?【オプション1】&【オプション2】&…
SSL設定等のオプションも、上記同様、URLパラメータとして記述できます
環境変数でのDB設定
URL形式での指定を応用して、環境変数にDB設定のURLを書いておき、configで読み込めます
これを使うと、本番環境のDBパスワード等を秘匿するのに便利です
prod.secret.exsは、レポジトリにデフォルトで登録されず不便なため、環境変数の方が使いやすいと思います(Gigalixirでもprod.secret.exsは削除が推奨されています)
また、DB設定がコード書換無で可能となる(ただし、アプリ再起動は必要)ので、テスト環境やステージング環境でのDB設定切替の運用を安全に行えます
set DATABASE_URL=ecto://postgres:password@localhost:5432/sample_db_dev?timeout=1000&pool_size=10
export DATABASE_URL=ecto://postgres:password@localhost:5432/sample_db_dev?timeout=1000&pool_size=10
configでは、System.get_env()を使って、環境変数を読み込みます
…
# Configure your database
config :sample_db, SampleDb.Repo,
url: System.get_env( "DATABASE_URL" ),
…
Gigalixirでの設定の書き方
Gigalixirでは、以下のように設定を書きます
…
# Configure your database
config :sample_db, SampleDb.Repo,
url: "${DATABASE_URL}",
…
${DATABASE_URL}に、どんな設定がされているかは、以下コマンドで確認できます
gigalixir config
{
"DATABASE_URL": "ecto://XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX-user:pw-XXXXXXXXXXXXXXXX@postgres-XXXXXXXX.gigalixir.com:5432/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
}
設定を変更する場合は、以下コマンドで行えます
gigalixir config:set DATABASE_URL="ecto://XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX-user:pw-XXXXXXXXXXXXXXXX@postgres-XXXXXXXX.gigalixir.com:5432/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
オマケ:DB設定のパース
Ecto.Repo.Supervisor.parse_url()でURLがパースされています
defmodule Ecto.Repo.Supervisor do
…
@doc """
Parses an Ecto URL allowed in configuration.
The format must be:
"ecto://username:password@hostname:port/database?ssl=true&timeout=1000"
"""
def parse_url(""), do: []
def parse_url(url) when is_binary(url) do
info = URI.parse(url)
if is_nil(info.host) do
raise Ecto.InvalidURLError, url: url, message: "host is not present"
end
if is_nil(info.path) or not (info.path =~ ~r"^/([^/])+$") do
raise Ecto.InvalidURLError, url: url, message: "path should be a database name"
end
destructure [username, password], info.userinfo && String.split(info.userinfo, ":")
"/" <> database = info.path
url_opts = [username: username,
password: password,
database: database,
hostname: info.host,
port: info.port]
query_opts = parse_uri_query(info)
for {k, v} <- url_opts ++ query_opts,
not is_nil(v),
do: {k, if(is_binary(v), do: URI.decode(v), else: v)}
end
defp parse_uri_query(%URI{query: nil}),
do: []
defp parse_uri_query(%URI{query: query} = url) do
query
|> URI.query_decoder()
|> Enum.reduce([], fn
{"ssl", "true"}, acc ->
[{:ssl, true}] ++ acc
{"ssl", "false"}, acc ->
[{:ssl, false}] ++ acc
{key, value}, acc when key in @integer_url_query_params ->
[{String.to_atom(key), parse_integer!(key, value, url)}] ++ acc
{key, value}, acc ->
[{String.to_atom(key), value}] ++ acc
end)
end
defp parse_integer!(key, value, url) do
case Integer.parse(value) do
{int, ""} ->
int
_ ->
raise Ecto.InvalidURLError,
url: url,
message: "can not parse value `#{value}` for parameter `#{key}` as an integer"
end
end
…
p.s.「いいね」よろしくお願いします
ページ左上の や のクリックを、どうぞよろしくお願いします
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください!