2011-12-26 92 views
6

閱讀文章http://jeffkreeftmeijer.com/2011/method-chaining-and-lazy-evaluation-in-ruby/後,我開始尋找更好的方法鏈和懶惰評估的解決方案。紅寶石挑戰 - 方法鏈和懶惰評估

我想我已經封裝了以下五個規格的核心問題;任何人都可以讓他們全部通過?

任何事情都會發生:繼承,授權,元編程,但不鼓勵後者。

這將有利於保持依賴性降到最低:

require 'rspec' 

class Foo 
    # Epic code here 
end 

describe Foo do 

    it 'should return an array corresponding to the reverse of the method chain' do 
    # Why the reverse? So that we're forced to evaluate something 
    Foo.bar.baz.should == ['baz', 'bar'] 
    Foo.baz.bar.should == ['bar', 'baz'] 
    end 

    it 'should be able to chain a new method after initial evaluation' do 
    foobar = Foo.bar 
    foobar.baz.should == ['baz', 'bar'] 

    foobaz = Foo.baz 
    foobaz.bar.should == ['bar', 'baz'] 
    end 

    it 'should not mutate instance data on method calls' do 
    foobar = Foo.bar 
    foobar.baz 
    foobar.baz.should == ['baz', 'bar'] 
    end 

    it 'should behave as an array as much as possible' do 
    Foo.bar.baz.map(&:upcase).should == ['BAZ', 'BAR'] 

    Foo.baz.bar.join.should == 'barbaz' 

    Foo.bar.baz.inject do |acc, str| 
     acc << acc << str 
    end.should == 'bazbazbar' 

    # === There will be cake! === 
    # Foo.ancestors.should include Array 
    # Foo.new.should == [] 
    # Foo.new.methods.should_not include 'method_missing' 
    end 

    it "should be a general solution to the problem I'm hoping to solve" do 
    Foo.bar.baz.quux.rab.zab.xuuq.should == ['xuuq', 'zab', 'rab', 'quux', 'baz', 'bar'] 
    Foo.xuuq.zab.rab.quux.baz.bar.should == ['bar', 'baz', 'quux', 'rab', 'zab', 'xuuq'] 
    foobarbaz = Foo.bar.baz 
    foobarbazquux = foobarbaz.quux 
    foobarbazquuxxuuq = foobarbazquux.xuuq 
    foobarbazquuxzab = foobarbazquux.zab 

    foobarbaz.should == ['baz', 'bar'] 
    foobarbazquux.should == ['quux', 'baz', 'bar'] 
    foobarbazquuxxuuq.should == ['xuuq', 'quux', 'baz', 'bar'] 
    foobarbazquuxzab.should == ['zab', 'quux', 'baz', 'bar'] 
    end 

end 
+3

到底爲什麼元編程泄氣? – 2011-12-26 04:08:04

+0

Ruby語言的設計方式,我敢肯定沒有任何類會在你的第一個「it」塊中傳遞規範,並在第二個「it」塊中通過測試,除非它非常怪異並且使用C擴展與一些解釋器掛鉤。第二塊是多餘的。 – 2011-12-26 07:33:37

+0

我唯一不鼓勵MP的原因是我不喜歡它,雖然有點武斷。如果有一個不需要它的實用解決方案,我寧可不使用它。 – Chris 2011-12-26 10:11:06

回答

3

瑣碎的,不是嗎?因爲1.9已經改變了Array#to_s作品

class Foo < Array 
    def self.bar 
    other = new 
    other << 'bar' 
    other 
    end 
    def self.baz 
    other = new 
    other << 'baz' 
    other 
    end 
    def bar 
    other = clone 
    other.unshift 'bar' 
    other 
    end 
    def baz 
    other = clone 
    other.unshift 'baz' 
    other 
    end 
end 

to_s標準失敗。改變這種兼容性:

Foo.baz.bar.to_s.should == ['bar', 'baz'].to_s 

我想要的蛋糕。

BTW - 這裏的元編程將削減下來的代碼大小和增加靈活性極大:

class Foo < Array 
    def self.method_missing(message, *args) 
    other = new 
    other << message.to_s 
    other 
    end 
    def method_missing(message, *args) 
    other = clone 
    other.unshift message.to_s 
    other 
    end 
end 
+0

在'self.method_missing'中,你可以用'new 1,message.to_s'來替換所有這三行。 – 2011-12-26 06:40:42

+0

我添加了一個額外的規範,使問題更一般地排除了您的初始實現。後者仍然有效,但是你能否獲得額外的規格傳遞(方法缺失)? – Chris 2011-12-26 10:11:52

+1

@ChristopherPatuzzo:我相當肯定你不能使用'method_missing'來做一個通用的解決方案,因爲我沒有其他的機制可以捕獲任意的消息。拋開你的不滿,這正是'method_missing'所爲。 – Amadan 2011-12-26 13:14:30

5

這是由Amadan的回答啓發,但使用更少的代碼:

class Foo < Array 
    def self.method_missing(message, *args) 
     new 1, message.to_s 
    end 
    def method_missing(message, *args) 
     dup.unshift message.to_s 
    end 
end 
+0

不錯!每天學些新東西... – Amadan 2011-12-26 13:17:10