Resource o recursos en Rails: CRUD con scaffold
Un recurso (resource) en Rails es un elemento sobre el que puedes hacer un CRUD: Create, Read, Update, Destroy (crear, leer, actualizar y borrar). Un curso, un usuario o un coche son recursos.
Con un curso, por ejemplo, puedes darlo de alta, consultarlo, corregir sus datos o darlo de baja. Rails reparte ese trabajo entre tres piezas, el modelo, la vista y el controlador (el patrón MVC), y te deja generarlas a mano o de golpe con un scaffold (en inglés, «andamio»).
Crear un recurso con scaffold
- Genera el recurso.
- Crea la tabla en la base de datos.
- Arranca el servidor y ábrelo en el navegador.
1. Genera el recurso
bin/rails generate scaffold NombreRecurso atributo:tipo
Por ejemplo, un curso con título:
bin/rails generate scaffold Course title:string
2. Crea la tabla
El scaffold genera una migración, un fichero que describe la tabla courses. Para aplicarla:
bin/rails db:migrate
Tienes más detalle en Migraciones en Ruby on Rails.
3. Arranca el servidor
bin/rails server
Y abre localhost:3000/courses en el navegador. Al principio la lista está vacía:
Con New Course creas un curso con los atributos que declaraste, en este caso solo el título:
Al volver a /courses, el curso nuevo aparece en la lista con sus enlaces para verlo, editarlo o borrarlo:
Las capturas son de Rails 5.2. En Rails 8 el aspecto cambia un poco, pero el flujo es el mismo.
Las rutas: resources :courses
El scaffold añade una línea a config/routes.rb:
resources :courses
Esa línea crea las siete rutas del CRUD (puedes verlas con bin/rails routes -c courses):
| Verbo HTTP | Ruta | Acción | Para qué |
|---|---|---|---|
| GET | /courses |
index |
Listar todos |
| GET | /courses/new |
new |
Formulario para crear |
| POST | /courses |
create |
Guardar el nuevo |
| GET | /courses/:id |
show |
Ver uno |
| GET | /courses/:id/edit |
edit |
Formulario para editar |
| PATCH / PUT | /courses/:id |
update |
Guardar los cambios |
| DELETE | /courses/:id |
destroy |
Borrar |
GET /courses: la ruta elige la acción, el controlador pide los datos al modelo y pinta la vista dentro del layout.El controlador: recibe la petición
El controlador recibe las peticiones del usuario y habla con el modelo. Cuando pides GET /courses, el servidor lo anota en el log, ejecuta la acción index, hace la consulta de todos los cursos y pinta la vista courses/index.html.erb dentro del layout application:
Started GET "/courses" for 127.0.0.1 at 2018-07-30 08:37:02 +0100
Processing by CoursesController#index as HTML
Rendering courses/index.html.erb within layouts/application
Course Load (0.2ms) SELECT "courses".* FROM "courses"
↳ app/views/courses/index.html.erb:14
Rendered courses/index.html.erb within layouts/application (18.8ms)
Completed 200 OK in 212ms (Views: 207.8ms | ActiveRecord: 1.3ms)
Cada pieza de ese log tiene su fichero en la carpeta app del proyecto:
app/
├── assets/
├── channels/
├── controllers/
│ ├── concerns/
│ ├── application_controller.rb
│ └── courses_controller.rb
├── helpers/
├── jobs/
├── mailers/
├── models/
│ ├── concerns/
│ ├── application_record.rb
│ └── course.rb
└── views/
├── courses/
│ ├── _course.json.jbuilder
│ ├── _form.html.erb
│ ├── edit.html.erb
│ ├── index.html.erb
│ ├── index.json.jbuilder
│ ├── new.html.erb
│ ├── show.html.erb
│ └── show.json.jbuilder
└── layouts/
├── application.html.erb
├── mailer.html.erb
└── mailer.text.erb
La acción que genera el scaffold es así de corta:
def index
@courses = Course.all
end
Por defecto, Rails pinta la vista con el mismo nombre que la acción, dentro de la carpeta del controlador, y usa el layout application.html.erb. Si quieres indicar otra plantilla u otro layout, hazlo con render:
def index
@courses = Course.all
render template: "courses/index", layout: "application"
end
En el artículo original, con Rails 5.2, la plantilla llevaba la extensión:
# GET /courses
# GET /courses.json
def index
@courses = Course.all
render template: "courses/index.html.erb", layout: "application"
end
El modelo: habla con la base de datos
El modelo representa, casi siempre, una tabla de la base de datos y te permite trabajar con ella sin escribir SQL. Course hereda de ApplicationRecord, que a su vez hereda de ActiveRecord::Base, donde están declarados métodos como all, new, save o where.
Los modelos están en app/models:
app/models/
├── concerns/
├── application_record.rb
└── course.rb
course.rb solo declara la herencia:
class Course < ApplicationRecord
end
Y application_record.rb, tal como lo generaba Rails 5.2:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
En Rails 8, application_record.rb usa primary_abstract_class en lugar de self.abstract_class = true; la idea es la misma: es una clase base que no tiene tabla propia.
Puedes probar el modelo directamente en la consola de Ruby on Rails, o hacer el CRUD completo desde ella con CRUD desde consola en Ruby on Rails.
Las vistas: lo que ve el usuario
Las vistas son ficheros HTML con Ruby incrustado (ERB). Están en app/views, normalmente con una carpeta por recurso y una vista por pantalla: listar, ver, crear, editar.
En Rails 5.2, el scaffold de Course generaba estas vistas:
app/views/courses/
├── _course.json.jbuilder
├── _form.html.erb
├── edit.html.erb
├── index.html.erb
├── index.json.jbuilder
├── new.html.erb
├── show.html.erb
└── show.json.jbuilder
En Rails 8 el scaffold genera index, show, new, edit, el formulario compartido _form y el parcial _course. Si tu proyecto incluye la gema jbuilder (viene por defecto con rails new), también genera las vistas .json.jbuilder para responder en JSON.
El layout layouts/application.html.erb tiene la estructura HTML común, y en el body un yield donde Rails incrusta la vista de cada acción:
<!DOCTYPE html>
<html>
<head>
<title>MiApp</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= yield %>
</body>
</html>
Ruta, controlador, modelo, vista: cada pieza hace una sola cosa.
Fuentes
- Getting Started with Rails y Rails Routing from the Outside In: resources (guías de Rails 8.1).
- Layouts and Rendering in Rails:
render templateylayout. - The Rails Command Line: generate scaffold.
Ejecutado con Rails 8.1.4.1 y Ruby 4.0.9 (imagen oficial ruby:latest de Docker): el scaffold de Course y de Coche (que falla), db:migrate, bin/rails routes -c courses y una petición GET /courses con render template con y sin extensión. Las capturas del navegador, los logs y el código de Rails 5.2 son de Rubén.