super with block

L

Lou Zell

Hi all,

I'm looking for a bit of help. I can't seem to get the block to "pass
through" to the parent class. I figured without an '&' in the parameter
list there would be no conversion to proc but maybe that is not the
case.

class Nstring < String
def gsub!(*args)
super
end
end

This works as I expected:

s = Nstring.new("hi")
s.gsub!(/(hi)/,'\1')
=> "hi"

But this, not so much:

s.gsub!(/(hi)/) {$1}
=> ""

Thanks,
Lou
 
J

Jesús Gabriel y Galán

Hi all,

I'm looking for a bit of help. I can't seem to get the block to "pass
through" to the parent class. I figured without an '&' in the parameter
list there would be no conversion to proc but maybe that is not the
case.

class Nstring < String
def gsub!(*args)
super
end
end

This works as I expected:

s = Nstring.new("hi")
s.gsub!(/(hi)/,'\1')
=> "hi"

But this, not so much:

s.gsub!(/(hi)/) {$1}
=> ""

Hi,

This worked for me:

irb(main):023:0> class NString < String
irb(main):024:1> def gsub!(*args, &blk)
irb(main):025:2> super(*args, &blk)
irb(main):026:2> end
irb(main):027:1> end
=> nil
irb(main):028:0> s = NString.new("hi")
=> "hi"
irb(main):029:0> s.gsub!(/(hi)/){$1}
=> "hi"

Hope this helps,

Jesus.
 
L

Lou Zell

Hi,
This worked for me:

irb(main):023:0> class NString < String
irb(main):024:1> def gsub!(*args, &blk)
irb(main):025:2> super(*args, &blk)
irb(main):026:2> end
irb(main):027:1> end
=> nil
irb(main):028:0> s = NString.new("hi")
=> "hi"
irb(main):029:0> s.gsub!(/(hi)/){$1}
=> "hi"

Hope this helps,

Jesus.


Hi Jesus, thank you for your response. Unfortunately, I could not
replicate your results:

=> ""


Perhaps it is my version of ruby?
~$ ruby -v
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]


Lou
 
M

Mikael Høilund



Looks like this is a problem with using $1 inside the block. Replacing =20=

{$1} with {|m| m} makes it work.

However, a different issue I now remember running into a long time ago =20=

is that `super` is pretty quirky regarding blocks:

Calling it as just `super` will pass the block.
`super(*args, &block)` will as well, obviously.
`super(*args)` will too!

The only way to avoid passing the block to super is `super(*args, =20
&nil)`, however ugly that might be.

All of this is tested in 1.8.6, and is probably the same in 1.9.


--=20
Mikael H=F8ilund
http://hoilund.org/
 
R

Robert Dober

Hi Jesus, thank you for your response. Unfortunately, I could not
replicate your results:


=> nil
your problem has nothing to do with passing the block along in gsub!guess what s is here?
If you guessed "", you guessed correctly
=> "hi"
=> ""
normal given that s was ""

Probably subclassing is not what you want, but it can be accomplished of course
Perhaps it is my version of ruby?
~$ ruby -v
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]


Lou

Here is the final solution
class NString < String
def initialize str
replace str
end
def gsub! *args
super( *args, &Proc::new ) # that is just to show you yet another
way to do this but passing &blk is better
end
end

n = Nstring::new( "123")
p n
p n.gsub!(/.../){ |m| m.reverse } # $1 is not set as somebody did
point out already

HTH
Robert
 
L

Lou Zell

Mikael, I can deal with that, I'll swap out the $'s with block params.
Thanks for the workaround!

Lou
 
R

Robert Dober

ERRATUM

use
replace str[0..-1] or
replace str.dup

instead of
replace str

bad mistake of my part sorry.

Robert
 
L

Lou Zell

your problem has nothing to do with passing the block along in gsub!
guess what s is here?
If you guessed "", you guessed correctly

Wait, why would s be empty? I can use Nstring.new just like String.new
hi


Lou
 
R

Robert Dober

If you guessed "", you guessed correctly
Wait, why would s be empty? I can use Nstring.new just like String.new
you are right I am wrong, no idea what I did OMG.
Seems your only problem was the $1 in the block
Sorry for the noise
R.
 
G

Gregory Brown

Wait, why would s be empty? I can use Nstring.new just like String.new

hi

Same here:

seltzer:~ sandal$ ruby -v
ruby 1.8.6 (2008-06-20 patchlevel 230) [i686-darwin8.11.1]
seltzer:~ sandal$ irbhi

