我有一个包装其他实例的类来提供额外的功能(演示者),并希望现在拥有提供相同功能的子类。所以它会是这样的:
class BasePresenter
attr_accessor :base_object
def initialize(base_object)
self.base_object = base_object
end
end
class WalrusPresenter < BasePresenter
end
我希望能够做到这一点:
BasePresenter(:bubbles)
#=> <# BasePresenter @base_object=:bubbles >
WalrusPresenter(:frank)
#=> <# WalrusPresenter @base_object=:frank >
更新
我认为功能差异超出了问题的范围,但它们似乎是一个棘手的问题所以我会添加它们。
我不是要委托.new
。
BasePresenter.new
只接受一个参数并将其包装在演示者中。 BasePresenter()
获取一个对象并且:
这更接近ActiveSupport的Array#wrap
,但我认为括号语法相当于其功能的交流,所以我想尽可能使用它。继承部分正在绊倒我;将它定义为基类,这样它就可以用于所有孩子。
BasePresenter(:bubbles)
#=> <# BasePresenter @base_object=:bubbles >
BasePresenter([:waldorf, :statler])
#=> [ <# BasePresenter @base_object=:waldorf >, <# BasePresenter @base_object=:staler> ]
WalrusPresenter.new(:frank)
#=> <# WalrusPresenter @base_object=:frank >
WalrusPresenter([:harold, :richard])
#=> [ <# WalrusPresenter @base_object=:harold >, <# WalrusPresenter @base_object=:richard > ]
WalrusPresenter(WalrusPresenter(WalrusPresenter(:frank)))
#=> <# WalrusPresenter @base_object=:frank >
答案 0 :(得分:4)
我可能会忽略您的问题,但对我而言,您似乎忘记使用new
创建类的实例:
BasePresenter.new(:bubbles)
# => #<BasePresenter:0x00000001ae33b8 @base_object=:bubbles>
WalrusPresenter.new(:frank)
# => #<WalrusPresenter:0x00000001ae7878 @base_object=:frank>
更新: Mischa已经以与我相同的方式回复了您的评论。 Kernel#Array尝试在其参数上调用to_ary
,然后尝试在其上调用to_a
,如果失败,则创建一个Array,其中参数作为唯一元素,如果失败的话。
目前尚不清楚你想要什么样的行为;看起来你只想用new
来解决,当然你可以这样做,但这很愚蠢:
def BasePresenter(base)
BasePresenter.new(base)
end
def WalrusPresenter(base)
WalrusPresenter.new(base)
end
你可以做一些元编程,以避免在创建包装器时重复自己。但我没有看到你想要避免new
的原因,并且没有充分的理由这样做,我不得不建议反对它。 Ruby使用new
来实例化对象。
更新2:感谢您澄清所需内容。这是我想到的第一件事。你肯定可以清理一下,但这样的事情应该让你开始。 (wrap
根本不需要BasePresenter
。)无论如何,你走了:
class BasePresenter
attr_accessor :base_object
def initialize(base_object)
self.base_object = base_object
end
def self.wrap klass
Object.class_eval do
define_method klass.to_s do |object|
case object
when BasePresenter
object
when Array
object.map{|base| klass.new(base)}
else
klass.new(object)
end
end
end
end
BasePresenter.wrap BasePresenter
def self.inherited child_klass
BasePresenter.wrap child_klass
end
end
class WalrusPresenter < BasePresenter
end
这似乎可以做你想要的:
BasePresenter(:bubbles)
# => #<BasePresenter:0x00000001db05a0 @base_object=:bubbles>
BasePresenter([:waldorf, :statler])
# => [#<BasePresenter:0x00000001da7c98 @base_object=:waldorf>,
#<BasePresenter:0x00000001da7c70 @base_object=:statler>]
WalrusPresenter.new(:frank)
# => #<WalrusPresenter:0x00000001da4070 @base_object=:frank>
WalrusPresenter([:harold, :richard])
# => [#<WalrusPresenter:0x00000001d773e0 @base_object=:harold>,
#<WalrusPresenter:0x00000001d773b8 @base_object=:richard>]
WalrusPresenter(WalrusPresenter(WalrusPresenter(:frank)))
# => #<WalrusPresenter:0x00000001d6c760 @base_object=:frank>
答案 1 :(得分:0)
首先,我不认为你想做的事情是超级明智的 - 有可能有更好的方法来实现你想要的。但是如果你真的想通过添加括号处理来创建一个针对类的新运算符,那么下面的代码可以完成我认为的工作。
我正在通过CodeErr从this great blog post复制gnarly类处理代码。
以下是我使用他的代码解决问题的方法:
class Object
alias_method :orig_method_missing, :method_missing
def method_missing(m, *a, &b)
klass = begin
(self.is_a?(Module) ? self : self.class).const_get(m)
rescue NameError
end
return klass.send(:parens, *a, &b) if klass.respond_to? :parens
orig_method_missing m, *a, &b
end
end
class X
@@class_arg = {}
def self.class_arg
@@class_arg[self.name]
end
def self.parens(*args)
@@class_arg[self.name] = args[0]
Class.new(self).class_eval do
define_method :initialize do
super(*args)
end
self
end
end
end
class Y < X
end
class Z < X
end
Z(:bubbles)
Y(:frank)
puts Z.class_arg # "bubbles"
puts Y.class_arg # "frank"
答案 2 :(得分:0)
我的解决方案:
class BasePresenter
attr_accessor :base_object
def initialize(base_object)
self.base_object = base_object
end
Object.class_eval do
def BasePresenter base_object
BasePresenter.new base_object
end
end
def self.inherited child_klass
Object.class_eval do
define_method child_klass.to_s.to_sym do |base_object|
child_klass.new base_object
end
end
end
end
class WalrusPresenter < BasePresenter
end
class Test < WalrusPresenter; end
puts BasePresenter(:bubbles)
puts WalrusPresenter(:frank)
puts Test(:test)