10. Rutas controladores & vistas - williamromero/curso-rails GitHub Wiki

Diagrama de Peticiones Para comprender un poco sobre el funcionamiento integrado entre Nginx & Puma, a continuación poseemos un diagrama ilustrativo en el cual podremos analizar un poco la interacción de las peticiones en nuestra aplicación.

Al lado izquierdo, tenemos la imagen de como funciona de forma real, una aplicación de RoR ejecutándose en un servidor web o bien, en un entorno local. Lo primero que necesitamos saber es que Puma, es un servidor basado en Rack, que es la interfaz que se ejecuta entre las peticiones URI y los frameworks web de Ruby.

Es común que conozcamos Nginx como un servidor de peticiones HTTP & que Puma sea reconocido como un servidor de aplicaciones ya que interactúa entre las peticiones HTTP, la interfaz Rack & la aplicación web.

Es así, que Puma es utilizado para respetar los principios fundamentales de separation of concerns o lo que vendría siendo en español, lo que conocemos como el principio de responsabilidad única, en el que Puma se encargará únicamente de la administración de las peticiones y nos permitirá manejar algunos métodos como: path_info, ip, request method, body, user_agent y el framework web, de la interacción con los recursos de la información.

🚘 ROUTES 🚍

El router de Rails, reconoce URLs y las despacha en forma de acciones de controlador o una aplicación Rack. Este enrutador también nos permite crear URLs para evitar establecer hipervínculos con texto sobre escrito. Cuando la aplicación Rails recibe una petición entrante, este pregunta al router si existe una acción de controlador que haga match con la que hemos nombrado.

Para visualizar las rutas de nuestro proyecto, podemos obtener una lista de las mismas ingresando el siguiente comando en consola:

  rails routes

      Prefix Verb   URI Pattern                       Controller#Action
        root GET    /                                 products#index
    products GET    /products(.:format)               products#index
             POST   /products(.:format)               products#create
 new_product GET    /products/new(.:format)           products#new
edit_product GET    /products/:uuid/edit(.:format)    products#edit
     product GET    /products/:uuid(.:format)         products#show
             PATCH  /products/:uuid(.:format)         products#update
             PUT    /products/:uuid(.:format)         products#update
             DELETE /products/:uuid(.:format)         products#destroy

Generando rutas singulares con código

Para ello, necesitaremos ir al archivo config/routes.rb e ingresar lo siguiente:

  # config/routes.rb
  get 'products', to: 'products#index'

Y luego llamar a esta ruta desde nuestra vista. En este caso nos enviará a la ruta principal para obtener la lista de todos los registros.

  # app/views/products/show.html.erb
  <%= link_to 'Ir a principal', products_path %>

O también podemos pedirle al controlador que ejecute una redirección cuando persistamos un registro mediante el método respond_to, que nos permite renderizar nuestra respuesta en diferentes formatos. En este caso, emplearemos el método redirect_url para enviar al usuario a la lista de productos luego de su

  # app/controllers/products_controller.rb

  def create
    @product = Product.new(product_params)

    respond_to do |format|
      if @product.save
        format.html { redirect_to product_url(@product.uuid), notice: "Product was successfully created." }
      else
        format.html { render :new, status: :unprocessable_entity }
      end
    end
  end

Cuando vayamos creando nuevas rutas, obtendremos más y más líneas como en el siguiente ejemplo. Con ellas, nos será fácil reconocer para que sirve cada una.

VERB URI Pattern Controlador/Acción Descripción
GET '/products/:uuid' 'products#show Obtenemos la acción show del controlador products
GET '/products/:uuid/purchase' 'products#purchase' Obtenemos la acción purchase del controlador products y el parámetro :uuid del mismo.

El único problema de esta solución manual, es que tendremos que describir todas para cada acción que necesitemos hacer. Para evitarlo, podemos hacer uso de un método muy importante llamado resources.

Resources (plural resources)

Los resources nos permiten crear un paquete de rutas que nos sirven para navegar entre los endpoints sobre los que manejaremos los registros de un modelo de datos de nuestra aplicación.

  resources :products

        root GET    /                                 products#index
    products GET    /products(.:format)               products#index
             POST   /products(.:format)               products#create
 new_product GET    /products/new(.:format)           products#new
edit_product GET    /products/:uuid/edit(.:format)    products#edit
     product GET    /products/:uuid(.:format)         products#show
             PATCH  /products/:uuid(.:format)         products#update
             PUT    /products/:uuid(.:format)         products#update
             DELETE /products/:uuid(.:format)         products#destroy

Namespaces

