Метод Overriding = ~ в подклассе String порождает несогласованность

Я перепробовал метод =~ для подкласса String:

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

В некоторых случаях корректно вызывается переопределенный метод:

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

а в других - обход, а вместо String#=~:

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

Я могу воспроизвести эти результаты на Ruby 1.8.7, 2.1.0. Кто-нибудь знает, почему это происходит? Это ошибка?

Ответ 1

В источнике метода String # = ~ Ruby обрабатывает специальный случай, когда аргумент имеет встроенный Regexp тип, используемый синтаксическим анализатором, который имеет место, когда мы пишем s =~ /abc/.

Метод rb_reg_match можно найти как метод Regexp # = ~.

Таким образом, если вы действительно хотите этого поведения, вы можете обезьяны заплатить класс Regexp, чтобы принимать объекты MyString для оператора = ~, но это может пойти не так очень, очень легко.

Другим подходом будет композиция. Вы можете либо включить модуль Forwardable, либо пойти с простым старым method_missing. Вот пример:

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"