0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Ruby】モジュールの名前空間を深掘りするースコープとグローバル名前空間ー

Last updated at Posted at 2022-12-26

Moduleの名前空間について簡単にまとめます。
書籍中心に勉強していますので、ご指摘あればコメントお願いいたします。

名前空間とは

Moduleを使用して、クラスなどの定義した定数が一意になるようにするための手段です。

開発を進めていくと、他のファイルにあるクラス名を重複するなどの問題が起こる。これを解決する手段として用いられます。

定義の仕方

定義したクラス名にmoduleをネストして使用します。

module Bar
    class Foo
        def say
            "hello"
        end
    end
end

foo = Bar::Foo.new
foo.say #=> "hello"

呼び出す場合はモジュール名::クラス名の形を取ります。
ここで、インスタンスを作るときに、Foo.newだけの形で定義するとエラーが出力されるので注意しましょう。

使用する場面

名前空間を使用する時はプロジェクトのディレクトリ構造に名前空間を合わせます。
上のクラスはBarディレクトリのFooクラスで定義されているということになります。
つまりBar::Fooといる名前は、Bar/Foo.rbというファイルに対応していることになります。

定数を定義した時

名前空間を使用して、定数を参照したい場合を考えてみましょう。

module Bar
    KEY = "password0123"
    
    class Foo
        def say(key = KEY)
            "hello, #{key}"
        end
    end
end

foo = Bar::Foo.new
foo.say  # => "hello, password0123"

上の記述の場合は、定数KEYを参照することができます。
これは、BarモジュールFooクラスのスコープ内にあるため、参照できています。

では、moduleをクラスの外側に定義してみましょう。

module Bar
    KEY = "password0123"
end
    
class Bar::Foo
    def say(key = KEY)
        "hello, #{key}"
    end
end

foo = Bar::Foo.new
foo.say # => NameError

この場合はエラーが発生します。
::を使用して名前空間を定義していますが、KEY定数を参照指定いないようです。
これには、RUBYの定数の探索の仕組みが関係しています。

RUBYの定数の探索

定数の探索の方法は2つあります。

  • 継承階層の探索
  • レキシカルスコープの探索

継承階層の探索は、メソッドなどを探索する場合に子クラスから親クラスへ遡っていく方法です。
この継承階層の探索をする前に、まず定数が定義されているレキシカルスコープ内を探索します。

module Bar
    KEY = "password0123"
    
    class Foo
        def say(key = KEY)
            "hello, #{key}"
        end
    end
end

foo = Bar::Foo.new
foo.say  # => "hello, password0123"

最初の例では、moduleがclassをネストしている形をとり、同じレキシカルスコープ内と言えます。
Fooクラスの中で呼び出しているため、KEY定数を使用することができます。

では、エラーが出た方の記述です。

module Bar
    KEY = "password0123"
end
    
class Bar::Foo
    def say(key = KEY)
        "hello, #{key}"
    end
end

foo = Bar::Foo.new
foo.say # => NameError

これは、moduleがclassの外でendしていますので、レキシカルスコープから外れています。
名前空間でBarモジュールを::で修飾していますが、定数の参照はできないようです。

解決法としては、KEY定数にも同様に::を使用することで定数を使用することができます。

module Bar
    KEY = "password0123"
end
    
class Bar::Foo
    def say(key = Bar::KEY)
       "hello, #{key}"
    end
end

foo = Bar::Foo.new
foo.say # => "hello, password0123"

モジュールでネスとしていないクラスは無名?

クラスを定義するとき、すべてのクラスに名前空間をつけるわけではありません。
では、名前空間を使っていないクラスには名前がないのでしょうか?

実は、名前のつかないクラスはすべてトップクラスのObjectクラスに存在します。

class Bar
    def say
        "hello"
    end
end

bar = Object::Bar.new  # =>::Bar.newでもok
p bar.say # => "hello"

この場合、わざわざObjectクラスを参照して定義する必要は全くありません。
このようなトップクラスに存在するクラスをグローバル名前空間に属するとしています。

ちなみにObject::Barを省略した形で、::Barでも呼び出せます。

使用する場面としては、元々Rubyに定義されているArrayクラスを名前空間で定義して、その別名でつけたArrayクラスの中で元にあるArrayクラスを使用したい場合などに活躍します。

module Cluster
    class Array
        def initialize (n)
            @disks = ::Array.new(n){|i| "disk#{i}"}
        end
    end
end
cluster = Cluster::Array.new(3)
# => #<Cluster::Array:0x000055fe3b0a6448 @disks=["disk0", "disk1", "disk2"]>
# 書籍を参照

終わりに

名前空間のレキシカルスコープについて、トップクラスでのグローバル名前空間については理解が深まった気がします。
gemのライブラリに書かれている::に恐れずに立ち向かえそうです...!

参考書籍

Effective Ruby

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?