2013-07-03 140 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)