Project

General

Profile

Actions

Bug #7262

closed

module extension (#include/#prepend) in refinements

Added by matz (Yukihiro Matsumoto) over 11 years ago. Updated over 11 years ago.

Status:
Closed
Target version:
ruby -v:
ruby 2.0.0dev (2012-11-01 trunk 37415) [i486-linux]
Backport:
[ruby-dev:46346]

Description

refinementの中でモジュールのincludeやprependがしたい(かつ、そのスコープの範囲内だけで有効にしたい)なんて思ったんですが、きっと困難ですよね。
「無理」と思ったら遠慮なくrejectしてください。

module Experiment
refine String do
include Enumerable
def foo; p :foo; end
end
end

using Experiment
"foo".foo
"foo".each(&:p)

Matz.

Updated by shugo (Shugo Maeda) over 11 years ago

matz (Yukihiro Matsumoto) wrote:

refinementの中でモジュールのincludeやprependがしたい(かつ、そのスコープの範囲内だけで有効にしたい)なんて思ったんですが、きっと困難ですよね。
「無理」と思ったら遠慮なくrejectしてください。

module Experiment
refine String do
include Enumerable
def foo; p :foo; end
end
end

using Experiment
"foo".foo
"foo".each(&:p)

以下では"refinement"はrefineのブロック中のselfになる匿名モジュールを指します。

Enumerableにはeachは定義されていないので、以下のようにしたいということでしょうか。

module Experiment
refine String do
include Enumerable
def foo; p :foo; end
def each; each_line; end
end
end

using Experiment
"foo".foo
p "foo\nbar".map { |i| i.upcase }

実はinclude自体は使えるのですが、上のコードは今の仕様ではエラーになります。

$ ruby /tmp/t.rb
:foo
/tmp/t.rb:11:in map': undefined method each' for "foo\nbar":String (NoMethodError)
from /tmp/t.rb:11:in `'

Enumerableのmap自体はrefinementが有効なスコープで呼べるのですが、
mapの中でeachを呼ぶ時にEnumerable#mapの中ではrefinementが有効で
ないためeachが見つかりません。

まとめると、refinementに対してモジュールをincludeすることはできるが、
そのモジュールでTemplate Methodパターンを使用している場合、テンプレート
メソッドがrefinementで定義されていてもincludeしたモジュールからは呼び
出すことができません。

これを許すようにしようと思うと、local rebindingが必要になってしまうと
思いますが、どうでしょうか。

Updated by Anonymous over 11 years ago

まつもと ゆきひろです

In message "Re: [ruby-dev:46351] [ruby-trunk - Bug #7262] module extension (#include/#prepend) in refinements"
on Fri, 2 Nov 2012 12:04:27 +0900, "shugo (Shugo Maeda)" writes:

|まとめると、refinementに対してモジュールをincludeすることはできるが、
|そのモジュールでTemplate Methodパターンを使用している場合、テンプレート
|メソッドがrefinementで定義されていてもincludeしたモジュールからは呼び
|出すことができません。
|
|これを許すようにしようと思うと、local rebindingが必要になってしまうと
|思いますが、どうでしょうか。

ローカルリバインディングは採用しないというのは大前提として決
めたことなので、この点は取り下げます。テンプレートメソッドパ
ターンを含むものを例題にしたのは失敗でしたね。

ということは、通常のメソッドを追加するだけであればrefinement
内でのinclude/prependは、現状のままで有効であるという理解で
正しいでしょうか。正しければ、この提案はcloseしてください。

                             まつもと ゆきひろ /:|)
Actions #3

Updated by shugo (Shugo Maeda) over 11 years ago

匿名ユーザ wrote:

ということは、通常のメソッドを追加するだけであればrefinement
内でのinclude/prependは、現状のままで有効であるという理解で
正しいでしょうか。正しければ、この提案はcloseしてください。

prependの方はちょっと変な挙動になっているようなので少し考えさせてください。

class C
def foo
[:C]
end
end

module M1
def foo
super << :M1
end
end

module M2
def foo
super << :M2
end
end

module M3
refine C do
include M1
prepend M2

def foo
  super << :M3
end

end
end

using M3
p C.new.foo #=> [:C, :M1, :M3, :M2, :M3]

Actions #4

Updated by shugo (Shugo Maeda) over 11 years ago

  • Status changed from Open to Assigned

Updated by shugo (Shugo Maeda) over 11 years ago

  • Status changed from Assigned to Closed

r38298とr38328で対応したのでcloseします。
期待と違う動作だったらreopenしてください。 > まつもとさん

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0