Are refinements in Ruby 2.0 totally useless? [closed]
Asked Answered
D

1

-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?

Daphene answered 5/3, 2013 at 9:45 Comment(9)
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.Fallingout
@WayneConrad: I agree. This is a blog post, which may or may not have an actual question hidden somewhere inside all that whining.Evadne
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.Daphene
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?"Fallingout
@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.Daphene
@WayneConrad would you please stop editing posts by adding unknown tags, thanks.Daphene
I did so with only the best intentions. I apologize.Fallingout
@WayneConrad Not at all, but the edit consisting of an only addition of the newly introduced tag looks a bit strange.Daphene
It took the tag edit some time to be approved.Fallingout
B
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.

Bankruptcy answered 5/3, 2013 at 12:38 Comment(2)
Well, sorry for I used “using” instead of “require”. Not requiring MyModule does exactly what not using MyRefinement does.Daphene
@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.Adnah

© 2022 - 2024 — McMap. All rights reserved.