rails objetos manejo lista importar funciones for clases bucle ruby-on-rails ruby ruby-on-rails-3 coding-style

ruby-on-rails - objetos - proc ruby



do... end vs llaves para bloques en Ruby (13)

Estoy votando por do / end

La convención es do .. end para multiline y { ... } para one-liners.

Pero me gusta do .. end mejor, así que cuando tengo un trazador de líneas, uso do .. end todos modos, pero formatealo como siempre para do / end en tres líneas. Esto hace que todos estén felices.

10.times do puts ... end

Un problema con { } es que es poesía-modo-hostil (porque se unen estrechamente al último parámetro y no a toda la llamada al método, por lo que debe incluir el método parens) y simplemente, en mi opinión, no se ven tan bonito. No son grupos de declaraciones y chocan con las constantes de hash para la legibilidad.

Además, he visto suficientes { } en programas C. El camino de Ruby, como de costumbre, es mejor. Hay exactamente un tipo de bloque if , y nunca tiene que volver atrás y convertir una declaración en una sentencia compuesta.

Tengo un compañero de trabajo que está tratando activamente de convencerme de que no debería usar do..end y en su lugar usar llaves para definir bloqueos multilínea en Ruby.

Estoy firmemente en el campo de usar solo llaves para frases cortas y hacer ... para todo lo demás. Pero pensé que me acercaría a la gran comunidad para obtener una resolución.

Entonces, ¿cuál es y por qué? (Ejemplo de algún código de shoulda)

context do setup { do_some_setup() } should "do somthing" do # some more code... end end

o

