💎
Rubyでモジュールの親モジュールを取得する
概要
以下のようなモジュールの階層があるときに、Child
モジュールからParent
モジュールのVALUE
を参照したかった。
module Parent
VALUE = 'PARENT'
module Child
VALUE = 'CHILD'
def self.parent_value
# 'PARENT' がほしい
end
end
end
Rails(activesupport) が使える環境なら簡単
流石に Module#parent
的なメソッドがRubyで提供されてるだろうと思い調べて見たが、どうやらRuby(2.7現在)本体にはそのような機能はなく、Rails
の拡張(activesupport
)で提供されていることがわかった。
そのため、Rails環境では以下のように単純にparent
を参照することで対応可能
module Parent
VALUE = 'PARENT'
module Child
VALUE = 'CHILD'
def self.parent_value
self.parent::VALUE
end
end
end
[4] pry(main)> Parent::Child.parent_value
=> "PARENT"
Rails以外でも使いたいので実装する
Railsでない(activesupport
が使用できない) 状態の場合、以下のようにparent
メソッドがないよと怒られてしまう。
irb(main):008:0> Parent::Child.parent_value
Traceback (most recent call last):
5: from /Users/shingo.sasaki/.rbenv/versions/2.6.5/bin/irb:23:in `<main>'
4: from /Users/shingo.sasaki/.rbenv/versions/2.6.5/bin/irb:23:in `load'
3: from /Users/shingo.sasaki/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):8
1: from /Users/shingo.sasaki/Docker/teachme/app/libraries/hoge.rb:8:in `parent_value'
NoMethodError (undefined method `parent' for Parent::Child:Module)
activesupport
では以下のように実装されている。
def parent
parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
end
def parent_name
if defined?(@parent_name)
@parent_name
else
parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
@parent_name = parent_name unless frozen?
parent_name
end
end
と、コードを追っていくと以下のような手段を用いていることがわかる
-
Module#name
を使って、モジュール名を文字列で取得 -
::
を元に、親モジュールまでのモジュール名を正規表現で抽出 -
Object.const_get
を用いて文字列からモジュールを取得
よって、Module
クラス自体を以下のように拡張すれば近いことができる(シンプルさを重視して、実際のactivesupport
ほど手広くカバーしてません)
class Module
def parent
parent_name = self.name =~ /::[^:]+\Z/ ? $`.freeze : nil
parent_name ? Object.const_get(parent_name) : Object
end
end
↑を読み込んだ状態ならこんな構造があっても
module Parent
module Child
module GrandChild
end
end
end
階層をたどることが出来る
[3] pry(main)> Parent::Child::GrandChild.parent
=> Parent::Child
[4] pry(main)> Parent::Child::GrandChild.parent.parent
=> Parent
[5] pry(main)> Parent::Child::GrandChild.parent.parent.parent
=> Object
Discussion