Ruby の定数についてまとめました。
定数
アルファベット大文字 ([A-Z]) で始まる識別子は定数です。 定数の定義と初期化は値の代入によって行われ、定数は定義されたクラス/モジュール内や継承関係がある場合は定数名のみで参照できます。
すべての定数は、ファイルシステムのようにツリー状に配置されていて、クラス/モジュールがディレクトリで、定数がファイルと考えると分かり易いです。
module A X = 'classAの定数X' class B Y = 'classBの定数Y' puts X #=> "classAの定数X" puts Y #=> "classBの定数Y" p Module.nesting #=> [A::B, A] end end
定数をメソッドの中では定義することはできません。
class MyClass def my_method M = 'my_methodの定数' end end #=> SyntaxError: (irb):14: dynamic constant assignment
一度定義された定数に再び値を代入をすると警告メッセージが出ますが代入した値に代わります。
puts M #=> "トップレベルの定数M" M = '再代入' #=> warning: already initialized constant M #=> warning: previous definition of M was here puts M #=> "再代入"
代入は警告がでますが変更は普通にできてしまいます。変更もさせたくない場合はfreeze
を使いましょう。
M << '追加' #=> "再代入追加" M.freeze M << '追加' RuntimeError: can't modify frozen String
定数の参照
定義されたクラス/モジュール内や継承関係がある場合は定数名のみで参照できますが、他のクラス/モジュールで定義された定数を参照するには::
演算子を使って定数のパスを指定します。トップレベルの定数を確実に参照するにはパスを::
から始めると外部の定数を絶対パスで指定できます。
M = 'トップレベルの定数M' module A class B M = 'moduleAclassBの定数M' puts M #=> "moduleAclassBの定数M" end end class C M = 'classCの定数M' puts M #=> "classCの定数M" クラスの定義内 puts A::B::M #=> "moduleAclassBの定数M" 外部の同名定数 puts ::M #=> "トップレベルの定数M" トップレベルの同名定数 end
BasicObject を継承したクラス(クリーンルーム)の場合、String
などもスコープ外になってしまうため、絶対パスで指定sるう必要があります。
class MyClass < BasicObject name = String.new("task") end #=> NameError: uninitialized constant MyClass::String class MyClass < BasicObject name = ::String.new("task") end #=> "task"
クラスとモジュールの定数
定数のパスという表現をしましたが、実はクラスもモジュールも定数です。生成されたクラスオブジェクトが指定したクラス名の定数に代入されているわけです。クラス名を参照することは文法上は定数を参照しているだけという事が理解できれば、::
演算子で表すパスも全てクラスやモジュールを含めてただの定数だということが分かります。
次の class 定義はどちらも同等の意味を持ちます。
class Project < ActiveRecord::Base end Project = Class.new(ActiveRecord::Base)
module 定義も同様です。
module Admin end Admin = Module.new
定数関連のメソッド
定数を操作するためのメソッドには以下のようなものが存在します。
メソッド | 機能 |
---|---|
Module.constants | このメソッドを呼び出した時点で参照可能な定数名の配列を返す |
Module#constants | モジュールまたはクラスで定義されている定数名の配列を返す |
Module#const_defined? | 引数で指定した名前の定数が定義されていればTrueを返す |
Module#const_get | 引数で指定した名前の定数の値を返す |
Module#const_set | 引数で指定した名前と値の定数を定義する |
Module#const_missing | 定義されていない定数を参照したときに呼びだされる |
Module#deprecate_constant | (private) 引数で指定した定数を deprecate に設定する |
Module#public_constant | (private) 引数で指定した定数の可視性を public に変更する |
Module#private_constant | (private) 引数で指定した定数の可視性を private に変更する |
Module#remove_const | (private) 引数で指定した定数を取り除いて設定されていた値を返す |
CONST_A = 'トップレベルの定数M' module MyModuleA class MyClassB CONST_B = 'moduleAclassBの定数M' end end class MyClassC CONST_C = 'classCの定数M' Module.constants.grep(/My|CONST/) #=> [:CONST_C, :CONST_A, :MyModuleA, :MyClassC] end MyModuleA::MyClassB.constants(false) #=> [:CONST_B] MyClassC.const_defined?(:CONST_C) #=> true MyClassC.const_set(:CONST_D, '追加した定数') #=> "追加した定数" MyClassC.const_get(:CONST_D) #=> "追加した定数" MyClassC.constants(false) #=> [:CONST_C, :CONST_D] class Hoge Bar = '非推奨の定数' deprecate_constant(:Bar) Foo = 'public定数' public_constant(:Foo) Piyo = 'private定数' private_constant(:Piyo) def self.const_missing(key) "#{key}の定数はありません" end end Hoge::Fuga #=> "Fugaの定数はありません" Hoge::Bar #=> "非推奨の定数" # warning: constant Hoge::Bar is deprecated Hoge::Foo #=> "public定数" Hoge::Piyo #=> NameError: private constant Hoge::Piyo referenced class Hoge puts Piyo #=> private定数 remove_const(:Piyo) puts Piyo #=> Piyoの定数はありません end