ruby1.9.1 : Override string method

T

Ts

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 :eek:ld_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>'
 
B

Brian Candler

Thomas said:
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.
 
D

David A. Black

Hi --

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.

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)
 
B

Brian Candler

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.
 
T

Thomas Sevestre

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 :eek:ld_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
 
C

Charles Oliver Nutter

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top