Este es el cuarto post de la serie sobre Ruby on Rails desde cero creada por @smarquesz, no dejes de revisar los otros posts.
En el capítulo anterior, generamos un pequeño módulo usando scaffold, ahora revisaremos y explicaremos el código generado, vamos a partir por las rutas, para lo cual usando su editor favorito (les recomiendo Sublime Text) vamos a abrir el proyecto y navegaremos hasta config/routes.rb
, ahí encontraremos el siguiente código:
Rails.application.routes.draw do resources :posts # mucho código comentado end
En este archivo se definen todas las rutas que serán soportadas por nuestra aplicación, en este ejemplo el scaffold, nos ha generado todos los recursos para posts, es decir todas las rutas Restful, pero, ¿que pasa si quiero agregar nuevas rutas?, ¿puedo cambiar el home por defecto de mi aplicación?, bueno si miramos un poco el código que se encuentra comentado, encontraremos las respuestas.
# You can have the root of your site routed with "root" root 'welcome#index'
Haciendo uso de root
, desactivaremos el home por defecto y crearemos uno personalizado, para eso ubiquen la linea del ejemplo anterior y le quitamos el comentario, tal como se observa en el ejemplo, ahora vamos a la terminal siempre dentro de my-first-app
y levantamos el servidor usando:
rails server
Luego abrimos el navegador y nos dirigimos a http://localhost:3000/ y:
¿Qué es eso? ¡Tranquilos! Recuerdan que en el capítulo anterior vimos que cuando se hace match a una ruta, se crea la instancia del controlador y se ejecuta la acción indicada, en este caso Welcome#index
no existe. ¿Cómo lo solucionamos? Sencillo, vamos a utilizar nuevamente los maravillosos generadores de Ruby on Rails, para eso nos dirigimos a la terminal, presionamos ctrl + c
, para detener nuestro servidor y ejecutamos lo siguiente:
rails g controller welcome index --no-test-framework
Al ejecutar esto, obtendremos la siguiente salida:
create app/controllers/welcome_controller.rb route get 'welcome/index' invoke erb create app/views/welcome create app/views/welcome/index.html.erb invoke helper create app/helpers/welcome_helper.rb invoke assets invoke coffee create app/assets/javascripts/welcome.js.coffee invoke scss create app/assets/stylesheets/welcome.css.scss
¡Excelente! ya hemos creado nuestro home personalizado, para verlo, ejecutamos nuevamente «rails server», vamos a nuestro navegador y nos dirigimos de nuevo a http://localhost:3000/ y listo, ¿Maravilloso, no? Pues sí, pero antes de personalizarlo realmente veremos otro tema importantísimo para poder entender el framework Ruby on Rails, llamado convención sobre configuración.
Este patrón de convención sobre configuración le permite a los frameworks como Ruby on Rails resolver problemas o tomar decisiones automáticamente por nosotros, si yo estoy llamando a la acción index
del controlador Welcome
, se hará el render de la vista index.html.erb
que se encuentra dentro de app/views/welcome
, por lo cual naveguemos hasta ahí y escribamos un mensaje personalizado: «Bienvenido a nuestra aplicación super cool».
Nos dirigimos nuevamente al navegador, refrescamos, y voilà! Ya hemos creado nuestro home personalizado.
Controlador
Muy bien es hora de analizar el controlador de posts y ver que encontramos ahí dentro, para lo cual en nuestro editor de texto favorito, abriremos el archivo posts_controller
que se encuentra dentro de app/controllers
, y veremos el siguiente código:
Lo primero que nos podría parecer extraño es:
before_action :set_post, only: [:show, :edit, :update, :destroy] def set_post @post = Post.find(params[:id]) end
before_action
es un método que recibe 2 argumentos: el primero es qué método será ejecutado antes de cada acción y el segundo, para que acciones será ejecutado, en este caso se ejecutará sólo para show, edit, update y destroy.
Interesante, pero:
¿Para qué sirve el método before_action?
La respuesta es sencilla, DRY (don’t repeat yourself), no te repitas, se imaginan que el día de mañana viene nuestro jefe y nos dice, «muchachos, ya no vamos a usar el id para buscar los posts, ahora vamos a usar el slug», si no usamos DRY, tendríamos que cambiar el código en 4 métodos, lo cual nos puede llevar a cometer errores; en cambio, en nuestro ejemplo, sólo cambiaríamos el código del método «set_post» que está al final del archivo posts_controller.
Listar todos los posts
Al seguir visualizando el código de nuestro controlador, encontraremos el método index
, el cual contiene los siguiente:
def index @posts = Post.all end
Como podemos intuir, estamos pidiendo todos los posts a nuestro modelo, para luego mostrarlos a los usuarios en pantalla. Recuerden que Ruby on Rails, se basa en convención sobre configuración, por lo tanto cuando se ejecute este método, se hará el render del archivo index.html.erb
, que podemos encontrar dentro app/views/posts.
En dicha vista, recibimos la colección de @posts
y los mostramos en una tabla. Para ejecutar código Ruby dentro de una vista se deben utilizar las etiquetas <%= %> (aunque también tenemos otros sistemas de plantillas como veremos más adelante)
Visualizando un post
def show end
Se preguntarán qué puede hacer un método vacío, pero recuerden el before_action
que explicamos el principio de este apartado, antes de cada una de esas acciones entre ellas show
, se ejecuta un método llamado set_post
, que pide al modelo el post que coincide con el parámetro id
que le estamos enviando en la petición y lo guarda en la variable @post
, por ejemplo, al visitar /posts/2
, el «2» corresponde al parámetro id
, por lo cual al llamarse a la vista app/views/posts/show.html.erb
, veremos en pantalla el registro que contenga ese id.
<%= notice %>
Title: <%= @post.title %>
Como podemos observar, estamos recibiendo un objeto del tipo Post almacenado en la variable de instancia @post
y mostrando en pantalla sus atributos.
Creando posts
Seguimos avanzando en nuestro controlador, es hora de ver como se crea un nuevo post.
# GET /posts/new def new @post = Post.new end
Como se muestra en el ejemplo anterior, podríamos indicar que este proceso se compone de 2 partes, primero, al hacer una petición GET a /posts/new
, veremos en pantalla la vista app/views/posts/new.html.erb
.
New post
<%= render 'form' %> <%= link_to 'Back', posts_path %>
Si ya observaron el código se habrán dado cuenta que en vez de un formulario HTML, nos encontramos con esto. Como ya hemos visto en este artículo, Ruby on Rails, hace mucho énfasis en DRY (don’t repeat yourself), por lo cual lo único que estamos haciendo es separar el formulario en otro archivo, para que luego podamos reutilizarlo, esto se hace a través de un partial.
Los partials son sub-plantillas que pueden reutilizarse dentro de otra plantilla, su nombre debe comenzar con un _ y para incluirlos en nuestras vistas, lo hacemos utilizando el método render
, el cual le pasamos como parámetro, el nombre de nuestro archivo, sin el _ ni la extensión (.html.erb). Ahora veamos que contiene este archivo:
<%= form_for(@post) do |f| %> <% if @post.errors.any? %><% end %><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:
<% @post.errors.full_messages.each do |message| %>
- <%= message %>
<% end %><%= f.label :title %>
<%= f.text_field :title %>
Al fin hemos encontrado el formulario que nos permitirá crear un nuevo post, para ello, hacemos uso de un helper
llamado form_for
, al cual le estamos pasando como argumentos, el objeto @post
y un bloque de código que contiene el cuerpo del formulario
Dentro del cuerpo del form, lo primero que encontramos es:
if @post.errors.any?
Al existir errores de validación de nuestro formulario, éstos serán mostrados en pantalla, para que el usuario los pueda corregir, luego podemos ver que usando:
<%= f.label :title %> <%= f.text_field :title %>
Se crean los label y los input, por último podemos ver que con f.submit
, se creará el botón de submit que enviará nuestro formulario con la información que será guardada en nuestra base de datos, el submit del form es manejado por el método create
de nuestro controlador.
# POST /posts def create @post = Post.new(post_params) respond_to do |format| if @post.save format.html { redirect_to @post, notice: 'Post was successfully created.' } format.json { render :show, status: :created, location: @post } else format.html { render :new } format.json { render json: @post.errors, status: :unprocessable_entity } end end end
Vemos acá, que estamos creando una nueva instancia de nuestro modelo Post y la inicializamos con algo llamado post_params
¿Qué hace el método post_params?
Si vamos al final de nuestro controlador, encontraremos parte de la respuesta.
def post_params params.require(:post).permit(:title, :body, :author, :publication_date) end
¿Para qué me sirve esto?. Con strong parameters, no podemos asignar parámetros de forma masiva a nuestro modelo, hasta que estos son incluidos en una especie de «whitelist», que es lo que estamos haciendo en el método post_params
, en él sólo estamos permitiendo, los parámetros title, body, author y publication_date
.
Bien ahora que ya entendemos lo que hace strong_parameters
, podemos seguir viendo que hace nuestro método create
, bueno, en realidad no mucho, sólo estamos llamando al método save
del objeto @post
, el cual nos devuelve true/false
, dependiendo si se creó o no el registro, en caso de que se haya creado correctamente, seremos redireccionados al detalle del post, de lo contrario seremos enviados nuevamente al formulario para que corrijamos los errores.
Editando un post
Es hora de que veamos qué sucede cuando queremos editar la información de un post en particular.
# GET /posts/1/edit def edit end
A esta altura ya no nos debemos sorprender que exista un método «vacío», ya que recuerden, que antes de la acción edit
, se ejecuta el método, set_post
, el cual busca el registro con el id que estamos enviando en la petición GET /posts/1/edit
, lo almacena en la variable @post
, para luego ser enviada a nuestra vista que se encuentra en app/views/posts/edit.html.erb
.
Editing post
<%= render 'form' %> <%= link_to 'Show', @post %> | <%= link_to 'Back', posts_path %>
Si se fijan nuevamente estamos llamando el mismo partial «form», eso quiere decir que estamos reutilizando el mismo formulario tanto para crear como para editar un registro, ahora se estarán preguntando como Rails sabe que acción debe ejecutar, como les dije anteriormente: Ruby on Rails, hace énfasis en la convención sobre configuración, por lo tanto al usar rutas Restful, el helper form_for
, al recibir como argumento un recurso (@post
), puede inferir a qué url y con que métodos HTTP, realizar la petición, pueden encontrar más información referente a esto en Estilo orientado a recursos.
Si seguimos las convenciones de Ruby on Rails, el framework nos ayudará en muchas tareas triviales y que si queremos hacerlas por nuestra cuenta, nos consumirán mucho de nuestro valioso tiempo.
Es hora de ver que sucede cuando el usuario edita la información y hace click en el botón Update post
.
def update respond_to do |format| if @post.update(post_params) format.html { redirect_to @post, notice: 'Post was successfully updated.' } format.json { render :show, status: :ok, location: @post } else format.html { render :edit } format.json { render json: @post.errors, status: :unprocessable_entity } end end end
Como podemos observar el método update
luce similar a create
, sin embargo podemos apreciar algunas diferencias, primero que nada, antes de la acción update
, se ejecuta el método set_post
, que recuerden que busca el registro de acuerdo al id y lo almacena en la variable @post
, luego se llama el método update
del objeto @post
que recibe como argumentos los parámetros del «whitelist» que pueden ser asignados al modelo, el cual nos devolverá true/false
, dependiendo si se actualizó correctamente el post o no.
Eliminado un post
Bueno, hemos llegado a la última parte del CRUD, ya hemos visto, Create, Read y Update, ahora veremos Delete. Cuando hacemos una petición al posts/1
, usando el método HTTP DELETE
, se ejecutará el código del método delete
de nuestro controlador.
# DELETE /posts/1 def destroy @post.destroy respond_to do |format| format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' } format.json { head :no_content } end end
Al igual que edit
, update
y show
, antes de ejecutar el método, destroy
, se ejecuta el método, set_post
, para que dentro del método destroy
, podamos hacer uso de dicho @post
y poder ejecutar su método @destroy, que eliminará el registro de la base de datos.
Layouts
En las vistas que hemos estudiado hasta ahora sólo hemos encontramos código inherente a dicha vista en particular y ningún código que contenga el header, footer, menús de navegación y otros. Esto es porque Ruby on Rails hace el uso de Layouts:
# codigo dentro de views/layouts/layouts.html.erbMyFirstApp <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> <%= yield %>
Como dije anteriormente todo el código común a todas las vistas, debe ir dentro de los layouts, como podemos observar acá, está el título, que por ahora será el mismo para todas las páginas, sin embargo en la práctica esto no debe ser así, pero la idea por ahora es mantenerlo sencillo, están además las llamadas a los archivos js
y css
y por último dentro del body
encontramos la instrucción <%= yield %> que es en donde será puesto el contenido de nuestras vistas. Los layouts siguen convenciones similares a las vistas, por lo cual si queremos un layout especial para un controlador en particular, basta sólo con colocar un archivo con el nombre de nuestro controlador dentro de app/views/layouts
.
Modelo
Hasta este punto, hemos visto, las Rutas, las Vistas y los Controladores, es hora de ver que sucede con los Modelos, para eso vamos abrir el archivo post.rb
, que se encuentra dentro de app/models
.
class Post < ActiveRecord::Base end
Cómo podemos observar, nuestro modelo hereda de ActiveRecord::Base
, lo cual nos entrega todas las herramientas necesarias para poder leer y escribir la información en nuestra base de datos, ahora podrá parecer algo mágico, pero a medida que vayan avanzando en el aprendizaje del framework y el lenguaje Ruby, entenderán como es que sucede la magia detrás de escena :).
Como les dije en el post anterior, el modelo tiene 2 responsabilidades, manipular la data y encapsular la lógica de negocios, para ilustrar esto último, supongamos que el título y el autor, de ahora en adelante deben ser requeridos, es decir no se permitirá crear un post, si no contiene esos datos, para lo cual vamos a ir a nuestro modelo de Post y agregar lo siguiente:
validates :title, :author, presence: true
Para probarel código recuerden dirigirse a la terminal siempre dentro de la carpeta de nuestro proyecto my-first-app
y ejecutar "rails server"
Luego vamos a ir a nuestro navegador a crear un nuevo post, http://localhost:3000/posts/new
y vamos a presionar el botón Create post
, dejando en blanco en título y el autor.
Como se pueden dar cuenta con una simple línea hemos agregado lógica de validación a nuestro modelo. Maravilloso ¿No?.
Conclusión
Este post es la continuación de nuestro post anterior, en donde se generó todo este código, usando los generadores de rails, sin embargo me quise dar el tiempo de explicarles que hace el código, ya que es la base para poder aprender como realmente funciona el framework, que sucede cuando se recibe una petición del cliente (usuario), como se manejan las rutas, etc.
Es cierto que en el mundo real, la programación no es tan sencilla como hacer un CRUD, sin embargo si entienden esto y siguen la filosofía de Ruby on Rails, les será mucho más simple abordar problemas más complejos.
Los espero en nuestra próxima entrega, en donde comenzaremos a ver temas más orientados a problemas del mundo real.
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.
Lección anterior MVC, Rutas y Scaffold en Ruby on Rails Lección siguiente Creando un sitio multi idioma con Ruby on Rails (i18n)