---
title: "Ejercicio guiado edificios"
---
{%hackmd @themes/orangeheart %}
<style>
body {
font-family: sans-serif;
}
h1 {
text-align: center;
}
img {
width: 100%;
}
</style>
{%pdf https://mach-911.github.io/assets/rails-pdf/enunciado_rails_1_N_edificios.pdf %}
## Configuración inicial (devise y bootstrap)
1) Crear un nuevo proyecto:
```bash
rails new edificios -d postgresql
```
2) Configurar los datos de conexión:
```yml=17
default: &default
adapter: postgresql
encoding: unicode
user: tu_usuario 👈
password: tu_password 👈
# For details on connection pooling, see Rails configuration guide
# https://guides.rubyonrails.org/configuring.html#database-pooling
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
```
3) Crear la base de datos
```bash
rails db:create
```
4) Añadir Devise al proyecto
```bash
bundle add devise
```
5) Instalar el generador de Devise
```bash
rails g devise:install
```
6) Añadir la condiguración de devise:
- colocar la url por defecto para el envió de correos
```ruby=39
# config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
```
- colocar los mensajes *flash* en el layouts principal:
```erb=14
<div class="container">
<p class="notice"><%= notice %></p>
<%= yield %>
</div>
```
7) Crear los archivos de migración junto con el modelo de User de Devise:
```bash
rails g devise User
```
8) Creamos las tablas en db ejecutando la migración:
```bash
rails db:migrate
```
### Creando usuarios desde consola
```bash
rails c
```
- **CREAR**:
```irb!
# primera forma
user = User.new
user.password = '123456'
user.password_confirmation='123456'
user.email = 'user1@gmail.com'
user.save
# segunda forma
User.create(
email: 'user2@gmail.com',
password: 'abcdefgh',
password_confirmation: 'abcdefgh'
)
```
- **LEER**:
```irb!
# Seleccionar todos los registros
User.all
# Seleccionar todos los usuarios que empiezan con u
User.all.select { |m| m.email[0] == 'u' }
```
- **ACTUALIZAR**:
```irb!
# primera forma
user = User.find_by(email: 'user1@gmail.com')
user.email = 'usuario1@gmail.com'
user.password = '123456'
password_confirmation: '123456'
user.save
# segunda forma
User.update(
1, # 👈 id del registro que se quiere actualizar
email: 'userio_1@gmail.com',
password: 'abc123456',
password_confirmation: 'abc123456'
)
```
- **ELIMINAR**
```irb!
# usando destroy (eliminará el registro de objeto actual de la base de datos y también su registro secundario asociado de la base de datos)
User.first.destroy
# usando delete (solo eliminará el registro de objeto actual de la base de datos, pero no sus registros secundarios asociados de la base de datos)
User.last.delete
```
### Crear landing page
La aplicación debe contar con una landing page estática que muestre el nombre de la
empresa “Inmobiliaria Virtual” con un diseño atractivo. Desde ahí puedo ir a ver tanto el
listado de edificios como el de departamentos.
10) Entonces creamos un controlador Home con un método index para tener una vista predeterminada:
```bash
rails g controller home index
```
11) Establecemos la ruta por defecto:
```ruby=1
# config/routes.rb
Rails.application.routes.draw do
devise_for :users
root "home#index"
end
```
12) Correr el servidor de desarrollo:
```bash
rails s
```
>Visitamos **http://localhost:3000/**
13) Añadimos bootstrap al proyecto vía CDN en el archivo ***app/views/layouts/application.html.erb***:
```erb
<!DOCTYPE html>
<html>
<head>
<title>Edificios</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
crossorigin="anonymous">
</head>
<body>
<div class="container">
<p class="notice"><%= notice %></p>
<%= yield %>
</div>
<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>
</body>
</html>
```
14) Creamos un partial en ***app/views/shared/_navbar.html.erb***, y añadimos la navegación usando un componente [navbar de bootstrap](https://getbootstrap.com/docs/5.3/components/navbar/):
```erb
<nav class="navbar navbar-expand-lg bg-light">
<div class="container">
<%= link_to("Home", root_path, class: 'navbar-brand') %>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent"
aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarContent">
<div class="navbar-nav ms-auto mb-2 mb-lg-0">
<%= link_to("Edificios", buildings_url, class: 'nav-link') %>
<%= link_to("Departamentos", apartments_url, class: "nav-link") %>
<div class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<%= user_signed_in?? "Hola 👋 #{current_user.email}" : "Debes registrarte" %>
</a>
<div class="dropdown-menu">
<% if user_signed_in? %>
<%= link_to 'Cerrar sesión', destroy_user_session_path, class: 'dropdown-item', 'data-turbo-method': :delete %>
<% else %>
<%= link_to 'Iniciar sesión', new_user_session_path, class: 'dropdown-item'%>
<hr class="dropdown-divider">
<%= link_to 'Registro', new_user_registration_path, class: 'dropdown-item'%>
<% end %>
</div>
</div>
</div>
</div>
</div>
</nav>
```
> Para que los links funcionen, debemos crear los modelos como se explica la sección de [Creando los modelos](#crear_los_modelos)
15) Y lo renderizamos en el layouts principal (*línea 15*):
```erb=
<!DOCTYPE html>
<html>
<head>
<title>Edificios</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
</head>
<body class="d-flex flex-column min-vh-100">
<%= render 'shared/navbar' %>
<% if flash[:notice] %>
<div class="alert alert-<%= flash[:context]? flash[:context] : 'success' %> alert-dismissible fade show" role="alert">
<strong><%= flash[:notice] %></strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<% end %>
<%= yield %>
<%= render 'shared/footer' %>
<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>
<script src="https://kit.fontawesome.com/6b8f0c7049.js" crossorigin="anonymous"></script>
</body>
</html>
```
### Ideas para la landing page
Ya tenemos entonces la navegación ahora trabajaremos en la vista index del home, que es nuestra landing page, podemos crear un partial para crear un hero como primera opción:
```erb=0
<!-- app/views/shared/_hero.html.erb -->
<section className="hero"
style="background: linear-gradient(rgba(0,0,0,.1),rgba(0,0,0,.7)),url(<%= background %>); background-size: cover;" class="min-vh-100 d-flex justify-content-center align-items-center">
<div class="hero__texts">
<% if title || subtitle %>
<h2 class="display-1 text-light px-4 fw-bolder" style="backdrop-filter: sepia(90%)"><%= title %></h2>
<h4 class="text-warning" style="backdrop-filter: blur(15px)">
<%= defined?(subtitle).nil? ? 'subtitle' : subtitle %>
</h4>
<%= link_to(link["name"],link["href"], class: "btn btn-lg btn-outline-light rounded-0") %>
<% end %>
</div>
</section>
```
```erb=
<%= render 'shared/hero', background:"https://imgclasificados2.emol.com/Proyectos/imagenes/proyecto/PR_FOTO_4349_CAM_Piloto-ComedorLiving_AlTA_01_Post.jpg", title: "Inmobiliaria Virtual", subtitle: "Un concepto diferente para tu estilo de vida", link: {"name" => "Proyectos inmobiliarios", "href" => buildings_url} %>
```

Por otro lado podemos, crear un simple array con url de imagenes desde el controlador y mostrarlas en un carrusel de bootstrap:
```ruby=0
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
@images = [
'https://www.iproyeccion.cl/content/uploads/2021/06/Las-Heras_ppal-7-EI-1920x1080-1-1440x900.jpg',
'https://www.planosdearquitectura.com/wp-content/uploads/2015/08/Dise%C3%B1o-de-sal-estar-moderno-de-departamento.jpg',
'https://p4.wallpaperbetter.com/wallpaper/241/699/670/274-apartment-condo-design-wallpaper-preview.jpg',
'https://www.adondevivir.com/noticias/wp-content/uploads/2016/08/depto-1024x546.jpg'
]
end
end
```
```erb=
<div id="carouselExample" class="carousel slide carousel-fade" data-bs-ride="carousel">
<div class="carousel-inner">
<% @images.each_with_index do |image, index| %>
<div class="carousel-item <%= index == 0 ? 'active' : '' %>"
style="background-image: linear-gradient(rgba(20,10,30,.3),rgba(105,140,150,.7)), url(<%= image %>); min-height: 100vh; background-size: cover;"
data-bs-interval="3000">
</div>
<% end %>
<div class="position-absolute top-50 z-3 text-center w-100">
<%= link_to('Edificios', buildings_url, class: 'btn btn-lg btn-light rounded-0 me-3') %>
<%= link_to('Departamentos', apartments_url, class: 'btn btn-lg btn-outline-light rounded-0') %>
</div>
</div>
</div>
```

---
<a id="crear_los_modelos"></a>
## Creando los modelos
De momento se necesitan dos modelos principales y dos modelos adicionales que debes descubrir en las indicaciones posteriores. Los modelos principales son:
```mermaid
erDiagram
BUILDING {
id int PK "Esta es la llave primaria"
name string
address string
}
APARTMENT {
id int PK
aparment_number string
building_id int FK "Un departamento debe pertenecer a un edificio"
}
BUILDING ||--|{ APARTMENT :has
```
Crear el modelo Building (edificio):
```bash
rails generate model Building name address
```
Crear el modelo Apartment (departamento):
```bash
rails g model Apartment num_apartment building:references
```
Nos piden que la aplicación debe contar con un sistema de autenticación con 3 tipos de roles.
Para ello vamos a crear una nueva migración para añadir un campo rol a los usuarios y que sea de tipo entero:
```
rails g migration addRoleToUsers role:integer
```
Revisar el archivo de migración generado y luego añadir lo siguiente para que los usuarios existentes se les asigne el rol 0 (que será equivalente a un usuario normal) y a los futuros usuarios creados:
```ruby=
#db/migrate/2023....._add_role_to_users.rb
class AddRoleToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :role, :integer, default: 0
end
end
```
Ahora ejecutamos la migración:
```bash
rails db:migrate
```
De manera opcional podemos conectarnos a postgres y validarlo por medio de una query:
```sql
select email, role from users;
```
```
+-[ RECORD 1 ]--------------------+
| email | user1@gmail.com |
| role | 0 |
+-[ RECORD 2 ]--------------------+
| email | user2@gmail.com |
| role | 0 |
+-[ RECORD 3 ]--------------------+
| email | user3@gmail.com |
| role | 0 |
+-[ RECORD 4 ]--------------------+
| email | user4@gmail.com |
| role | 0 |
+-[ RECORD 5 ]--------------------+
| email | user5@gmail.com |
| role | 0 |
+-------+-------------------------+
```
Ahora en nuestro modelo **User**, necesitamos definir la enumeración de la siguiente forma:
```ruby=
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
enum :role, [:usuario,:conserje, :admin]
end
```
El edificio debe contar con la información de en qué ciudad está ubicado. Para esto necesitamos crear otro modelo de la siguiente manera:
```
rails g model City name
```
Y debemos relacionarlo con edificio como se ve a continuación:
```mermaid
erDiagram
CITY {
id integer PK
name string
}
BUILDING {
id integer PK
name string
address string
city_id int FK "Un edificio debe estar en una ciudad"
}
BUILDING ||--|{ CITY :had
```
Para añadir la clave foránea mediante la migración de Rails, lo hacemos con:
```
rails g migration addCityIdToBuildings city:references
```
Lo que nos daría como resultado un nuevo archivo de migración con el siguiente contenido:
```ruby=0
# db/migrate/2023...add_city_id_to_buildings.rb
class AddCityIdToBuildings < ActiveRecord::Migration[7.0]
def change
add_reference :buildings, :city, null: false, foreign_key: true
end
end
```
Y ejecutamos la migración con `rails db:migrate`.
El edificio debe contar con al menos 10 ciudades, las cuales deben cargarse por medio de un archivo CSV al ejecutar el seed:
El contenido del archivo **seeds**
```rb!
# db/seeds.rb
require 'csv'
csv_text = File.read('db/ciudades.csv')
csv = CSV.parse(csv_text, :headers => true, :encoding => 'utf-8')
csv.each do |i|
City.create(name: i)
end
```
El contenido del archivo ciudades.csv:
```!
name
arica
santiago
concepción
iquique
la serena
osorno
antofagasta
valparaiso
taltal
temuco
```
Tambien nos pides saber si un departamento se encuentra o no disponible. Esto se debe lograr utilizando una columna estado. Para ello agregamos un nuevo campo al modelo Apartment por medio de la migración:
```
rails g migration AddStatusToApartments status:integer
```
Y no nos olvidemos de agregar en el archivo de migración el valor por defecto para los registros existentes:
```ruby=0
# db/migrate/2023..._add_status_to_apartments
class AddStatusToApartments < ActiveRecord::Migration[7.0]
def change
add_column :apartments, :status, :integer, default: 0
end
end
```
Y ahora en modelo de Apartment definir el status por medio de un **enum**:
```ruby=0
# app/models/apartment.rb
class Apartment < ApplicationRecord
belongs_to :building
enum :status, ['no disponible', 'disponible']
end
```
## Accesos
**Usuarios**
Pueden ver las páginas de index y show de los edificios
**Conserjes**
El conserje puede acceder tanto a edificios como departamentos y solamente editar la información de los modelos (building y apartment).
**Admin**
El admin puede realizar todas las acciones del crud en ambos modelos (building y apartments).
---
## Creando los controladores
**CRUD PARA BUILDING**
En el index de building, se listarán todos los edificios **en una tabla con sus datos respectivos**
Para building vamos a tener el siguiente controlador:
```
rails d controller buildings index new show
```
Configurar las rutas de building usando **resources**:
```
Rails.application.routes.draw do
devise_for :users
resources :buildings
root "home#index"
end
```
**CRUD PARA APARTMENT**
```ruby
def index
@apartments = Apartment.all
end
```
---
## Crear las vistas y formularios de los modelos