4

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

私は持っている

# 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

次に、次のようなクラスがあります。

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

別の場所/ファイルで、 User クラスを再度開き、それにフックします

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

インスタンスメソッドへのフックは機能します。ただし、クラス メソッドにフックしようとしても、何もしません。それは、FollowingHooks モジュールがそれを実装していないためです。クラスメソッドのフックを実装するにはどうすればよいですか? 私はまったく無知です。

4

2 に答える 2

1

まあ、これはうまくいきます。

    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
于 2013-03-01T00:53:26.247 に答える
1

You need to include the FollowingHook code on Class and then call following so that it applies to the 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 which 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>
于 2013-03-01T00:33:39.447 に答える