Intercept initialize using macro

I want to intercept the initialize method using macro in order to do few instructions before the initialize

I tried to overwrite the Class.new methods but I was note able to propagate the input parameters to the initialize method

Hello,

If you have something like that

class MyClass
  def initialize(@foo : Int32, @bar : Int32)
  end
end

You can try this: (the previous_def will call the new(Int32, Int32) that was defined with the initialize method)

class MyClass
  def self.new(foo : Int32, bar : Int32)
    puts "Do something before initialize"
    previous_def(foo + 1, bar + 2)
  end
end

pp MyClass.new(1, 2) 
# => Do something before initialize
# => #<MyClass:0x7f751bf59ea0 @bar=4, @foo=2>
2 Likes

thanks a lot this solved one of my problem
but Is It possibile to intercept more initialize methods each one with differenti signature?

1 Like

humm… a signature like self.new(*args, **options) would catch everything, the problem id you do that, more specific calls would always be selected before our self.new(*args, **options). An alternative I think for that is to use another name. E.g.

class MyClass
  def self.create(*args, **options)
    puts "Do something before initialize"
    new(*args, **options)
  end
end

And then:

class MyClass
  def initialize(@foo : Int32, @bar : Int32)
    puts "initialize 1"
  end

  def initialize(@foo : Int32)
    puts "initialize 2"
    @bar = 0
  end

  def initialize(foo : Array(Int32), *args)
    puts "initialize 3"
    @foo = foo[0]
    @bar = args.sum
  end
end

pp MyClass.create(1, bar: 2)
pp MyClass.create(3)
pp MyClass.create([4], 5, 3)

# => Do something before initialize
# => initialize 1
# => Do something before initialize
# => initialize 2
# => Do something before initialize
# => initialize 3
2 Likes

thanks a lot, using your suggestione and macros I was able to intercept every initialize method called

I did something like this:

{% for mtd in @type.methods %}
  {% if mtd.name == "initialize" %}
    {% mtd_ini = mtd.stringify.split("\n")[0].gsub(/def\s+/, "") %}
      def {{mtd_ini.id}}
        puts "before"
        previous_def
        puts "after"    
      end
    {% end %}
  {% end %}
1 Like

Maybe you also need to handle the case where the only initialize is the parent class.

{% if @type.superclass %}
       def initialize(*args)
          super
        end
{% end %}

yes thanks good point, I did not consider this case