---
title: "Ejercicio guiado blog"
---
{%hackmd @themes/orangeheart %}
<style>
img {
width: 100%;
border: 2px solid #ef7060;
}
</style>
## Crear proyecto
1) Crear el proyecto, utilizando PostgreSql como base de datos:
```bash
rails new blog -d postgresql
```
2) Creamos la base de datos:
```
rails db:create
```
---
## Devise
[Devise](https://github.com/heartcombo/devise) es una gema que facilita la implementación de la autenticación en las aplicaciones.
1) Agregamos devise al proyecto en nuestro Gemfile:
```bash
echo gem 'devise' >> Gemfile
```
2) Ejecuta el siguiente comando para instalar la gema:
```
bundle
```
3) Ejecutar el generador de Devise
```bash
rails g devise:install
```
4) Crear el modelo de usuarios de la aplicación, generalmente se usa **User** como nombre:
```bash
rails g devise User
```
5) Corremos las migraciones:
```
rails db:migrate
```
## Probar las rutas de Devise
Al momento que ejecutamos `rails g devise:install` Devise crea, entre otras cosas, las siguientes rutas:
|Ruta|Nombre|Descripción|
|----|------|-----------|
|**GET**<br> `/users/sign_up`|new_user_registration<br>new_user_registration_path|***registrations/new.html.erb***<br>Formulario de registro|
|**POST**<br>`/users`||Registrarse|
|**GET**<br>`/users/sign_in`|new_user_session<br>new_user_session_path|***sessions/new.html.erb***<br>Formulario de login|
|**DELETE**<br>`/users/sign_out`|destroy_user_session<br>destroy_user_session_path||
Para ver el listado completo de rutas podemos ejecutar el siguiente comando:
```
rails routes -c devise --expanded
```
<details>
<summary>Mostrar rutas</summary>
```
--[ Route 1 ]--------------------------------------
Verb | GET
URI | /users/sign_in(.:format)
Controller#Action | devise/sessions#new
Prefix | new_user_session
--[ Route 2 ]--------------------------------------
Verb | POST
URI | /users/sign_in(.:format)
Controller#Action | devise/sessions#create
Prefix | user_session
--[ Route 3 ]--------------------------------------
Verb | DELETE
URI | /users/sign_out(.:format)
Controller#Action | devise/sessions#destroy
Prefix | destroy_user_session
--[ Route 4 ]--------------------------------------
Verb | GET
URI | /users/password/new(.:format)
Controller#Action | devise/passwords#new
Prefix | new_user_password
--[ Route 5 ]--------------------------------------
Verb | GET
URI | /users/password/edit(.:format)
Controller#Action | devise/passwords#edit
Prefix | edit_user_password
--[ Route 6 ]--------------------------------------
Verb | PATCH
URI | /users/password(.:format)
Controller#Action | devise/passwords#update
Prefix | user_password
--[ Route 7 ]--------------------------------- ----
Prefix |
Verb | PUT
URI | /users/password(.:format)
Controller#Action | devise/passwords#update
--[ Route 8 ]--------------------------------------
Prefix |
Verb | POST
URI | /users/password(.:format)
Controller#Action | devise/passwords#create
--[ Route 9 ]--------------------------------------
Prefix | cancel_user_registration
Verb | GET
URI | /users/cancel(.:format)
Controller#Action | devise/registrations#cancel
--[ Route 10 ]-------------------------------------
Prefix | new_user_registration
Verb | GET
URI | /users/sign_up(.:format)
Controller#Action | devise/registrations#new
--[ Route 11 ]-------------------------------------
Prefix | edit_user_registration
Verb | GET
URI | /users/edit(.:format)
Controller#Action | devise/registrations#edit
--[ Route 12 ]-------------------------------------
Prefix | user_registration
Verb | PATCH
URI | /users(.:format)
Controller#Action | devise/registrations#update
--[ Route 13 ]-------------------------------------
Prefix |
Verb | PUT
URI | /users(.:format)
Controller#Action | devise/registrations#update
--[ Route 14 ]-------------------------------------
Prefix |
Verb | DELETE
URI | /users(.:format)
Controller#Action | devise/registrations#destroy
--[ Route 15 ]-------------------------------------
Prefix |
Verb | POST
URI | /users(.:format)
Controller#Action | devise/registrations#create
```
</details>
Para ello primero lanzamos el servidor de desarrollo con el comando`rails s` y luego visitamos las siguientes rutas:
- http://localhost:3000/users/sign_in

- http://localhost:3000/users/sign_up

