2013-07-03 142 views
2

我有一个包装其他实例以提供附加功能(演示者)并希望现在具有提供相同功能的子类的类。因此,这将是这样的:Ruby包装函数

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 > 

回答

4

我可能会丢失你的问题的地步,但对我来说,它看起来像是你忘记了使用new来创建类的实例:

BasePresenter.new(:bubbles) 
# => #<BasePresenter:0x00000001ae33b8 @base_object=:bubbles> 
WalrusPresenter.new(:frank) 
# => #<WalrusPresenter:0x00000001ae7878 @base_object=:frank> 

更新:米沙早已我的意见与我的意见相似。 Kernel#Array尝试在其参数上调用to_ary,然后尝试在其上调用to_a(如果该参数失败),然后创建一个以参数作为唯一元素的数组(如果该参数失败)。

你不清楚你想要什么行为;好像你只是想避开使用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> 
+0

我希望创建一个相当于'阵列()'。该功能将不会与'Class#new'完全相同@ – Chris

+0

@Chris,从您的问题看,它的功能* *是相同的。请编辑您的问题并显示完全不同的内容。如果唯一的一点是摆脱'新',我会建议忘掉它。这是不值得的努力。 – Mischa

+0

好的,我更新了这个问题。我并没有试图摆脱'.new',而是在ArraySuite中包含Array()或Array#wrap()。 – Chris

0

首先,我不认为你正在试图做的是超级聪明的 - 有可能是更好的方法来实现你想要的。但如果你真的想通过添加圆括号处理来创建一个新的操作符,下面的代码完成了我认为的工作..

我正在复制CodeErr的this great blog post的粗糙类处理代码。

下面是使用他的代码,我对你的问题的解决方案:

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" 
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)