Crear proyecto

  1. Crear el proyecto, utilizando PostgreSql como base de datos:
rails new blog -d postgresql
  1. Creamos la base de datos:
rails db:create

Devise

Devise es una gema que facilita la implementación de la autenticación en las aplicaciones.

  1. Agregamos devise al proyecto en nuestro Gemfile:
echo gem 'devise' >> Gemfile
  1. Ejecuta el siguiente comando para instalar la gema:
bundle
  1. Ejecutar el generador de Devise
rails g devise:install
  1. Crear el modelo de usuarios de la aplicación, generalmente se usa User como nombre:
rails g devise User
  1. 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
/users/sign_up
new_user_registration
new_user_registration_path
registrations/new.html.erb
Formulario de registro
POST
/users
Registrarse
GET
/users/sign_in
new_user_session
new_user_session_path
sessions/new.html.erb
Formulario de login
DELETE
/users/sign_out
destroy_user_session
destroy_user_session_path

Para ver el listado completo de rutas podemos ejecutar el siguiente comando:

rails routes -c devise --expanded
Mostrar rutas
--[ 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 

Para ello primero lanzamos el servidor de desarrollo con el comandorails s y luego visitamos las siguientes rutas:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →


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:

Rails.application.routes.draw do devise_for :users root "pages#home" end

Abrimos app/views/pages/home.html.erb y agrega lo siguiente:

<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í:

<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>

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →


Scaffold de Post

  1. Crear un modelo de Post usando scaffold:
rails g scaffold Post image title description:text

publica

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

  1. Agregamos la migración para relacionar los modelos:
rails g migration AddUsersToPosts user:references
  1. Ejecutamos la migración con rails db:migrate

  2. Asociamos el modelo de User con los Post:

# app/models/post.rb class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :posts end
  1. Y lo mismo con Post hacia User:
# app/models/user.rb class Post < ApplicationRecord belongs_to :user end
  1. Podemos crear Post mediante consola para ver si está conectado:
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                                                    |
+-------------+------------------------------------------------------+
  1. 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)
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):

<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:

gif post

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:

# 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:

#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:

# app/controllers/application_controller.rb class ApplicationController < ActionController::Base def after_sign_in_path_for(resource) posts_path end end

Fuente: Wiki Devise


Añadir paginación en los posts

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:

bundle add pagy

Configuración

Abrimos el archivo app/controller/aplication_controller.rb y añadimos lo siguiente:

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:

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:

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:

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) %>:
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
  • <%== pagy_info(@pagy) %>:
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

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:

require 'pagy/extras/bootstrap'

Y ahora en lugar de usar <%== pagy_nav(@pagy) %> usamos el siguiente helper en la vista:

  • <%== pagy_bootstrap_nav(@pagy) %>:
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

Demostración

pagy demo

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í 👈, esto afectará a todos los objetos de Pagy.

Limitar la cantidad de items

Abrimos el archivo config/initializers/pagy.rb y agregamos lo siguiente:

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 👈 el de español.

Para usar esta configuración integrada abrimos el archivo config/initializers/pagy.rb y agregamos lo siguiente:

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

require 'pagy/extras/countless'

Abrimos la vista de posts app/views/posts/index.html.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:

<%= 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 %>