Hijacking `super'

M

Michael Jackson

I know the following is probably impossible, but I just thought I'd
ping the list to see if any adventurous meta programmer knows of a way
to essentially change the binding of a block. Here's essentially what
I'm trying to do:

$ cat super.rb
class A
def call
"A"
end
end

class B < A
def call(&b)
instance_eval(&b)
end
end

puts B.new.call { super }

$ ruby super.rb
super.rb:13:in `block in <main>': super called outside of method (NoMethodError)
from super.rb:9:in `instance_eval'
from super.rb:9:in `call'
from super.rb:13:in `<main>'

Now, when `super' is used inside the block it tries to call a method
with the same name as the method that it's used in, but in the parent
class. Obviously, since the block is created outside of any method
here Ruby complains that super was called outside of a method.
However, if you change the code to use a string and plain old `eval',
you can mimic the desired behavior.

$ cat super1.rb
class A
def call(s)
"A"
end
end

class B < A
def call(s)
eval(s)
end
end

puts B.new.call 'super'

$ ruby super1.rb
A

The only problem now is that you're limited to using strings and you
can't use blocks anymore. The conclusion I've come to is that you
essentially need to be able to alter the binding of the given block
within `call' to be the binding that exists within the `call' method.
Then, super should be able to find the method that it's called in and
the superclass. However, unlike most other things in Ruby, bindings
are pretty opaque.

Any ideas are very much appreciated.

Michael
 
R

Rick DeNatale

I know the following is probably impossible, but I just thought I'd
ping the list to see if any adventurous meta programmer knows of a way
to essentially change the binding of a block. Here's essentially what
I'm trying to do:

$ cat super.rb
class A
=A0def call
=A0 =A0"A"
=A0end
end

class B < A
=A0def call(&b)
=A0 =A0instance_eval(&b)
=A0end
end

puts B.new.call { super }

$ ruby super.rb
super.rb:13:in `block in <main>': super called outside of method (NoMetho= dError)
=A0 =A0 =A0 =A0from super.rb:9:in `instance_eval'
=A0 =A0 =A0 =A0from super.rb:9:in `call'
=A0 =A0 =A0 =A0from super.rb:13:in `<main>'

Now, when `super' is used inside the block it tries to call a method
with the same name as the method that it's used in, but in the parent
class. Obviously, since the block is created outside of any method
here Ruby complains that super was called outside of a method.
However, if you change the code to use a string and plain old `eval',
you can mimic the desired behavior.

$ cat super1.rb
class A
=A0def call(s)
=A0 =A0"A"
=A0end
end

class B < A
=A0def call(s)
=A0 =A0eval(s)
=A0end
end

puts B.new.call 'super'

$ ruby super1.rb
A

The only problem now is that you're limited to using strings and you
can't use blocks anymore. The conclusion I've come to is that you
essentially need to be able to alter the binding of the given block
within `call' to be the binding that exists within the `call' method.
Then, super should be able to find the method that it's called in and
the superclass. However, unlike most other things in Ruby, bindings
are pretty opaque.

Any ideas are very much appreciated.


I don't think it's a question of binding. To evaluate super, the ruby
vm looks in the current stack frame for the method name and arguments
(if no arguments are provided on the super call). The block
evaluation happens in it's own stack frame. Even if the vm were to
look at calling frames for one which had a method, it would find
instance_eval before it found call.



--=20
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale
 
R

Robert Dober

Any ideas are very much appreciated.
This could be a start
class Object
def _super *args, &blk
name = caller[2].split.last.delete("'`")
mthd = self.class.ancestors[1..-1].inject( nil ){ |mthd, mod|
mthd = mthd || ( mod.instance_method name rescue nil )
}
raise NoMethodError, "wassit?" unless mthd
mthd.bind( self ).call( *args, &blk )
end
end
class A
def a; p 42 end
end

class B < A
def a &blk
instance_eval( &blk )
end
def b &blk
instance_eval( &blk )
end

end
B::new.a{ _super }
B::new.b{ _super }

very quick, very dirty and not very much tested (well just the two calls below)
but that might get you somewhere

HTH
R.
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top