Forum: Ruby ruby1.9.1 : Override string method

Posted by Thomas Sevestre (thomas-sevestre)
on 2009-11-02 12:49
The following script doesn't work...
Do you have any idea of what is wrong?
Is it a ruby bug? (ruby version: ruby 1.9.1p243 (2009-07-16 revision
24175) [i386-darwin9])

require 'time'

Time.parse("1999-10-31 16:46:50") # this works

class String
  alias_method :old_sub!, :sub!
  def sub!(*args, &block)
    old_sub!(*args, &block)
  end
end

Time.parse("1999-10-31 16:46:50") # this crashes

# =>
#
# ArgumentError: argument out of range
#   from /opt/local/lib/ruby1.9/1.9.1/time.rb:202:in `local'
#   from /opt/local/lib/ruby1.9/1.9.1/time.rb:202:in `make_time'
#   from /opt/local/lib/ruby1.9/1.9.1/time.rb:261:in `parse'
#   from (irb):10
#   from /opt/local/bin/irb1.9:12:in `<main>'
Posted by Brian Candler (candlerb)
on 2009-11-02 13:08
Thomas Sevestre wrote:
> 
> The following script doesn't work...
> Do you have any idea of what is wrong?

There are loads of sub! calls in date/format.rb, but I have no idea why 
delegating sub! in this way would break it.

I note that the same error occurs in 1.8.6 too:
ArgumentError: argument out of range
  from /usr/lib/ruby/1.8/time.rb:184:in `local'
  from /usr/lib/ruby/1.8/time.rb:184:in `make_time'
  from /usr/lib/ruby/1.8/time.rb:243:in `parse'
  from (irb):19

I copied time.rb to my local directory and modified it to puts the 
arguments to local. I see that it is calling

 Time.local(1999, 10, 31, 16, 46, 50, 0)

before the sub! change, but

 Time.local(2009,0,1,0,0,0,0)

after it. Most bizarre.
Posted by David A. Black (Guest)
on 2009-11-02 13:40
(Received via mailing list)
Hi --

On Mon, 2 Nov 2009, Brian Candler wrote:

>  from /usr/lib/ruby/1.8/time.rb:184:in `local'
>
> Time.local(2009,0,1,0,0,0,0)
>
> after it. Most bizarre.

I believe it's about the $1, $2... variables:

"abc".sub!(/(.)/, "z")
p $1                          # nil
"abc".old_sub!(/(.)/, "z")
p $1                          # "a"

When sub! calls old_sub!, the $n variables inside sub! are not set,
but the ones in old_sub! are. When the date parser hits _parse_iso (or
whichever of that family of methods it hits), there are calls to sub!
followed by usage of the $n variables, which are actually not set
because they're two methods removed.

A more generic example:

def y(str)
   /(.)/.match(str)
   p $1
end

def x(str)
   y(str)
   p $1
end

x("abc")

   =>   "a"
        nil


David

--
The          Ruby training with D. Black, G. Brown, J.McAnally
Compleat     Jan 22-23, 2010, Tampa, FL
Rubyist      http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)
Posted by Brian Candler (candlerb)
on 2009-11-02 14:23
> A more generic example:

So the $~ variables are both thread-local *and* scoped to the enclosing
method? That's definitely new to me. Thanks for the explanation.
Posted by Thomas Sevestre (thomas-sevestre)
on 2009-11-02 16:31
Interesting! I wasn't aware of that.

Is there any way to make it work?

It is feasible in your generic example:

def y(str, b)
   eval "/(.)/.match(str)", b
   p $1
end

def x(str)
   y(str, binding)
   p $1
end

x("abc")

# =>
#
# nil
# "a"

But in my case, I need to find the caller's binding :

class String
  alias_method :old_sub!, :sub!
  def sub!(*args, &block)
    eval "old_sub!(*args, &block)", caller_binding
  end
end

But I havn't been able to retrieve it :( Do you have any idea?

Thomas
Posted by Charles Nutter (headius)
on 2009-11-02 18:13
(Received via mailing list)
On Mon, Nov 2, 2009 at 9:31 AM, Thomas Sevestre <ts@tcare.fr> wrote:
> Interesting! I wasn't aware of that.
>
> Is there any way to make it work?

It is not possible to make it work. I have written on this previously,
but it's a particular ugly wart of Ruby that the $_ and $~ variables
(and related vars like $1, $&, etc) are "special", and only certain
core class methods are able to modify them (in the caller's scope)
while no Ruby code can. It is for this reason that you can't alias and
wrap any of those methods without breaking them.

To be honest, Ruby would be *far* better off if $_ and $~ went away,
since I believe they're overreaching by modifying the caller's scope
without your knowledge.

- Charlie
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.