That having been said, I generally avoid sub-classing core Ruby
objects because I think some of them use optimizations that cause
internal C methods to be called rather than the methods of a subclass,
resulting in strange behaviour. That having been said, I don't know
that I've ever seen it in practice. Does anyone have an example of
this, or am I just being paranoid?

-greg
 
L

Lou Zell

Lou said:
Mikael, I can deal with that, I'll swap out the $'s with block params.
Thanks for the workaround!

Lou

Actually, this did not work the way I expected. It is fine for a case
like this:
str = "match1"
gsub!(/(match1)/) {|m| m}

But not this:
str = "match1match2"
gsub!(/(match1)(match2)/) {|m1,m2| m1 + m2}

Where I am really looking for:
str = "match1match2"
gsub!(/(match1)(match2)/) { $1 + $2}


So let me ask this instead: How do I pass a block from one method to
another without invoking the block?

For instance, let's say I want to create a method nsub! that behaves
EXACTLY like gsub!

class String
def nsub!(*args)
gsub!(args[0]) {yield}
end
end

This will not work, say I do this:

s = String.new("whatever")
s.nsub!(/(what)/) {$1}

The yield in the body of nsub! invokes the block {$1}, but in this
context $1 is nil, so it is like I am calling

gsub!(args[0]) {nil}

Which is obviously not what I want. I would like to pass the original
code block:

gsub!(args[0]) {$1}


Any ideas?

Lou
 
G

Gregory Brown

Which is obviously not what I want. I would like to pass the original
code block:

gsub!(args[0]) {$1}


Any ideas?

If you don't mind a little redundancy, you can match the string first,
pull the MatchData object, and yield that:
class String
def nsub!(*args)
m = match(args[0])
gsub!(args[0]) { yield(m) }
end
end => nil
str = "match1match2" => "match1match2"
str.nsub!(/(match1)(match2)/) { |m| m[1] + "---" + m[2] } => "match1---match2"
str
=> "match1---match2"

It would be nice if MatchData was yielded by gsub instead of a string.

-greg
 
G

Gregory Brown

Actually, this did not work the way I expected. It is fine for a case
like this:
str = "match1"
gsub!(/(match1)/) {|m| m}

But not this:
str = "match1match2"
gsub!(/(match1)(match2)/) {|m1,m2| m1 + m2}

Where I am really looking for:
str = "match1match2"
gsub!(/(match1)(match2)/) { $1 + $2}


So let me ask this instead: How do I pass a block from one method to
another without invoking the block?

I forgot to answer this before:
class String
def nsub!(*args,&block)
gsub!(args[0],&block)
end
end

But unfortunately, this does not fix your problem:
NoMethodError: undefined method `+' for nil:NilClass
 
R

Robert Dober

It would be nice if MatchData was yielded by gsub instead of a string.

-greg
Well, maybe, but do not despair, while the $ special variables are not
available in the block Regexp.last_match *is*

506/6 > cat gsub.rb && ruby gsub.rb
'abc'.gsub(/(.)(.)/){ p Regexp.last_match }
#<MatchData "ab" 1:"a" 2:"b">

HTH
Robert
 
L

Lou Zell

But unfortunately, this does not fix your problem:

Indeed. But all is not lost. I *think* this will work fine for what I
need to do (it's tweaked from the solution you gave earlier, thank you).

class String
def nsub!(*args)
if m = match(args[0])
gsub!(args[0]) {yield(*m.to_a[1..-1])}
end
end
end
=> "match2match1"

I haven't done much with it yet, but from some preliminary tests it
seems to work.

Thanks for the help everyone!

Lou
 
J

Jesús Gabriel y Galán

Hi Jesus, thank you for your response. Unfortunately, I could not
replicate your results:

Sorry, it seems that my $1 was set by previous tests in IRB :-(
The others are right and the discussion has been interesting.
It also shows you have to be careful with tests in IRB...

Jesus.
 
R

Robert Dober

Sorry, it seems that my $1 was set by previous tests in IRB :-(
The others are right and the discussion has been interesting.
It also shows you have to be careful with tests in IRB...
I know that and I still managed to screw up my String subclass
constructor :-(. But I agree, interesting thread, always
good to refresh some "basic" things.
Cheers
Robert
 

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,780
Messages
2,569,607
Members
45,240
Latest member
pashute

Latest Threads

Top