Help us understand the problem. What is going on with this article?

トップレベルで定義したメソッドは全クラスのプライベートメソッドとなる

イントロダクション

西遊記より
孫悟空にお釈迦様は言われます「私の掌から飛び出すことができれば、お前を自由にしてやる」。悟空は筋斗雲に飛び乗り世界の端を目指します。ここまで来たら大丈夫だろうとそこの山に「悟空参上!」と書きました。お釈迦様に報告した所、お釈迦様の掌にはその文字が書いてありました。

一見奇妙なコード

ある日、肥大化したクラスをリファクタリングしようと処理を分割していた時、不思議な現象に遭遇しました。

class Hoge
  def execute
    @str = "Hello Ruby!"
    hello
  end
end

def hello
  puts @str
end

hoge = Hoge.new
hoge.execute
# => Hello Ruby!

このコードは問題なく動作して「Hello Ruby!」を出力します。
しかしなぜ分離独立している筈のhelloメソッド内部でHogeクラスのインスタンス変数strを参照できているのでしょうか?他の言語ならエラーになりそうなのに。

何がおきているのか

まずRubyのトップレベルに書いたメソッドがどこに定義されるのかというとKernelモジュールのprivateに定義されます。
(追記:コメントにて指摘されていますが、正しくはObjectに定義されます)

p Kernel.private_methods
# => [:hello, ...]

helloメソッドが追加されていることが確認できます。
このKernelモジュールはObjectクラスにてインクルードされ、さらにすべてのクラスはObjectを継承します。
この関係を確認してみましょう。

p Hoge.ancestors
# => [Hoge, Object, Kernel, BasicObject]

ここまで来ればHogeクラスにもhelloメソッドが定義されていることに気が付くでしょう。

p Hoge.private_methods
# => [:hello, ...]

結論

トップレベルで定義したメソッドは全クラスのプライベートメソッドとなる
helloメソッドはHogeクラスから分離独立しているように見えるが、実際は内部に定義されていたのです。だから普通にインスタンス変数にもアクセスできたのです。
Rubyの基礎階層を理解していないと孫悟空の様にホワッ?となるエピソードでした。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away