Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding =~ method on a String subclass generates inconsistency

I overrode the =~ method for a subclass of String:

class MyString < String
  def =~(obj)
    "Overridden method."
  end
end
s = MyString.new "abc"

The overridden method is correctly called in some cases:

r = /abc/
s =~ r             # => "Overridden method."
s.send(:=~, r)     # => "Overridden method."
s.send(:=~, /abc/) # => "Overridden method."

while in others it is bypassed, and String#=~ is called instead:

s =~ /abc/         # => 0
s =~ (/abc/)       # => 0

I can reproduce these results on Ruby 1.8.7, 2.1.0. Does someone know why this happens? Is it a bug?

like image 274
Gabriel Avatar asked Feb 28 '14 18:02

Gabriel


1 Answers

In the source for the String#=~ method, Ruby handles the special case where the argument is of a built-in Regexp type used by the parser, which is the case when we write s =~ /abc/.

The rb_reg_match method can be found as being the Regexp#=~ method.

Thus, if you really want this behavior you can monkey patch the Regexp class to accept objects of MyString for the =~ operator, but that might go wrong very, very easily.

Another approach would be composition. You can either include the Forwardable module or go with plain old method_missing. Here's an example:

class MyString
  attr_reader :string

  def initialize(str)
    @string = str
  end

  def method_missing(*args)
    string.public_send(*args)
  end

  def =~(obj)
    "Overriden"
  end
end

s = MyString.new "abc"
s =~ /abc/              # => "Overriden"
s =~ Regexp.new("abc")  # => "Overriden"
like image 159
Ivan Zarea Avatar answered Nov 15 '22 19:11

Ivan Zarea