<style>
.reveal h2 {
text-align:center;
font-size: 50px;
color: #fd3;
margin-bottom: 8px;
}
p.part {
text-align: left;
font-size: 20px;
}
.reveal .code-wrapper {
border-radius: 4px;
width: 100%;
}
.code-wrapper > *::-moz-selection {
color: #222;
background: #fd3;
}
.path {
color: #fd3;
}
.comando {
background: #333;
border-radius: 4px;
padding: 0 6px 3px 6px;
}
.comando::before {
content: "> "
}
.language-bash::before {
content: "$ "
}
.language-dos::before {
content: "> "
}
.irb {
background: #282828;
border-radius: 4px;
padding: 0 6px 3px 6px;
}
.irb::before {
content: "irb(main):001:0> ";
color: #2f3;
}
.code-wrapper > *::selection,
.hljs-tag::selection,
.hljs-tag > *::selection,
.hljs-symbol::selection,
.language-ruby > *::selection,
.hljs > *::selection,
.hljs-ln-code::selection,
.hljs-ln-code > *::selection,
.hljs-ln-code > * > *::selection,
.hljs-ln-code > * > * > *::selection,
.hljs-class > *::selection,
.comando > *::selection,
.irb > *::selection{
color: #222;
background: #fd3;
}
.reveal .code-wrapper code {
white-space: pre;
}
pre.flow-chart, pre.sequence-diagram, pre.graphviz, pre.mermaid, pre.abc, pre.vega {
background: #222;
border-bottom: 1px solid #ccc;
width: 100%;
}
img {
width: 100%;
margin: 0 !important;
}
h4 {
text-align: left;
}
.reveal ul{
text-align: left !important;
font-size: 23px;
}
</style>
## REQ1: Set-up inicial
Creación del proyecto:
```bash
rails new the_rial_news -d postgresql
```
Modificar datos de conexión a través de credenciales:
**BASH** <i class="fa fa-terminal"></i>
```bash
EDITOR="code --wait" rails credentials:edit
```
**CMD** <i class="fa fa-terminal"></i>
```bat
set EDITOR=code --wait && rails credentials:edit
```
<div style="display: flex; font-size: 32px; gap: 12px">
<div style="">
Modifica los valores de conexión
```yml=
db:
username: user_db
password: password_db
```
</div>
<div style="">
En el archivo <span class="path">config/database.yml</span> carga los valores
```yml=23
username: <%= Rails.application.credentials.db[:username] %>
password: <%= Rails.application.credentials.db[:password] %>
```
</div>
</div>
Crear la base de datos con el comando: <span class="comando">`rails db:create`</span>
---
### Entidades y relaciones
Visto desde la perspectiva de publicaciones, usuarios y administrador
```mermaid
%%{
init: {
'theme': 'base',
'themeVariables': {
'background': '#fff',
'primaryColor': '#FFB439',
'primaryTextColor': '#222',
'primaryBorderColor': '#FFB439',
'tertiaryColor': '#fff'
},
'themeCSS': '.er.attributeBoxEven { fill:#ddd} .er.relationshipLine { stroke:#ccc}'
}
}%%
erDiagram
USER ||--|{ COMMENT :escribe
ADMIN ||--|{ PUBLICATION :publica
COMMENT ||--|{ PUBLICATION :esta
```
---
Visto desde la perspectiva de comentarios
```mermaid
%%{
init: {
'theme': 'base',
'themeVariables': {
'background': '#333',
'primaryColor': '#FFB439',
'primaryTextColor': '#',
'primaryBorderColor': '#FFB439',
'tertiaryColor': '#fff'
},
'themeCSS': '.er.attributeBoxEven { fill:#333} .er.relationshipLine { stroke:#ccc} .er.entityBox { fill: #fd9 }'
}
}%%
erDiagram
COMMENT ||--|{ USER :escribe
COMMENT ||--|{ PUBLICATION :esta
```
---
<div style="display: flex; justify-content: space-between">
<div style="">
1\. añadir Devise al Gemfile:
```ruby=74
gem 'devise'
```
</div>
<div style="">
2\. actualizar la instalación:
```bash
bundle
```
</div>
<div style="">
3\. instalar el generador de Devise:
```bash
rails generate devise:install
```
</div>
</div>
Crear el modelo de usuario con el generador de Devise:
```bash
rails generate devise User name role:integer
```
En el archivo de migración generado vamos agregar un valor por defecto para el rol:
```ruby=36
t.integer :role, default: 0
```
Generar un scaffold para el modelo Publicación:
```bash
rails g scaffold Publication user:references title description:text
```
Generar un modelo para Comentario:
```bash
rails g model Comment user:references publication:references body:text
```
Corremos la migración:
```bash
rails db:migrate
```
---
Añadimos la asociación en <span class="path">app/models/user.rb</span> :
```ruby=3
has_many :comments
has_many :publications
```
Añadimos la asociación en <span class="path">app/models/publication.rb</span>:
```ruby=2
belongs_to :user
has_many :comments
```
Y revisamos que tengamos la asociación en <span class="path">app/models/comment.rb</span>:
```ruby=2
belongs_to :user
belong_to :publication
```
Gracias a esta asociación, ahora podemos realizar acciones comunes con las relaciones de uno a muchos. Tenemos 3 formas de crear un comentario relacionado:
```ruby
p = Publication.first
u = User.first
Comment.create(publication: p, user: u, body: "Primer comentario")
p.comments.create(user_id: u.id, body: "Segundo comentario")
u.comments.create(publication: p, body: "Tercer comentario")
```
---
## Requerimiento 2
El usuario que no se haya registrado o iniciado sesión solo podrá ver las publicaciones e ingresar a su detalle. (*Los usuarios registrados podrán dejar comentarios en las noticias, que podrán ser eliminados por parte de los administradores si no lo consideran apropiados*)
Añadimos los roles usando el método [enum](https://api.rubyonrails.org/v7.0.5/classes/ActiveRecord/Enum.html) en el modelo User <span style="color: #fd3">app/models/user.rb</span>:
```ruby=2
enum :role, [:normal_user, :admin]
```
Creamos un método para verificar el tipo de usuario en <span style="color: #fd3">app/controllers/application_controller.rb</span>:
```ruby=2
def authorized_request(kind = nil)
if current_user
unless kind.include?(current_user.role)
redirect_to publications_path,
notice: "Solo el administrador puede realizar esta acción"
end
else
redirect_to publications_path,
notice: "Debes iniciar sesión como administrador"
end
end
```
---
Abrimos el archivo <span style="color: #fd3">app/controllers/publications_controller.rb</span> y añadimos lo siguiente:
```ruby=2
before_action :authenticate_user!, except: %i[ index show ]
before_action only: %i[ new create edit update destroy ] do
authorized_request(["admin"])
end
```
Abrimos el archivo <span class="path">config/routes.rb</span> para establecer las rutas:
```ruby=
Rails.application.routes.draw do
resources :publications do
resources :comments, only: [:create, :destroy]
end
devise_for :users
root "publications#index"
end
```
Creamos un administrador desde el <span class="path">db/seeds.rb</span>:
```ruby=9
if User.all.count == 0
User.create(name: "administrador",email: "admin@gmail.com",password: "123456",password_confirmation: "123456",role: 1)
end
```
Ejecutar el seed con <span class="comando">`rails db:seed`</span>
---
Crear un controlador para los comentarios:
```bash
rails generate controller comments
```
Abrimos el archivo <span style="color: #fd3">app/controllers/comments_controller.rb</span> y definimos el método de crear y eliminar:
```ruby=2
before_action :authenticate_user!, only: %w[ create ]
before_action only: [:destroy] do
authorized_request(["admin"])
end
def create
@comment = current_user.comments.new(comment_params)
@comment.publication_id = params[:publication_id]
respond_to do |format|
if @comment.save
format.html { redirect_to publication_path(params[:publication_id]), notice: "Comentario creado correctamente." }
else
format.html { render publication_path(params[:publication_id]), status: :unprocessable_entity }
format.json { render json: @comment.errors, status: :unprocessable_entity }
end
end
end
def destroy
@publication = Publication.find(params[:publication_id])
@comment = @publication.comments.find(params[:id])
@comment.destroy
respond_to do |format|
format.html { redirect_to publication_path(@publication), notice: "Comentario borrado" }
format.json { head :no_content }
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
```
Corremos el servidor para probar implementación:
```bash
rails s
```
---
Si vemos, al momento de querer crear una publicación, nos envía a iniciar sesión y cuando queramos registrarnos, se puede observar que nos falta un campo en el formulario que es para el nombre del usuario:

Usamos el generador para manipular la vista de registro de Devise:
```bash
rails g devise:views users -v registrations
```
Usamos el generador para sobrescribir métodos en el controlador:
```bash
rails g devise:controllers users -c registrations
```
---
Y abrimos el archivo <span class="path">app/controllers/users/registrations_controller.rb</span> y descomentamos las siguientes líneas:
```ruby=4
before_action :configure_sign_up_params, only: [:create]
```
```ruby=13
def create
super
end
```
```ruby=44
def configure_sign_up_params # cambiar 👇
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
end
```
Abrimos el archivo <span style="color: #fd3">config/routes.rb</span> para aseguranos que se use esa clase en las rutas de Devise:
```ruby=
Rails.application.routes.draw do
resources :publications do
resources :comments, only: [:create, :destroy]
end
devise_for :users, controllers: {
registrations: 'users/registrations'
}
root "publications#index"
end
```
---
Abrimos el archivo <span class="path">app/views/users/registrations/new.html.erb</span> y añadimos el nuevo campo:
```erb
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true, autocomplete: "name" %>
</div>
```
Probamos la implementación creando un usuario en http://localhost:3000/users/sign_up :

Se creó sin problema con el nuevo campo en la base de datos, para comprobarlo entramos en la consola con <span class="comando">`rails console`</span> y dentro ejecutamos <span class="irb">`User.last.name`</span>
---
Abrimos el archivo <span style="color: #fd3">app/controllers/publications_controller.rb</span> y modificamos el método **create**:
```ruby=27
def create
@publication = Publication.new(publication_params)
@publication.user = current_user # 👈 le asignamos usuario a la publicación
respond_to do |format|
if @publication.save
format.html { redirect_to publication_url(@publication), notice: "Publication was successfully created." }
format.json { render :show, status: :created, location: @publication }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @publication.errors, status: :unprocessable_entity }
end
end
end
```
Bajamos a la siguiente línea en el mismo archivo para descartar el **`:user_id`** en los strong params:
```ruby=72
def publication_params
params.require(:publication).permit(:title, :description)
end
```
---
Abrimos el archivo <span style="color: #fd3">app/views/publications/_form.html.erb</span> y modificamos la vista eliminando el campo de usuario:
```diff=14
- <div>
- <%= form.label :user_id, style: "display: block" %>
- <%= form.text_field :user_id %>
- </div>
```
Y hacemos lo mismo en el archivo <span style="color: #fd3">app/views/publications/_publication.html.erb</span> y eliminamos las siguientes líneas:
```diff=2
- <p>
- <strong>User:</strong>
- <%= publication.user_id %>
- </p>
```
Abrimos <span style="color: #fd3">app/views/publications/index.html.erb</span> y vamos al final donde se encuentra el link de crear una publicación para que sólo el administrador pueda usarlo, podemos ir añadiendo algunas clases de bootstrap:
```erb
<% if current_user && current_user.role == "admin" %>
<%= link_to "New publication", new_publication_path, class: "btn btn-primary" %>
<% end %>
```
---
Y hacemos lo mismo en la vista <span style="color: #fd3">app/views/publications/show.html.erb</span> para esconder los controles para editar y eliminar la publicación :
```erb
<p style="color: green"><%= notice %></p>
<%= render @publication %>
<% if current_user && current_user.role == "admin" %>
<div class="btn-group">
<%= link_to "Edit this publication", edit_publication_path(@publication), class: "btn btn-warning" %>
<%= link_to "Destroy this publication", @publication, class: "btn btn-danger", data: { turbo_method: :delete} %>
</div>
<% end %>
<%= link_to "Back to publications", publications_path, class: "btn btn-primary" %>
```
Y agregamos bootstrap, la parte de CSS en <span class="path">app/views/layouts/application.html.erb</span> (línea: 8)
```html=8
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
```
Y agregamos bootstrap, la parte de CSS en <span class="path">app/views/layouts/application.html.erb</span> (línea: 15)
```html=15
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
```
---
Añadimos una barra de navegación en <span class="path">app/views/shared/_nav.html.erb</span>
```erb=
<nav class="navbar navbar-light navbar-expand-lg border-bottom border-2 border-secondary">
<div class="container">
<%= link_to "The rial news", root_path, class: "navbar-brand" %>
<%= button_tag type: :button,
class: "navbar-toggler",
data: {bs_toggle: "collapse", bs_target: "#navbar"} do %>
<%= tag.span class: "navbar-toggler-icon" %>
<% end %>
<div class="collapse navbar-collapse" id="navbar">
<div class="navbar-nav ms-auto gap-2">
<% if user_signed_in? %>
<%= button_to "Sign Out", destroy_user_session_path, class: "btn btn-secondary", method: :delete %>
<% else %>
<%= link_to "Sign In", new_user_session_path, class: "btn btn-outline-dark" %>
<%= link_to "Sign Up", new_user_registration_path, class: "btn btn-dark" %>
<% end %>
</div>
</div>
</div>
</nav>
```
Y renderizamos este **partial** en <span class="path">app/views/layouts/application.html.erb</span> y agremos margen al contenido de las vistas:
```html=14
<%= render 'shared/nav' %>
<div class="container">
<%= yield %>
</div>
```
---
Para mostrar los comentarios dentro de una publicación, abrimos <span style="color: #fd3">app/views/publications/show.html.erb</span> y añadimos al final lo siguiente:
```erb
<h3><u>Comentarios</u></h3>
<%= render @publication.comments %>
```
Esto buscará un **partial** en <span style="color: #fd3">app/views/comments/_comment.html.erb</span>, lo creamos y añadimos el siguiente contenido:
```erb
<p><%= comment.body %></p>
Por: <strong><%= comment.user.email %></strong>
<% if current_user && current_user.role == "admin" %>
<%= link_to("Eliminar comentario", publication_comment_path(@publication, comment), data: {turbo_method: :delete}) %>
<% end %>
<hr>
```
Creamos un comentario por consola para probar implementación:
```
p = Publication.first
u = User.first
Comment.create(publication: p, user: u, body: "Comentario random")
```
Corremos el servidor y visitamos el detalle de la primera publicación: <span class="comando">`rails s`</span> y visitamos la URL: http://localhost:3000/publications y entramos a la primera publicación.
---
Ahora abrimos <span style="color: #fd3">app/views/publications/show.html.erb</span> y añadimo lo siguiente:
```erb
<h3><u>Comentarios</u></h3>
<%= render @publication.comments %>
<%# agregamos esta línea 👇 %>
<%= render partial: 'comments/form', locals: { publication: @publication } %>
```
Creamos ese partial en <span style="color: #fd3">app/views/comments/_form.html.erb</span> y añadimos lo siguiente:
```erb
<%= form_with(url: publication_comments_path(publication), method: :post, :html => {:style => "width: 500px;"}) do |form| %>
<div>
<%= form.label "comment[body]", "Comentar", style: "display: block" %>
<%= form.text_area "comment[body]", class:"form-control" %>
</div>
<div>
<%= form.submit "Comentar", class:"btn btn-primary my-3" %>
</div>
<% end %>
```
Y ahora probamos la implementación, creando un comentario desde alguna publicación existente. Por ejemplo la primera http://localhost:3000/publications/1
---
## Requerimiento 3
Si un usuario olvida su contraseña, puede recibir un email para restablecerla
{%speakerdeck mach911/contrasena-para-aplicaciones-gmail %}
---
## Requerimiento 4
Las noticias (publicaciones) deben tener sus tests funcionales
**Setup para los test 📝:**
Correr las migraciones para crear las tablas en la base de datos en el entorno de TESTING:
```bash
rails db:migrate RAILS_ENV=test
```
Definimos dos usuarios de prueba un admin y uno normal en <span style="color: #fd3">test/fixtures/users.yml</span>:
```yml=
one:
email: adm@admin.com
encrypted_password: $1b$221d9dbb083a7f33428d7c2a3c3198ae925614d70210e28716ccaa7cd4ddb79
name: 'admin_test'
role: 1
two:
email: usr@email.com
encrypted_password: $1b$221d9dbb083a7f33428d7c2a3c3198ae925614d70210e28716ccaa7cd4ddb79
name: 'user_test'
role: 0
```
---
Abrimos el archivo <span style="color: #fd3">test/controllers/publications_controller_test.rb</span> y añadimos lo siguiente:
```ruby=5
include Devise::Test::IntegrationHelpers
setup do
@publication = publications(:one)
@admin = users(:one)
@user = users(:two)
end
```
Luego podemos escribir el primer test a continuación de lo añadido anteriormente en el mismo archivo:
```ruby=13
test "should get index" do
get publications_url
assert_response :success
assert_equal 200, response.status
assert_equal "utf-8", response.charset
end
```
Ejecutamos el test con el siguiente comando:
```bash
rails t test/controllers/publications_controller_test.rb -n test_should_get_index
```
---
Abrimos el archivo <span style="color: #fd3">test/controllers/publications_controller_test.rb</span> y añadimos lo siguiente:
```ruby=20
test "should get new" do
sign_in @admin
get new_publication_url
assert_response :success
assert_equal 200, response.status
assert_equal "utf-8", response.charset
end
```
Ejecutamos el segundo test con el comando:
```bash
rails t test/controllers/publications_controller_test.rb -n test_should_get_new
```
Revisamos los resultados:

---
Abrimos nuevamente <span style="color: #fd3">test/controllers/publications_controller_test.rb</span> y añadimos lo siguiente:
```ruby=28
test "should create publication" do
sign_in @admin
assert_difference("Publication.count") do
post publications_url, params: { publication: { description: @publication.description, title: @publication.title, user_id: @publication.user_id } }
end
assert_equal 302, response.status
assert_redirected_to publication_url(Publication.last)
end
```
Ejecutamos el tercer test con el comando:
```bash
rails t test/controllers/publications_controller_test.rb -n test_should_create_publication
```
Revisamos los resultados:

---
Seguimos en <span style="color: #fd3">test/controllers/publications_controller_test.rb</span> y añadimos lo siguiente:
```ruby=37
test "should show publication" do
get publication_url(@publication)
assert_response :success
assert_equal 200, response.status
assert_equal "utf-8", response.charset
end
```
Ejecutamos el cuarto test con el comando:
```bash
rails t test/controllers/publications_controller_test.rb -n test_should_show_publication
```
Revisamos los resultados:

---
Seguimos con <span style="color: #fd3">test/controllers/publications_controller_test.rb</span> y añadimos lo siguiente:
```ruby=44
test "should get edit" do
sign_in @admin
get edit_publication_url(@publication)
assert_response :success
assert_equal 200, response.status
assert_equal "utf-8", response.charset
end
```
Ejecutamos el quinto test con el comando:
```bash
rails t test/controllers/publications_controller_test.rb -n test_should_get_edit
```
Revisamos los resultados:

---
Abrir nuevamente <span style="color: #fd3">test/controllers/publications_controller_test.rb</span> y añadimos lo siguiente:
```ruby=52
test "should update publication" do
sign_in @admin
patch publication_url(@publication), params: { publication: { description: @publication.description, title: @publication.title, user_id: @publication.user_id } }
assert_equal 302, response.status
assert_redirected_to publication_url(@publication)
end
```
Ejecutamos el sexto test con el comando:
```bash
rails t test/controllers/publications_controller_test.rb -n test_should_update_publication
```
Revisamos los resultados:

---
Antes de ir a probar el último test, debemos modificar este archivo <span style="color: #fd3">app/models/publication.rb</span> y añadimos lo siguiente:
```ruby=
class Publication < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
end
```
De lo contrario si tratamos de eliminar una publicación en los test nos mostrará el siguiente error:

Con esto le indicamos que cuando se destruya el objeto en este caso la publicación, se eliminarán todos sus objetos asociados. Evidentemente no podemos especificar esta opción en el modelo **Comment** que está conectado con una asociación de **has_many** en la otra clase. Si lo hacemos, quedarán registros huérfanos en la base de datos al momento de eliminar un comentario ya que eliminará a su vez la noticia.
---
Abrir <span style="color: #fd3">test/controllers/publications_controller_test.rb</span> y añadimos lo siguiente:
```ruby=59
test "should destroy publication" do
sign_in @admin
assert_difference("Publication.count", -1) do
delete publication_url(@publication)
end
assert_redirected_to publications_url
end
```
Ejecutamos el último test con el comando:
```bash
rails t test/controllers/publications_controller_test.rb -n test_should_destroy_publication
```
Revisamos los resultados:

---
## Requerimiento 5
Desplegar la aplicación
Para hacerlo a través de [fly.io](https://fly.io/) debemos instalar un programa de consola
#### <i class="fa fa-windows"></i> **Windows**
Correr el siguiente comando en PowerShell:
```cmd
pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"
```
#### <i class="fa fa-linux"></i> **Linux**
Correr el siguiente comando en la terminal:
```bash
curl -L https://fly.io/install.sh | sh
```
#### <i class="fa fa-apple"></i> macos
Si tiene el administrador de paquetes [Homebrew](https://brew.sh/), puede instalar con el siguiente comando:
```bash
brew install flyctl
```
---
#### Registrarse
Para poder obtener una cuenta, podemos ejecutar el comando:
```bash
fly auth signup
```
Esto nos llevará a la página de registro donde podemos:
<i class="fa fa-google"></i> [**Registrarnos con correo electrónico**](https://fly.io/app/auth/google?state=phoenix&return_to=%2Flaunchpad) :point_left:
<i class="fa fa-github"></i> [**Registrarnos con github**](https://fly.io/app/auth/github?state=phoenix&return_to=%2Flaunchpad) :point_left:
También nos solicitará la información de pago por medio de una tarjeta de crédito, para efectos de las aplicaciones de rails y con bases de datos de postgres no nos generará ningún costo asi que no tendremos cargos adicionales por su uso.
Ya con una cuenta podemos iniciar sesión con el siguiente comando:
```bash
fly auth login
```
Esto nos abrirá el navegador en la página de iniciar sesión de Fly.io, seleccionamos la forma con la cuál nos registramos y volvemos a la terminal.
Es posible que los usuarios de WSL tengan que ejecutar el siguiente comando para que esto funcione:
```bash
ln -s /usr/bin/wslview /usr/local/bin/xdg-open
```
---
#### Iniciar la aplicación
Al instalar el programa de consola nos proporciona una herramienta llamada **FLy Launch** que nos va ayudar a implementar ráoidamente la aplicación utilizando una imagen de Docker.
Antes de invocar a **Fly Launch** debemos modificar el archivo <span style="color: #fd3">config/environments/production.rb</span> en la siguiente línea:
```ruby=36
config.assets.compile = true
```
Esto nos permitirá compilar correctamente los assets y tag helpers de rails.
Si estamos desarrollando en **Windows** (no WSL) es recomendable ejecutar el siguiente comando:
```cmd
bundle lock --add-platform x86_64-linux
```
Ahora podemos usar la herramienta antes mencionada con el comando <span class="comando">fly launch</span> a continuación nos va ir haciendo varias preguntas como el nombre para la aplicación. Por ejemplo :point_down:
```
? Choose an app name (leave blank to generate one): m6-the-rial-news
```
A continuación, se le pedirá que seleccione una región:
```
? Choose a region for deployment: Chicago, Illinois (US) (ord)
```
---
A continuación le preguntará si necesita una base de datos Postgres seleccionamos (**y**):
```
? Would you like to set up a Postgresql database now? y
```
Luego nos dejará escoger 3 opciones, seleccionamos la primera plan **developer**.
A continuación le preguntará si necesita una base de datos Redis seleccionamos que no (**N**):
```
? Would you like to set up an Upstash Redis database now? N
```
En este punto, el programa **flyctl** crea unos scripts para la aplicación y escribe una configuración inicial en un archivo **fly.toml** y otros archivos para la máquina de Docker.
Ahora sólo nos queda llevar a cabo la implementación con el comando <span class="comando">fly deploy</span>.
Este proceso puede tardar varios minutos.
{"slideOptions":"{\"theme\":\"black\",\"transition\":\"slide\",\"progress\":true,\"center\":false}","description":"Creación del proyecto:","title":"The rial news","contributors":"[{\"id\":\"bbc6d0c6-5474-4bf0-8e86-464eb17a9a61\",\"add\":35711,\"del\":8488}]"}