Domina la sintaxis de Ruby: variables, control flow y el concepto de todo-es-objeto

DUGLAS MORENODUGLAS MORENO 👁 3

Introducción

Ruby es un lenguaje de programación dinámico y orientado a objetos que se destaca por su sintaxis elegante y su filosofía "todo es un objeto". Ya seas un principiante que recién conoce Ruby o un desarrollador experimentado que busca refrescar los conceptos básicos, este artículo te guiará a través de los pilares fundamentales de la sintaxis de Ruby: variables, estructuras de control y la naturaleza orientada a objetos del lenguaje.

El enfoque aquí es práctico. Verás ejemplos concretos de cómo declarar variables, escribir condicionales, iterar sobre colecciones y trabajar con objetos de manera natural en Ruby. Aunque el código no se ejecuta directamente en este artículo (debido a las limitaciones del entorno de ejecución), cada fragmento incluye la salida esperada para que puedas probarlo localmente y ver cómo funciona Ruby en tu máquina.

Al finalizar, tendrás una comprensión clara de:

  • Cómo declarar y utilizar variables (including nil, true, false).
  • Cómo controlar el flujo de ejecución con if/else, case, while, for y el poderoso each de Ruby.
  • Por qué "todo es un objeto" en Ruby: clases, herencia, métodos, objetos singleton y valores especiales.
  • Las mejores prácticas y errores comunes a los que hay que prestar atención.

Este artículo está dirigido a lectores que tienen experiencia básica en programación y quieren aprender Ruby, o a aquellos que quieren afinar su estilo y escribir código más idiomatico de Ruby.


Variables en Ruby

Declaración y asignación

En Ruby, una variable es simplemente un contenedor para un valor. No es necesario declarar el tipo de una variable; Ruby infiere el tipo a partir del valor que le asignas.

# Asignación simple
nombre = "Ana"
edad = 30
activo = true

El operador de asignación (=) funciona igual que en la mayoría de los lenguajes, pero Ruby permite reasignar una variable cualquier cantidad de veces:

contador = 0
contador = contador + 1   # => 1
contador += 1             # => 2

Convenciones de nombres

  • Identificadores: Los nombres de variables pueden incluir letras, dígitos y guiones bajos, pero deben comenzar con una letra minúscula o un guión bajo.
  • Convención de snake_case: Ruby favorece los nombres en snake_case para variables (variable_name), mientras que los métodos usan snake_case.
  • Constantes: Los nombres que comienzan con mayúsculas son constantes (CONSTANTE). Mantienen el mismo valor durante toda la ejecución del programa.
PI = 3.14159
MAX_INT = 2**31 - 1

Valores nil, true y false

Ruby tiene tres objetos especiales:

  • nil – el valor nulo, que representa "no hay valor". Se usa comúnmente para indicar la ausencia de un resultado.
  • true y false – los valores booleanos utilizados en controles de flujo y lógica.
resultado = nil
exitoso = true
fracasado = false

Todos estos son objetos de instancia de Object (o NilClass, TrueClass, FalseClass), lo que refuerza el concepto de que todo es un objeto.

Alcance de las variables

  • Variables de instancia: Prefijadas con @, visibles dentro de una clase e sus subclases.
  • Variables de clase: Prefijadas con @@, compartidas entre todos los objetos de una clase.
  • Constantes: Mayúsculas, pueden ser sobrescritas pero se espera que sean inmutables.
  • Variables locales: Sin prefijo, accesibles solo dentro del método o bloque donde están definidas.
class Cliente
  attr_accessor :nombre, :email

  def initialize(nombre, email)
    @nombre = nombre   # variable de instancia
    @email = email
  end

  def saludar
    puts "Hola, #{@nombre}!"
  end
end

Consejo: Siempre que puedas, usa métodos de acceso (attr_reader, attr_writer, attr_accessor) en lugar de acceder directamente a las variables de instancia.


Estructuras de Control de Flujo

Ruby adopta una sintaxis minimalista para las decisiones y los bucles, haciendo que el código sea conciso y legible.

