Define_method with super in the proc

ran into a serious problemo! i’m trying to create a subclass with
Class.new and then override a method with define_method, using super (of
course) to call up to the superclass. ex-

class A
def doit
@x = 5
end
end

def makeproc
Proc.new {
super
@y = 10
}
end

B = ClassNew(A)
B.class_eval { define_method(:doit, makeproc) }

the problem is that #makeproc is interpreting the super for itself and
thus reports an error of ‘makeproc’ super: no superclass method
’makeproc’ (NameError). obviously that’s not what i want. how strange.
it dosen’t try to define @y = 10 for makeproc’s object. how do i work
around this?

~transami

(") dobee dobee do…
\v/
^ ^

Tom Sawyer wrote:

the problem is that #makeproc is interpreting the super for itself and
thus reports an error of ‘makeproc’ super: no superclass method
’makeproc’ (NameError). obviously that’s not what i want. how strange.
it dosen’t try to define @y = 10 for makeproc’s object.

Even worse, it seems to define @y = 10 for the class object B. In fact,
you’re not getting tied to the makeproc’s object, you’re getting tied to
the class object B itself.

I expanded on your example:
class A
attr_reader :x, :y
def doit
@x = 5
puts "A.doit"
end
end

def makeproc
Proc.new {
@y = 10
puts "B.doit " + self.inspect
}
end

B = Class.new(A)
B.class_eval {
alias doit_super_A doit
define_method(:doit, makeproc )
}
b = B.new
b.doit #=> B.doit B
puts b.y #=> nil
puts B.instance_eval{@y} #=> 10

By the output of “b.doit”, you see that the proc is executed in the
context of the class object B, and not the instance.

Is this a bug? It violates my PoLS atleast. I’m running
ruby 1.6.6 (2001-12-26) [i586-linux-gnu] and
ruby 1.6.7 (2002-03-01) [i686-linux]

how do i work
around this?

I was going to suggest passing :doit_super_A into the makeproc and using
self.send, but in light of the above that won’t help much.

back to scratching head

···


([ Kent Dahl ]/)_ ~ [ http://www.stud.ntnu.no/~kentda/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 4. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

ruby 1.6.6 (2001-12-26) [i586-linux-gnu] and
ruby 1.6.7 (2002-03-01) [i686-linux]

  * eval.c (rb_call0): self in a block given to define_method now be
    switched to the receiver of the method.

Guy Decoux

···

Tue Oct 16 23:29:26 2001 Yukihiro Matsumoto <matz@ruby-lang.org>

thank goodness. after reading kents post i was about to cry. guess its
time to upgrade.

···

On Tue, 2002-07-23 at 05:15, ts wrote:

  • eval.c (rb_call0): self in a block given to define_method now be
    switched to the receiver of the method.


~transami

ts wrote:

    * eval.c (rb_call0): self in a block given to define_method now be
      switched to the receiver of the method.

Thank you. Since this is on the main branch, I take it the best bet
would be to compile from CVS and stay on the 1.7.* branch, if we want to
use define_method.

However, after compiling it up (ruby 1.7.2 (2002-07-13) [i686-linux]),
I notice that this doesn’t solve the super problem Tom had. (Which
probably means I probably was of on a big goose chase.)

Anyway, back to the original problem. Tom, you could alias the super
method and pass its aliased name into the makeproc and call it
explicitly:

class A
attr_reader :x, :y
def doit
@x = 5
end
end

def makeproc( supername )
Proc.new {
self.send(supername)
@y = 10
}
end

B = Class.new(A)
B.class_eval {
alias doit_super_A doit
define_method(:doit, makeproc(:doit_super_A) )
}
b = B.new
b.doit
p b #=> #<B:0x401c92f8 @x=5, @y=10>

HTH, and sorry for depressing you in my previous post :slight_smile:

···

Tue Oct 16 23:29:26 2001 Yukihiro Matsumoto matz@ruby-lang.org


([ Kent Dahl ]/)_ ~ [ http://www.stud.ntnu.no/~kentda/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 4. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

thanks kent,

well, i’m going to upgrade and try out this alias bit. i was surprised
to find my program working (under a simple test) even without the use of
#super, but it may because it was using the class and not the instance.
i’m a bit confused. so let me go into a little more detail and see what
pointers you(s) can give me.

basically, i have a class with an attribute like so:

class A
attr_accessor :a
end

then i subclass it:

B = Class.new(A)

i’m not sure if B should be capitalized, perhaps just b? anyway, then i
override #a= in B.

B.class_eval { define_method(:a=, Proc.new { super; puts a })

so the idea for B is to have #a= still set a, but also to print a.

what’s the best approach to solving this? it looks like i have to
upgrade for starters. then try aliasing. i’ll try that. does anyone have
any other notions for accomplishing this? looks like building classes on
the fly ain’t as smooth as the rest of ruby. but then again, at least
you can do it at all.

like i said, right now my program dosen’t have the #super and yet it
seems to work. how does that make any sense?

~transami

···

On Tue, 2002-07-23 at 06:06, Kent Dahl wrote:

ts wrote:

Tue Oct 16 23:29:26 2001 Yukihiro Matsumoto matz@ruby-lang.org

    * eval.c (rb_call0): self in a block given to define_method now be
      switched to the receiver of the method.

Thank you. Since this is on the main branch, I take it the best bet
would be to compile from CVS and stay on the 1.7.* branch, if we want to
use define_method.

However, after compiling it up (ruby 1.7.2 (2002-07-13) [i686-linux]),
I notice that this doesn’t solve the super problem Tom had. (Which
probably means I probably was of on a big goose chase.)

Anyway, back to the original problem. Tom, you could alias the super
method and pass its aliased name into the makeproc and call it
explicitly:

class A
attr_reader :x, :y
def doit
@x = 5
end
end

def makeproc( supername )
Proc.new {
self.send(supername)
@y = 10
}
end

B = Class.new(A)
B.class_eval {
alias doit_super_A doit
define_method(:doit, makeproc(:doit_super_A) )
}
b = B.new
b.doit
p b #=> #<B:0x401c92f8 @x=5, @y=10>

HTH, and sorry for depressing you in my previous post :slight_smile:


([ Kent Dahl ]/)_ ~ [ http://www.stud.ntnu.no/~kentda/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 4. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)


~transami

Tom Sawyer wrote:

  • eval.c (rb_call0): self in a block given to define_method now be
    switched to the receiver of the method.

thank goodness. after reading kents post i was about to cry. guess its
time to upgrade.

Is this feasible?

class A
def doit
@x = 5
end
end

def makeproc
’; super; @y = 10’
#^ args can go before here
end

B = Class.new(A)
B.class_eval “def doit #{makeproc}; end”

b = B.new
b.doit
p b

produces:

#<B:0x402544f0 @y=10, @x=5>

···

On Tue, 2002-07-23 at 05:15, ts wrote:

Hi –

thanks kent,

well, i’m going to upgrade and try out this alias bit. i was surprised
to find my program working (under a simple test) even without the use of
#super, but it may because it was using the class and not the instance.

One thing that may be involved is the fact that super is actually a
keyword, not a method. That may have something to do with why it has
the effect it does (i.e., being triggered in relation to the method
where the proc is defined, instead of being called in the context of
the method that’s being added).

i’m a bit confused. so let me go into a little more detail and see what
pointers you(s) can give me.

basically, i have a class with an attribute like so:

class A
attr_accessor :a
end

then i subclass it:

B = Class.new(A)

i’m not sure if B should be capitalized, perhaps just b? anyway, then i
override #a= in B.

B.class_eval { define_method(:a=, Proc.new { super; puts a })

so the idea for B is to have #a= still set a, but also to print a.

Why not the old-fashioned way?

class A
attr_accessor :a
end

class B < A
def a=(x)
super
puts @a
return @a # #puts returns nil; this is more conventional
end
end

[…]

like i said, right now my program dosen’t have the #super and yet it
seems to work. how does that make any sense?

Because you’re using alias :slight_smile: alias is a multi-purpose way of
creating a new name for an existing method. super is a specialized
case which searches up the class hierarchy for a method of the same
name as the one it (super) is used in.

So you can, at least theoretically, use alias to mimic super. But in
general, the two are quite different. I’ve found it helpful to think
of them as operating along perpendicular axes. alias is horizontal:
it creates a new name at the same logical and procedural level as the
old name. super is vertical and, as I mentioned, specialized: its job
is to bump control up the inheritance chain, based on method name
similarity.

There have been discussions of this here, which you can track down if
interested, some in the context of the question of combining alias and
super (which I’ve never thought was practicable, since they address
different needs), and/or in the context of general concern about the
safety and cleanness of alias (which I think I do understand, though I
really like alias :slight_smile:

David

···

On Tue, 23 Jul 2002, Tom Sawyer wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Mr. Ogata,

very nice! very nice indeed.

thank you,
transami

···

On Tue, 2002-07-23 at 09:06, George Ogata wrote:

Tom Sawyer wrote:

On Tue, 2002-07-23 at 05:15, ts wrote:

  • eval.c (rb_call0): self in a block given to define_method now be
    switched to the receiver of the method.

thank goodness. after reading kents post i was about to cry. guess its
time to upgrade.

Is this feasible?

class A
def doit
@x = 5
end
end

def makeproc
’; super; @y = 10’
#^ args can go before here
end

B = Class.new(A)
B.class_eval “def doit #{makeproc}; end”

b = B.new
b.doit
p b

produces:

#<B:0x402544f0 @y=10, @x=5>


~transami

Hi –

Mr. Ogata,

very nice! very nice indeed.

thank you,
transami

class A
def doit
@x = 5
end
end

def makeproc
’; super; @y = 10’
#^ args can go before here
end

B = Class.new(A)
B.class_eval “def doit #{makeproc}; end”

b = B.new
b.doit
p b

produces:

#<B:0x402544f0 @y=10, @x=5>

I’m losing sight of the goal here. What’s the advantage of this kind
of indirectness and extra work, when you can just write the code?

class B < A
def doit
super
@y = 10
end
end

Normally I would assume that a small example is encapsulating
something that one would want to do one a larger scale, but I’m not
sure what that would be here. (By “here” I mean the question raised
in the whole thread, not just this example.)

David

···

On Wed, 24 Jul 2002, Tom Sawyer wrote:

On Tue, 2002-07-23 at 09:06, George Ogata wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

well here’s my code (slight out of date now). you’ll notice i have super
remarked out, and i’m just redefining the the method. but like i said i
don’t know how this works exactly in some cases, i.e. when the method is
an assignment (#x=). (i didn’t include any of the widget classes this
draws upon, but you can imagine)

# Widget Factory
#   Returns a new widget object of specialized widget class
def WidgetFactory.new(widget, bindings={}, attributes={})
  
  # a string name will work too
  widget = widget.intern if widget.is_a?(String)
   
  # makes an anoynomous class as subclass of desired widget
  widgetClass = Class.new(WidgetFactory.const_get(widget))
   
  # specialize class via bindings
  bindings.each do |k, v|
    widgetClass.class_eval {
      define_method(k) { |*args|
    #super
        v[0].send(v[1], *args)
      }
    }
  end
    
  return widgetClass.new(attributes)
  
end

so you see i’m sort of dynamically creating classes. does that help?

···

On Tue, 2002-07-23 at 09:32, David Alan Black wrote:

I’m losing sight of the goal here. What’s the advantage of this kind
of indirectness and extra work, when you can just write the code?

class B < A
def doit
super
@y = 10
end
end

Normally I would assume that a small example is encapsulating
something that one would want to do one a larger scale, but I’m not
sure what that would be here. (By “here” I mean the question raised
in the whole thread, not just this example.)


~transami