考虑以下扩展(多年来由多个Rails插件推广的模式):
module Extension
def self.included(recipient)
recipient.extend ClassMethods
recipient.send :include, InstanceMethods
end
module ClassMethods
def macro_method
puts "Called macro_method within #{self.name}"
end
end
module InstanceMethods
def instance_method
puts "Called instance_method within #{self.object_id}"
end
end
end
如果您希望将其暴露给每个班级,您可以执行以下操作:
Object.send :include, Extension
现在您可以定义任何类并使用宏方法:
class FooClass
macro_method
end
#=> Called macro_method within FooClass
实例可以使用实例方法:
FooClass.new.instance_method
#=> Called instance_method within 2148182320
但即使Module.is_a?(Object)
,您也无法在模块中使用宏方法。
module FooModule
macro_method
end
#=> undefined local variable or method `macro_method' for FooModule:Module (NameError)
即使您明确将原始Extension
包含在Module
Module.send(:include, Extension)
中也是如此。
对于单个模块,您可以手动包含扩展并获得相同的效果:
module FooModule
include Extension
macro_method
end
#=> Called macro_method within FooModule
但是如何为所有Ruby模块添加类似宏的方法呢?
答案 0 :(得分:22)
考虑以下扩展(多年来由多个Rails插件推广的模式)
这是不是模式,并没有“普及”。这是一个反模式,由1337 PHP h4X0rZ goods-culted ,他们不懂Ruby。值得庆幸的是,由于Yehuda Katz,Carl Lerche和其他人的硬话,Rails 3已经消除了许多(所有?)这种反模式的实例。在他最近关于清理Rails代码库的讨论中,Yehuda甚至使用了与你发布的完全相同的代码作为反例,他写了an entire blog post这个反模式。
如果您希望将其暴露给每个班级,您可以执行以下操作:
Object.send :include, Extension
如果您想将其添加到Object
,那么为什么不这样做呢?
class Object
def instance_method
puts "Called instance_method within #{inspect}"
end
end
但是如何为所有Ruby模块添加类似宏的方法呢?
简单:将它们添加到Module
:
class Module
def macro_method
puts "Called macro_method within #{inspect}"
end
end
一切正常:
class FooClass
macro_method
end
#=> Called macro_method within FooClass
FooClass.new.instance_method
#=> Called instance_method within #<FooClass:0x192abe0>
module FooModule
macro_method
end
#=> Called macro_method within FooModule
只有10行代码而不是16行代码,而这10行代码中只有0行是元编程或钩子或任何甚至远程复杂的代码。
你的代码和我的代码之间的唯一区别是,在你的代码中,mixins出现在继承层次结构中,因此调试起来更容易,因为你实际上看添加了一些东西到Object
。但这很容易解决:
module ObjectExtensions
def instance_method
puts "Called instance_method within #{inspect}"
end
end
class Object
include ObjectExtensions
end
module ModuleExtensions
def macro_method
puts "Called macro_method within #{inspect}"
end
end
class Module
include ModuleExtensions
end
现在我与16行的代码绑在一起,但我认为我的代码比你的简单,特别是考虑到你的代码不起作用,你和我,以及差不多190000的StackOverflow用户都无法找出原因。