LoginSignup
3
0

More than 1 year has passed since last update.

Refinement で定義したメソッドが Symbol#to_proc で使えない

Last updated at Posted at 2018-09-09

はじめに

Ruby は組込みクラスさえ自由に改変できるけれど,ライブラリーなんかでそんなことをしたら大変なことになってしまう。

そこで,Ruby 2.1 では,既に存在するクラスの改変を限定されたスコープ内でのみ行う仕組みとして,refinement というものが導入された。

導入当初は制約が大きかったが,のちのバージョンで制約が緩められた。

refinement とその制約については既にいくつも記事が書かれてきたが,本記事では(おそらく)まだ書かれていない制約について記述する。

準備

本記事は,文字列をグダグダにする String#sloppy なるメソッドを導入する(英語苦手なので,メソッド名が適切かは知らん)。
以下に示すように,文字列の文字の並びをシャッフルして返すだけ。

refinement は,まず何らかのクラスを拡張するモジュールを

module MyStringExtension
  refine String do
    def sloppy
      chars.shuffle.join
    end
  end
end

のように作っておいて,これを適用したい場所で

using MyStringExtension

のようにすれば,そのスコープで拡張が効くようになる。

しかし,モジュールを定義してすぐその場で使うだけなら

using Module.new{
  refine String do
    def sloppy
      chars.shuffle.join
    end
  end
}

なんて書けばよい。
無名のモジュールを Module.new で作ってすぐに using するわけだ。

ここで注意してほしいのが,{ }begin end do end にしちゃうと,using のブロックと解釈されちゃうこと。もし do end を使うなら,using の引数全体を ( ) で囲まなくてはならない。

これで準備は整った。
ここ以降の説明では,上記の using がなされたスコープでのコードを示す。

うまくいくケース

当然ながら,

"Ruby".sloppy # => "byRu"

のように使える。

また,

p %w(Ruby Python).map(&:sloppy) # => ["uRby", "ohytPn"]

も OK。
この書き方は,Ruby 2.3 では使えなかった(NoMethodError)が,Ruby 2.4 での制約緩和で使えるようになった。ありがたい。

ブロックを与えるべきメソッドに &:sloppy を与えると,まず :sloppy#to_proc

Proc.new{ |receiver, *args| receiver.send(:sloppy, *args) }

的な Proc オブジェクトが作られ,それがブロックとして与えられるわけだが,Ruby 2.4 で send に関する refinement の制約が取り除かれたことでイケるようになったのだろう。

send の件は下記の拙記事参照:
Refinement で追加したメソッドが send で呼べない - Qiita

うまくいかないケース

さきほどの例では,refinement で追加したメソッドが Symbol#to_proc で使えることが確認できた。

当然

puts :sloppy.to_proc.call("Ruby")

もイケるだろうと踏んだが,残念ながら NoMethodError となる。

理由は判らなかった。

追記(2022-03-09) できるようになった

「うまくいかないケース」で挙げたコードを改めて試したところ,

p :sloppy.to_proc.call("Ruby") # => "bRuy"

のように,使えるようになっていた。
少なくとも Ruby 2.7.5 では使える。それ以前のバージョンは調べていない。
この記事を書いたときのバージョンが詳しくは分からない(覚えていない)が,Ruby 2.4 に言及していることから,Ruby 2.4.x と思われる。

3
0
11

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
3
0