2011-12-26 27 views
6

記事http://jeffkreeftmeijer.com/2011/method-chaining-and-lazy-evaluation-in-ruby/を読んだ後、メソッド連鎖とレイジー評価のためのより良いソリューションを探し始めました。Rubyチャレンジ - メソッド連鎖とレイジー評価

私は以下の5つの仕様でコアの問題をカプセル化したと思います。誰もそれらをすべて渡すことができますか?

何でもあります:サブクラス化、委任、メタプログラミングですが、後者についてはお勧めしません。

最低限に依存関係を維持するために有利になります:

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

なぜ地球上でメタプログラミングう落胆しますか? –

+0

Ruby言語の設計方法では、最初の 'it'ブロックに仕様を渡すクラスがなく、2番目の' it'ブロックでテストを失敗させるクラスはないと確信しています。いくつかのインタプリタフック付きの拡張。 2番目のブロックは冗長です。 –

+0

MPを落胆させる私の唯一の理由は、やや恣意的な制限ではあるが、私はそれを好きではないということだ。私はむしろそれを必要としない実用的な解決策があれば、それを使用したくないでしょう。 – Chris

答えて

3

簡易に、それはないですか?

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 

1.9は道Array#to_s作品を変更されたためto_s基準は失敗します。互換性のためにこれに変更してください:

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

私はケーキが欲しいです。

ところで - ここでのメタプログラミングは、コードサイズを削減うと途方もなく、柔軟性を高める:

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'では、3行全てを単に' new 1、message.to_s'で置き換えることができます。 –

+0

私は余分な仕様を追加して、問題をより一般的なものにして、最初の実装を除外しました。後者はまだ動作しますが、余分なスペックを渡すことはできますか? – Chris

+1

@ChristopherPatuzzo:私は、 'method_missing'を使わずに一般的な解決策を行うことはできません。私が知っている任意のメカニズムがないので、任意のメッセージを捕まえることはできません。あなたの嫌いなことは、これはまさに 'method_missing'が作られたことです。 – Amadan

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

関連する問題