Los namespaces nos sirven para organizar un grupo de controladores dentro de módulo con un nombre determinado. Es común que los controladores se organicen en grupos de información similar, es por ello que con el identificador de módulo determinado generamos un block con el namespace y posterior a ello, seremos capaces de movilizar nuestros archivos (en el controlador y en las vistas) a un folder con ese nombre.

Por último, pero no menos importante es que la ruta también se verá afectada, puesto que para ingresar a ese recurso, estableceremos una estructura de la siguiente forma namespaces:modelo. A continuación podremos ver un ejemplo.

En el archivo de rutas:

  # config/routes.rb

  namespace :admin do
    resources :articles, :comments
  end 

En la consola:

  mkdir app/controllers/admin
  mkdir app/views/admin

  mv app/controllers/articles_controller.rb app/controllers/admin/
  mv app/views/articles app/views/admin/

Y por último, agregamos en nuestro controlador el nombre del namespaces de la siguiente manera:

  class Admin::ArticlesController < ApplicationController

Luego, si corremos el comando rails routes obtendremos nuestras nuevas rutas de la siguiente forma:

HTTP Verb Path Controller#Action Named Route Helper
GET /admin/articles admin/articles#index admin_articles_path
GET /admin/articles/new admin/articles#new new_admin_article_path
POST /admin/articles admin/articles#create admin_articles_path
GET /admin/articles/:id admin/articles#show admin_article_path(:id)
GET /admin/articles/:id/edit admin/articles#edit edit_admin_article_path(:id)
PATCH/PUT /admin/articles/:id admin/articles#update admin_article_path(:id)
DELETE /admin/articles/:id admin/articles#destroy admin_article_path(:id)

Namespaces VS Scopes

Cuando usamos namespaces, estamos obteniendo predefinidos los valores de las opciones que podemos modificar en las rutas, que son los campos path, as y module. Estos atributos de nuestras rutas, nos permiten modificar tanto el prefijo con el que obtendremos el named route helper (que son las rutas que emplearemos dentro de las vistas de nuestro proyecto para dirigirnos de una pantalla a otra), o bien para configurar un prefijo de modulo que nos servirá para que podamos depositar diferentes tipos de vistas de datos, dentro de una carpeta en específico.

Por ejemplo:

namespace "admin" do
  resources :articles
end

El siguiente snippet representa lo mismo pero usando scope:

scope "/admin", as: "admin", module: "admin" do
  resources :articles
end

Ambos snippets van a generar las rutas de artículos de la siguiente forma:

HTTP Verb Path Controller#Action Named Route Helper
GET /admin/articles admin/articles#index admin_articles_path
GET /admin/articles/new admin/articles#new new_admin_article_path
POST /admin/articles admin/articles#create admin_articles_path
GET /admin/articles/:id admin/articles#show admin_article_path(:id)
GET /admin/articles/:id/edit admin/articles#edit edit_admin_article_path(:id)
PATCH/PUT /admin/articles/:id admin/articles#update admin_article_path(:id)
DELETE /admin/articles/:id admin/articles#destroy admin_article_path(:id)

Definición de las acciones que representan los verbos HTTP

VERBO  Acción
GET Obtener un recurso
POST Crear un recurso / Enviar a su creación
PUT Actualizar un recurso
DELETE Eliminar un recurso
PATCH Actualizar un recurso de forma parcial

Diferencias entre verbos PUT & PATCH

PUT VERB PATCH VERB
PUT es un método de modificación de recursos en el que el cliente envía datos que actualizan todo el recurso. PATCH es un método de modificación de recursos en el que el cliente envía datos parciales que se actualizarán sin modificar los datos completos.
En una solicitud PUT, la entidad adjunta se considera una versión modificada del recurso almacenado en el servidor de origen y el cliente solicita que se reemplace la versión almacenada. Sin embargo, con PATCH, la entidad adjunta contiene un conjunto de instrucciones que describen cómo se debe modificar un recurso que reside actualmente en el servidor de origen para producir una nueva versión..
Se dice que HTTP PUT es idempotente, por lo que si envía una solicitud de reintento varias veces, eso debería ser equivalente a una sola modificación de solicitud HTTP PATCH básicamente se dice que no es -idempotente. Entonces, si vuelve a intentar la solicitud N veces, terminará teniendo N recursos con N URI diferentes creados en el servidor.
Tiene un uso alto de ancho de banda Dado que solo los datos que deben modificarse si se envían en el cuerpo de la solicitud como carga útil, tiene un uso ancho de banda bajo .

Referencias

Puma server github repository

Puma app server

Rack middleware

Purpose of Rails Router

Differences between _path & _url prefix

Rails routes in depth

SOLID - Principio de responsabilidad única

Form Submit Patch and Put requests

⚠️ **GitHub.com Fallback** ⚠️