configの基本的な使い方
configファイルに, Mix.Configモジュールのconfigマクロを使い
config(application_name, key, value)
と値を登録し, 使用したいときはApplicationモジュールのget_env/2関数等を使い
value = Application.get_env(application_name, key)
という形で取得する
使用例を見てみる
手元のconfigファイルには, DBとの接続設定として, 以下の様な記述があります.
# Configure your database
config :phoenix_sample, PhoenixSample.Repo,
adapter: Ecto.Adapters.MySQL,
username: "phoenix",
password: "****",
database: "phoenix_sample_dev",
hostname: "localhost",
pool_size: 10
この記述は, 上記の呼び出しと随分違うものにみえるかもしれませんが,
Elixirの
- 関数呼び出し時, 引数を囲む括弧は省略してもよい
- 関数の引数としてキーワードリストを渡す場合は, カギ括弧を省略しても良い
というルールを使って書いているだけなので, 実際は
# Configure your database
config(:phoenix_sample, PhoenixSample.Repo, [adapter: Ecto.Adapters.MySQL, username: "phoenix", password: "****", database: "phoenix_sample_dev", hostname: "localhost", pool_size: 10])
と書かれているのと同じで, PhoenixSample.Repoというkeyにキーワードリストを対応させているわけです.
実際にこの設定を使っている部分を追ってみると, まずPhoenixSample.Repoの中でEcto.Repoがuseされ
use Ecto.Repo, otp_app: :phoenix_sample
Ecto.Repoの__using__マクロ(use時に呼び出されるマクロ)の中で, さらにEcto.Repo.Supervisorのparse_config関数を呼び出し
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
@behaviour Ecto.Repo
{otp_app, adapter, config} = Ecto.Repo.Supervisor.parse_config(__MODULE__, opts)
呼びだされたparse_config関数の中で, モジュール名とオプションリストに含まれているアプリケーション名を元に設定のキーワードリストを取得し
@doc """
Parses the OTP configuration for compile time.
"""
def parse_config(repo, opts) do
otp_app = Keyword.fetch!(opts, :otp_app)
config = Application.get_env(otp_app, repo, [])
adapter = opts[:adapter] || config[:adapter]
unless adapter do
raise ArgumentError, "missing :adapter configuration in " <>
"config #{inspect otp_app}, #{inspect repo}"
end
unless Code.ensure_loaded?(adapter) do
raise ArgumentError, "adapter #{inspect adapter} was not compiled, " <>
"ensure it is correct and it is included as a project dependency"
end
{otp_app, adapter, config}
end
parse_configの結果をPhoenixSample.Repoモジュールのattributeに設定します.
{otp_app, adapter, config} = Ecto.Repo.Supervisor.parse_config(__MODULE__, opts)
@otp_app otp_app
@adapter adapter
@config config
@before_compile adapter
そして, DBとの接続を開く際には, このattributeの値を使うことになるわけです.
この__using__
マクロの引数を使って設定を読み取り, モジュールのattributeに値をセットするというテクニックは非常に便利そうですね.
微妙に気になるのは, 設定からadpterを読み取る部分が
adapter = opts[:adapter] || config[:adapter]
となっている点.
このoptsは, Ecto.Repoモジュールをuseする際の引数のはずなのですが, ここで指定した
アダプタを設定ファイルで指定したアダプタより優先するのはどんな想定なんでしょうかね?
使ってみる
先日作成した, Memcachedアクセスモジュールの接続情報を設定ファイルに移動してみたいと思います.
まず, 設定ファイルにmemcachedへの接続情報を追加.
config :phoenix_sample, PhoenixSample.Memcached,
host: 'localhost',
port: 11211
そして, 子プロセス立ち時に, 接続情報を設定から取得するように変更.
# arg = ['localhost', 11211]
config = Application.get_env(:phoenix_sample, __MODULE__, [])
arg = [config[:host], config[:port]]
動作を確認してみると...
[vagrant@vagrant-centos7 phoenix_sample]$ iex -S mix phoenix.server
Erlang/OTP 18 [erts-7.2] [source-e6dd627] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
...
iex(1)> [info] Creating interface #PID<0.451.0> to memcached on 'localhost':11211
[info] Creating interface #PID<0.454.0> to memcached on 'localhost':11211
[info] Creating interface #PID<0.457.0> to memcached on 'localhost':11211
[info] Creating interface #PID<0.460.0> to memcached on 'localhost':11211
[info] Creating interface #PID<0.463.0> to memcached on 'localhost':11211
23 Mar 09:10:37 - info: compiled 5 files into 2 files, copied 3 in 1.2 sec
nil
iex(2)> Application.get_all_env :phoenix_sample
[{PhoenixSample.Endpoint,
[url: [host: "localhost"], root: "/home/vagrant/phoenix_sample",
secret_key_base: "y6QJ70XCl3Vxy2GdAcDuT2fA6guGddfwhFz4jDYfm330ENhgzPSI6eM+jI0mCz9T",
render_errors: [accepts: ["html", "json"]],
pubsub: [name: PhoenixSample.PubSub, adapter: Phoenix.PubSub.PG2],
http: [port: 4000], debug_errors: true, code_reloader: true,
check_origin: false,
watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin"]],
live_reload: [patterns: [~r/priv\/static\/.*(js|css|png|jpeg|jpg|gif|svg)$/,
~r/priv\/gettext\/.*(po)$/, ~r/web\/views\/.*(ex)$/,
~r/web\/templates\/.*(eex)$/]]]},
{PhoenixSample.Memcached, [host: 'localhost', port: 11211]},
{PhoenixSample.Repo,
[adapter: Ecto.Adapters.MySQL, username: "phoenix", password: "****",
database: "phoenix_sample_dev", hostname: "localhost", pool_size: 10]},
{:included_applications, []}]
iex(3)> Application.get_env :phoenix_sample, PhoenixSample.Memcached
[host: 'localhost', port: 11211]
iex(4)> PhoenixSample.Memcached.get "name"
"YMST"
無事, 設定ファイルから接続情報を取得するようにできました.
ちょっとした疑問
ここまで書いてきて不思議に思っているのは, 各設定にkeyだけでなく, アプリケーション名まで指定してやる必要があるのはなぜか?
通常設定は設定ファイルは特定のアプリケーションの為に作られ, その有効範囲もアプリケーション内部のはず.
そのアプリケーションが内部で2つ以上の名前をもっているという状態も考えにくい以上, 設定ファイルでアプリケーション名を指定してやる必要性は内容に思える.
ひょっとして, この設定は複数のアプリケーションをまたがって読み書きできたりするんですかね?
参考
Mix Config - Elixir プロジェクトの設定管理 : http://qiita.com/yulii/items/326af9cc802f3cf3670a
Elixir.Application : http://elixir-lang.org/docs/stable/elixir/Application.html