Condicionales if/elsif/else

edad = 20

if edad < 13
  puts "Eres un niño"
elsif edad >= 13 && edad < 18
  puts "Eres un adolescente"
else
  puts "Eres un adulto"
end

Salida esperada:

Eres un adulto

El if es un método en Ruby, por lo que podés escribirlo como una expresión:

mensaje = edad < 13 ? "Eres un niño" : "Eres un adulto"
puts mensaje

case/when

El case es útil cuando tenés múltiples valores para comparar.

día = :viernes

case día
when :lunes
  puts "¡Buen inicio de semana!"
when :viernes
  puts "¡El viernes es el día de pago!"
else
  puts "Día normal"
end

Salida esperada:

¡El viernes es el día de pago!

Bucle while

contador = 0
while contador < 3
  puts "Contador: #{contador}"
  contador += 1
end

Salida esperada:

Contador: 0
Contador: 1
Contador: 2

Bucle for

El for en Ruby es sintácticamente similar a while, pero itera sobre una colección.

colores = [:rojo, :verde, :azul]
for color in colores
  puts "Color: #{color}"
end

Salida esperada:

Color: :rojo
Color: :verde
Color: :azul

Iteración con each

El método each es el bucle preferido de Ruby para recorrer colecciones. Funciona sobre Array, Hash, Range y cualquier objeto enumerable.

numeros = [1, 2, 3, 4, 5]
numeros.each do |n|
  puts n * 2
end

Salida esperada:

2
4
6
8
10

Puedes usar each con una expresión de ( ) para una sintaxis más compacta:

numeros.each { |n| puts n * 2 }

while modificado vs. until

Ruby también ofrece un while modificado (ejecuta el cuerpo al menos una vez) y un until (mientras la condición sea falsa):

i = 0
loop do
  puts i
  break if i == 2
  i += 1
end

Salida esperada:

0
1
2

Mejor práctica: Evita goto y break/next/redo no esenciales. Los bloques, each y map suelen expresar la intención de manera más clara.


Todo es un Objeto

Esta es probablemente la característica más icónica de Ruby: todo es un objeto. Incluso los valores literales (true, false, nil, números, cadenas) tienen comportamiento de clase completo, métodos y pueden ser extendidos.

Clases y Objetos

Una clase agrupa datos (estado) y comportamiento (comportamiento). Los objetos se crean a partir de una clase usando new (o un constructor personalizado).

class Libro
  attr_accessor :titulo, :autor

  def initialize(titulo, autor)
    @titulo = titulo
    @autor = autor
  end

  def descripcion
    "\"#{@titulo}\" por #{@autor}"
  end

  def to_s
    descripcion
  end
end

mi_libro = Libro.new("Cien años de soledad", "Gabriel García Márquez")
puts mi_libro          # => "Cien años de soledad" por Gabriel García Márquez

Herencia

Ruby soporta herencia simple: una subclase hereda métodos e instanciadores de su clase padre.

class Animal
  def hablar
    puts "Sonido genérico"
  end
end

class Perro < Animal
  def hablar
    puts "¡Guau guau!"
  end
end

perro = Perro.new
perro.hablar   # => ¡Guau guau!

Objetos Singleton y extend

Ruby permite agregar métodos a un objeto individual sin necesidad de una clase completa.

module Utilerías
  def saludar
    "Hola desde #{self}"
  end
end

obj = Object.new
obj.extend(Utilerías)
puts obj.saludar

Salida esperada:

Hola desde #<Object:0x...>

Valores como Objetos

# Cadenas
"hola".upcase   # => "HOLA"
# Números
5.times { puts "¡Hola!" }   # imprime "¡Hola!" cinco veces
# Booleanos
true && false   # => false
# `nil`
nil.to_s        # => "" (cadena vacía)

Dado que cada valor es un objeto, puedes enviar mensajes a ellos directamente.

Objetos Clave-Valor (Hash)

Un Hash es un objeto que representa una colección de parejas de clave-valor, lo que permite una búsqueda rápida.

