2

Given the following example in Ruby 2.0.0:

class Regexp
  def self.build
    NumRegexp.new("-?[\d_]+")
  end
end

class NumRegexp < Regexp
  def match(value)
    'hi two'
  end
  def =~(value)
    'hi there'
  end
end

var_ex = Regexp.build
var_ex =~ '12'   # => "hi there" , as expected
'12' =~  var_ex  # => nil , why? It was expected "hi there" or "hi two"

According to the documentation of Ruby of the =~ operator for the class String:

str =~ obj → fixnum or nil

"If obj is a Regexp, use it as a pattern to match against str,and returns the position the match starts, or nil if there is no match. Otherwise, invokes obj.=~, passing str as an argument. The default =~ in Object returns nil."

http://www.ruby-doc.org/core-2.0.0/String.html#method-i-3D-7E

It is a fact that the variable var_ex is an object of class NumRegexp, hence, it is not a Regexp object. Therefore, it should invoke the method obj.=~ passing the string as an argument, as indicated in the documentation and returning "hi there".

In another case, maybe as NumRegexp is a subclass of Regexp it could be considered a Regexp type. Then, "If obj is a Regexp use it as a pattern to match against str". It should return "hi two" in that case.

What is wrong in my reasoning? What do I have to do to achieve the desired functionality?

toro2k
  • 19,020
  • 7
  • 64
  • 71
David Clavijo
  • 387
  • 1
  • 11

2 Answers2

1

The reason is that you are calling =~ method on a string, not on your NumRegexp object. You need to tell String how to behave:

class String
  def =~(reg)
    return reg=~self if reg.is_a? NumRegexp
    super
  end
end
BroiSatse
  • 44,031
  • 8
  • 61
  • 86
  • I think It won't work as expected, as when calling super , it will try to call the superclass of string method, not the previous method. The solution by Малъ Скрылевъ, implements this right. Although, Ruby keeps having a weird behaviour – David Clavijo Feb 03 '14 at 13:15
  • @DavidDíazClavijo - When you override the method it points to the previous implementation. Try it in irb. – BroiSatse Feb 03 '14 at 13:18
  • class String def to_s 'hello' + super end end a = String.new ('abc') puts a.to_s # => hello# but expected was : helloabc – David Clavijo Feb 03 '14 at 13:54
1

I've found that the record:

var_ex =~ '12'

isn't the same of:

'12' =~  var_ex

It seems that there are no method that calls to class #~= method back, this is a bug already reported, and expected to be solved in 2.2.0. So you have to declare it explicitly:

class String
   alias :__system_match :=~ 
   def =~ regex
      regex.is_a?( Regexp ) && ( regex =~ self ) || __system_match( regex )
   end
end

'12' =~ /-?[\d_]+/
# => 0

This is a possible and acceptable solution using monkey patching but it presents some problems to take into account:

The problem with this is that we have now polluted the namespace with a superfluous __system_match method. This method will show up in our documentation, it will show up in code completion in our IDEs, it will show up during reflection. Also, it still can be called, but presumably we monkey patched it, because we didn't like its behavior in the first place, so we might not want other people to call it.

Community
  • 1
  • 1
Малъ Скрылевъ
  • 16,187
  • 5
  • 56
  • 69
  • "It seems there are no string method that calls to Regexp class #~= method back". In the documentation, it can be read the source of the method implementation. As I understand, it is calling the Regexp methods: in this line "return rb_reg_match(y, x);" when it is a Regexp obj, and in this one "return rb_funcall(y, rb_intern("=~"), 1, x);", when it is another object type. Although, I appreciate your answer, I am asking why this has not the behaviour that should be expected from the documentation. – David Clavijo Feb 03 '14 at 13:12
  • If you try modifying the Regexp's code it has the same behaviour. I found it is a bug already reported: https://bugs.ruby-lang.org/issues/8953 – David Clavijo Feb 04 '14 at 11:04
  • I updated your answer, i hope you are ok with the changes, i did my best, – David Clavijo Feb 04 '14 at 11:14