2009-05-02 9 views
47

Vi esta pieza de código en un libro de Ruby on Rails. Este primero es desde una vista y el segundo es un módulo auxiliar. No entiendo cómo funciona el &block y el attributes={}. ¿Alguien puede guiarme a un tutorial de algún tipo que explique esto?¿Qué es esto y bloque en Ruby? ¿Y cómo se pasa en un método aquí?

<% hidden_div_if(@cart.items.empty?, :id => "cart") do %> 
<%= render(:partial => "cart", :object => @cart) %> 
<% end %> 

module StoreHelper 
def hidden_div_if(condition, attributes = {}, &block) 
    if condition 
    attributes["style"] = "display: none" 
    end 
    content_tag("div", attributes, &block) 
    end 
end 

Respuesta

78

Los bloques son una parte bastante básica de ruby. Están delimitados por do |arg0,arg1| ... end o { |arg0,arg1,arg2| ... }.

Permiten especificar una devolución de llamada para pasar a un método. Esta devolución de llamada se puede invocar dos maneras - ya sea mediante la captura de especificando un argumento final con el prefijo &, o por utilizando el yield palabra clave:

irb> def meth_captures(arg, &block) 
     block.call(arg, 0) + block.call(arg.reverse , 1) 
    end 
#=> nil 
irb> meth_captures('pony') do |word, num| 
     puts "in callback! word = #{word.inspect}, num = #{num.inspect}" 
     word + num.to_s 
    end 
in callback! word = "pony" num = 0 
in callback! word = "ynop" num = 1 
#=> "pony0ynop1" 
irb> def meth_yields(arg) 
     yield(arg, 0) + yield(arg.upcase, 1) 
    end 
#=> nil 
irb> meth_yields('frog') do |word, num| 
     puts "in callback! word = #{word.inspect}, num = #{num.inspect}" 
     word + num.to_s 
    end 
in callback! word = "frog", num = 0 
in callback! word = "FROG", num = 1 
#=> "frog0FROG1" 

Tenga en cuenta que nuestra devolución de llamada es el mismo en cada caso - nos puede eliminar la repetición guardando nuestra devolución de llamada en un objeto, y luego pasándola a cada método . Esto se puede hacer usando lambda para capturar la devolución de llamada en un objeto, y luego se pasa a un método prefijándolo con &.

irb> callback = lambda do |word, num| 
     puts "in callback! word = #{word.inspect}, num = #{num.inspect}" 
     word + num.to_s 
    end 
#=> #<Proc:[email protected](irb):22> 
irb> meth_captures('unicorn', &callback) 
in callback! word = "unicorn", num = 0 
in callback! word = "nrocinu", num = 1 
#=> "unicorn0nrocinu1" 
irb> meth_yields('plate', &callback) 
in callback! word = "plate", num = 0 
in callback! word = "PLATE", num = 1 
#=> "plate0PLATE1" 

Es importante entender los diferentes usos de & aquí como un prefijo para el último argumento de una función

  • en una definición de función, que capta cualquier bloque pasado a ese objeto
  • en una llamada de función, expande el objeto de devolución de llamada dado en un bloque

Si mira alrededor, los bloques se utilizan por todas partes, especialmente en iteradores, como Array#each.

+0

¡Gracias por esto, realmente útil para mí! – duykhoa

+0

Gracias, hombre, muy buen ejemplo, pero ¿de qué sirve llamar para inspeccionar un Fixnum? – sekmo

10

El &block es una forma de enviar una pieza de código Ruby en a un método y a continuación, la evaluación de que el código en el ámbito de aplicación de ese método. En su código de ejemplo anterior, significa que un carrito con nombre parcial se representará en un div. Creo que el término closure se usa para esto en ciencias de la computación.

Así que en su ejemplo la &block es:

<%= render(:partial => "cart", :object => @cart) %> 

una buena lectura y una explicación de bloques, procsos y lamdas se puede encontrar en Robert Sosinski's blog.

+1

Sí, los bloques en Ruby son solo cierres. Para una buena explicación de esto, vea http://reprog.wordpress.com/2010/02/27/closures-finally-explained/. –

4

Re attributes = {}, ese es solo un argumento de método con un valor predeterminado. Por lo tanto, si llama al hidden_div_if(whatever), es decir, al pasar solo el primer argumento, attributes se convertiría automáticamente en un hash vacío.

Esto es útil porque simplifica la configuración attributes["style"] más adelante, ya que attributes no tiene que inicializarse primero en un hash. (Lo cual, sin embargo, podría hacerse simplemente como (attributes ||= {})["style"] = ….)


&block es sólo un poco más complicado.

Los métodos de Ruby pueden tomar un último argumento que es un bloque, usando la sintaxis especial method(args) { |block_args| block_code }. &block básicamente captura ese bloque en la variable block como un objeto Proc. Entonces block es solo una variable que apunta a un procedimiento anónimo aquí.

Cuando más tarde content_tag se llama, y ​​&block se hace pasar como su último argumento, se expandió en un bloque, como si la llamada era de hecho content_tag(…) { block originally passed to hidden_if_div }


Así que tal vez fue muy confuso aquí. Lo que debes buscar en Google es "argumentos predeterminados de ruby" y "bloques de ruby".

1

funciona así:

@cart.items.empty? es el codition

:id => "cart" Se convierte en atributos como por convención puede quitar {} en un hash parámetro si se trata de la última.

el bloque es

render(:partial => "cart", :object => @cart)

