开发者

How to decorate a method in Ruby without alias_method_chain

We all know, that if the target class is composed with modules, you can just call super in a new module. But what if it is an ordinary method in a class?

class Logger
  def message(msg)
    puts msg
  end
end

Say, Logger is a class I can't change (e.g. it is in a gem). And I want Logger to put a "================" line before each message. How do I do that in a beauty way? Inh开发者_运维问答eritance? Aggregation? How?


You could save the original method as an unbound method object instead of saving it in an alias.

Then, you can use define_method with a block. The block will capture the unbound method_object in a closure, allowing you to use it in your new method, without having pollute your module/class.

The only downside is, you probably can't define a method that yields to or takes a block in this way:

module Mod
  unbound_method = instance_method(:original_method)
  define_method :original_method do |*args|
    #do something before
    #call the original method
    unbound_method.bind(self).call(*args)
    #do something after
  end
end


I would either perform subclassing (per @iain's answer) or a custom module inclusion in your own instances:

module LoggerLiner
  def message(*args)
    puts "="*15
    super
  end
end

log = Logger.new(STDOUT)
log.extend(LoggerLiner)


This may not be what you mean by "beauty" but probably the simplest way is to have, in your code somewhere:

class Logger
  alias message old_message
  def message(msg)
    puts "================"
    old_message(msg)
  end
end


You could inherit from logger and use namespacing to use it:

class LinedLogger < Logger
  def message(*)
    puts "=" * 15
    super
  end
end

And when you want to use it:

class Post
  Logger = LinedLogger
end

Only within the namespace Post, you will get LinedLogger instead of Logger. It's a nice way of limiting your patches. It will not work globally though.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