reordenar articulos

This commit is contained in:
f 2019-10-18 17:35:09 -03:00
parent 8a5bfbd5f2
commit 14aa09b31e
No known key found for this signature in database
GPG key ID: 2AE5A13E321F953D
10 changed files with 87 additions and 174 deletions

View file

@ -90,4 +90,17 @@ class PostsController < ApplicationController
service.destroy
redirect_to site_posts_path(@site)
end
# Reordenar los artículos
def reorder
@site = find_site
authorize @site
service = PostService.new(site: @site,
usuarie: current_usuarie,
params: params)
service.reorder
redirect_to site_posts_path(@site)
end
end

View file

@ -221,47 +221,6 @@ class Site < ApplicationRecord
status == 'enqueued'
end
# Verifica si los posts están ordenados
def ordered?(lang: nil)
posts(lang: lang).map(&:order).all?
end
# Reordena la colección usando la posición informada
#
# new_order es un hash cuya key es la posición actual del post y el
# valor la posición nueva
#
# TODO: Refactorizar y testear
def reorder_collection(collection, new_order)
# Tenemos que pasar el mismo orden
return if new_order.values.map(&:to_i).sort != new_order.keys.map(&:to_i).sort
# Solo traer los posts que vamos a modificar
posts_to_order = posts_for(collection).values_at(*new_order.keys.map(&:to_i))
# Recorre todos los posts y asigna el nuevo orden
posts_to_order.each_with_index do |p, i|
# Usar el index si el artículo no estaba ordenado, para tener una
# ruta de adopción
oo = (p.order || i).to_s
no = new_order[oo].to_i
# No modificar nada si no hace falta
next if p.order == no
p.update_attributes order: no
p.save
end
posts_to_order.map(&:ordered?).all?
end
# Reordena la colección usando la posición actual de los artículos
def reorder_collection!(collection = 'posts')
order = Hash[posts_for(collection).count.times.map { |i| [i.to_s, i.to_s] }]
reorder_collection collection, order
end
alias reorder_posts! reorder_collection!
# Obtener una ruta disponible para Sutty
#
# TODO: Refactorizar y testear

View file

@ -85,9 +85,13 @@ class Site
!commits.empty?
end
# Guarda los cambios en git, de a un archivo por vez
# Guarda los cambios en git
def commit(file:, usuarie:, message:, remove: false)
remove ? rm(file) : add(file)
file = [file] unless file.respond_to? :each
file.each do |f|
remove ? rm(f) : add(f)
end
# Escribir los cambios para que el repositorio se vea tal cual
rugged.index.write

View file

@ -67,6 +67,11 @@ class SitePolicy
pull?
end
# Solo les usuaries pueden reordenar artículos
def reorder?
site.usuarie? usuarie
end
private
def current_role

View file

@ -39,19 +39,50 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
post
end
# Reordena todos los posts que soporten orden de acuerdo a un array
# con las nuevas posiciones. La posición actual la da la posición en
# el array.
#
# [ 1, 0, 2 ] => mover el elemento 2 a la posición 1, mantener 3
def reorder
posts = site.posts(lang: lang)
reorder = params.require(:post).permit(reorder: [])
.try(:[], :reorder)
.try(:map, &:to_i) || []
# Tenemos que pasar un array con la misma cantidad de elementos
return false if reorder.size != posts.size
files = reorder.map.with_index do |new, cur|
post = posts[cur]
next unless post.attributes.include? :order
post.usuaries << usuarie
post.order.value = new
post.path.absolute
end.compact
# TODO: Implementar transacciones!
posts.save_all && commit(action: :reorder, file: files)
end
private
def commit(action:)
site.repository.commit(file: post.path.absolute,
def commit(action:, file: nil)
site.repository.commit(file: file || post.path.absolute,
usuarie: usuarie,
remove: action == :destroyed,
message: I18n.t("post_service.#{action}",
title: post.title.value))
title: post.try(:title).try(:value)))
end
# Solo permitir cambiar estos atributos de cada articulo
def post_params
params.require(:post).permit(post.params)
end
def lang
params[:post][:lang] || I18n.locale
end
end
# rubocop:enable Metrics/BlockLength

View file

@ -19,6 +19,7 @@ en:
created: 'Created "%{title}"'
updated: 'Updated "%{title}"'
destroyed: 'Removed "%{title}"'
reorder: 'Reorder'
metadata:
array:
cant_be_empty: 'This field cannot be empty'

View file

@ -19,6 +19,7 @@ es:
created: 'Creado "%{title}"'
updated: 'Modificado "%{title}"'
destroyed: 'Eliminado "%{title}"'
reorder: 'Reordenados'
metadata:
array:
cant_be_empty: 'El campo no puede estar vacío'

View file

