設定ファイルから環境変数をセットするfigaroのコードリーディングをしました。バージョンは1.1.1です
まずRailtieのconfig.before_configurationのフックでFigaro.loadを呼び出します
module Figaro
module Rails
class Railtie < ::Rails::Railtie
config.before_configuration do
Figaro.load
end
end
end
end
Figaro.loadではFigaro::Application#loadを呼び出します
module Figaro
extend self
attr_writer :adapter, :application
def env
Figaro::ENV
end
def adapter
@adapter ||= Figaro::Application
end
def application
@application ||= adapter.new
end
def load
application.load
end
def require_keys(*keys)
missing_keys = keys.flatten - ::ENV.keys
raise MissingKeys.new(missing_keys) if missing_keys.any?
end
end
initializerファイルで Figaro.require_keys(‘hoge’, …)
を呼び出すことで設定ファイルに含まれていないキーがあればエラーを返すようにできますが、パラメータのキーから::ENV.keysを引いてパラメータが残っていればエラーをraiseしています。
Figaro::Application#loadは設定ファイルを読み込んで各設定に応じて環境変数をセットします。
module Figaro
class Application
def load
each do |key, value|
skip?(key) ? key_skipped!(key) : set(key, value)
end
end
def each(&block)
configuration.each(&block)
end
private
def set(key, value)
non_string_configuration!(key) unless key.is_a?(String)
non_string_configuration!(value) unless value.is_a?(String) || value.nil?
::ENV[key.to_s] = value.nil? ? nil : value.to_s
::ENV[FIGARO_ENV_PREFIX + key.to_s] = value.nil? ? nil: value.to_s
end
def skip?(key)
::ENV.key?(key.to_s) && !::ENV.key?(FIGARO_ENV_PREFIX + key.to_s)
end
def non_string_configuration!(value)
warn "WARNING: Use strings for Figaro configuration. #{value.inspect} was converted to #{value.to_s.inspect}."
end
def key_skipped!(key)
warn "WARNING: Skipping key #{key.inspect}. Already set in ENV."
end
end
end
環境変数は指定のキー値とFIGARO_ENV_PREFIX + キー値にセットされます。ロード前に既に環境変数をセットしている場合はskip?がtrueを返し環境変数を上書きしません。またフィガロで設定した環境変数に関してはFIGARO_ENV_PREFIXの方にも環境変数がセットされているのでskip?がfalseを返して再ロードされます。また、設定キーと値がStringではないときはWARNINGを表示します。
eachはconfiguration.eachにブロックを渡します
module Figaro
class Application
def initialize(options = {})
@options = options.inject({}) { |m, (k, v)| m[k.to_sym] = v; m }
end
def path
@options.fetch(:path) { default_path }.to_s
end
def path=(path)
@options[:path] = path
end
def environment
environment = @options.fetch(:environment) { default_environment }
environment.nil? ? nil : environment.to_s
end
def environment=(environment)
@options[:environment] = environment
end
def configuration
global_configuration.merge(environment_configuration)
end
private
def raw_configuration
(@parsed ||= Hash.new { |hash, path| hash[path] = parse(path) })[path]
end
def parse(path)
File.exist?(path) && YAML.load(ERB.new(File.read(path)).result) || {}
end
def global_configuration
raw_configuration.reject { |_, value| value.is_a?(Hash) }
end
def environment_configuration
raw_configuration[environment] || {}
end
end
end
configurationでYAMLファイルをパースして取得した設定ファイルからネストしている(=値がハッシュのもの。環境ごとの設定も含む)設定と、環境ごとの設定をマージします。このハッシュ値に対してeachで回して環境変数をセットしています。
figaro installのCLIの処理はThorを使っています。
require "thor/group"
module Figaro
class CLI < Thor
class Install < Thor::Group
include Thor::Actions
class_option "path",
aliases: ["-p"],
default: "config/application.yml",
desc: "Specify a configuration file path"
def self.source_root
File.expand_path("../install", __FILE__)
end
def create_configuration
copy_file("application.yml", options[:path])
end
def ignore_configuration
if File.exists?(".gitignore")
append_to_file(".gitignore", <<-EOF)
# Ignore application configuration
/#{options[:path]}
EOF
end
end
end
end
end