-3

There were so-called refinements introduced in Ruby 2.0. I was playing with them and now I’m totally cajoled:

— The main declared advantage of refine is that they are not global scoped. Bah.

module MyModule
  class ::String
    def my_locally_needed_func
      # do smth 
    end
  end
end

# here I need it
require 'mymodule'
"".my_locally_needed_func

is isolated not worse.

— Refinements do not support class methods. Bah. Of course they are through a hack (remember, everything is an object):

module VoidRefinements
  refine String do
    def self.singleton_method_for_string_class
      puts "inside singleton_method_for_string_class"
    end 
  end 
end

module VoidRefinementsOK
  refine Class do
    def singleton_method_for_string_class
      err_msg = "NoMethodError: undefined method ‘#{__method__}’ for ‘#{self}:#{self.class}’"
      raise NoMethodError.new(err_msg) unless String == self
      puts "inside proper singleton_method_for_string_class"
    end 
  end 
end

using VoidRefinements
String.singleton_method_for_string_class rescue puts $!

using VoidRefinementsOK
String.singleton_method_for_string_class rescue puts $!

# undefined method `singleton_method_for_string_class' for String:Class
# inside proper singleton_method_for_string_class

The latter is not even resulting in performance penalties, since nobody would call Fixnum.substr on purpose.

— Refinements are executed through eval. refine is not a keyword. Bah. (well, “bah!” again.)

So, my question is: am I missing smth or everybody sees no advantages in the newly introduced feature?

dgilperez
  • 10,716
  • 8
  • 68
  • 96
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • 3
    I've voted to close because the title and text are argumentative. With a title and text that does not presuppose that refinements are useless (and without all the bah's), I think this question would be a keeper. – Wayne Conrad Mar 05 '13 at 12:08
  • 5
    @WayneConrad: I agree. This is a blog post, which may or may not have an actual question hidden somewhere inside all that whining. – Jörg W Mittag Mar 05 '13 at 12:14
  • Really? Then give me a little hint of why do I see “there is nothing invented” and explain, what’s new had being introduced (keeping in mind that isolation **was not** a very feature of `refine`. – Aleksei Matiushkin Mar 05 '13 at 12:23
  • 4
    I am not entering into the argument about whether or not refinements are useful. What I am saying is that a question that presupposes from the very beginning _that they are not_ is not a good question. Better would be "What are refinements useful for?" – Wayne Conrad Mar 05 '13 at 13:16
  • @WayneConrad I have read the FAQ thoroughly and totally accepted the term “show what you have already tried.” The answers below (like “Refinements aren't globally scoped…” while old good `require` are not as well) precisely show, that actually the question stated in the proper manner. Sorry. – Aleksei Matiushkin Mar 05 '13 at 13:23
  • @WayneConrad would you please stop editing posts by adding unknown tags, thanks. – Aleksei Matiushkin Mar 05 '13 at 16:48
  • I did so with only the best intentions. I apologize. – Wayne Conrad Mar 05 '13 at 18:05
  • @WayneConrad Not at all, but the edit consisting of an only addition of the newly introduced tag looks a bit strange. – Aleksei Matiushkin Mar 05 '13 at 19:35
  • It took the tag edit some time to be approved. – Wayne Conrad Mar 05 '13 at 19:37

1 Answers1

22

You completely dismiss the fact that Refinements aren't globally scoped, but that's the very reason for their introduction. Of course, if you simply ignore the reason for something's existence, then you obviously won't see any value in it.

But, see the isolation in action. Here is your example modified to use Refinements:

module MyModule
  refine String do
    def my_locally_needed_func
      # do smth 
    end
  end
end

module MyOtherModule
  # The monkeypatch is invisible:
  "".my_locally_needed_func
  # NoMethodError: undefined method `my_locally_needed_func' for "":String

  # I first have to use the Refinement:
  using MyModule
  "".my_locally_needed_func
end

# The monkeypatch is scoped. Even though we were able to use 
# it in MyOtherModule, we still cannot use it at the top-level:
"".my_locally_needed_func
# NoMethodError: undefined method `my_locally_needed_func' for "":String

# We have to call `using` again, for the top-level:
using MyModule
"".my_locally_needed_func

Here is your example for comparison:

module MyModule
  class ::String
    def my_locally_needed_func
      # do smth 
    end
  end
end

# here I need it
"".my_locally_needed_func

Note: I removed the call to using which didn't make sense since you didn't use Refinements anyway.

In your case, the monkeypatch is available globally, because you simply modified the String class. This functionality is called an "open class", and it is precisely what Refinements are there to avoid.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • Well, sorry for I used “using” instead of “require”. Not requiring `MyModule` does exactly what not `using MyRefinement` does. – Aleksei Matiushkin Mar 05 '13 at 12:48
  • 3
    @mudasobwa No, it doesn't do the same thing. Suppose your project has two files in it, 'foo.rb' and 'bar.rb'. If 'foo.rb' requires MyModule, 'bar.rb' will see the changes installed by MyModule, even if it doesn't require MyModule itself. If 'foo.rb' uses MyRefinement, 'bar.rb' *won't* see those changes, unless it also uses MyRefinement. – Becca Royal-Gordon Feb 12 '15 at 02:51