Este proyecto es la prueba final del modulo 05 'rails y javascript' de la carrera 'fullstack-g39'.
Se utilizó ruby versión 3.0.0 y rails versión 6.1.3.1.
Entregar un diagrama de relaciones, de tal manera que explique cómo interactúa cada uno de los modelos.
- Tabla
bookmark_type
: tipos de marcadores (paper, artículo, video, etc.) - Tabla
category
: categorías - Tabla
bookmark
: los marcadores, que tienen una categoría y un tipo cada uno
Entregar un CRUD para la administración de Categorías, Marcador y Tipo.
El CRUD se creó usando el generador de scaffold
de rails:
rails g scaffold BookmarkType name
rails g scaffold Category name public:boolean category:belongs_to
rails g scaffold Bookmark name url bookmark_type:belongs_to category:belongs_to
El formulario para agregar Marcadores debe ser enviado a través de una petición AJAX y antes de ser enviado debe pedir confirmación.
Para facilitar un poco las manipulaciones del DOM, agregué jQuery
usando
yarn add jquery
... y después agregando lo siguiente a config/webpack/environment.js
:
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
Es necesario hacer unos ajustes a las vistas creadas por el scaffold. Por ejemplo, crear una vista parcial para bookmark
: app/views/bookmarks/_bookmark.html.erb
<tr id="bookmark-row-<%= bookmark.id %>">
<td><%= bookmark.name %></td>
<td><%= bookmark.url %></td>
<td><%= link_to bookmark.bookmark_type, bookmark.bookmark_type %></td>
<td><%= link_to bookmark.category, bookmark.category %></td>
<td><%= link_to 'Show', bookmark, remote: true %></td>
<td><%= link_to 'Edit', edit_bookmark_path(bookmark), remote: true %></td>
<td><%= link_to 'Destroy', bookmark, method: :delete, data: { confirm: 'Are you sure?' }, remote: true %></td>
</tr>
Importante: agregar el remote: true
a los links, para que los request se ejecute de forma js
.
La idea es mostrar el formulario en la misma vista index
cuando se gatilla la un link para crear o editar un registro. Para lograr esto, hay que crear vistas de tipo javascript para cada acción (extension: .js.erb
).
Acá hay dos pasos: la acción new
muestra el formulario, la acción create
agrega el nuevo registro a la lista y oculta el formulario de nuevo (todo sin recargar la página).
Se crea un archivo app/views/bookmarks/new.js.erb
:
// show form
cont = $("#form-container");
cont.hide();
cont.html("<%= escape_javascript(render partial: 'bookmarks/form', locals: { bookmark: @bookmark } ) %>");
cont.slideDown('fast');
// show/hide links
$("#cancel-link").removeClass('hidden');
$("#new-bookmark-link").addClass('hidden');
Después, un archivo app/views/bookmarks/create.js.erb
:
// hide form
cont = $("#form-container");
cont.slideUp('fast');
cont.html();
// show/hide links
$("#cancel-link").addClass('hidden');
$("#new-bookmark-link").removeClass('hidden');
// add new row to table
$("#bookmarks-table").append("<%= escape_javascript(render partial: 'bookmarks/bookmark', locals: { bookmark: @bookmark } ) %>");
Crear un endpoint que retorne un JSON con los datos de una categoría (debe incluir subcategorías y marcadores). El esquema del JSON de salida debe quedar a su criterio.
Con el scaffold, ya existe la posibilidad de obtener un json por las rutas que se agrega automáticamente mediante el resources
:
Esta línea en routes.rb
resources :categories
crea, entre otros, esta ruta:
PREFIX VERB URI Pattern Controller#Action
category GET /categories/:id(.:format) categories#show
Por ejemplo, la siguiente URL ya entrega una categoría en el formato JSON:
/categories/5.json
Sólo hay que cambiar el contenido del JSON para que incluya también las categorías subordenadas y los detalles de los bookmarks asociados. Por eso, yo definí primero un methodo to_json
en la clase Bookmark
:
def to_json
{
name: name,
url: url,
type: bookmark_type.to_s
}
end
También en la clase Category
:
def to_json
json = {
name: name,
public: public,
parent_category: parent_category.to_s
}
json['bookmarks'] = []
bookmarks.each do |b|
json['bookmarks'] << b.to_json
end
json
end
Ojo que acá ya se agrega los detalles de los bookmarks asociados, usando el mísmo método to_json
creado antes. Ahora todo lo que falta es agregar las categorías subordenadas a cada categoría. Eso se hace en el método category_deep_hash
en el controlador CategoriesController
:
def category_deep_hash(category)
hash = category.to_json
hash['children'] = []
category.child_categories.each do |c|
hash['children'] << category_deep_hash(c)
end
hash
end
Es una función recursiva, es decir que recorre todas las subcategorías de la categoría actual y llama la misma función para cada una. Eso se repite hasta que una categoría no tenga más subcategorías. El paso final es llamar este método en la acción show:
def show
respond_to do |format|
format.json { render json: category_deep_hash(@category) }
format.html
end
end
Inicialmente, nuestro sistema debe contener al menos 20 registros predefinidos.
En la generación del seed estoy ocupando la gema faker
para crear categorías y URLs al azar. El siguiente es el archivo seeds.rb
:
require 'faker'
## clean DB
Bookmark.delete_all
Category.delete_all
BookmarkType.delete_all
## Categories
10.times do
Category.create!(name: Faker::Hipster.word, public: Faker::Boolean.boolean, parent_category: Category.all.sample)
end
## Bookmark types
BookmarkType.create!(name: 'Video')
BookmarkType.create!(name: 'Article')
BookmarkType.create!(name: 'Paper')
BookmarkType.create!(name: 'Interview')
BookmarkType.create!(name: 'Audio')
BookmarkType.create!(name: 'Other')
## Bookmarks
50.times do
Bookmark.create(name: Faker::Lorem.words(number: 3).join(' '), url: Faker::Internet.url, bookmark_type: BookmarkType.all.sample, category: Category.all.sample)
end
Se ejecuta el seed llamando rails db:seed
desde la consola.
Mostrar un gráfico de torta que muestre cómo están distribuidos los tipos de marcadores.
Para hacer el gráfico, uso la gema chartkick
(https://chartkick.com). El uso de la gema es bien fácil, para dibujar un gráfico de torta es suficiente poner una línea en la vista:
<%= pie_chart bookmark_cnt_per_type %>
Sin obstante, la generación de los datos generó un problema. El pie_chart
necesita un hash, donde el key
es el nombre de la serie y el value
es el valor (la cantidad de bookmarks en este caso).
Se puede obtener la cantidad con lo siguiente:
Bookmark.group(:bookmark_type).count
Pero eso resulta en un hash donde el key es un objeto de la clase BookmarkType
y eso no se mostró correctamente en el gráfico. Así que creé un método en el BookmarkTypesHelper
que cambia el key por el simple nombre del tipo:
def bookmark_cnt_per_type
hash = Bookmark.group(:bookmark_type).count
keys = hash.keys.map(&:name)
[keys, hash.values].transpose.to_h
end