Pryではトップレベルで定義したメソッドがObjectクラスのpublicなインスタンスメソッドになる


表題の通り。

今日Pry触ってて気づいたんだけど、Pryのトップレベルで定義したメソッドはObjectクラスのpublicなインスタンスメソッドになるっぽくて、大抵のオブジェクトから呼び出せる様になる。意図した仕様なのかバグなのかは分からないけど、びっくりしたからメモっとく。

ちなみにRubyのversionは2.1.5。Pryは普通にgem installした。irbやRubyを普通に起動した場合には挙動が違ってて、privateなインスタンスメソッドになる。

```zsh
$ ruby -v
ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-darwin14.0]
$ pry -v
Pry version 0.10.1 on Ruby 2.1.5
```

# 何に驚いたのか？

以下にコードで例を示す。

```ruby
[1] pry(main)> def my_method
[1] pry(main)*   p 'my_method'
[1] pry(main)* end
=> :my_method
[2] pry(main)> [].my_method // arrayオブジェクトのメソッドとしてmy_methodが呼び出せる!!
"my_method"
=> "my_method"
[3] pry(main)> [].methods.include?(:my_method) // arrayオブジェクトの呼び出し可能なメソッドにmy_methodが含まれてる!!
=> true
```

トップレベルで定義した`my_method`メソッドが、空のarrayオブジェクトである`[]`から呼び出せている。割と驚きの挙動。

# なぜこうなるのか？
表題にも書いたけど、`Object`クラスのインスタンスメソッドとして`my_method`が定義されてるのが原因。Objectクラスはほとんどのオブジェクトの上位クラスに位置しているので、大抵のオブジェクトから`my_method`が呼び出せるようになる。

```ruby
[4] pry(main)> Object.instance_methods(false).include?(:my_method) // instance_methods(false)で、そのクラスで定義されたinstance_methodsが取得できる
=> true
[5] pry(main)> [].class
=> Array
[6] pry(main)> [].class.superclass // Arrayの上位クラスにObjectが位置している
=> Object
```

自分で適当に定義したクラスもObjectクラスを継承した状態になるので、そこから生成したオブジェクトも`my_method`を呼び出せる。

```ruby
[7] pry(main)> class MyClass; end
=> nil
[8] pry(main)> my_obj = MyClass.new
=> #<MyClass:0x007fbf6972ce20>
[9] pry(main)> my_obj.my_method
"my_method"
=> "my_method"
```

Objectよりもさらに上位のBaicObjectを継承すれば、こうはならない。

```ruby
[10] pry(main)> class MyBasicClass < BasicObject; end
=> nil
[11] pry(main)> my_basic_obj = MyBasicClass.new
=> #<MyBasicClass:0x3fdfb62dab38>
[12] pry(main)> my_basic_obj.my_method
NoMethodError: undefined method `my_method' for #<MyBasicClass:0x007fbf6c5b5670>
from (pry):12:in `__pry__'
```

ここまでがPryでの話。

# irbやRubyコマンドで起動した場合はどうなるか
Objectのprivateなインスタンスメソッドとして定義されるので、外部から呼び出しは出来ない。

```ruby
irb(main):001:0> def my_method
irb(main):002:1>   p 'my_method'
irb(main):003:1> end
=> :my_method
irb(main):004:0> [].my_method
NoMethodError: private method `my_method' called for []:Array
	from (irb):4
	from /Users/(ユーザ名)//.rbenv/versions/2.1.5/bin/irb:11:in `<main>'
```

じゃあ何でこんな挙動(わざわざObjectクラスにprivateなインスタンスメソッドとして定義)になってるかと言うと、オブジェクトのメソッドを定義する時なんかにトップレベルで定義されたメソッドを使えるようにする為。

```ruby
irb(main):005:0> class MyClass
irb(main):006:1>   def try_2_my_method
irb(main):007:2>     my_method           // my_methodが使える!!
irb(main):008:2>     my_method           // my_methodが使える!!
irb(main):009:2>   end
irb(main):010:1> end
=> :try_2_my_method
irb(main):011:0> my_obj = MyClass.new
=> #<MyClass:0x007fefdb285938>
irb(main):012:0> my_obj.try_2_my_method
"my_method"
"my_method"
=> "my_method"
```

要は、Rubyではいわゆるグローバルスコープ的な機能を提供する為にObjectクラスを利用してるっぽい。

この事は、BaiscObjectを継承するクラスの中でインスタンスメソッドを定義する時にはトップレベルで定義したメソッドが使えない事からも確認できる。

```ruby
irb(main):013:0> class MyBasicClass < BasicObject
irb(main):014:1>   def try_3_my_method
irb(main):015:2>     my_method
irb(main):016:2>     my_method
irb(main):017:2>     my_method
irb(main):018:2>   end
irb(main):019:1> end
=> :try_3_my_method
irb(main):020:0> my_basic_obj = MyBasicClass.new
(Object doesn't support #inspect)
=>
irb(main):021:0> my_basic_obj.try_3_my_method
NameError: undefined local variable or method `my_method' for #<MyBasicClass:0x007fefdc86dc50>
	from (irb):15:in `try_3_my_method'
	from (irb):21
	from /Users/(ユーザ名)/.rbenv/versions/2.1.5/bin/irb:11:in `<main>'
```

`try_3_my_method`メソッドの定義の中で`my_method`を呼び出そうとしてエラーが出てる事が分かる。`my_method`はグローバルに定義されてる訳では無くて、あくまで`Object`クラスのインスタンスメソッドとして定義されてる事が確認出来たと言える。

# まとめ
Pryの驚きの挙動から始めて、Rubyのメソッドスコープの仕組みについての知見を得た。あとどうでも良いかもだけど、`methods`とか`instance_methods`とか`private_instance_methods`とか`singleton_methods`みたいな定義されたメソッドをarrayで取得する系のメソッドが今回かなり役立ったので、使っていくと良いと思う。特に、falseを引数として渡すと上位クラス由来のものは取り除いてくれるので、どこでメソッドが定義されてるかが特定しやすかった。