在Ruby中,是否可以定义一个可以作为类方法直接调用的方法,并且也可以作为类方法混合使用?也就是说,不使用self.included
或self.extended
来创建等效的类或实例方法。
这两种方法都不起作用:
module A
def foo(s)
puts s
end
end
class One
extend A
end
One.foo("one")
#A.foo("a")
module B
def self.foo(s)
puts s
end
end
class Two
include B
end
#Two.foo("two")
B.foo("b")
对于被问到的内容似乎有些混乱。这是一个不那么抽象的场景。 A是可以直接使用的mixin。 B是一种混合物,旨在独立于A使用,“包裹”A的一种方法。
module A
# #foo has to be defined in order to be mixed in via `extend`.
# Being mixed in via `include` has the same issue but inverted.
def foo(s) A.foo(s) end
def self.foo(s) puts "A: " + s end
end
module B
def foo(s) A.foo("B: " + s) end
end
class One; extend A end
class Two; extend B end
One.foo("one")
Two.foo("two")
为了实现这一点,必须单独定义A#foo
和A::foo
。 Module#module_function
等现有设施在此方案下无效。
答案 0 :(得分:2)
我会尽量不确定,但据我所知,你问题的答案是否定的。如果你想混合使用实例和类方法,那么标准方法就是这样的:
module A
def self.included(base)
#this will extend the class you included A in
#using A::ClassMethods definition
base.extend(ClassMethods)
end
#these methods will be added as class_methods to any class
#that includes A
module ClassMethods
def foo(s)
"You fooed the class with #{s}"
end
end
#this will be added as an instance method as it would be in a standard include
def bar(s)
"You barred an instance with #{s}"
end
end
class Mixed
include A
end
Mixed.foo("Hello")
#=> "You fooed the class with Hello"
Mixed.new.bar("Hello")
#=> "You barred an instance with Hello"
我希望这能回答你的问题,因为你的意图有点不清楚。由于你的问题似乎不需要实例方法,你也可以这样做
module A
def foo(s)
"called foo with #{s}"
end
end
module B
include A
alias_method :a_foo, :foo
def foo(s)
"B called foo from A #{a_foo(s)}"
end
end
class Mixed
extend B
end
Mixed.foo("Mixed")
#=>"B called foo from A called foo with Mixed"
再次更新
这是一个奇怪的模式,但它适用于我相信的用例
module A
def foo(s)
"fooed with #{s}"
end
def bar(s)
"barred with #{s}"
end
end
module B
include A
included_modules.each do |mod|
(mod.instance_methods - Object.methods).each do |meth|
alias_method "#{mod.name.downcase}_#{meth}", meth
end
end
end
class Mixed
extend B
end
Mixed.methods - Object.methods
#=> [:a_foo, :a_bar, :foo, :bar]
通过这种方式,您可以覆盖B
中的方法并调用A
版本,但如果您不覆盖,则仍会调用A
版本。
如果您希望将此功能设为通用
,您还可以修补Module
课程
class Module
def include_with_namespace(*mods)
#Module#include runs in reverse so to maintain consistency my patch does as well
mods.reverse.each do |mod|
include mod
(mod.instance_methods - Object.methods).each do |meth|
alias_method "#{mod.name.downcase}_#{meth}", meth
end
end
end
end
然后这将起作用
module C
def foo(s)
"C's foo with #{s}"
end
def see_me
"You can see C"
end
end
module B;include_with_namespace A, C; end
class Mixed;extend B;end
Mixed.methods - Object.methods
#=> [:a_foo, :a_bar, :c_foo,:c_see_me, :foo, :bar, :see_me]
Mixed.foo("name")
#=> "fooed with name"
Mixed.c_foo("name")
#=> "C's foo with name"
答案 1 :(得分:0)
你可以写:
module A
def foo(s)
puts s
end
end
class One
singleton_class.include A
end
One.foo('hi')
#=> 'hi'
但这非常接近Object#extend。
如果:
module A
def self.foo(s)
puts s
end
end
并且您想知道是否可以从类A
引用C
以使foo
成为C
的类方法,我的理解是答案是“没有”。我说“我的理解”,因为它不是可以证明的东西;据我所知,这是Matz做出的一项设计决定。