Ruby: How to connect to class methods

From: http://cheind.blogspot.com/2008/12/method-hooks-in-ruby.html

I have

# Contains methods to hook method calls module FollowingHook module ClassMethods private # Hook the provided instance methods so that the block # is executed directly after the specified methods have # been invoked. # def following(*syms, &block) syms.each do |sym| # For each symbol str_id = "__#{sym}__hooked__" unless private_instance_methods.include?(str_id) alias_method str_id, sym # Backup original # method private str_id # Make backup private define_method sym do |*args| # Replace method ret = __send__ str_id, *args # Invoke backup block.call(self, # Invoke hook :method => sym, :args => args, :return => ret ) ret # Forward return value of method end end end end end # On inclusion, we extend the receiver by # the defined class-methods. This is an ruby # idiom for defining class methods within a module. def FollowingHook.included(base) base.extend(ClassMethods) end end 

Then I have a class:

 class User def self.get #class method end def name #instance method end end 

In another place / file, I open the User class again and connect to it

 class User include FollowingHooks # include the hook module following :name do |receiver, args| #do something. This works!! end following :get do |reciever, args| #do something. THIS DOESNT WORK # Which is to be expected looking at the FollowingHooks module definition. end end 

Work with any methods. However, trying to connect to class methods does nothing that I get, because the NextHooks module does not implement it. How can I implement interceptors for class methods? I don’t know at all.

+4
source share
2 answers

You need to point the NextHook code to Class , and then call the following so that it applies to class methods.

 Class.send(:include, FollowingHook) class User class << self following :get do |reciever, args| # Your awesome code here end end end 

Edit:

Here is my complete working solution that followed this suggestion:

 # Contains methods to hook method calls module FollowingHook module ClassMethods private # Hook the provided instance methods so that the block # is executed directly after the specified methods have # been invoked. # def following(*syms, &block) syms.each do |sym| # For each symbol str_id = "__#{sym}__hooked__" unless private_instance_methods.include?(str_id) alias_method str_id, sym # Backup original # method private str_id # Make backup private define_method sym do |*args| # Replace method ret = __send__ str_id, *args # Invoke backup block.call(self, # Invoke hook :method => sym, :args => args, :return => ret ) ret # Forward return value of method end end end end end def self.included(base) base.send(:extend, FollowingHook::ClassMethods) end end class User def self.foo puts "foo" end def bar puts "bar" end end # You can put this in the class << self block if you prefer that the # methods only be available on the User class. Class.send(:include, FollowingHook) class User include FollowingHook following :bar do |receiver, args| puts receiver.inspect end class << self # If you prefer only the User class include FollowingHooks, use the # following instead of including them in Class. # include FollowingHook following :foo do |receiver, args| puts receiver.inspect end end end User.foo #=> # foo # User User.new.bar #=> # bar # #<User:0x338d9d> 
+1
source

Well, that works.

  def following_c(*syms, &block) metaclass = (class << self; self; end) syms.each do |sym| str_id = "__#{sym}__hooked__" unless metaclass.send(:private_instance_methods).include?(str_id) metaclass.send :alias_method, str_id, sym metaclass.send :private, str_id metaclass.send :define_method, sym do |*args| ret = send str_id, *args block.call(self, :method => sym, :args => args, :return => ret ) ret end end end end 
+1
source

All Articles