我想定义第二天返回的实例方法Date#next
。所以我创建了一个DateExtension
模块,如下所示:
module DateExtension
def next(symb=:day)
dt = DateTime.now
{:day => Date.new(dt.year, dt.month, dt.day + 1),
:week => Date.new(dt.year, dt.month, dt.day + 7),
:month => Date.new(dt.year, dt.month + 1, dt.day),
:year => Date.new(dt.year + 1, dt.month, dt.day)}[symb]
end
end
使用它:
class Date
include DateExtension
end
调用方法d.next(:week)
会使Ruby抛出错误ArgumentError: wrong number of arguments (1 for 0)
。
如何使用next
模块中声明的方法覆盖Date
类中的默认DateExtension
方法?
答案 0 :(得分:103)
在Ruby 2.0及更高版本中,您可以使用Module#prepend
:
class Date
prepend DateExtension
end
旧版Ruby的原始答案如下。
include
的问题(如the following diagram所示)是该类的方法不能被该类中包含的模块覆盖(解决方案遵循图表):
仅针对此方法的子类日期:
irb(main):001:0> require 'date'; module Foo; def next(a=:hi); a; end; end
#=> nil
irb(main):002:0> class MyDate < Date; include Foo; end
#=> MyDate
irb(main):003:0> MyDate.today.next(:world)
#=> :world
使用您自己的方法扩展您需要的实例:
irb(main):001:0> require 'date'; module Foo; def next(a=:hi); a; end; end
#=> nil
irb(main):002:0> d = Date.today; d.extend(Foo); d.next(:world)
#=> :world
在包含你的模块时,执行严重的黑客攻击并进入课堂内部并销毁旧的“下一个”以便你的被调用:
irb(main):001:0> require 'date'
#=> true
irb(main):002:0> module Foo
irb(main):003:1> def self.included(klass)
irb(main):004:2> klass.class_eval do
irb(main):005:3* remove_method :next
irb(main):006:3> end
irb(main):007:2> end
irb(main):008:1> def next(a=:hi); a; end
irb(main):009:1> end
#=> nil
irb(main):010:0> class Date; include Foo; end
#=> Date
irb(main):011:0> Date.today.next(:world)
#=> :world
这种方法比仅包含一个模块更具侵入性,但是(目前为止显示的技术)的唯一方法是使系统方法返回的新Date实例将自动使用您自己模块中的方法。
但如果你打算这样做,你可以完全跳过这个模块,然后直接去monkeypatch土地:
irb(main):001:0> require 'date'
#=> true
irb(main):002:0> class Date
irb(main):003:1> alias_method :_real_next, :next
irb(main):004:1> def next(a=:hi); a; end
irb(main):005:1> end
#=> nil
irb(main):006:0> Date.today.next(:world)
#=> :world
如果您确实需要在自己的环境中使用此功能,请注意banisterfiend的Prepend库可以让您在混合它的类之前在模块中进行查找。
Module#prepend
看起来是coming in Ruby 2.0。 答案 1 :(得分:7)
next
的{{1}}方法在Date
类中定义,类中定义的方法优先于包含模块中定义的方法。所以,当你这样做时:
Date
您正在提取class Date
include DateExtension
end
版本,但next
中定义的next
仍然优先。您必须将Date
权限置于next
:
Date
来自Classes and Objects的编程Ruby章节:
当一个类包含一个模块时,该模块的实例方法可用作该类的实例方法。这几乎就像模块成为使用它的类的超类一样。毫不奇怪,这是关于它是如何工作的。当您包含一个模块时,Ruby会创建一个引用该模块的匿名代理类,并将该代理作为执行该类的类的直接超类插入。