así dentro de la función si el carro está vacío va a añadir el atributo style con el valor "display: none"

A continuación, se creará una etiqueta div lleno con el contenido del resultado de ejecutar el bloque que será el resultado de renderizar el carrito de vista parcial con el contenido de @cart.

3

Ruby implementa Blocks, Procs y lambdas que se conocen como cierres en la comunidad de la informática. Si comienzas a aprender Ruby, te encontrarás rápidamente con un código que se ve así.

a = ["dog", "cat", "bird"] 
a.alter_each! do |n, i| 
    "#{i}_#{n}" 
end 

Así que hay de nuevo aquí?

Comenzamos con una variedad de nombres de animales y llamamos alter_each! método pasando un bloque. En este bloque de código podemos especificar cómo queremos alterar cada elemento. Nuestro ejemplo prefijará el nombre de cada animal con su posición en la matriz. Como el alter_each! El método itera a través de cada elemento y ejecutará nuestro bloque pasando el valor y el índice. Nuestro bloque captura estos parámetros, prefije el índice al nombre y devuelve el resultado. ¡Ahora veamos el alter_each! método.

Observe que el método no especifica ningún parámetro, es porque un bloque se asigna automáticamente para ceder palabra clave. el rendimiento se denomina como una función que pasa en el valor y el índice de cada elemento en la matriz y anula el valor original.

class Array 
    def alter_each! 
    self.each_with_index do |n, i| 
     self[i] = yield(n,i) 
    end 
    end 
end 

¿Qué pasa si usted necesita para pasar un parámetro de este método?

Puede modificar la firma del método para aceptar parámetros y finalmente atrapar el bloque con un parámetro que comience con un signo de &. En el siguiente ejemplo, nuestro bloque se capturará con el parámetro de bloque &, que invocaremos el método de llamada.Esto es, en lugar de que el rendimiento en

class Array 
    def modify_each!(add_one = true, &block) 
    self.each_with_index do |n, i| 
     j = (add_one) ? (i + 1) : i 
     self[i] = block.call(n,j) 
    end 
    end 
end 

Full article on ruby blocks

18

Bloques, Procs y lambdas (referidos como cierres en Informática) son uno de los aspectos más poderosos de Ruby, y también uno de los más incomprendida . Esto es probablemente porque Ruby maneja los cierres de una manera bastante única. Hacer las cosas más complicadas es que Ruby tiene cuatro formas diferentes de usar cierres, cada uno de los cuales es un poco diferente, y algunas veces sin sentido. Hay bastantes sitios con información muy buena sobre cómo funcionan los cierres en Ruby. Pero todavía tengo que encontrar una buena y definitiva guía por ahí.

class Array 
    def iterate!(&code) 
    self.each_with_index do |n, i| 
     self[i] = code.call(n) 
    end 
    end 
end 

array = [1, 2, 3, 4] 

array.iterate! do |n| 
    n ** 2 
end 

Procedimientos, AKA, Proc

Los bloques son muy práctico y sintácticamente simple, sin embargo es posible que desee tener muchos bloques diferentes a nuestra disposición y les utilizar varias veces. Como tal, pasar el mismo bloque una y otra vez requeriría que nos repitiéramos. Sin embargo, como Ruby está completamente orientado a objetos, se puede manejar de forma bastante limpia guardando el código reutilizable como un objeto en sí mismo. Este código reutilizable se llama Proc (abreviatura de procedimiento). La única diferencia entre bloques y Procs es que un bloque es un Proc que no se puede guardar, y como tal, es una solución de uso único. Al trabajar con procs, podemos empezar a hacer lo siguiente:

class Array 
    def iterate!(code) 
    self.each_with_index do |n, i| 
     self[i] = code.call(n) 
    end 
    end 
end 

array_1 = [1, 2, 3, 4] 
array_2 = [2, 3, 4, 5] 

square = Proc.new do |n| 
    n ** 2 
end 

lambdas

Proc Hasta el momento, se ha utilizado de dos maneras, pasarlas directamente como un atributo y guardarlas como una variable. Estos Procs son muy similares a lo que otros lenguajes llaman funciones anónimas, o lambdas. Para hacer las cosas más interesantes, las lambdas también están disponibles en Ruby. Echar un vistazo:

class Array 
    def iterate!(code) 
    self.each_with_index do |n, i| 
     self[i] = code.call(n) 
    end 
    end 
end 

array = [1, 2, 3, 4] 

array.iterate!(lambda { |n| n ** 2 }) 

puts array.inspect 

Bloques

El más común, más fácil y posiblemente más “Ruby como” forma de usar cierres en Ruby es con bloques. Tienen la siguiente sintaxis familiar:

array = [1, 2, 3, 4] 

array.collect! do |n| 
    n ** 2 
end 

puts array.inspect 

# => [1, 4, 9, 16] 
+0

He visto un ejemplo similar aquí http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/ Sin embargo, estoy confundido en cuanto a lo que se pasa en '| n, i | '¿es el valor y la ubicación del índice dentro de la matriz? – Tom

+0

OK. Estaba siendo tonto. Each_with_index es un método: 'each_with_index | item, index |' así que realmente no necesitaba hacer la pregunta. – Tom

+1

Cuando copiaste esta respuesta del sitio de robersosinki, olvidaste incluir las líneas que pasan el programa "cuadrado" en ambas matrices. Estaba un poco confundido hasta que leí su versión original. Tal vez señale su fuente la próxima vez. – moosefetcher

Cuestiones relacionadas