context { setup { do_some_setup() } should("do somthing") { # some more code... } }

Personalmente, solo mirando las respuestas anteriores, la pregunta para mí, pero quería abrir esto a la comunidad en general.


Desde la programación de Ruby :

Las llaves tienen una alta precedencia; do tiene una baja precedencia. Si la invocación del método tiene parámetros que no están entre paréntesis, la forma de llave de un bloque se vinculará al último parámetro, no a la invocación general. El formulario do se vinculará a la invocación.

Entonces el código

f param {do_something()}

Vincula el bloque a la variable param mientras que el código

f param do do_something() end

Vincula el bloque a la función f .

Sin embargo, esto no es un problema si encierra los argumentos de función entre paréntesis.


En realidad, es una preferencia personal, pero una vez dicho esto, durante los últimos 3 años de mi ruby ​​experimenté lo que aprendí es que el rubí tiene su estilo.

Un ejemplo sería, si vienes de un fondo JAVA, para un método booleano que podrías usar

def isExpired #some code end

observe el caso camel y, a menudo, el prefijo ''es'' para identificarlo como un método booleano.

Pero en el mundo ruby, el mismo método sería

def expired? #code end

así que personalmente pienso, es mejor ir con ''ruby way'' (Pero sé que toma un tiempo para que uno lo entienda (me tomó alrededor de 1 año: D)).

Finalmente, iría con

do #code end

bloques.


Hay algunos puntos de vista sobre esto, es realmente una cuestión de preferencia personal. Muchos rubyistas toman el enfoque que tú haces. Sin embargo, otros dos estilos que son comunes es usar siempre uno u otro, o usar {} para bloques que devuelven valores, y do ... end para bloques que se ejecutan para efectos secundarios.


Hay un beneficio importante para las llaves: muchos editores tienen MUCHO más fácil tiempo de igualarlas, lo que hace que ciertos tipos de depuración sean mucho más fáciles. Mientras tanto, la palabra clave "do ... end" es bastante difícil de igualar, especialmente porque "end" s también coincide con "if" s.


Hay una sutil diferencia entre ellos, pero {} se une más que do / end.


Hay una tercera opción: escribir un preprocesador para inferir "fin" en su propia línea, a partir de la sangría. Los pensadores profundos que prefieren el código conciso tienen razón.

Mejor aún, piratear ruby, así que esta es una bandera.

Por supuesto, la solución más simple de "elige tus peleas" es adoptar la convención de estilo de que una secuencia de finales aparece en la misma línea, y enseña el color de tu sintaxis para silenciarlos. Para facilitar la edición, se podrían usar scripts de editor para expandir / colapsar estas secuencias.

El 20% al 25% de mis líneas de código ruby ​​son "finales" en su propia línea, todas inferidas trivialmente por mis convenciones de sangría. Ruby es el lenguaje de ceceo para haber logrado el mayor éxito. Cuando la gente discute esto, preguntando dónde están todos los espantosos paréntesis, les muestro una función de rubí que termina en siete líneas de "fin" superfluo.

Codifiqué durante años usando un preprocesador de lisp para inferir la mayoría de los paréntesis: una barra ''|'' abrió un grupo que se cerró automáticamente al final de la línea, y un signo de dólar ''$'' sirvió como un marcador de posición vacío donde de otro modo no habría símbolos para ayudar a inferir las agrupaciones. Esto es, por supuesto, territorio de guerra religiosa. Lisp / esquema sin los paréntesis es el más poético de todos los idiomas. Aún así, es más fácil simplemente silenciar paréntesis usando coloreado de sintaxis.

Sigo codificando con un preprocesador para Haskell, para agregar heredocs y para poner por defecto todas las líneas enrasadas como comentarios, todo sangrado como código. No me gustan los personajes de comentarios, sea cual sea el idioma.


La convención general es usar do ... end para bloques multilineales y llaves para bloques de una sola línea, pero también hay una diferencia entre los dos que se puede ilustrar con este ejemplo:

puts [1,2,3].map{ |k| k+1 } 2 3 4 => nil puts [1,2,3].map do |k| k+1; end #<Enumerator:0x0000010a06d140> => nil

Esto significa que {} tiene una precedencia más alta que do .. end, así que tenlo en cuenta cuando decidas qué quieres usar.

PD: un ejemplo más a tener en cuenta mientras desarrollas tus preferencias.

El siguiente código:

task :rake => pre_rake_task do something end

realmente significa:

task(:rake => pre_rake_task){ something }

Y este código:

task :rake => pre_rake_task { something }

realmente significa:

task :rake => (pre_rake_task { something })

Para obtener la definición real que desea, con llaves, debe hacer:

task(:rake => pre_rake_task) { something }

Tal vez el uso de llaves para los parámetros sea algo que quieras hacer de todos modos, pero si no lo haces, probablemente sea mejor usar do ... fin en estos casos para evitar esta confusión.


La regla más común que he visto (más recientemente en Eloquent Ruby ) es:

  • Si se trata de un bloque de varias líneas, utilice do / end
  • Si se trata de un solo bloque de línea, usa {}

Mi estilo personal es enfatizar la legibilidad sobre las reglas rígidas de { ... } vs do ... end choice, cuando tal elección es posible. Mi idea de legibilidad es la siguiente:

[ 1, 2, 3 ].map { |e| e + 1 } # preferred [ 1, 2, 3 ].map do |e| e + 1 end # acceptable [ 1, 2, 3 ].each_with_object [] do |e, o| o << e + 1 end # preferred, reads like a sentence [ 1, 2, 3 ].each_with_object( [] ) { |e, o| o << e + 1 } # parens make it less readable Foo = Module.new do # preferred for a multiline block, other things being equal include Comparable end Foo = Module.new { # less preferred include Comparable } Foo = Module.new { include Comparable } # preferred for a oneliner Foo = module.new do include Comparable end # imo less readable for a oneliner [ [ 1 ], [ 1, 2 ] ].map { |e| e.map do |e| e + 1 end } # slightly better [ [ 1 ], [ 1, 2 ] ].map { |e| e.map { |e| e + 1 } } # slightly worse

En una sintaxis más compleja, como bloques anidados multilínea, trato de intercalar { ... } y do ... delimitadores end para el resultado más natural, por ej.

Foo = Module.new { if true then Bar = Module.new { # I intersperse {} and keyword delimiters def quux "quux".tap do |string| # I choose not to intersperse here, because puts "(#{string.size} characters)" # for multiline tap, do ... end in this end # case still loks more readable to me. end } end }

Aunque la falta de reglas rígidas puede producir elecciones ligeramente diferentes para los diferentes programadores, creo que la optimización caso por caso para la legibilidad, aunque subjetiva, es una ganancia neta sobre la adherencia a las reglas rígidas.


Puse otra Respuesta, aunque ya se señaló la gran diferencia (prcedence / binding), y eso puede causar problemas difíciles de encontrar (Tin Man y otros lo señalaron). Creo que mi ejemplo muestra el problema con un fragmento de código no tan habitual, incluso los programas experimentados no se leen como los domingos:

module I18n extend Module.new { old_translate=I18n.method(:translate) define_method(:translate) do |*args| InplaceTrans.translate(old_translate, *args) end alias :t :translate } end module InplaceTrans extend Module.new { def translate(old_translate, *args) Translator.new.translate(old_translate, *args) end } end

Luego hice un código que embellece ...

#this code is wrong! #just made it ''better looking'' module I18n extend Module.new do old_translate=I18n.method(:translate) define_method(:translate) do |*args| InplaceTrans.translate(old_translate, *args) end alias :t :translate end end

si cambias {} aquí para do/end obtendrás el error, ese método de translate no existe ...

Por qué sucede esto se señala aquí más de uno: precedencia. ¿Pero dónde poner llaves aquí? (@the Tin Man: siempre uso llaves, como tú, pero aquí ... supervisado)

así que cada respuesta como

If it''s a multi-line block, use do/end If it''s a single line block, use {}

es simplemente incorrecto si se usa sin el "PERO ¡Tenga cuidado con las llaves / prioridad!"

de nuevo:

extend Module.new {} evolves to extend(Module.new {})

y

extend Module.new do/end evolves to extend(Module.new) do/end

(lo que sea, el resultado de extender lo hace con el bloque ...)

Entonces, si quieres usar do / end usa esto:

#this code is ok! #just made it ''better looking''? module I18n extend(Module.new do old_translate=I18n.method(:translate) define_method(:translate) do |*args| InplaceTrans.translate(old_translate, *args) end alias :t :translate end) end


Se reduce al sesgo personal, prefiero las llaves en un bloque do / end, ya que es más comprensible para un mayor número de desarrolladores debido a que la mayoría de los lenguajes de fondo los usan sobre la convención do / end. Con eso se dice que la verdadera clave es llegar a un acuerdo dentro de su tienda, si do / end es utilizado por 6/10 desarrolladores de CADA UNO debería usarlos, si 6/10 usa llaves, entonces apegue a ese paradigma.

Se trata de hacer un patrón para que el equipo en su conjunto pueda identificar las estructuras de código más rápido.


Un par de rubyists influyentes sugieren usar llaves cuando usas el valor de retorno, y do / end cuando no lo haces.

http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace (en archive.org)

http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc (en archive.org)

Esto parece una buena práctica en general.

Modificaría un poco este principio para decir que debes evitar usar do / end en una sola línea porque es más difícil de leer.

Debes tener más cuidado al utilizar llaves porque se unirá al parámetro final de un método en lugar de a la llamada al método completo. Simplemente agregue paréntesis para evitar eso.