Refinements as files instead of modules
If refinements are to remain file-scoped, then it would be more convenient if
using worked at the file level, akin to
require, rather than behave like a module
include. For instance, instead of:
# foo.rb module Foo refine String do def some_method ... end end end
require 'foo' using Foo
We could do:
# foo.rb class String def some_method ... end end
This would make
using, in a certain sense, polymorphic --if we
require it will extend the classes directly, but if
using then they will be refined instead.
#1 [ruby-core:61881] Updated by Yukihiro Matsumoto over 2 years ago
So your key idea is sharing implementation between monkey-patching and refinements.
The motivation behind introducing refinements is discouraging monkey-patching. So it is not attractive for me to something that helps monkey-patching.
What is your intention behind the proposal? Maybe from monkey-patching to refinements transition?
#2 [ruby-core:61882] Updated by Thomas Sawyer over 2 years ago
Yes, the transition from monkey-patching to refinements is a major part of the intention.
But I also do not expect monkey-patching to ever completely go away. (Do you?) Monkey patching is more convenient, in that it can be done via one require for an entire project, where as refining has to specified per-file. So there will be cases where monkey-patching is still preferred.
Besides personal projects and small end-user tools, a good example, I think, is ActiveSupport. While some of it could be used as refinements, I suspect much of it will remain core extensions b/c it represents Rails' DSL, so to speak. And the parts that can become refinements, I would imagine users would still have an option to load them in as extensions.
Even so, I think the main intent is to ask the question: Do refinements need to be modules? If there is no compelling reason for them to be so, then making refinements file-based would simplify reuse and allow us to shed unnecessary boiler-plate.
#3 [ruby-core:62128] Updated by Thomas Sawyer over 2 years ago
Just FYI, I created this project https://github.com/rubyworks/reusing. I don't much care for what I had to do to implement it, i.e.
- it's not possible to simply override
- had to use my finder gem to find libraries
- had to use clunky gsub and eval
But it appears to be working.
#4 [ruby-core:62136] Updated by Thomas Sawyer over 2 years ago
I realized there is a downside to this approach.
# a.rb require 'b' class String def ab self + "a" + b end end # b.rb class String def b self + "b" end end
So what would happen with
using 'a'? Should the
require 'b' become a
using? If so, then what happens when it requires something that is not an extension, e.g.
If that is an unsolvable issue. The only fix, it seems, would be a way to have something like
require_or_using which would act according to the initial loading method used. And that starts to look pretty ugly.
#5 [ruby-core:62158] Updated by Shugo Maeda over 2 years ago
Monkey-patching style has another problem that super cannot be supported in monkey patching.
I prefer another approach to extend classes both globally and locally in the same manner.
How about to provide a way to activate refinements globally?
using Foo, global: true
# Refinements in Foo are activated globally.
Module#refine creates a module as a refinement, so it can be globally activated by Module#prepend-ing that module to the target class.