~saiya/hatenablog

No Code, No Life.

ENV.fetch のすすめ

Ruby にて環境変数の値を使う場合には ENV.fetch を使うと良いのではないか、という小ネタ。

そうすることで環境変数が存在しないエラーを早期に検知できる上、エラーの根本原因が例外に反映されるので分かりやすくなる。

この2行だけでほぼ言い尽くしてしまっているが、以下詳細説明:

ENV を使う背景

環境変数を読む理由は色々ありうるが、特に Heroku や Docker などの近代的な PaaS 環境・コンテナ環境において環境変数を読むことが多い。

例えば Ruby on Rails アプリで DB に接続する場合、接続先の DB のホストやユーザー情報を database.yml に決め打ちするのではなく、Heroku デプロイ時や Docker コンテナ起動時に指定したいということが多いためである。

また、Ruby on Rails では database.yml などの各種設定ファイルにて erb を使えるため、設定ファイル内で気軽に ENV を読めるという背景もある *1

ENV[...] より ENV.fetch

上記のような場合に

mysql:
  host: <%= ENV['MYSQL_HOST'] %>

のような書き方がよく見られるが、これは

mysql:
  host: <%= ENV.fetch('MYSQL_HOST') %>

の方がベターではないかというのが本エントリの趣旨。

読もうとしている環境変数が存在する場合は ENV[...] と ENV.fetch(...) は全く同じ振る舞いである。

しかし環境変数が存在しない場合、ENV[...] は nil になる*2が、fetch であれば設定ファイルの読み込み時点で以下のように例外が上がる。

Cannot load `Rails.application.database_configuration`: key not found: "MYSQL_HOST"

このように、あるべき環境変数が無いというエラーを早期に検知でき、かつエラー内容も分かりやすくなるのがこの手法のメリットである。

デフォルト値

なお、環境変数が無かった場合のデフォルト値を指定したい場合

mysql:
  host: <%= ENV.fetch('MYSQL_HOST', 'localhost') %>

のようにすれば良い。

このようにすると、環境変数が存在しなければ第二引数(上の例では localhost)が使われ、例外は発生しない。

*1:ERB.new(ファイル内容).result という風にすれば自前の設定ファイルに対しても erb を使える。Rails も内部ではこのようにしている。

*2:そのため、設定値を使う側でエラーになってしまい、エラー原因をたどる面倒が発生する