Los ejemplos a seguir son importantes. -- Oficial Alex J. Murphy, RoboCop
Una cosa que siempre me ha molestado como desarrollador en Ruby es, que los desarrolladores en Python tienen una genial referencia de estilo para programar (la PEP-8), y nosotros, los que desarrollamos en Ruby, nunca hemos tenido una guía oficial que documente el estilo del código y las mejores practicas de los programas escritos. Yo creo que el estilo importa. También creo que tan finos compañeros, como somos nosotros, debemos ser muy capaces de producir éste documento tan necesario.
Ésta guía comenzó su vida como los lineamientos internos para la escritura de código en Ruby de nuestra compañía (escrita por mí, con sinceridad). En algún punto decidí que el trabajo que estaba haciendo podría ser de interés para los miembros de la comunidad de Ruby en general y aunque el mundo tenia poca necesidad de otro lineamiento interno, bien podría beneficiarse de un conjunto de practicas, expresiones y sugerencias de estilo para la programación en Ruby que es encausado y sancionado por la comunidad.
Desde la concepción de la guía he recibido bastante retroalimentación por parte de los miembros de la excepcional comunidad de Ruby alrededor del mundo. ¡Gracias por su apoyo! Juntos podemos construir un recurso en beneficio de todos y cada uno de los desarrolladores en Ruby ahí afuera.
Por cierto, si estás trabajando con Rails tal vez te gustaría revisar la Guía de estilo para Ruby on Rails 3 & 4 como complemento.
Ésta guía de estilo de Ruby recomienda buenas practicas para que programadores de Ruby del mundo real puedan escribir código que pueda ser mantenido por otros programadores de Ruby en el mundo real. Una guía de estilo que refleja los usos en el mundo real, es utilizada, y una guía de estilo que se apega a un ideal que ha sido rechazado por las personas a las que se supone debería ayudar, se arriesga a quedar en el olvido – sin importar que tan buena es.
La guía está dividida en varias secciones que agrupan convenciones relacionadas. He intentado agregar el razonamiento detrás de cada una de ellas (a menos que sea muy obvio, en cuyo caso las he omitido).
Las convenciones no aparecieron de la nada – la mayoría tienen como base mi extensa experiencia como ingeniero de software profesional, la retroalimentación y las sugerencias de miembros de la comunidad entorno a Ruby, así como varias fuentes que son altamente estimadas como recursos para la programación en Ruby, tales como «Programming Ruby 1.9» y «The Ruby Programming Language».
Hay algunas áreas en las que no hay un consenso dentro de la comunidad de Ruby respecto a un estilo en particular (como el entrecomillado de cadenas de texto, los espacios al definir diccionarios, la posición del punto cuando se ocupan varias líneas al encadenar métodos, etc.). En tales casos, se recopilan los estilos populares y queda a tu elección el aplicar uno de ellos de forma consistente.
La guía aun es un trabajo en progreso - algunas convenciones no tienen ejemplos de código, otras no tienen ejemplos que las ilustren con suficiente claridad. Mientras esos detalles son arreglados - sólo tenlos en mente por ahora.
Puedes generar una copia en PDF o HTML de está guía usando Transmuter y encontrarás la versión original en inglés en éste repositorio. También, puedes revisar tus programas usando RuboCop, un analizador de código escrito en Ruby, basado en ésta guía de estilo.
Traducciones de ésta guía están disponibles en los siguientes lenguajes:
- 简体中文 (Chino Simplificado)
- 簡體中文 (Chino Tradicional)
- Français (Frances)
- 日本語 (Japones)
- Português (Portugues)
- Tiếng Việt (Vietnamita)
- Estructura del código
- Sintaxis
- Identificadores
- Comentarios
- Anotaciones
- Clases
- Excepciones
- Colecciones
- Cadenas
- Expresiones regulares
- Literales percentiles
- Metaprogramación
- Misceláneos
Casi todos están convencidos de que cada estilo a excepción del suyo es feo e imposible de leer. Deja fuera el "a excepción del suyo" y probablemente tengan razón...
-- Jerry Coffin (sobre identación)
-
Usa
UTF-8
como la codificación de los archivos del código fuente. -
Usa dos espacios por nivel de identación.
# no recomendado - cuatro o más espacios def some_method do_something end # recomendado def some_method do_something end
-
Usa terminadores de líneas con estilo Unix (LF). Los usuarios de BSD, Solaris, Linux y OSX no deben de tener problemas, pero los usuarios de Windows deben de tener cuidado.)
-
Si usas Git tal vez quisieras agregar la siguiente opción de configuración para proteger tu proyecto de terminaciones de línea estilo Windows (CR+LF) que pudieran colarse.
$ git config --global core.autocrlf true
-
-
Usa espacios alrededor de operadores (
+
,-
,*
,/
), después de las comas, los:
, los;
, alrededor de{
y antes de}
. El espacio en blanco puede ser (en su mayoría) irrelevante, pero su uso apropiado es muy importante para escribir código que se lea fácilmente.sum = 1 + 2 a, b = 1, 2 1 > 2 ? true : false; puts 'Helo' [1, 2, 3].each { |e| puts e }
La única excepción es cuando se usa el operador para exponentes:
# no recomendado e = M * c ** 2 # recomendado e = M * c**2
-
No usar espacios antes de
(
,[
o después de]
,)
.some(arg).other [1, 2, 3].length
-
Identa los
when
entrecase
yend
.case when song.name == 'Trololo' puts 'Not again!' when song.duration > 120 puts 'Too long!' when Time.now.hour > 21 puts "It's too late" else song.play end
-
Usa lineas vacías dentro de los métodos para dividirlos en párrafos lógicos.
def some_method data = initialize(options) data.manipulate! data.result end def some_method result end
-
Identa y alinea los parámetros de la llamada a un método si se extienden en varias lineas.
# no recomendado: la linea es demasiado larga def send_mail(source) Mailer.deliver(to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end # recomendado def send_mail(source) Mailer.deliver( to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text ) end
-
Usa RDoc y sus convenciones para documentar las APIs. No coloques una linea vacía entre un comentario y una definición.
-
Mantén las lineas en un tamaño de 80 caracteres o menos.
-
Evita el uso innecesario de espacios en blanco al final de las lineas.
-
Prefiere el uso de paréntesis en la definición de un método cuando éste reciba argumentos.
def some_method_with_arguments(arg1, arg2) # cuerpo omitido end def some_method # cuerpo omitido end
-
Evita el uso del ciclo
for
a menos que tengas una buena razón. A diferencia de los iteradores, las variables definidas dentro defor
son visibles fuera de él.arr = [1, 2, 3] # no recomendado for elem in arr do puts elem end # recomendado arr.each { |elem| puts elem }
-
Las expresiones
if x: ...
,if x; ...
,when x: ...
ywhen x; ...
fueron removidas en Ruby 1.9, por lo tanto...-
Utiliza el operador ternario en vez de
if x: ...
# no recomendado result = if some_condition: something else something_else end # recomendado result = some_condition ? something : something_else
-
Utiliza la expresión alternativa
when x then ...
en vez dewhen x: ...
en instrucciones de una línea.genre = case year when 1850..1889 then 'Blues' when 1890..1909 then 'Ragtime' when 1910..1929 then 'New Orleans Jazz' when 1930..1939 then 'Swing' when 1940..1950 then 'Bebop' else 'Jazz' end
-
-
Si utilizas el operador ternario, procura sólo tener una expresión por rama. Esto también quiere decir que debe evitarse el uso de anidaciones dentro de los operadores ternarios. Tampoco lo expandas sobre varias lineas. Es preferible utilizar construcciones
ìf/else
en esos casos.# no recomendado some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else # recomendado if some_condition nested_condition ? nested_something : nested_something_else else something_else end
-
Procura utilizar
&&/||
para expresiones booleanas, yand/or
para flujo de control. (Regla del pulgar: Si tienes que usar paréntesis, estás utilizando los operadores equivocados).# expresión booleana if some_condition && some_other_condition do_something end # flujo de control document.saved? or document.save!
-
Favorece el uso de los modificadores
if/unless
cuando tengas un cuerpo de una sola línea. Otra buena alternativa es el uso deand/or
.# no recomendado if some_condition do_something end # recomendado do_something if some_condition # otra buena opción some_condition and do_something
-
Favorece el uso de
unless
sobreif
para condiciones negativas (o utilizaor
).# no recomendado do_something if !some_condition # recomendado do_something unless some_condition # otra buena opción some_condition or do_something
-
Procura no usar
unless
conelse
. Reescribe la instrucción con el caso positivo primero.# no recomendado unless success? puts 'failure' else puts 'success' end # recomendado if success? puts 'success' else puts 'failure' end
-
Si la condición de un
if/unless/while
es sencilla, se lee más claro si no se rodea con paréntesis, a menos que la condición contenga una asignación.# no recomendado if (x > 10) # cuerpo omitido end # recomendado if x > 10 # cuerpo omitido end # recomendado cuando hay asignaciones if (x = self.next_value) # cuerpo omitido end
-
Favorece el uso de los modificadores
while/until
cuando tengas un cuerpo de una sola línea.# no recomendado while some_condition do_something end # recomendado do_something while some_condition
-
Procura utilizar
until
sobrewhile
para condiciones negativas.# no recomendado do_something while !some_condition # recomendado do_something until some_condition
-
Es mejor omitir los paréntesis alrededor de parámetros en métodos que pertenecen un DSL interno (como Rake, Rails, RSpec...), métodos que tienen un estado de "palabra clave" en Ruby (como attr_reader, puts...) y métodos de acceso a atributos. De no ser así, es mejor rodear los parámetros del método con paréntesis.
class Person attr_reader :name, :age # omitido end temperance = Person.new('Temperance', 30) temperance.name puts temperance.age x = Math.sin(y) array.delete(e)
-
Si el primer argumento de un método comienza con un paréntesis abierto, usa otro juego de paréntesis para invocar el método. Por ejemplo, escribe
f((3 + 2) + 1)
. -
Procura no dejar espacios entre el nombre del método y el paréntesis de apertura.
# no recomendado f (3 + 2) + 1 # recomendado f(3 + 2) + 1
-
Prefiere
{...}
para bloques de una sola línea y expresiones encadenadas. Es es mejor usardo...end
para bloques de varias líneas, flujo de control y definiciones de métodos. (Evita usar expresiones encadenadas en varias líneas. Pregúntate si no pueden ser separadas en métodos de una forma elegante).names = ['Bozhidar', 'Steve', 'Sarah'] # recomendado names.each { |name| puts name } # no recomendado names.each do |name| puts name end # recomendado names.select { |name| name.start_with?('S') }.map { |name| name.upcase } # no recomendado names.select do |name| name.start_with?('S') end.map { |name| name.upcase }
-
Evita usar
self
donde no es requerido.# no recomendado def ready? if self.last_reviewed_at > self.last_updated_at self.worker.update(self.content, self.options) self.status = :in_progress end self.status == :verified end # recomendado def ready? if last_reviewed_at > last_updated_at worker.update(content, options) self.status = :in_progress end status == :verified end
-
Es importante que evites hacer sombra a los métodos con variables locales a menos que sean equivalentes.
class Foo attr_accessor :options # bien def initialize(options) self.options = options # options y self.options son equivalentes aquí end # no recomendado def do_something(options = {}) unless options[:when] == :later output(self.options[:message]) end end # recomendado def do_something(params = {}) unless params[:when] == :later output(options[:message]) end end end
-
Mejora la legibilidad al dejar espacios alrededor del operador de asignación en los valores por defecto para los parámetros de un método.
# no recomendado def some_method(arg1=:default, arg2=nil, arg3=[]) # do something... end # recomendado def some_method(arg1 = :default, arg2 = nil, arg3 = []) # do something... end
-
Evita las continuaciones de linea (\) cuando no son requeridas. Es mejor si estructuras tu código para evitar su uso de ser posible.
# no recomendado result = 1 - \ 2
-
Usar el valor retornado de una asignación esta bien, pero rodea la asignación con paréntesis.
# no recomendado if v = array.grep(/foo/) ... # bien - muestra la intención de uso de la asignación if (v = array.grep(/foo/)) ... # bien - muestra el uso de la asignación y tiene una precedencia correcta if (v = self.next_value) == 'hello' ...
-
Está bien utilizar
||=
para inicializar variables, excepto si se trata de variables booleanas. (Considera lo que puede pasar si el valor esfalse
).# asina Bozhidar como el nombre, sólo si es nulo o falso name ||= 'Bozhidar' # no recomendado - sería asignado verdadero aun si es falso enabled ||= true # recomendado enabled = true if enabled.nil?
-
Utiliza la sintaxis literal de Ruby 1.9 para los hashes. (A menos que estés trabajando con una versión que no la soporte, claro). De igual forma, utiliza la nueva sintaxis literal para lambda.
# no recomendado hash = { :one => 1, :two => 2 } # recomendado hash = { one: 1, two: 2 } # no recomendado lambda = lambda { |a, b| a + b } lambda.call(1, 2) # recomendado lambda = ->(a, b) { a + b } lambda.(1, 2)
-
Usa un guión bajo
_
para los parámetros no usados en un bloque.# no recomendado result = hash.map { |k, v| v + 1 } # recomendado result = hash.map { |_, v| v + 1 }
-
Evita utilizar las variables especiales estilo Perl (como
$0-9
, `$``, etc). Son bastante crípticas y su uso fuera de scripts de una sola línea no es bien visto. -
Para recordar algunas de las recomendaciones sobre la sintaxis del código, ejecuta el interprete con la opción
-w
. Así generara advertencias cuando encuentre alguna practica no recomendada.
Las únicas dificultades reales al programar son la validación del cache y el nombrar cosas.
-- Phil Karlton
-
Utiliza
snake_case
para métodos y variables. -
Utiliza
CamelCase
para clases y módulos. (Deja en mayúsculas los acrónimos como HTTP, RFC y XML). -
Utiliza
SCREAMING_SNAKE_CASE
para otras constantes. -
Los nombres de métodos predicados (métodos que devuelven un valor booleano) deben terminar con un signo de interrogación:
?
(por ejemploArray#empty?
). -
Los nombres de métodos potencialmente peligrosos (por ejemplo, métodos que modifican los argumentos o
self
, que no ejecutan los terminadores como lo haceexit
, etc.) deben terminar con una marca de exclamación!
si existe una versión segura de ese método peligroso.# no recomendado - no hay una versión segura del método class Person def update! end end # bien class Person def update end end # recomendado class Person def update! end def update end end
-
Define el método seguro en términos del método peligroso cuando sea posible.
class Array def flatten_once! res = [] each do |e| [*e].each { |f| res << f } end replace(res) end def flatten_once dup.flatten_once! end end
-
Cuando uses
reduce
en bloques cortos, nombra los argumentos como|a, e|
(acumulador, elemento). -
Cuando definas operadores binarios, nombra el argumento como
other
.def +(other) # cuerpo omitido end
-
Prefiere
map
sobrecollect
,find
sobredetect
,select
sobrefind_all
,reduce
sobreinject
ysize
sobrelength
. Escribirás menos. :-)