---
## Agregar links de Devise
Para que el usuario pueda registrarse, ingresar y salir de la aplicación vamos a agregar un controlador **pages** como página de inicio y definirla como la ruta raíz:
```
rails g controller pages home
```
Abrimos `config/routes.rb` y agrega lo siguiente:
```ruby=
Rails.application.routes.draw do
devise_for :users
root "pages#home"
end
```
Abrimos `app/views/pages/home.html.erb` y agrega lo siguiente:
```erb=
<div>
<% if signed_in? %>
<%= link_to "Salir", destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to "Registrarse", new_user_registration_path %>
<%= link_to "Ingresar", new_user_session_path %>
<% end %>
</div>
```
Rails 7 ahora usa Turbo y el método debe llamarse un poco diferente. Así que el código fijo ahora se vería así:
```er=
<div>
<% if signed_in? %>
<%= link_to "Salir", destroy_user_session_path, data: { turbo_method: :delete } %>
<% else %>
<%= link_to "Registrarse", new_user_registration_path %>
<%= link_to "Ingresar", new_user_session_path %>
<% end %>
</div>
```

---
## Scaffold de Post
1) Crear un modelo de Post usando scaffold:
```
rails g scaffold Post image title description:text
```
```mermaid
erDiagram
USER {
id integer PK
email string
password string
}
POST {
id integer PK
title string
description text
image string
user_id integer FK "Un post pertenece a un usuario"
}
USER ||--|{ POST :publica
```
2) Agregamos la migración para relacionar los modelos:
```
rails g migration AddUsersToPosts user:references
```
3) Ejecutamos la migración con `rails db:migrate`
4) Asociamos el modelo de **User** con los **Post**:
```ruby=0
# app/models/post.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :posts
end
```
5) Y lo mismo con **Post** hacia **User**:
```ruby=0
# app/models/user.rb
class Post < ApplicationRecord
belongs_to :user
end
```
6) Podemos crear Post mediante consola para ver si está conectado:
```irb!
Post.create!(image: 'https:/robohash.org/1', title: 'Servicio generador de imágenes', description: 'Servicio web generador de robots, aliens y monstruos', user_id: User.last.id)
```
Consultando la información en la tabla:
```
+-[ RECORD 1 ]+------------------------------------------------------+
| id | 1 |
| image | https:/robohash.org/1 |
| title | Servicio generador de imágenes |
| description | Servicio web generador de robots, aliens y monstruos |
| created_at | 2023-07-11 08:35:19.064116 |
| updated_at | 2023-07-11 08:35:19.064116 |
| user_id | 1 |
+-------------+------------------------------------------------------+
```
7) Al crear el post se lo vamos asociar al usuario conectado agregamos lo siguiente en el controlador **app/controllers/posts_controller.rb** (línea:25)
```ruby=23
def create
@post = Post.new(post_params)
@post.user = current_user
respond_to do |format|
if @post.save
format.html { redirect_to post_url(@post), notice: "Post was successfully created." }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
```
---
## Publicar post
Ahora abrimos de nuevo **app/views/pages/index.html.erb** y agremos el link para permitirle publicar al usuario conectado (en línea:5):
```erb=
<div>
<% if signed_in? %>
<%= link_to "Salir", destroy_user_session_path, data: { turbo_method: :delete } %>
<br>
<%= link_to "Publicar", new_post_path %>
<% else %>
<%= link_to "Registrarse", new_user_registration_path %>
<%= link_to "Ingresar", new_user_session_path %>
<% end %>
</div>
```
Luego de eso, ya podemos publicar posts como se ve a continuación:

