elixir地域化の話: アプリケーションのロード

地域化の話ではあるけれど、Elixir/Erlangのコードローディングとアプリケーションの話でもある。
地域化の基本設計の段階で、翻訳リソースはアプリケーション毎(OTPでいうところのアプリケーション)に決定した。
ところが、Code.get_docs/2では、モジュールを指定してドキュメントを取得するので、モジュールからアプリケーションを検索する必要が出てきた。

appはどこ?

:application.get_application/1を使うと、モジュールからアプリケーションを検索してくれる。

iex(6)> :application.get_application(List)
{:ok, :elixir}
iex(7)> 

しかし、現在ロードされていないアプリケーションに属するモジュールは答えてくれない。

iex(3)> :application.get_application(L10nIex)                        
:undefined
iex(4)> :application.load(:l10n_iex)         
:ok
iex(5)> :application.get_application(L10nIex)
{:ok, :l10n_iex}

アプリケーションをロードすると答えてくれるが、アプリケーションをロードするためにはアプリケーション名が分かっている事が必要。アプリケーション名が分からないから苦労しているというのになんとういうこと。
さてどうしたものか。

:code.is_loaded/1

まず、コードがロードされているなら、:code.is_loaded/1でそのファイルが分かるので、Code.ensure_loaded/1してからbeamファイルのパス名を取得する。

iex(27)> Code.ensure_loaded(L10nIex)
{:module, L10nIex}
iex(28)> :code.is_loaded(L10nIex)                  
{:file, '/**(snip)**/l10n_iex/ebin/Elixir.L10nIex.beam'}

パス名が取得されたら、通常は同じディレクトリに*.appファイルがある筈なので、それを探す。pathをbeamファイルのパス名とすると、

           app = Path.dirname(path) |>
              Path.join("*.app") |>
              Path.wildcard |>
              Path.basename(".app") |>
              String.to_atom

こんなふうにしてappファイルを取得し、その拡張子を除き、アトムに変換すると、OTPの規約ではアプリケーション名となるはず。毎回これを実施するのは大変なので、:application.load(app)しておく。
関係ないがパイプ演算子は只の第一引数を使い回すための糖衣構文なので、こんな事も当然出来る。
なお、さらりと書いているが、実際はerlangのドキュメントを調べまくってようやく分かったという。

成果物

これによって、ElixirでデフォルトでロードされるアプリケーションではないExUnit(ExUnitは同梱されるが、Elixir起動時に標準でロードされる訳ではない)に対しても、iexのExgettext.Helper.h/1コマンドや、ex_docのmake docsから翻訳リソースを参照することが出来るようになった。

これを使って、日本語のElixirリファレンスをアップデート。v1.0.1に対応。