LoginSignup
9
6

More than 5 years have passed since last update.

Refinement で追加したメソッドが irb で呼べない?

Last updated at Posted at 2016-03-16

先日,「Refinement で追加したメソッドが send で呼べない」件を書いたんだが,その後,別のデキナイコトを見つけた。

ただ,refinement に課された制約というわけではないのだと思う。

まず動く例を

まず,ふつうに動く例をお見せする。

以下のコードは,Integer に,〈3 の倍数か(10進で)3 が付く場合に「あほ」を返す〉ようなメソッド nabeatsu を追加する。

sample.rb
module Nabeatsu
  refine Integer do
    def nabeatsu
      "あほ" if modulo(3).zero? || to_s.match(/3/)
    end
  end
end

using Nabeatsu

puts 31.nabeatsu
#=> "あほ"

この sample.rb は期待通りに動く。

irb ではダメ

上記のコードをコピーして,irb 上でペーストしてみる。
すると

using Nabeatsu

までは OK だが,最後の

puts 31.nabeatsu

undefined method `nabeatsu' for 31:Fixnum

が出た。

なんでやねんっ?

irb は 1 行ずつ標準入力を読み込んでいって,式が完結するごとに逐次実行される。おそらく,using の行を実行するときと,次の puts の行を実行する時とでは,refinement のスコープとしてつながっていないんだろうな。

だとすると,両者を 1 行で表現すればイケるのでは?
試しに,

using Nabeatsu; puts 31.nabeatsu

と入力してみたら,期待通り「あほ」が表示された。

そうか,じゃあ

begin
using Nabeatsu
puts 31.nabeatsu
end

もイケるか? うん,イケた。

この例では,puts の行を読み込んでも begin に対応する end がまだ現れていないので,Ruby の式として完結せず,実行が行われない。

次の end を読み込んで初めて一つの式として解釈でき,ようやく begin から end までが実行されるわけだ。

その後(2018-12-27 追記)

この記事の内容が少し古くなったので追記。

この記事の時点では,

using Nabeatsu

はエラーを出さなかったが,少なくとも Ruby 2.3.3 以降は

RuntimeError: main.using is permitted only at toplevel

が出る。

記事を書いたときの Ruby のバージョンが分からない。ちゃんと書いておくべきだった。反省。

irb 上で self ってやると main と表示されるのに,トップレベルじゃない,ってどーゆーこと?

で,(ここ重要)この記事で書いた問題は,irb の起動時のオプションで解消することが分かった。
つまり,

irb --context-mode=1

で起動すれば,irb 上でも最初に掲げたようなコードが期待どおりに動作する。

この issue にそのへんのことが書かれていた。
Bug #9580: Refinements regression in IRB - Ruby trunk - Ruby Issue Tracking System

この話はたしか Qiita の記事で知ったはずだが,いくら探しても記事が見つからず。
この話は以下の記事で知った(2018-12-28 追記)。
数値クラスに逆数を取得するメソッドを追加(+refinementsの勉強) - Qiita

ちなみに pry にはこういうオプションは無いっぽい。

9
6
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
9
6