Prototype

I’m reading a paper called [Metaprogramming in Ruby – A Pattern Catalog] by Sebastian Gunther and Marco Fischer. They gave some code example, that I did not understand properly in the beginning. Hence, this post aims to clarify the mystery of those lines.

This part of code is from Figure 11, the implementation of Prototype module and its usage inside Hydra class. I modified the code to add some output to see how the code is executed.

module Prototype
  def self.included ( base )
    puts "#{self} included in #{base}"

    base.class_eval do
      puts "#{self}.class_eval is called"

      def initialize
        puts 'Prototype Initialize Called'
        raise " InstantiationError "
      end
    end

    base.instance_eval do
      puts "#{self}.instance_eval is called"

      def create
        puts "create() is called, self = #{self}"
        prototype = self.clone

        prototype.instance_eval do
          puts "prototype.instace_eval() is called"

          def initialize
            puts 'prototype.instance_eval.initialize() is called'
            raise " InstantiationError "
          end
        end

        prototype.instance_eval { def is_prototype?; true ; end }

        puts "... returning a cloned prototype #{prototype}"
        return prototype
      end
    end
  end
end

When we include a module Prototype in a class, we can see the execution. The function included is called when Prototype is included in Hydra. Inside that function, the class_eval and instance_eval do their jobs. `

class Hydra
  include Prototype
end

===> We get
Prototype included in Hydra
Hydra.class_eval is called
Hydra.instance_eval is called

We cannot initiate an instance from Hydra class dge

hydra = Hydra.create

===> We get
create() is called, self = Hydra
prototype.instace_eval() is called
... returning a cloned prototype #<Class:0x007feadd8429f0>

Looking a bit deeper

irb(main):093:0> hydra.methods false
=> [:initialize, :create, :is_prototype?]
irb(main):094:0> hydra.instance_methods false
=> []
irb(main):107:0> hydra
=> #<Class:0x007feadd8429f0>
irb(main):109:0> hydra.included_modules
=> [Prototype, Kernel]

And it’s getting weird here. So Hydra.create is returning cloned object of Hydra instead of an instance of Hydra. And that cloned object, in return, also have a class method create.

irb(main):115:0> Hydra.create.create.create
create() is called, self = Hydra
prototype.instace_eval() is called
... returning a prototype #<Class:0x007feadc8240a0>
create() is called, self = #<Class:0x007feadc8240a0>
prototype.instace_eval() is called
... returning a prototype #<Class:0x007feadd8fbc70>
create() is called, self = #<Class:0x007feadd8fbc70>
prototype.instace_eval() is called
... returning a prototype #<Class:0x007feadd8fb888>
=> #<Class:0x007feadd8fb888>
Advertisements