@ -45,6 +45,7 @@ Rails.application.routes.draw do
post 'collaborate', to: 'collaborations#accept_collaboration'
# Gestionar artículos
post :'posts/reorder', to: 'posts#reorder'
resources :posts
# Gestionar traducciones

View file

@ -1,134 +1,15 @@
# Reordenar los articulos
La interfaz reordena los articulos y los envia en ese orden particular
(se puede enviar un numero de orden completado con js para estar mas
segurxs). Entonces el algoritmo...
Todos los posts tienen un campo `order`.
* Chequea que los posts tengan fechas en orden
* Si alguno(s) no tienen, busca fechas intermedias
* Cuando todos tienen fechas en orden, guarda los cambios modificando
cada post
---
* Compara el nuevo orden con el viejo para saber las diferencias
---
* Recorre el nuevo orden uno por uno
* Se fija si el anterior tiene una fecha menor
* Se fija si el siguiente tiene una fecha mayor
* Se autoasigna una fecha en el medio
* Se guarda al final
---
* Todos los posts tienen un ID (su id en el array de la colección)
* Toma el post actual
* Mueve el post a una posición arbitraria con un nuevo id
* Toma la fecha del post siguiente (nuevo_id + 1)
* Recorre hacia atrás los anteriores, corriéndolos de a un día hasta una
fecha anterior
* Los posts posteriores no se tocan
Teniendo estos artículos
```
1 2017-01-01
2 2017-01-02
3 2017-01-03
4 2017-01-04
5 2017-01-05
```
Movemos el artículo 2 a la posición 4
```
1 1 2017-01-01
2 3 2017-01-03
3 4 2017-01-04
4 2 2017-01-02
5 5 2017-01-05
```
Reordenamos las fechas
```
1 1 2017-01-01 2017-01-01
2 3 2017-01-03 2017-01-02
3 4 2017-01-04 2017-01-03
4 2 2017-01-02 2017-01-04
5 5 2017-01-05 2017-01-05
```
Movemos varios
```
1 4 2017-01-04
2 5 2017-01-05
3 2 2017-01-02
4 3 2017-01-03
5 1 2017-01-01
```
Cual es la fecha desde la que se empieza? Vamos hacia atras o hacia
adelante?
Hacia atrás
```
1 4 2017-01-04 2016-12-28
2 5 2017-01-05 2016-12-29
3 2 2017-01-02 2016-12-30
4 3 2017-01-03 2016-12-31
5 1 2017-01-01 2017-01-01
```
Hacia adelante
```
1 4 2017-01-04 2017-01-04
2 5 2017-01-05 2017-01-05
3 2 2017-01-02 2017-01-06
4 3 2017-01-03 2017-01-07
5 1 2017-01-01 2017-01-08
```
En si las fechas no importan, porque estamos priorizando el orden, las
fechas son arbitrarias para engañar a jekyll a tener los posts en cierto
orden.
Por el contrario, para mantener los cambios mínimos, podemos reemplazar
hacia adelante comenzando desde la fecha mas baja del orden original.
```
1 4 2017-01-04 2017-01-01
2 5 2017-01-05 2017-01-02
3 2 2017-01-02 2017-01-03
4 3 2017-01-03 2017-01-04
5 1 2017-01-01 2017-01-05
```
No quiere decir que en ordenes de fechas mas dispersas se mantengan los
cambios mínimos.
También podemos tomar todo el set original de fechas y asociarselo al
orden nuevo de posts. Las fechas se mantienen igual, pero cambia el
orden de los posts.
Qué pasa cuando dos o más artículos comparten la misma fecha?
Normalmente se ordenan por nombre, pero en este algoritmo no entra ese
orden, se asume que siempre están una fecha adelante o atrás
---
Todos los posts tienen un campo order.
El orden se actualiza en base al orden cronologico.
El orden se actualiza en base al orden cronológico.
En la plantilla se puede ordenar cronólogicamente o por orden numérico.
Es más simple de implementar y no tiene comportamientos inesperados,
como "por qué cambió de fecha?"
El orden es independiente de otros metadatos (como layout, categoria,
etc), todos los artículos siguen un orden
Como el orden es un metadato, tenemos que ignorar los tipos de posts que
no lo tienen
El orden por defecto es orden natural, más bajo es más alto.

View file

@ -141,4 +141,21 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
assert_equal 200, response.status
assert_match I18n.t('metadata.image.not_an_image'), response.body
end
test 'se pueden reordenar' do
lang = :es
posts = @site.posts(lang: lang)
reorder = posts.each_index.to_a.shuffle
post site_posts_reorder_url(@site),
headers: @authorization,
params: { post: { lang: lang, reorder: reorder } }
@site = Site.find @site.id
assert_equal I18n.t('post_service.reorder'),
@site.repository.rugged.head.target.message
assert_equal reorder,
@site.posts(lang: lang).map(&:order).map(&:value)
end
end