🛤️ Módulo 11-12: Introducción a Ruby on Rails

Duración: Semanas 11 y 12

Objetivos del Módulo

  • Entender qué es Ruby on Rails y por qué es importante
  • Conocer el patrón MVC (Modelo-Vista-Controlador)
  • Crear una aplicación Rails básica
  • Configurar rutas, controladores y vistas
  • Conectar con bases de datos usando ActiveRecord

Contenido Teórico

1. ¿Qué es Ruby on Rails?

Ruby on Rails (o simplemente Rails) es un framework web escrito en Ruby que sigue el patrón MVC. Fue creado para hacer el desarrollo web más rápido y eficiente.

Principios de Rails:

  • DRY (Don’t Repeat Yourself): No repetir código
  • Convention over Configuration: Convenciones sobre configuración
  • RESTful Design: Arquitectura REST para APIs

2. Instalación de Rails

# Instalar Rails
gem install rails

# Verificar instalación
rails --version

# Crear nueva aplicación
rails new mi_app
cd mi_app

# Iniciar servidor
rails server

3. Estructura de una aplicación Rails

mi_app/
├── app/
│   ├── controllers/     # Controladores
│   ├── models/         # Modelos
│   ├── views/          # Vistas
│   └── assets/         # CSS, JS, imágenes
├── config/
│   ├── routes.rb       # Configuración de rutas
│   └── database.yml    # Configuración de BD
├── db/
│   └── migrate/        # Migraciones de BD
├── Gemfile             # Dependencias
└── public/             # Archivos estáticos

4. Patrón MVC en Rails

Modelo (Model)

Representa los datos y la lógica de negocio:

# app/models/product.rb
class Product < ApplicationRecord
  validates :name, presence: true
  validates :price, presence: true, numericality: { greater_than: 0 }
  
  def price_with_tax
    price * 1.21
  end
end

Vista (View)

Presenta la información al usuario:

<!-- app/views/products/index.html.erb -->
<h1>Lista de Productos</h1>

<% @products.each do |product| %>
  <div class="producto">
    <h3><%= product.name %></h3>
    <p>Precio: €<%= product.price %></p>
    <p>Con IVA: €<%= product.price_with_tax %></p>
  </div>
<% end %>

Controlador (Controller)

Maneja las peticiones y coordina modelo y vista:

# app/controllers/products_controller.rb
class ProductsController < ApplicationController
  def index
    @products = Product.all
  end
  
  def show
    @product = Product.find(params[:id])
  end
  
  def new
    @product = Product.new
  end
  
  def create
    @product = Product.new(product_params)
    
    if @product.save
      redirect_to @product, notice: 'Producto creado exitosamente.'
    else
      render :new
    end
  end
  
  private
  
  def product_params
    params.require(:product).permit(:name, :price, :description)
  end
end

5. Rutas en Rails

# config/routes.rb
Rails.application.routes.draw do
  root 'home#index'
  
  # Rutas RESTful completas
  resources :products
  
  # Rutas personalizadas
  get 'acerca', to: 'pages#about'
  post 'contacto', to: 'pages#contact'
  
  # Rutas anidadas
  resources :categories do
    resources :products, only: [:index, :show]
  end
end

6. ActiveRecord (Modelos)

ActiveRecord es el ORM (Object-Relational Mapping) de Rails:

# Migraciones
class CreateProducts < ActiveRecord::Migration[7.0]
  def change
    create_table :products do |t|
      t.string :name, null: false
      t.decimal :price, precision: 8, scale: 2
      t.text :description
      t.references :category, foreign_key: true
      
      t.timestamps
    end
  end
end

# Modelo con relaciones
class Product < ApplicationRecord
  belongs_to :category
  has_many :order_items
  has_many :orders, through: :order_items
  
  validates :name, presence: true, length: { minimum: 3 }
  validates :price, presence: true, numericality: { greater_than: 0 }
  
  scope :expensive, -> { where('price > ?', 100) }
  scope :by_category, ->(cat) { where(category: cat) }
end

7. Comandos Útiles de Rails

# Generar controlador
rails generate controller Products index show new create

# Generar modelo
rails generate model Product name:string price:decimal

# Generar scaffold (completo)
rails generate scaffold Product name:string price:decimal description:text

# Ejecutar migraciones
rails db:migrate

# Rollback migraciones
rails db:rollback

# Consola de Rails
rails console

# Ver rutas
rails routes

8. Formularios en Rails

<!-- app/views/products/_form.html.erb -->
<%= form_with model: @product, local: true do |form| %>
  <% if @product.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@product.errors.count, "error") %> impidieron guardar:</h2>
      <ul>
        <% @product.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>

  <div class="field">
    <%= form.label :price %>
    <%= form.number_field :price, step: :any %>
  </div>

  <div class="field">
    <%= form.label :description %>
    <%= form.text_area :description %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

9. Helpers en Rails

# app/helpers/application_helper.rb
module ApplicationHelper
  def format_price(price)
    number_to_currency(price, unit: "€", separator: ",", delimiter: ".")
  end
  
  def pretty_date(date)
    date.strftime("%d de %B de %Y")
  end
  
  def product_status(product)
    if product.stock > 10
      content_tag :span, "En stock", class: "badge badge-success"
    elsif product.stock > 0
      content_tag :span, "Poco stock", class: "badge badge-warning"
    else
      content_tag :span, "Agotado", class: "badge badge-danger"
    end
  end
end

10. Testing Básico

# test/models/product_test.rb
require 'test_helper'

class ProductTest < ActiveSupport::TestCase
  test "debe tener nombre" do
    product = Product.new(price: 10.0)
    assert_not product.valid?
    assert_includes product.errors[:name], "can't be blank"
  end
  
  test "precio debe ser positivo" do
    product = Product.new(name: "Test", price: -5)
    assert_not product.valid?
    assert_includes product.errors[:price], "must be greater than 0"
  end
  
  test "calcular precio con IVA" do
    product = Product.new(name: "Test", price: 100)
    assert_equal 121.0, product.price_with_tax
  end
end

Ejercicios Prácticos

Ve al archivo ejercicios.md para ejercicios paso a paso.

Proyecto del Módulo

Ve al archivo proyecto_blog_rails.md para completar el proyecto final.