如何在没有 alias_method_chain 的情况下在 Ruby 中修饰方法

发布于 2024-10-06 00:00:59 字数 285 浏览 7 评论 0原文

我们都知道,如果目标类是由模块组成的,则只需在新模块中调用 super 即可。但如果它是类中的普通方法呢?

class Logger
  def message(msg)
    puts msg
  end
end

比如说,Logger 是一个我无法更改的类(例如,它位于 gem 中)。 我希望 Logger 在每条消息之前放置一个“================”行。我如何以一种美观的方式做到这一点?遗产?聚合?如何?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

倥絔 2024-10-13 00:00:59

您可以将原始方法保存为未绑定方法对象,而不是将其保存在别名中。

然后,您可以将 define_method 与块一起使用。该块将捕获闭包中未绑定的 method_object ,允许您在新方法中使用它,而不会污染您的模块/类。

唯一的缺点是,您可能无法以这种方式定义一个产生或获取块的方法:

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

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
俯瞰星空 2024-10-13 00:00:59

我要么执行子类化(根据 @iain 的答案),要么在您自己的实例中包含自定义模块:

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

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

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)
遮云壑 2024-10-13 00:00:59

这可能不是你所说的“美丽”,但最简单的方法可能是在你的代码中的某个地方:

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

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
贱贱哒 2024-10-13 00:00:59

您可以继承 logger 并使用命名空间来使用它:

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

当您想使用它时:

class Post
  Logger = LinedLogger
end

仅在命名空间 Post 内,您将得到 LinedLogger 而不是 Logger。这是限制补丁的好方法。但它不会在全球范围内发挥作用。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文