2011-12-26 12 views
6

Después de leer el artículo http://jeffkreeftmeijer.com/2011/method-chaining-and-lazy-evaluation-in-ruby/, comencé a buscar una mejor solución para el método de encadenamiento y evaluación perezosa.Ruby Challenge - Método de encadenamiento y evaluación diferida

Creo que he encapsulado el problema principal con las cinco especificaciones siguientes; ¿Alguien puede hacer que todos pasen?

Todo vale: subclases, delegación, meta-programación, pero desalentados para este último.

Sería favorable mantener al mínimo las dependencias:

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

¿Por qué iba metaprogramming estar desanimado? –

+0

La forma en que está diseñado el lenguaje Ruby, estoy bastante seguro de que no hay clases que pasen las especificaciones en tu primer bloque 'it' y fallen las pruebas en tu segundo bloque' it' a menos que sea realmente extraño y use una C extensión con algunos ganchos de intérprete. El segundo bloque es redundante. –

+0

Mi única razón para desalentar a MP es que no soy fanático de ella, aunque es una restricción algo arbitraria. Prefiero no usarlo si hay una solución pragmática que no lo requiera. – Chris

Respuesta

3

trivial, no es así?

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 

El criterio to_s falla debido a 1.9 ha cambiado la forma Array#to_s obras. Cambie a esto por compatibilidad:

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

I want cake.

BTW - metaprogramming aquí sería reducir el tamaño del código y aumentar la flexibilidad 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

En 'self.method_missing', puede reemplazar las tres líneas con simplemente' new 1, message.to_s'. –

+0

Agregué una especificación adicional que hace que el problema sea más general, descartando su implementación inicial. El último todavía funciona, pero ¿puedes obtener las especificaciones adicionales que pasan (método que falta)? – Chris

+1

@ChristopherPatuzzo: Estoy razonablemente seguro de que no se puede hacer una solución general sin usar 'method_missing', ya que no hay otro mecanismo del que tenga conocimiento que capture mensajes arbitrarios. Su aversión a un lado, esto es exactamente lo que 'method_missing' está hecho para. – Amadan

5

Esto es inspirada por la respuesta de Amadan pero utiliza menos líneas de código:

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

¡Agradable! Aprenda algo nuevo cada día... – Amadan

Cuestiones relacionadas