既可以作为 class 方法调用又可以作为 class 方法混合的方法?
method that is both callable as class method and mixed in as a class method?
在Ruby中,是否可以定义一个方法,可以直接调用为class方法,也可以混入为class方法?也就是说,不使用 self.included
或 self.extended
创建等效的 class 或实例方法。
这些方法都不起作用:
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 是一个 mixin,旨在独立于 A 使用,即 "wraps" 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
等现有设施在这种情况下不起作用。
你可以这样写:
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
并且您想知道是否可以从 class C
引用 A
以使 foo
成为 [=13= 的 class 方法],我的理解是答案是"no"。我说 "my understanding" 因为它不是可以证明的东西;据我了解,这是 Matz 做出的设计决定。
我会尽量不明确,但据我所知,你的问题的答案是否定的。如果你想混合实例和 class 方法,那么标准方法是这样的:
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
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"
在Ruby中,是否可以定义一个方法,可以直接调用为class方法,也可以混入为class方法?也就是说,不使用 self.included
或 self.extended
创建等效的 class 或实例方法。
这些方法都不起作用:
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 是一个 mixin,旨在独立于 A 使用,即 "wraps" 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
等现有设施在这种情况下不起作用。
你可以这样写:
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
并且您想知道是否可以从 class C
引用 A
以使 foo
成为 [=13= 的 class 方法],我的理解是答案是"no"。我说 "my understanding" 因为它不是可以证明的东西;据我了解,这是 Matz 做出的设计决定。
我会尽量不明确,但据我所知,你的问题的答案是否定的。如果你想混合实例和 class 方法,那么标准方法是这样的:
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
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"