How to wrap a Ruby method call by including a module?

I want to be notified when certain things happen in some of my classes. I want to configure this so that the implementation of my methods in these classes does not change.

I thought I would have something like the following module:

module Notifications
  extend ActiveSupport::Concern

  module ClassMethods
    def notify_when(method)
      puts "the #{method} method was called!"
      # additional suitable notification code
      # now, run the method indicated by the `method` argument
    end
  end
end

Then I can mix it with my classes as follows:

class Foo
  include Notifications

  # notify that we're running :bar, then run bar
  notify_when :bar

  def bar(...)  # bar may have any arbitrary signature
    # ...
  end
end

My key desire is that I do not want to change :barin order to receive notifications correctly. It can be done? If so, how can I write an implementation notify_when?

I also use Rails 3, so if there are ActiveSupport or other methods that I can use, feel free to share them. (I looked at ActiveSupport :: Notifications , but that will require me to change the method bar.)


, " + ". , - , - ?

+3
3

, , ( ) .

2.0 prepend , - .

, , . . . .

alias_method instance_method, self, , , , .

module Prepender

  def wrap_me(*method_names)
    method_names.each do |m|
      proxy = Module.new do
        define_method(m) do |*args|
          puts "the method '#{m}' is about to be called"
          super *args
        end
      end
      self.prepend proxy
    end
  end
end

:

class Dogbert
  extend Prepender

  wrap_me :bark, :deny

  def bark
    puts 'Bah!'
  end

  def deny
    puts 'You have no proof!'
  end
end

Dogbert.new.deny

# => the method 'deny' is about to be called
# => You have no proof!
+11

, .

- :

def notify_when(method)  
  alias_method "#{method}_without_notification", method
  define_method method do |*args|
    puts "#{method} called"
    send "#{method}_without_notification", args
  end
end

.

+7

:

(1) Foo, .

(2) -, Foo ,

- , Jakub, alias_method , :

def notify_when(meth)  
  orig_meth = instance_method(meth)
  define_method(meth) do |*args, &block|
    puts "#{meth} called"
    orig_meth.bind(self).call *args, &block
  end
end

method_missing :

class Interceptor
  def initialize(target)
    @target = target
  end

  def method_missing(name, *args, &block)
    if @target.respond_to?(name)
      puts "about to run #{name}"
      @target.send(name, *args, &block)
    else
      super
    end
  end
end

class Hello; def hello; puts "hello!"; end; end

i = Interceptor.new(Hello.new)
i.hello #=> "about to run hello"
        #=> "hello!"

(, , ), -, , - . , .

+3

Source: https://habr.com/ru/post/1775723/


All Articles