2011-12-26 13 views
6

Dopo aver letto l'articolo http://jeffkreeftmeijer.com/2011/method-chaining-and-lazy-evaluation-in-ruby/, ho iniziato a cercare una soluzione migliore per il concatenamento dei metodi e la valutazione lazy.Sfida rubino - Metodo concatenamento e valutazione pigra

Penso di aver incapsulato il problema principale con le cinque specifiche seguenti; qualcuno può farli passare tutti?

Tutto va: sottoclasse, delega, meta-programmazione, ma scoraggiato per quest'ultimo.

Sarebbe favorevole a mantenere le dipendenze al minimo:

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

Perché mai metaprogrammazione scoraggiare? –

+0

Il modo in cui il linguaggio Ruby è progettato, sono abbastanza sicuro che non ci sia una classe che passerà le specifiche nel tuo primo blocco 'it' e fallirà i test nel tuo secondo blocco' it' a meno che non sia davvero pazzesco e usi una C estensione con alcuni ganci di interprete. Il secondo blocco è ridondante. –

+0

La mia unica ragione per scoraggiare MP è che non ne sono un fan, anche se una restrizione alquanto arbitraria. Preferirei non usarlo se c'è una soluzione pragmatica che non lo richiede. – Chris

risposta

3

Trivial, non è vero?

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 

Il criterio to_s esito negativo perché 1.9 ha cambiato il modo Array#to_s opere. Passare a questo per la compatibilità:

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

Voglio torta.

BTW - metaprogrammazione qui sarebbe ridurre la dimensione del codice e aumentare la flessibilità enormemente:

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

In 'self.method_missing', puoi sostituire tutte e tre le linee con semplicemente' nuovo 1, message.to_s'. –

+0

Ho aggiunto una specifica aggiuntiva per rendere il problema più generale escludendo l'implementazione iniziale. Quest'ultimo funziona ancora, ma puoi ottenere il passaggio delle specifiche aggiuntive (metodo mancante)? – Chris

+1

@ChristopherPatuzzo: Sono ragionevolmente certo che non si possa fare una soluzione generale senza usare 'method_missing', poiché non c'è nessun altro meccanismo di cui sono a conoscenza che rileverà messaggi arbitrari. A parte la tua avversione, questa è esattamente la cosa per cui "metodo_missing" è fatto. – Amadan

5

Questo si ispira la risposta di Amadan ma utilizza un minor numero di righe di codice:

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

Bello! Impara qualcosa di nuovo ogni giorno ... – Amadan

Problemi correlati