It was suggested on the IRC channel that I try the ML for this problem ...
While looking at how to undefine a method, I found out that there is a difference in how define_method and the def ... end block works. Example coming up:
# Two classes to play with.
class Parent
def lol
puts "P: LOL"
end
end
class Child < Parent
# Commenting out the redefinition doesn't help
def lol
puts "C: LOL"
end
end
# test1.rb
c = Child.new
c.lol # => "C: LOL" or "P: LOL" if commented out
Child.class_eval { undef_method :lol }
# Try to redefine method and call super.
class Child < Parent
def lol
print "LOL: "
begin
super
rescue NameError => e
puts e.to_s
end
end
end
c = Child.new
c.lol # => "C: LOL", or "P: LOL" if commented out.
Child.class_eval { undef_method :lol }
# Try to redefine method and call super.
Child.class_eval do
define_method(:lol) { print "LOL: "; super }
end
c.lol # => "LOL: P: LOL"
Now ... Why can I call super in the redefinition of the method using define_method, and not using the def ... end block? For me this seems like an inconsistency. First I thought it was neat that I could not call super in the method after using undef_method and redefining it again, but then I found out I could using define_method.
As the comments state, I also tried to run the code with the redefinition of the lol method in the Child class, but to no help. The output is roughly the same (P instead of C obviously).
It was suggested on the IRC channel that I try the ML for this problem ...
While looking at how to undefine a method, I found out that there is a difference in how define_method and the def ... end block works. Example coming up:
<snip>
# Try to redefine method and call super.
class Child < Parent
you are not redefining the method here, you are redefining the class. if you
simply redefine the method it does what you expect:
harp:~ > cat a.rb
class Parent
def lol() puts "P: LOL" end
end
class Child < Parent
def lol() puts "C: LOL" end
end
c = Child.new
Child.class_eval { undef_method :lol }
class Child #< Parent
def lol
print "LOL: "
super
rescue NameError => e
puts e.to_s
end
end
class Child; def lol() print "LOL: "; super end; end
c.lol # raises exception (superclass method disabled)
Child.class_eval { define_method(:lol) { print "LOL: "; super } }
c.lol # prints "LOL: P: LOL"
So you see: defining the method using the def ... end block raises an
exception if you call super after the method has been undefined, while
defining it using define_method does not. I expected it to either raise or not
raise the exception in both cases (but do the same for both).
I hope that clarified it (although I'm not sure it did, as explaining things
can be hard to do even in your native language).
... or am I still redefining the class using the class ... end block?
···
On 31. okt. 2006, at 17:10, ara.t.howard@noaa.gov wrote:
On Tue, 31 Oct 2006, Rune Hammersland wrote:
# Try to redefine method and call super.
class Child < Parent
you are not redefining the method here, you are redefining the class.
if you simply redefine the method it does what you expect:
harp:~ > cat a.rb
class Parent
def lol() puts "P: LOL" end
end
class Child < Parent
def lol() puts "C: LOL" end
end
c = Child.new
Child.class_eval { undef_method :lol }
class Child #< Parent
def lol
print "LOL: "
super
rescue NameError => e
puts e.to_s
end
end
> Hello dear Rubyists.
>
> It was suggested on the IRC channel that I try the ML for this problem
...
>
> While looking at how to undefine a method, I found out that there is a
> difference in how define_method and the def ... end block works. Example
> coming up:
<snip>
> # Try to redefine method and call super.
> class Child < Parent
you are not redefining the method here, you are redefining the class.
[...]
Ara I am afraid that is not so :(, run this e.g.
class Parent
end
class Child < Parent
puts self.object_id
def one; puts "one" end
end
class Child < Parent
puts self.object_id
end
Child.new.one
It does the same as OP's code, I really feel this is strange.
Rune, on philiosophical grounds, I prefer the second behavior the super
method being available again, can you elaborate on why you prefer the first?
Cheers
Robert
···
On 10/31/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
On Tue, 31 Oct 2006, Rune Hammersland wrote:
--
The reasonable man adapts himself to the world; the unreasonable one
persists in trying to adapt the world to himself. Therefore all progress
depends on the unreasonable man.
>> # Try to redefine method and call super.
>> class Child < Parent
>
> you are not redefining the method here, you are redefining the class.
> if you simply redefine the method it does what you expect:
>
> harp:~ > cat a.rb
> class Parent
> def lol() puts "P: LOL" end
> end
>
> class Child < Parent
> def lol() puts "C: LOL" end
> end
>
> c = Child.new
> Child.class_eval { undef_method :lol }
>
> class Child #< Parent
> def lol
> print "LOL: "
> super
> rescue NameError => e
> puts e.to_s
> end
> end
>
> c.lol
>
> harp:~ > ruby a.rb
> LOL: superclass method `lol' disabled
That is actually the same output I had. My problem (and excuse me for
not
stating it clearly) is that if I use define_method after undef_method:
class Child; def lol() print "LOL: "; super end; end
c.lol # raises exception (superclass method disabled)
Child.class_eval { define_method(:lol) { print "LOL: "; super } }
c.lol # prints "LOL: P: LOL"
So you see: defining the method using the def ... end block raises an
exception if you call super after the method has been undefined, while
defining it using define_method does not. I expected it to either
raise or not
raise the exception in both cases (but do the same for both).
I hope that clarified it (although I'm not sure it did, as explaining
things
can be hard to do even in your native language).
.. or am I still redefining the class using the class ... end block?
--
Vennlig Hilsen / Regards
Rune Hammersland
Hmm, very odd! You can get around this by doing something like this:
class Object
def supercall(meth, *args)
method_name = meth.to_s
current_class = self.class
m = nil
until m
raise NoMethodError if current_class == Object
current_class = current_class.superclass
if current_class.instance_methods.include?(method_name)
m = current_class.instance_method(method_name)
end
end
m.bind(self).call(*args)
end
end
You then replace the "super" keyword with a supercall(:foo) in your
example and it works. This is much slower though.
···
On 31. okt. 2006, at 17:10, ara.t.howard@noaa.gov wrote:
> On Tue, 31 Oct 2006, Rune Hammersland wrote:
It does the same as OP's code, I really feel this is strange.
Rune, on philiosophical grounds, I prefer the second behavior the super
method being available again, can you elaborate on why you prefer the
first?
I don't necessarily prefer the first, I prefer them to behave the same way.
Actually having the super method available makes more sense.
The reason I stumbled upon this is because I'm writing about sandboxing and
would like to explain how you can supply "safe" versions of classes in the
sandbox by "disabling" unsafe methods (of course it doesn't help if you can
simply redefine the method yourself, but it's a first step). When playing
around with undef_method and remove_method I found that remove_method only
removes method defined in the class it self (and not derived methods), while
undef_method prevents objects of the class from responding to the method (as
is explained in `ri Module.undef_method`). And then I found this inconsistency
in behavior.
While we're on it: any good suggestions for how to supply "safe" versions of
classes to the sandbox? Something like how Python's Bastion module is/was
supposed to work (it's now deprecated, together with the RExec module). I
guess the best would be to call remove_method on the unsafe methods in the
classes they are defined, and hope that an attacker don't redefine the method
himself (which is hard to prevent).
···
On 10/31/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
I agree, I think consistent behavior would be preferable, not losing the
super method seems also more POLS to me.
Now after all that talk
Great You Came Up With This , Rune!!!
···
On 11/1/06, Rune Hammersland <rune@snuskete.com> wrote:
Robert Dober wrote:
> On 10/31/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
> <Ara's version of code snipped>
>> harp:~ > ruby a.rb
>> LOL: superclass method `lol' disabled
>
> It does the same as OP's code, I really feel this is strange.
> Rune, on philiosophical grounds, I prefer the second behavior the
> super
> method being available again, can you elaborate on why you prefer the
> first?
I don't necessarily prefer the first, I prefer them to behave the
same way.
Actually having the super method available makes more sense.
=======================
Might this go away?
The reason I stumbled upon this is because I'm writing about
sandboxing and
would like to explain how you can supply "safe" versions of classes
in the
sandbox by "disabling" unsafe methods (of course it doesn't help if
you can
simply redefine the method yourself, but it's a first step). When
playing
around with undef_method and remove_method I found that remove_method
only
removes method defined in the class it self (and not derived
methods),
there was a thread about that, if you are interested it might pay to search
the archives
<SNIP>
--
Vennlig Hilsen / Regards
Rune Hammersland
--
The reasonable man adapts himself to the world; the unreasonable one
persists in trying to adapt the world to himself. Therefore all progress
depends on the unreasonable man.