How Rails.env works with EnvironmentInquirer & StringInquirer (Example) | GoRails
をみていました。
自然すぎて、気にもしなかった、Rails.env.development? のコード解説です。中身はエレガントな実装になってました。
12分ほどの動画なのでさらっと見れます。
rails consoleで確認
>> Rails.env => "development" >> Rails.env.test? => false >> Rails.env.development? => true >> Rails.env == "test" => false
となりますが、これは一体どうやってるのか。
ActiveSupport::Stringinquirer
まずは、ActiveSupport::Stringinquirer。
こいつは、String を継承していて、?
付きのメソッド呼び出しがあれば、selfと?
を削除した文字列との比較を行うようになってます。
# https://github.com/rails/rails/blob/v7.1.1/activesupport/lib/active_support/string_inquirer.rb class StringInquirer < String private def respond_to_missing?(method_name, include_private = false) method_name.end_with?("?") || super end def method_missing(method_name, *arguments) if method_name.end_with?("?") self == method_name[0..-2] else super end end end
要はこういうこと。
>> fruit = ActiveSupport::StringInquirer.new("apple") => "apple" >> fruit.apple? => true >> fruit.apple /Users/rochefort/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/activesupport-7.0.4.2/lib/active_support/string_inquirer.rb:29:in `method_missing': undefined method `apple' for "apple":ActiveSupport::StringInquirer (NoMethodError) >>fruit.orange? => false
Rails.env
続いて、Rails.env を見てみると、環境名を渡して、ActiveSupport::EnvironmentInquirer を newしています。
# https://github.com/rails/rails/blob/v7.1.1/railties/lib/rails.rb module Rails class << self def env @_env ||= ActiveSupport::EnvironmentInquirer.new(ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development") end
ActiveSupport::EnvironmentInquirer
ここでは、initializeで環境ごとのインスタンス変数(@development, @test, @production )を生成し、 class_eval で 環境ごとのメソッド(development?, test?, production?)を作成しています。
#https://github.com/rails/rails/blob/v7.1.1/activesupport/lib/active_support/environment_inquirer.rb module ActiveSupport class EnvironmentInquirer < StringInquirer # :nodoc: DEFAULT_ENVIRONMENTS = %w[ development test production ] def initialize(env) raise(ArgumentError, "'local' is a reserved environment name") if env == "local" super(env) DEFAULT_ENVIRONMENTS.each do |default| instance_variable_set :"@#{default}", env == default end @local = in? LOCAL_ENVIRONMENTS end DEFAULT_ENVIRONMENTS.each do |env| class_eval <<~RUBY, __FILE__, __LINE__ + 1 def #{env}? @#{env} end RUBY end
こういう仕組みになっていたわけですね。
Rails.envはこのクラスのオブジェクトです。
>> Rails.env.class => ActiveSupport::EnvironmentInquirer
String
最初に説明した、 ActiveSupport::StringInquirer をnewする inquiry を用意しています。
# https://github.com/rails/rails/blob/v7.1.1/activesupport/lib/active_support/core_ext/string/inquiry.rb class String def inquiry ActiveSupport::StringInquirer.new(self) end