前回の記事からかなり期間が空いてしまいました。
dotenv の Gem のコードリーディングをしたので、まとめておきたいと思います。 github.com
dotenvとは
説明不要だと思いますが、環境変数をファイル管理することができる Gem で rails 以外の WAF や素のRuby でも使うことができます。
コードリーディング
rails で使用したケースを追っていきたいと思います。
まず config.before_configuration
は Rails::Application
から定数を継承した直後に実行されるので lib/dotenv/rails.rb
の以下のコードが呼ばれます。
config.before_configuration { load }
load
メソッドは同ファイル内の以下のメソッドです。
def load Dotenv.load(*dotenv_files) end
dotenv_files
は private メソッドが定義されていて env ファイルの path を取得します。
def dotenv_files [ root.join(".env.#{Rails.env}.local"), (root.join(".env.local") unless Rails.env.test?), root.join(".env.#{Rails.env}"), root.join(".env") ].compact end
Dotenv.load
は lib/dotenv.rb
で定義されているメソッドで、初めに with
メソッドが呼ばれます。
def load(*filenames) with(*filenames) do |f| ignoring_nonexistent_files do env = Environment.new(f, true) instrument("dotenv.load", env: env) { env.apply } end end end
以下が with
メソッドです。
def with(*filenames) filenames << ".env" if filenames.empty? filenames.reduce({}) do |hash, filename| hash.merge!(yield(File.expand_path(filename)) || {}) end end
with
メソッドでは filename を取得して、yield で以下のブロックを処理します。
ignoring_nonexistent_files
は対象の file ポインタが存在しなければ skip する処理が書かれています。
ignoring_nonexistent_files do env = Environment.new(f, true) instrument("dotenv.load", env: env) { env.apply } end
file ポインタが存在すれば Environment.new(f, true)
で file 内の文字列を hash に parse して返却しています。
実際の parse処理は lib/dotenv/parser.rb
で行なっています。
そして最後に instrument("dotenv.load", env: env) { env.apply }
で環境変数に読み込まれます。
def apply each { |k, v| ENV[k] ||= v } end
この時 ActiveSupport::Notifications.instrument
でイベントが登録されます。
以上です。
おまけというか疑問
lib/dotenv/rails.rb
で以下のようにリッスンされているので .env ファイルが変更されたら Spring が再読み込みする認識なのですが、更新されずでして理解が間違っているんですかね。。
begin require "spring/commands" ActiveSupport::Notifications.subscribe(/^dotenv/) do |*args| event = ActiveSupport::Notifications::Event.new(*args) Spring.watch event.payload[:env].filename if Rails.application end rescue LoadError, ArgumentError # Spring is not available end
分かる方いましたら教えて下さい。