Feature #12533
closedRefinements: allow modules inclusion, in which the module can call internal methods which it defines.
Description
Right now this isn't possible:
module Extensions
def vegetables ; potatoe ; end
def potatoe ; "potatoe" ; end
end
module Refinary
refine String do
# this doesn't work
include Extensions
# this would work...
# def vegetables ; potatoe ; end
# def potatoe ; "potatoe" ; end
end
end
using Refinary
puts "tomatoe".vegetables
#=> in <main>': undefined method 'vegetables' for "tomatoe":String
Wrongly reported as a bug here.
According to Shugo Maeda, this was expected behaviour. I argued that this is the way most monkey-patches work, and if Refinements can't cover the use case of inserting a custom DSL which references itself in the classes it refines, it can't fully replace monkey-patches, which I read was the main reason Refinements have been added to the language.
Updated by chucke (Tiago Cardoso) over 8 years ago
Sorry about the UI for the gist, apparently I don't know anymore how to properly declare code blocks in redmine...
Updated by akhramov (Artem Khramov) over 8 years ago
- Description updated (diff)
Updated by akhramov (Artem Khramov) over 8 years ago
Hi Tiago, the syntax is
your(code).goes here
Sorry for offtop.
Updated by shugo (Shugo Maeda) over 8 years ago
- Status changed from Open to Assigned
- Assignee set to matz (Yukihiro Matsumoto)
Matz, what do you think of this?
Local rebinding may be worth considering, but there is a trade-off.
Updated by shevegen (Robert A. Heiler) over 7 years ago
Sorry for slight off-topic from me here - I wonder if refinements will be refined when ruby 3.x comes out ... :)
Updated by duerst (Martin Dürst) about 7 years ago
chucke (Tiago Cardoso) wrote:
module Refinary
If possible, please change the spelling to "Refinery".
Updated by shyouhei (Shyouhei Urabe) about 7 years ago
We looked at this issue at a developer meeting today.
The OP's intension is clear. We can describe how it works, but that seems to be different from how it should work. Matz was there at the discussion so he understands the situation. I'm sure he's going to have some decision.
Updated by matz (Yukihiro Matsumoto) about 7 years ago
As you may know, include
inserts the module in the inheritance hierarchy. In this case, module Extensions
is inserted above String
in the using Refinary
scope. That means the lexical scope of vegetables
and potatoes
are different from the refined scope so that potatoes
cannot be called from vegetables
because the scope is not refined. The situation is a bit complex. Do you follow me?
In some other class extension proposals found in other languages (for example, ClassBox in Java), scopes of methods called from within ClassBox are also modified. This is called local rebinding. But we don't choose that because it has bigger side effects. We chose the safer side.
The issue is by include
we might expect the features from the included module are available but in fact, they aren't due to the mechanism of include
and refinements.
So I counter-propose a new feature, Module#inject
. Unlike include
, inject
does not modify inheritance hierarchy. Instead, it copies attributes (constants, modules, and refinements) into the target class/module.
shevegen,
We have no concrete plan to refine the refinement. We are vaguely thinking about combining require
and using
in some way. Just idea.
Matz.
Updated by chucke (Tiago Cardoso) about 7 years ago
How about redefining #include
in the context of refine as what would be expected of the new #inject
method? I get that the semantics of module inclusion differ in both context regarding inheritance hierarchy order, but I'm still thinking from the user perspective: "if I do it, what do I expect to happen?".
From this user perspective, I'd prefer an #include
method which does what I expect, instead of yet another method (#inject
) that I have to learn.
But there might be other implications. I'm fine with whichever proposal which makes refinements more usable for meta-programming.
Updated by nobu (Nobuyoshi Nakada) about 7 years ago
- Related to Bug #14012: NameError is raised when use class variables in Refinements added
Updated by jeremyevans0 (Jeremy Evans) about 3 years ago
- Status changed from Assigned to Closed
Refinement#include
has been deprecated. Refinement#import_methods
can now be used to handle this use case:
module Extensions
def vegetables ; potatoe ; end
def potatoe ; "potatoe" ; end
end
module Refinary
refine String do
import_methods Extensions
end
end
using Refinary
p ''.vegetables
# => "potatoe"
Updated by chucke (Tiago Cardoso) about 3 years ago
Thx for the reply. Is import_methods a new ruby 3.1 feature?
Updated by jeremyevans0 (Jeremy Evans) about 3 years ago
chucke (Tiago Cardoso) wrote in #note-12:
Thx for the reply. Is import_methods a new ruby 3.1 feature?