def info_estudiante(identificacion)
  {
    nombre: "Alice",
    edad: 22,
    curso: "Matemáticas"
  }
end

datos = info_estudiante(1)
puts datos[:nombre]   # => Alice

Objetos Array

Un Array es un objeto que almacena una colección ordenada de elementos.

notas = [85, 90, 78]
notas << 95           # agrega al final => [85, 90, 78, 95]
notas.each { |n| puts n }

Métodos y Receivers

Todos los objetos responden al mensaje methods (o public_methods) para mostrar los métodos disponibles.

puts "Hola".methods.grep(/upcase/)  # => [:upcase]

Conclusión: Tratar todo como un objeto fomenta la composición y la reutilización. Puedes pasar cualquier valor a un método, y ese método puede invocar cualquier método del objeto.


Bloques, Procs y Lambdas

Ruby ofrece un sistema flexible para definir unidades de código anónimas.

Bloques ({ |...| ... })

Los bloques son trozos de código que pueden ser pasados a métodos como each.

calentador = [1, 2, 3]
calentador.each { |x| puts x * 2 }

Salida esperada:

2
4
6

Procs

Un Proc es un objeto que encapsula un bloque. Puedes almacenarlo, pasarlo como argumento o llamar explícitamente.

duplicar = Proc.new { |n| n * 2 }
puts duplicar.call(21)   # => 42

Lambdas

Las lambdas son como Procs pero stricter: devuelven un valor y tienen un número fijo de argumentos.

restar = ->(a, b) { a - b }
puts restar.call(10, 4)   # => 6

Mejor práctica: Usa lambdas cuando necesites un procedimiento con un número conocido de argumentos; usa Procs cuando necesites flexibilidad (argumentos variables).


Buenas Prácticas y Errores Comunes

  1. Usa métodos de acceso en lugar de acceso directo a variables de instanciaattr_reader/attr_writer/attr_accessor hacen que la API sea más clara y permiten validación futura.
  2. Prefiere each, map, select y inject sobre los bucles for y while – esos métodos de colección son más idiomáticos de Ruby y son chaineables.
  3. Evita mutar objetos compartidos – Si pasas un Array o Hash a otro método, ten cuidado de no modificarlo a menos que sea intencional. Considera copiar el objeto (dup/clone) primero.
  4. No uses nil como falso – Ruby tiene booleanos (true/false), pero muchas construcciones evalúan nil como falso. Escribe if something en lugar de if something != nil para mayor claridad.
  5. Usa case para múltiples comparaciones – Es más legible que una cadena de if/elsif cuando hay muchos valores discretos.
  6. No sobrescribas constantes – Aunque Ruby lo permite, sobrescribir una constante puede ocultar errores y hacer que el código sea más difícil de entender. Si necesitas un valor configurable, usa una variable de clase o una opción.
  7. Sigue la convención de snake_case para variables y métodos – Hace que el código sea más legible y se alinea con la comunidad de Ruby.
  8. Usa private/protected para ocultar métodos – Limita la exposición pública e indica la intención.

Conclusión

En este artículo, exploramos los bloques fundamentales de la sintaxis de Ruby:

  • Variables – nombres, alcance y las convenciones de nil, true y false.
  • Estructuras de controlif/elsif/else, case, while, for y each.
  • El principio de "todo es un objeto" – clases, herencia, objetos singleton y la naturaleza de los valores.

Al practicar con los ejemplos proporcionados, deberías sentirte cómodo escribiendo código de Ruby claro y idiomático. Recuerda que Ruby valora la legibilidad y la elegancia; elige el enfoque más natural y aprovecha el poder de los objetos.

Próximos pasos: Una vez que domines los conceptos básicos, considera profundizar en módulos, mixturación, metaprogramación y el uso de rails (si estás interesado en el desarrollo web). Sumergirte en la rica colección de gemas de Ruby también puede acelerar tu productividad.

¡Feliz programación en Ruby!

Comentarios (0)

Sé el primero en comentar.

Dejá tu comentario