Forum: Ruby Tricks with method as superclass

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
260b864377cc125e575de59843cb02b3?d=identicon&s=25 Clifford Heath (Guest)
on 2007-01-31 06:35
(Received via mailing list)
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 Heath.
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2007-01-31 06:51
(Received via mailing list)
On Wed, 31 Jan 2007, Clifford Heath 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
E6a1fe85299e663566dd1ea7d4f74e76?d=identicon&s=25 Max Muermann (Guest)
on 2007-01-31 06:59
(Received via mailing list)
>
> 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
260b864377cc125e575de59843cb02b3?d=identicon&s=25 Clifford Heath (Guest)
on 2007-01-31 07:30
(Received via mailing list)
ara.t.howard@noaa.gov 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 Heath.
260b864377cc125e575de59843cb02b3?d=identicon&s=25 Clifford Heath (Guest)
on 2007-01-31 07:55
(Received via mailing list)
Max Muermann 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? :-)
This topic is locked and can not be replied to.