Tricks with method as superclass


#1

I’m intrigued by the way Camping uses a method invocation to
serve as a superclass, and while exploring, possibly found a
bug? At any rate, some behaviour I can’t explain… but first
a simple example of what Camping does:

class Foo
def initialize(*args)
puts “Initializing Foo”
end
end
=> nil

def Foo(*args)
puts args.inspect
Class.new(Foo) # Interesting that this works…
end
=> nil

class Bar < Foo(“Hi there!”)
end
[“Hi there!”]
=> nil

Bar.new
Initializing Foo
=> #Bar:0x2f84358

Ok, all’s well, and it’s basically what Camping does.
Now what if I want to pass a block to the method
that will return my superclass?

def Foo(*args, &block)
puts args.inspect
block.call if block
Class.new(Foo)
end
=> nil

class Bar < Foo(“Hi There!”) {
puts “Surprise!”
}
end
[“Hi there!”]
Surprise!
=> nil

Cool, that worked well. Now I want to keep the args and block passed
that was passed to the method, so I tried the following, which makes
an anonymous subclass of Foo, and Bar is a subclass of that:

class Foo
def initialize(*args)
puts “Initializing Foo”
end
end
=> nil

def Foo(*args, &block)
puts args.inspect
block.call if block
Class.new(Foo).instance_eval <<-END
def initialize
# I’m going to do something with block and args here
puts “Double surprise!”
end
self
END
end
=> nil

class Bar < Foo(“Hi there!”) {
puts “Surprise!”
}
end
[“Hi there!”]
Surprise!
=> nil

Bar.new
Initializing Foo
=> #Bar:0x2f776b0

What happened to my Double surprise? It seems that instance_eval hasn’t
caused Bar.new to use my extra layer of initialize… The instance of
Bar has the anonymous superclass in it’s hierarchy, but the initialize()
doesn’t appear in the call stack. What gives here?

Clifford H…


#2

On Wed, 31 Jan 2007, Clifford H. wrote:

    => nil
    => nil

puts args.inspect
Surprise!
end
self
=> nil

Bar.new
Initializing Foo
=> #Bar:0x2f776b0

What happened to my Double surprise? It seems that instance_eval hasn’t
caused Bar.new to use my extra layer of initialize… The instance of
Bar has the anonymous superclass in it’s hierarchy, but the initialize()
doesn’t appear in the call stack. What gives here?

 harp:~ > cat a.rb
 class Foo
   def initialize(*args)
       puts "Initializing Foo"
   end
 end

 def Foo(*args, &block)
   puts args.inspect
   block.call if block
   # Class.new(Foo).instance_eval <<-END
   Class.new(Foo).module_eval <<-END
           def initialize
           # I'm going to do something with block and args here
               puts "Double surprise!"
           end
           self
       END
 end

 class Bar < Foo("Hi there!") {
       puts "Surprise!"
   }
 end

 Bar.new


 harp:~ > ruby a.rb
 ["Hi there!"]
 Surprise!
 Double surprise!

regards.

-a


#3

def Foo(*args, &block)
puts args.inspect
block.call if block
Class.new(Foo).instance_eval <<-END
def initialize
# I’m going to do something with block and args here
puts “Double surprise!”
end
self
END

This is really cool. Regarding the double surprise problem, class_eval
instead of instance_eval (and a call to super) does the trick:

Class.new(Foo).class_eval <<-END
def initialize
super
# I’m going to do something with block and args here
puts “Double surprise!”
end
self
END

=> [“Hi there!”]
Surprise!
Initializing Foo
Double surprise!

–max


#4

removed_email_address@domain.invalid wrote:

  # Class.new(Foo).instance_eval <<-END
  Class.new(Foo).module_eval <<-END

Thanks Ara - super-quick response too! Caught out by the singleton,
which I didn’t expect to find on a Class instance. Here’s a complete
example that shows most of what’s possible, assuming the attachment
comes through.

Clifford H…


#5

Max M. wrote:

This is really cool.

Thanks, I agree. Ara beat you to it, but I thought I’d
post a simpler example too. The cute thing about this is
that you can use method_missing magic to make the attached
block use a different DSL than the class body.

$ cat superclass-method-simple.rb
class Foo
def initialize(*args)
puts “Initializing a Foo”
end
end

def Foo(*args, &block)
Class.new(Foo).class_eval <<-END
@@_args = args
@@_block = block
def initialize
super
puts @@_args.inspect
@@_block.call if @@_block
end
self
END
end

class Bar < Foo(“Hi there!”) {
puts “Surprise!”
}
end

puts “All ready, here goes:”
Bar.new

$ ruby superclass-method-simple.rb
All ready, here goes:
Initializing a Foo
[“Hi there!”]
Surprise!
$

Cute, huh? :slight_smile: