Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

RubyのNamespace機能を試す

Rubyの開発版にNamespaceが導入されました。1つのRubyプロセスの中に分離された名前空間を提供することが目的の機能で、先日開催されたRubyKaigi 2025でも大きなトピックの1つとなっていました。

github.com

この記事では、Namespaceの目的と使い方を簡単に見ていこうと思います。より詳しく知りたい方は、記事末尾の参考資料を読んだり、ぜひ手元で動かしてみたりしてください。

Namespaceの目的

Namespaceの目的は、大きく以下の3つです。これはNamespaceが提案されているチケットに書かれています。

  • ライブラリ間での名前の衝突を避けること
  • 意図しないクラス定義の共有などを避けること
  • 複数バージョンのgemを1つのプロセスからロードすること

⁠試しに使ってみる

では、早速使ってみましょう。

開発版Rubyの準備

使うには最新の開発版のRubyが必要です。手元に開発版のRubyがない方は、rbenvを使って導入すると簡単でしょう。

$ rbenv install ruby-dev
$ rbenv shell ruby-dev
$ ruby -v
ruby 3.5.0dev (2025-05-12T02:09:22Z master 4464cbe5cd) +PRISM [arm64-darwin24]

これで開発版のRubyの用意ができました。 ここからは、Namespaceの使用例を2つ紹介します。

例1: 同名のクラスを定義してみる

1つ目の例として、同名のクラスを定義してみましょう。app1.rb, app2.rb, そしてmain.rbの3つのファイルを用意します。

# app1.rb

class App
  def hello
    puts "hello, app1"
  end
end
# app2.rb

class App
  def hello
    puts "hello, app2"
  end
end
# main.rb

class App
  def hello
    puts "hello, app main"
  end
end

ns1 = Namespace.new
ns2 = Namespace.new

ns1.require './app1'
ns2.require './app2'

ns1::App.new.hello
ns2::App.new.hello
App.new.hello

これら3つのファイルでは、どれもAppという同名のクラスを定義して、同名のhelloメソッドを定義しています。今までのRubyだと、これら3つのクラスは同じAppクラスだと認識されてしまい、どれか1つのhelloメソッドの定義しか使えませんでした。

Namespaceはこの問題を解決します。main.rbを見てみましょう。 main.rbでは、通常のrequireではなくNamespace#requireを呼んでいます。これによって、Namespace.newで作られたオブジェクトの下に、app1.rbapp2.rbrequireされます。そのためそれぞれのAppクラスは衝突することがなく、別のものとして扱えます。

実行結果を次に示します。なお注意点としてRUBY_NAMESPACE環境変数を定義して実行する必要があります。

$ env RUBY_NAMESPACE=1 ruby main.rb
ruby: warning: Namespace is experimental, and the behavior may change in the future!
See doc/namespace.md for know issues, etc.
hello, app1
hello, app2
hello, app main

呼ばれているApp#helloメソッドがそれぞれ別のものであることが確認できました!

例2: オープンクラスをしてみる

もう1つの例として、オープンクラスを試してみましょう。integer_patch.rbmain.rbの2つのファイルを用意します。

# integer_patch.rb

class Integer
  prepend(Module.new {
    def +(rhs)
      super(rhs.to_i)
    end
  })

end

puts "In integer_patch.rb"
puts 42 + '42'
# main.rb

ns = Namespace.new

ns.require './integer_patch'

puts "In main.rb"
puts 42 + '42'

integer_patch.rb ではInteger#+ メソッドを変更してrhsをIntegerとして暗黙的に型変換した上で足し算をするようにしています。つまり、1 + '2'3になるということです。なんて便利なのでしょうか!

しかしIntegerクラスへのオープンクラスは影響範囲がとても大きいです。Namespaceを使えば、この影響範囲を狭くすることができます。

今回のコードでは、integer_patch.rb, main.rbともに、puts 42 + '42'を実行しています。今までのRubyだとどちらのファイルでもこのコードは実行できてしまっていましたが、NamespaceがあればInteger#+にパッチが当たっているinteger_patch.rbの中でのみこの足し算が実行できるはずです。ということでコードを実行してみましょう。

$ env RUBY_NAMESPACE=1 ruby main.rb
ruby: warning: Namespace is experimental, and the behavior may change in the future!
See doc/namespace.md for know issues, etc.
In integer_patch.rb
84
In main.rb
main.rb:6:in 'Integer#+': String can't be coerced into Integer (TypeError)

puts 42 + '42'
          ^^^^
        from main.rb:6:in '<main>'

integer_patch.rbでは42 + '42'の実行が成功して84が表示されていますが、main.rbではこれがエラーになっているのがわかります!

まとめ

RubyのNamespace機能を軽く触ってみました。ぜひ手元でも色々試してみてください。

⁠参考資料