Bug #7262

module extension (#include/#prepend) in refinements

Added by Yukihiro Matsumoto over 1 year ago. Updated over 1 year ago.

[ruby-dev:46346]
Status:Closed
Priority:Normal
Assignee:Shugo Maeda
Category:core
Target version:2.0.0
ruby -v:ruby 2.0.0dev (2012-11-01 trunk 37415) [i486-linux] Backport:

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.

History

#1 Updated by Shugo Maeda over 1 year 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 methodeach' 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が必要になってしまうと
思いますが、どうでしょうか。

#2 Updated by Anonymous over 1 year ago

まつもと ゆきひろです

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

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

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

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

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

#3 Updated by Shugo Maeda over 1 year 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]

#4 Updated by Shugo Maeda over 1 year ago

  • Status changed from Open to Assigned

#5 Updated by Shugo Maeda over 1 year ago

  • Status changed from Assigned to Closed

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

Also available in: Atom PDF