1

I'm writing aspect-ish code more or less lifted from the chosen solution for this question that looks like the following:

class Module
  def add_logging klass, *method_names
    method_names.each do |method_name|
      original_method = instance_method method_name
      define_method method_name do |*args, &blk|
        log.debug("#{klass}.#{method_name} called")
        original_method.bind(klass).call(*args, &blk)
      end
    end
  end
end

The solution in the other post doesn't require the klass parameter, but it only works for instance methods, whereas I hope to call my code like this:

module MyModule
  def MyModule.module_method
    p "hello"
  end
  class << self
    add_logging self, :module_method1
  end
end

Unfortunately when I run this code I get in 'bind': singleton method called for a different object (TypeError). Seeing as I pass self in the context of the class << self block, I don't understand why the bind call in the above code thinks it isn't binding to the exact same metaclass.

Community
  • 1
  • 1
iftheshoefritz
  • 5,829
  • 2
  • 34
  • 41

1 Answers1

2

This should work for you:

class Module
  def add_logging(*method_names)
    method_names.each do |method_name|
      original_method = method(method_name).unbind
      define_singleton_method(method_name) do |*args, &blk|
        puts "#{self}.#{method_name} called"
        original_method.bind(self).call(*args, &blk)
      end
    end
  end
end

# class method example
module MyModule
  def self.module_method1
    puts "hello"
  end

  add_logging :module_method1
end

MyModule.module_method1

# output:
#
# MyModule.module_method1 called
# hello
Ryan LeCompte
  • 4,281
  • 1
  • 14
  • 14
  • Great. I take it my error is due to define_method not liking singleton methods? Or is there some other obvious reason why my code wouldn't have worked? – iftheshoefritz Jan 20 '12 at 08:56
  • Well if you look closely, you are actually binding the method back on klass, which in your example is the singleton class. The method should be bound to the single instance of the singleton class. Here's a simpler example to see what's going on: – Ryan LeCompte Jan 21 '12 at 05:37
  • [11] pry(main)> def String.foo [11] pry(main)* puts 'hi' [11] pry(main)* end => nil [12] pry(main)> String.foo hi => nil [13] pry(main)> m = String.singleton_class.instance_method(:foo) => ##foo> [15] pry(main)> m.bind(String).call hi => nil [16] pry(main)> m.bind(String.singleton_class).call TypeError: singleton method called for a different object from (pry):47:in `bind' [17] pry(main)> – Ryan LeCompte Jan 21 '12 at 05:37
  • I recently encountered this, but another option is to convert the original (not unbound) method to a proc with `#to_proc`. – Adam Eberlin Oct 07 '14 at 16:27