我必须在执行时添加方法。
class ExtendableClass
end
要添加的方法在独立的类中声明。
module ExtensionClassOne
def method_one
end
end
module ExtensionClassTwo
def method_two
end
end
我正在寻找一种(优雅)机制,将所有扩展类方法添加到ExtendableClass
中。
我正在考虑明确包含扩展类,如:
ExtendableClass.send( :include, ExtensionClassOne )
ExtendableClass.send( :include, ExtensionClassTwo )
但每次定义新的扩展类时,看起来有点不得不调用此私有方法。
所以我正在寻找一种自动方式将include
这些方法导入我的ExtendableClass
课程。
我在考虑为此扩展类声明一个特定的祖先:
class ExtensionClassOne < Extension
def method_one
end
end
然后我需要一种机制来了解一个类的所有孩子 ......类似于ancestors的对话。
一旦我有了这个列表,我就可以轻松地ExtendableClass.include
所有类列表。即使我必须在这里调用私有方法。
当此类用作祖先时,还继承Extension
类,检测声明时间。以ActiveSupport.included method的工作方式,如事件绑定。然后在那里制作include
。
实施方法2 或方法3 的任何解决方案?你推荐接近1 吗?新方法?
答案 0 :(得分:1)
included
方法实际上是一个钩子。无论何时继承自:
module Extensions
def someFunctionality()
puts "Doing work..."
end
end
class Foo
def self.inherited(klass)
klass.send(:include, Extensions) #Replace self with a different module if you want
end
end
class Bar < Foo
end
Bar.new.someFunctionality #=> "Doing work..."
还有included
钩子,当你被包括在内时调用它:
module Baz
def self.included(klass)
puts "Baz was included into #{klass}"
end
end
class Bork
include Baz
end
输出:
Baz was included into Bork
答案 1 :(得分:1)
方法4是在Object
中的类级别定义一个宏class Object
def self.enable_extension
include InstanceExtension
extend ClassExtension
end
end
并在您想要扩展的所有课程中调用此宏。
class Bacon
enable_extension
end
Car.enable_extension
这样,
下行:你monkeypatch内置类可能打破世界。选择长名称和描述性名称。
答案 2 :(得分:1)
编辑:鉴于您对我对该问题的评论的回答,我认为这不是您想要的。在这种情况下,我认为你的“方法1”没有问题;这就是我要做的。或者,不要使用send
绕过私有方法,只需重新打开类:
class ExtendableClass
include ExtensionOne
end
假设我明白你想要什么,我会这样做:
module DelayedExtension
def later_include( *modules )
(@later_include||=[]).concat( modules )
end
def later_extend( *modules )
(@later_extend||=[]).concat( modules )
end
def realize_extensions # better name needed
include *@later_include unless !@later_include || @later_include.empty?
extend *@later_extend unless !@later_extend || @later_extend.empty?
end
end
module ExtensionOne
end
module ExtensionTwo
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def class_can_do_it!; end
end
end
class ExtendableClass
extend DelayedExtension
later_include ExtensionOne, ExtensionTwo
end
original_methods = ExtendableClass.methods
p ExtendableClass.ancestors
#=> [ExtendableClass, Object, Kernel, BasicObject]
ExtendableClass.realize_extensions
p ExtendableClass.ancestors
#=> [ExtendableClass, ExtensionOne, ExtensionTwo, Object, Kernel, BasicObject]
p ExtendableClass.methods - original_methods
#=> [:class_can_do_it!]
答案 3 :(得分:1)
class Extendable
end
class Extendable
def method_one
puts "method one"
end
end
class Extendable
def method_two
puts "method two"
end
end
...换句话说,如果你定义的模块一旦被定义就会自动包含在一个类中,为什么还要为模块烦恼呢?只需将“扩展”方法直接添加到课程中即可!
答案 4 :(得分:0)
在你做了一个非常有趣的命题后,我意识到显式方式是最干净的方法。如果我们从你的答案中添加一些建议,我想我会选择这个:
# extendable.rb
class Extendable
def self.plug( _module )
include( _module )
end
end
# extensions/extension_one.rb
module ExtensionOne
def method_one
puts "method one"
end
end
Extendable.plug( ExtensionOne )
# extensions/extension_two.rb
module ExtensionTwo
def method_two
puts "method two"
end
end
Extendable.plug( ExtensionTwo )
# result
Extendable.new.method_one # => "method one"
Extendable.new.method_two # => "method two"
答案 5 :(得分:0)
一个非常棘手的解决方案,我认为过度工程化太多,将采用@Linux_iOS.rb.cpp.c.lisp.m.sh评论过的继承的钩子并保留所有和Set
中的每个子类,并将其与 method_missing
的@Mikey Hogarth命题相结合,以便每次调用{中的方法时查找所有这些子类方法{1}}课程。像这样:
Extendable
但是# code simplified and no tested
# extendable.rb
class Extendable
@@delegators = []
def self.inherited( klass )
@@delegators << klass
end
def self.method_missing
# ... searching in all @@delegators methods
end
end
# extensions/extension_one.rb
class ExtensionOne < Extendable
def method_one
end
end
(和method_missing
)的逻辑会变得非常复杂和肮脏。
我不喜欢这个解决方案,只是让它在这里研究它的可能性。