Por lo general, cuando visitas un Blog, el usuario se encuentra con la vista index de posts. Para redirigir y permitir mostrar esta vista, tenemos varias opciones.
**Opción 1:** dejar post index como ruta root:
```ruby=
# config/routes.rb
Rails.application.routes.draw do
resources :posts
root "posts#index"
end
```
**Opción 2:** renombrar la ruta index de post, como "user_root", de esta forma cuando inicia sesión el usuario de va a redirigir a esta ruta:
```ruby=
#config/routes.rb
Rails.application.routes.draw do
resources :posts, except: [:index]
root "home#index"
get '/posts', to: 'posts#index', as: 'user_root'
end
```
**Opción 3:** Sobreescribir el método de Devise `after_sign_in_path_for`:
```ruby=
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
posts_path
end
end
```
> Fuente: [Wiki Devise](https://github.com/heartcombo/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in)
---
## Añadir paginación en los posts
[Pagy](https://github.com/ddnexus/pagy) es una gema que está disponible para la paginación y que está tomando mucha popularidad ya que presume ser la mejor en cuanto a eficiencia y optimización.
### Instalación de Pagy
Agregamos la gema **pagy** al proyecto:
```bash
bundle add pagy
```
### Configuración
Abrimos el archivo **app/controller/aplication_controller.rb** y añadimos lo siguiente:
```ruby=
class ApplicationController < ActionController::Base
include Pagy::Backend
end
```
>Podemos incluir el módulo `Pagy::Backend` en cada controlador o incluirlo de forma global en el archivo que muestra el ejemplo anterior.
Abrimos el archivo **app/helpers/application_helper.rb** y añadimos lo siguiente:
```ruby=
module ApplicationHelper
include Pagy::Frontend
end
```
>Podemos incluir el módulo `Pagy::Frontend` en cada controlador o incluirlo de forma global en el archivo que muestra el ejemplo anterior
### Implementación
Una vez instalada la gema e incluidos los módulos, podemos comenzar a trabajar la paginación en las colecciones.
El primer paso es crear una instancia de la clase Pagy en nuestro controlador **app/controllers/posts_controller.rb** podemos usar el método `pagy` y pasarle la colección que queremos paginar, esto creará una instancia y devolverá el objeto de Pagy por nosotros:
```ruby=5
def index
@pagy, @posts = pagy(Post.all)
end
```
Esto nos permite acceder fácilmente a la información sobre la paginación, como la página actual, la cantidad de elementos por página y la cantidad total de páginas.
Para ver el funcionamiento hasta ahora, podemos agregar lo siguiente en nuestro archivo **db/seeds.rb**:
```ruby
130.times { |i|
i+=1
Post.create(
image: "https://robohash.org/#{i}",
title: "Post #{i}",
description: "Post #{i} description",
user_id: 2)
}
```
Y ejecutamos la siembra con `rails db:seed`, con esto tendremos una buena cantidad de posts para la demostración:
### Visualizar los enlaces de páginación
El módulo `Pagy::Frontend` ofrece varios métodos útiles para facilitar la visualización y los aspecto de navegación de la paginación, como los siguientes helpers para usar en las vistas:
- `<%== pagy_nav(@pagy) %>`: <img src="https://hackmd.io/_uploads/BkHP7kTKh.png" style="width:140px" />
- `<%== pagy_info(@pagy) %>`: <img src="https://hackmd.io/_uploads/B13ioyTt3.png" style="width: 180px" />
Esto mostrará los enlaces de paginación en el formato estándar, pero también podemos personalizar la apariencia usando diferentes estilos. Por ejemplo, podemos usar el helper `pagy_bootstrap_nav`. En primer lugar debemos incluir este extra en `config/initializers/pagy.rb`:
```ruby
require 'pagy/extras/bootstrap'
```
Y ahora en lugar de usar `<%== pagy_nav(@pagy) %>` usamos el siguiente helper en la vista:
- `<%== pagy_bootstrap_nav(@pagy) %>`: <img src="https://hackmd.io/_uploads/Hk5btGRKh.png" style="width:210px" />
### Demostración

## Valores predeterminados de Pagy
Por medio de un nuevo archivo que podemos crear en **config/initializers/pagy.rb**, podemos modificar todas las opciones que nos proporciona Pagy [aquí](https://github.com/ddnexus/pagy/blob/master/lib/config/pagy.rb) 👈, esto afectará a todos los objetos de Pagy.
### Limitar la cantidad de items
Abrimos el archivo **config/initializers/pagy.rb** y agregamos lo siguiente:
```ruby=
Pagy::DEFAULT[:items] = 10
```
> Todas las asignaciones con `Pagy::DEFAULT[:key]` se fusionarán con cada nueva instancia de Pagy
### Cambiar el idioma
Se necesita cargar un diccionario para el idioma español. Pagy ya proporciona varios diccionarios listos para usar en nuestras aplicaciones, entre ellos se encuentra el disponible en [este archivo](https://github.com/ddnexus/pagy/blob/master/lib/locales/es.yml) 👈 el de español.
Para usar esta configuración integrada abrimos el archivo **config/initializers/pagy.rb** y agregamos lo siguiente:
```ruby=
Pagy::I18n.load(locale: 'es')
```
### Pagy::Countless
Esta es una subclase de Pagy que proporciona paginación sin necesidad de ningún `:count`, lo que resulta ser especialmente útil en los siguientes casos:
- desplazamiento infinito
- cuando la barra de navegación completa no es un requisito y se desea mejor rendimiento
Para configurar este modo, debemos abrir **config/initializers/pagy.rb**
```ruby=
require 'pagy/extras/countless'
```
Abrimos la vista de posts **app/views/posts/index.html.erb**:
```erb=
<p style="color: green"><%= notice %></p>
<h1>Posts</h1>
<%= turbo_frame_tag :posts do %>
<%= render @posts %>
<% end %>
<%= turbo_frame_tag :pagination,
loading: :lazy,
src: posts_path(format: :turbo_stream) %>
```
Creamos un nuevo archivo en **app/views/index.turbo_stream.erb** y la añadimos lo siguiente:
```erb=
<%= turbo_stream.append :posts do %>
<%= render @posts %>
<% end %>
<%= turbo_stream.replace :pagination do %>
<% if @pagy.next.present? %>
<%= turbo_frame_tag :pagination,
loading: :lazy,
src: posts_path(format: :turbo_stream, page: @pagy.next)
%>
<% end %>
<% end %>
```