Mejores Practicas
## Objetivos de Aprendizaje
- Escribir codigo Ruby idiomatico
- Seguir las convenciones de la guia de estilo de Ruby
- Usar las fortalezas de Ruby efectivamente
- Evitar errores comunes
## Guia de Estilo de Ruby
### Convenciones de Nombres
```ruby
# Variables y metodos: snake_case
primer_nombre = "Alicia"
def calcular_total
end
# Clases y modulos: CamelCase
class CuentaUsuario
end
module ProcesamientoPagos
end
# Constantes: SCREAMING_SNAKE_CASE
MAXIMO_REINTENTOS = 3
TIEMPO_POR_DEFECTO = 30
# Booleanos: terminar con ? o is_
valido? = true
esta_activo = true
tiene_hijos = false
# Metodos bang: terminar con !
def ordenar!
@datos.ordenar!
end
# Metodos peligrosos: terminar con !
def eliminar!
@registro.destruir
end
```
### Indentacion y Espaciado
```ruby
# Usar 2 espacios para indentacion (no tabs)
def nombre_metodo
if condicion
hacer_algo
end
end
# Espacios alrededor de operadores
total = a + b
resultado = x * y / z
# Sin espacios alrededor de parentesis en definiciones de metodos
def saludar(nombre)
"Hola, #{nombre}"
end
# Espacio despues de comas
array = [1, 2, 3]
hash = { nombre: "Alicia", edad: 30 }
```
### Longitud de Linea
```ruby
# Mantener lineas bajo 120 caracteres
# Si es necesario, romper con continuacion
nombre_metodo_largo(arg1, arg2, arg3,
arg4, arg5)
# O usar barra invertida
resultado_largo = algun_metodo(arg1) +
otro_metodo(arg2)
```
## Ruby Idiomatico
### Preferir Metodos de Enumerable
```ruby
# Mal
for n in numeros
puts n
end
# Bien
numeros.each { |n| puts n }
# Mal
resultado = []
numeros.each { |n| resultado << n * 2 }
# Bien
resultado = numeros.map { |n| n * 2 }
# Mal
encontrado = nil
items.each { |item| encontrado = item if item.valido? }
# Bien
encontrado = items.find { |item| item.valido? }
```
### Usar Symbol to Proc
```ruby
# Forma larga
nombres.map { |nombre| nombre.upcase }
# Idiomatico
nombres.map(&:upcase)
```
### Usar ||= Con Mesura
```ruby
# Puede causar problemas con valores falsy
valor = false
valor ||= predeterminado # valor se convierte en predeterminado (incorrecto!)
# Enfoque mas seguro
valor = predeterminado if valor.nil?
# O
valor = valor ? valor : predeterminado
# O (Ruby 2.3+)
valor &&= valor # Solo asigna si es truthy
```
### Asignacion Paralela
```ruby
# Intercambiar variables idiomatically
a, b = b, a
# Evitar cuando no es necesario
x = 1
y = 2
z = x + y # Solo usar lineas separadas
```
### Condicionales
```ruby
# Ternario para casos simples
estado = aprobado ? "Exito" : "Fallo"
# Clausulas de guardia
def procesar(datos)
return unless datos.valido?
# ... logica principal
end
# Modificador de una linea
puts "debug" if modo_depuracion
```
## Errores Comunes
### Mutando Constantes
```ruby
# Mal
DATOS = [1, 2, 3]
DATOS << 4 # Modifica el original!
# Bien - freeze
DATOS = [1, 2, 3].freeze
# O clonar cuando sea necesario
modificado = DATOS.dup << 4
```
### Shadowing de Variables
```ruby
# Esto sombrea 'nombre' externo accidentalmente
nombre = "Alicia"
[1, 2, 3].each do |nombre|
# Ahora nombre es 1, 2, 3 en cada iteracion
end
puts nombre # "Alicia" - aun funciona pero confuso
```
### Optimizacion Prematura
```ruby
# No sacrificar legibilidad por micro-optimizaciones
# Mal
resultado = mapa { |e| e.to_s } * ""
# Bien y claro
resultado = mapa(&:to_s).join
```
### Monkey Patching
```ruby
# Arriesgado - puede romper otro codigo
class String
def es_email?
self =~ /@/
end
end
# Mas seguro - usar refinements
module ExtensionesString
refine String do
def es_email?
self =~ /@/
end
end
end
```
## Organizacion del Codigo
### Responsabilidad Unica
```ruby
# Mal - hace multiples cosas
def procesar_usuario(usuario)
validar(usuario)
guardar(usuario)
enviar_email(usuario)
registrar(usuario)
end
# Bien - separar responsabilidades
def registrar_usuario(usuario)
validar(usuario)
guardar(usuario)
end
def saludar_usuario(usuario)
enviar_email(usuario)
registrar(usuario)
end
```
### Longitud de Metodos
```ruby
# Mal - metodo hace demasiado
def importar_y_procesar_datos
# 100 lineas de codigo
end
# Bien - separar en metodos mas pequenos
def importar_datos
end
def procesar_datos
end
```
### Longitud de Clases
```ruby
# Mal - Dios object
class GestorUsuario
def crear_usuario
end
def eliminar_usuario
end
def autenticar
end
def enviar_email
end
def generar_informe
end
# 50 mas metodos...
end
# Bien - Responsabilidad Unica
class ServicioUsuario
def crear_usuario
end
end
class ServicioAutenticacion
def autenticar
end
end
class ServicioNotificacion
def enviar_email
end
end
```
## Documentacion
### Comentarios
```ruby
# Buen comentario - explica POR QUE, no QUE
# Logica de reintento necesaria porque la API externa puede ser inestable
MAXIMO_REINTENTOS = 3
# Mal comentario - redundante
# Incrementar contador por 1
contador += 1
```
### RDoc/YARD
```ruby
# @param nombre [String] el nombre del usuario
# @param edad [Integer] la edad del usuario
# @return [Usuario] el usuario creado
# @raise [ArgumentError] si el nombre esta vacio
def crear_usuario(nombre:, edad:)
end
```
## Consejos de Rendimiento
### Evaluacion Perezosa
```ruby
# Mal - crea array completo
datos = (1..1_000_000).map { |n| calculo_pesado(n) }.select { |n| n.valido? }
# Bien - procesa uno a la vez
datos = (1..1_000_000).lazy.map { |n| calculo_pesado(n) }.select { |n| n.valido? }.first(100)
```
### Construccion de Cadenas
```ruby
# Mal - crea cadenas intermedias
html = ""
partes.each { |parte| html += parte }
# Bien
html = partes.join
```
### Usar metodos bang cuando sea apropiado
```ruby
# Cuando no necesitas el original
lista.ordenar! # Mas eficiente que lista = lista.ordenar
```
## Mejores Practicas de Pruebas
```ruby
# Probar comportamiento, no implementacion
# Mal
expect(usuario.instance_variable_get(:@nombre)).to eq("Alicia")
# Bien
expect(usuario.nombre).to eq("Alicia")
# Usar nombres de pruebas descriptivos
# Mal
it "test1" do
end
# Bien
it "retorna el nombre completo del usuario cuando primero y ultimo estan presentes" do
end
# Una asercion por prueba (generalmente)
# Pero las aserciones compuestas estan bien
expect(usuario).to have_attributes(nombre: "Alicia", edad: 30, valido: true)
```
## Mejores Practicas de Seguridad
### Validacion de Entrada
```ruby
# Siempre validar entrada
def procesar_pedido(id_pedido)
raise ArgumentError, "ID de pedido invalido" unless id_pedido.is_a?(Integer)
end
# Sanitizar antes de SQL (usar consultas parametrizadas)
# Usuario.where("nombre = ?", params[:nombre]) # Con ActiveRecord
```
### Evitar eval
```ruby
# Peligroso - nunca usar con entrada del usuario
eval(entrada_usuario)
# Alternativas mas seguras
# Para codigo: analizar con Ripper
# Para datos: usar JSON/YAML
```
## Resumen
- Seguir convenciones de nomenclatura de Ruby: snake_case, CamelCase, SCREAMING_SNAKE_CASE
- Usar metodos de Enumerable sobre bucles manuales
- Preferir `map`, `select`, `find` sobre `each` con condicionales
- Usar clausulas de guardia para retornos anticipados
- Mantener metodos cortos y enfocados
- Escribir pruebas para comportamiento, no implementacion
- Usar metodos bang cuando se intended mutacion
- Evitar monkey patching sin refinements
- Documentar POR QUE, no QUE
- Mantener lineas bajo 120 caracteres
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →