From: Michael Jackson on 29 Apr 2010 16:19 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 -- Michael Jackson http://mjijackson.com @mjijackson
From: Rick DeNatale on 29 Apr 2010 20:18 On Thu, Apr 29, 2010 at 4:19 PM, Michael Jackson <mjijackson(a)gmail.com> wrote: > 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. 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. -- 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
From: Robert Dober on 30 Apr 2010 06:10 > 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. > -- > Michael Jackson > http://mjijackson.com > @mjijackson > > -- The best way to predict the future is to invent it. -- Alan Kay
|
Pages: 1 Prev: Ruby require local file problem Next: Code refactoring: request for comment |