Benedict
Benedict

Reputation: 43

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? Inheritance? Aggregation? How?

Upvotes: 4

Views: 2129

Answers (4)

Petr Skocik
Petr Skocik

Reputation: 60097

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

Upvotes: 3

Phrogz
Phrogz

Reputation: 303361

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)

Upvotes: 2

iain
iain

Reputation: 16274

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.

Upvotes: 2

Jonathan
Jonathan

Reputation: 3253

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

Upvotes: 2

Related Questions