From 232cda13794f4ed3d2e22093802e48fb2ea55403 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 25 Oct 2023 15:29:22 -0300 Subject: [PATCH 01/18] refactor: guardar las asociaciones en PostService (cherry picked from commit 4cf57a441a8d255cfd60a6e59c466f7b40934c04) --- .../concerns/metadata/inverse_concern.rb | 23 ++++ app/models/metadata_belongs_to.rb | 78 +---------- .../metadata_has_and_belongs_to_many.rb | 43 +----- app/models/metadata_has_many.rb | 56 +------- app/services/post_service.rb | 122 +++++++++++++----- app/views/posts/attribute_ro/_belongs_to.haml | 4 +- .../_has_and_belongs_to_many.haml | 4 +- app/views/posts/attribute_ro/_has_many.haml | 4 +- 8 files changed, 124 insertions(+), 210 deletions(-) create mode 100644 app/models/concerns/metadata/inverse_concern.rb diff --git a/app/models/concerns/metadata/inverse_concern.rb b/app/models/concerns/metadata/inverse_concern.rb new file mode 100644 index 00000000..aa300fa7 --- /dev/null +++ b/app/models/concerns/metadata/inverse_concern.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Metadata + module InverseConcern + extend ActiveSupport::Concern + + included do + # Hay una relación inversa? + # + # @return [Boolean] + def inverse? + inverse.present? + end + + # La relación inversa + # + # @return [Nil,Symbol] + def inverse + @inverse ||= layout.metadata.dig(name, 'inverse')&.to_sym + end + end + end +end diff --git a/app/models/metadata_belongs_to.rb b/app/models/metadata_belongs_to.rb index 204d7bed..93a1f710 100644 --- a/app/models/metadata_belongs_to.rb +++ b/app/models/metadata_belongs_to.rb @@ -3,6 +3,8 @@ # Almacena el UUID de otro Post y actualiza el valor en el Post # relacionado. class MetadataBelongsTo < MetadataRelatedPosts + include Metadata::InverseConcern + # TODO: Convertir algunos tipos de valores en módulos para poder # implementar varios tipos de campo sin repetir código # @@ -20,86 +22,12 @@ class MetadataBelongsTo < MetadataRelatedPosts document.data[name.to_s] end - def validate - super - - errors << I18n.t('metadata.belongs_to.missing_post') unless post_exists? - - errors.empty? - end - - # Guardar y guardar la relación inversa también, eliminando la - # relación anterior si existía. - def save - super - - # Si no hay relación inversa, no hacer nada más - return true unless changed? - return true unless inverse? - - # Si estamos cambiando la relación, tenemos que eliminar la relación - # anterior - if belonged_to.present? - belonged_to[inverse].value = belonged_to[inverse].value.reject do |rej| - rej == post.uuid.value - end - end - - # No duplicar las relaciones - belongs_to[inverse].value = (belongs_to[inverse].value.dup << post.uuid.value) unless belongs_to.blank? || included? - - true - end - - # El Post actual está incluido en la relación inversa? - def included? - belongs_to[inverse].value.include?(post.uuid.value) - end - - # Hay una relación inversa y el artículo existe? - def inverse? - inverse.present? - end - - # El campo que es la relación inversa de este - def inverse - @inverse ||= layout.metadata.dig(name, 'inverse')&.to_sym - end - - # El Post relacionado con este artículo - # - # @return [Post,nil] - def belongs_to - posts.find_by(post_id: value)&.post if value.present? - end - - # El artículo relacionado anterior - # - # @return [Post,nil] - def belonged_to - posts.find_by(post_id: value_was)&.post if value_was.present? - end - - def related_posts? - true - end - - def related_methods - @related_methods ||= %i[belongs_to belonged_to].freeze - end - def indexable_values - belongs_to&.title&.value + posts.find_by_post_uuid(value).try(:title) end private - def post_exists? - return true if sanitize(value).blank? - - sanitize(value).present? && belongs_to.present? - end - def sanitize(uuid) uuid.to_s.gsub(/[^a-f0-9\-]/i, '') end diff --git a/app/models/metadata_has_and_belongs_to_many.rb b/app/models/metadata_has_and_belongs_to_many.rb index 2c4f3d43..2c1b0f96 100644 --- a/app/models/metadata_has_and_belongs_to_many.rb +++ b/app/models/metadata_has_and_belongs_to_many.rb @@ -1,46 +1,5 @@ # frozen_string_literal: true -# Establece una relación de muchos a muchos artículos. Cada campo es un -# Array de UUID que se mantienen sincronizados. -# -# Por ejemplo: -# -# Un libro puede tener muches autores y une autore muchos libros. La -# relación has_many tiene que traer todes les autores relacionades con -# el libro actual. La relación belongs_to tiene que traer todes les -# autores que tienen este libro. La relación es bidireccional, no hay -# diferencia entre has_many y belongs_to. +# Establece una relación de muchos a muchos artículos class MetadataHasAndBelongsToMany < MetadataHasMany - # Mantiene la relación inversa si existe. - # - # La relación belongs_to se mantiene actualizada en la modificación - # actual. Lo que buscamos es mantener sincronizada esa relación. - # - # Buscamos en belongs_to la relación local, si se eliminó hay que - # quitarla de la relación remota, sino hay que agregarla. - # - def save - # XXX: No usamos super - self[:value] = sanitize value - - return true unless changed? - return true unless inverse? - - # XXX: Usamos asignación para aprovechar value= que setea el valor - # anterior en @value_was - (had_many - has_many).each do |remove| - remove[inverse].value = remove[inverse].value.reject do |rej| - rej == post.uuid.value - end - end - - (has_many - had_many).each do |add| - next unless add[inverse] - next if add[inverse].value.include? post.uuid.value - - add[inverse].value = (add[inverse].value.dup << post.uuid.value) - end - - true - end end diff --git a/app/models/metadata_has_many.rb b/app/models/metadata_has_many.rb index 82ec333a..c5f01f7c 100644 --- a/app/models/metadata_has_many.rb +++ b/app/models/metadata_has_many.rb @@ -6,59 +6,5 @@ # Localmente tenemos un Array de UUIDs. Remotamente tenemos una String # apuntando a un Post, que se mantiene actualizado como el actual. class MetadataHasMany < MetadataRelatedPosts - # Todos los Post relacionados - # - # @return [Array] - def has_many - return default_value if value.blank? - - posts.where(post_id: value).map(&:post) - end - - # La relación anterior - # - # @return [Array] - def had_many - return default_value if value_was.blank? - - posts.where(post_id: value_was).map(&:post) - end - - def inverse? - inverse.present? - end - - # La relación inversa - # - # @return [Nil,Symbol] - def inverse - @inverse ||= layout.metadata.dig(name, 'inverse')&.to_sym - end - - # Actualizar las relaciones inversas. Hay que buscar la diferencia - # entre had y has_many. - def save - super - - return true unless changed? - return true unless inverse? - - (had_many - has_many).each do |remove| - remove[inverse]&.value = remove[inverse].default_value - end - - (has_many - had_many).each do |add| - add[inverse]&.value = post.uuid.value - end - - true - end - - def related_posts? - true - end - - def related_methods - @related_methods ||= %i[has_many had_many].freeze - end + include Metadata::InverseConcern end diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 0d989871..dc04dda6 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -3,7 +3,7 @@ # Este servicio se encarga de crear artículos y guardarlos en git, # asignándoselos a une usuarie PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do - # Crea un artículo nuevo + # Crea un artículo nuevo y modificar las asociaciones # # @return Post def create @@ -15,9 +15,21 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do post.slug.value = p[:slug] if p[:slug].present? end - commit(action: :created, add: update_related_posts) if post.update(post_params) + if post.update(post_params) + added_paths = [] + added_paths << post.path.value - update_site_license! + # Recorrer todas las asociaciones y agregarse donde corresponda + update_associations_forward(post) + + associated_posts_to_save.each do |associated_post| + next unless associated_post.save(validate: false) + + added_paths << associated_post.path.value + end + + commit(action: :created, add: added_paths) + end # Devolver el post aunque no se haya salvado para poder rescatar los # errores @@ -25,6 +37,8 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end # Crear un post anónimo, con opciones más limitadas. No usamos post. + # + # @todo Permitir asociaciones? def create_anonymous # XXX: Confiamos en el parámetro de idioma porque estamos # verificándolos en Site#posts @@ -36,20 +50,30 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do post end + # Al actualizar, modificamos un post pre-existente, todas las + # relaciones anteriores y las relaciones actuales. def update post.usuaries << usuarie params[:post][:draft] = true if site.invitade? usuarie - # Eliminar ("mover") el archivo si cambió de ubicación. if post.update(post_params) + # Eliminar ("mover") el archivo si cambió de ubicación. rm = [] rm << post.path.value_was if post.path.changed? - # Es importante que el artículo se guarde primero y luego los - # relacionados. - commit(action: :updated, add: update_related_posts, rm: rm) + added_paths = [] + added_paths << post.path.value - update_site_license! + # Recorrer todas las asociaciones y agregarse donde corresponda + update_associations_forward(post) + + associated_posts_to_save.each do |associated_post| + next unless associated_post.save(validate: false) + + added_paths << associated_post.path.value + end + + commit(action: :updated, add: added_paths, rm: rm) end # Devolver el post aunque no se haya salvado para poder rescatar los @@ -128,30 +152,6 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do ] end - # Actualiza los artículos relacionados según los métodos que los - # metadatos declaren. - # - # Este método se asegura que todos los artículos se guardan una sola - # vez. - # - # @return [Array] Lista de archivos modificados - def update_related_posts - posts = Set.new - - post.attributes.each do |a| - post[a].related_methods.each do |m| - next unless post[a].respond_to? m - - # La respuesta puede ser una PostRelation también - posts.merge [post[a].public_send(m)].flatten.compact - end - end - - posts.map do |p| - p.path.absolute if p.save(validate: false) - end.compact << post.path.absolute - end - # Si les usuaries modifican o crean una licencia, considerarla # personalizada en el panel. def update_site_license! @@ -159,4 +159,62 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do site.update licencia: Licencia.find_by_icons('custom') end end + + # @return [Array] + def associated_posts_to_save + @associated_posts_to_save ||= Set.new + end + + # Recolectar campos asociados que no estén vacíos + # + # @param [Post] + # @return [Array] + def association_attributes(post) + post.attributes.select do |attribute| + post[attribute].try(:inverse?) + end + end + + # @param :post_ids [Array] + # @return [Association] + def associated_posts(post_ids) + site.indexed_posts.where(post_id: post_ids).map(&:post) + end + + # Modificar las asociaciones. + # + # Si el valor actual es una String, es un BelongsTo + # + # + def update_associations_forward(post) + association_attributes(post).each do |attribute| + metadata = post[attribute] + + next unless metadata.changed? + + inverse_attribute = post[attribute].inverse + value_was = metadata.value_was.dup + value = metadata.value.dup + + case metadata.type + when 'has_and_belongs_to_many' + binding.pry + associated_posts(value_was - value).each do |remove_post| + remove_post[inverse_attribute].value.delete(post.uuid.value) + + associated_posts_to_save << remove_post + end + + associated_posts(value - value_was).each do |add_post| + add_post[inverse_attribute].value << post.uuid.value + add_post[inverse_attribute].value.uniq! + + associated_posts_to_save << add_post + end + when 'has_many' + when 'belongs_to' + when 'locales' + end + end + end end diff --git a/app/views/posts/attribute_ro/_belongs_to.haml b/app/views/posts/attribute_ro/_belongs_to.haml index c7e06be8..7410e921 100644 --- a/app/views/posts/attribute_ro/_belongs_to.haml +++ b/app/views/posts/attribute_ro/_belongs_to.haml @@ -1,6 +1,6 @@ %tr{ id: attribute } %th= post_label_t(attribute, post: post) %td{ dir: dir, lang: locale } - - p = metadata.belongs_to + - p = site.indexed_posts.find_by_post_id(metadata.value) - if p - = link_to p.title.value, site_post_path(site, p.id) + = link_to p.title, site_post_path(site, p.path) diff --git a/app/views/posts/attribute_ro/_has_and_belongs_to_many.haml b/app/views/posts/attribute_ro/_has_and_belongs_to_many.haml index d6b51a7a..29c0816f 100644 --- a/app/views/posts/attribute_ro/_has_and_belongs_to_many.haml +++ b/app/views/posts/attribute_ro/_has_and_belongs_to_many.haml @@ -2,5 +2,5 @@ %th= post_label_t(attribute, post: post) %td %ul{ dir: dir, lang: locale } - - metadata.has_many.each do |p| - %li= link_to p.title.value, site_post_path(site, p.id) + - site.indexed_posts.where(post_id: metadata.value).find_each do |p| + %li= link_to p.title, site_post_path(site, p.path) diff --git a/app/views/posts/attribute_ro/_has_many.haml b/app/views/posts/attribute_ro/_has_many.haml index d6b51a7a..29c0816f 100644 --- a/app/views/posts/attribute_ro/_has_many.haml +++ b/app/views/posts/attribute_ro/_has_many.haml @@ -2,5 +2,5 @@ %th= post_label_t(attribute, post: post) %td %ul{ dir: dir, lang: locale } - - metadata.has_many.each do |p| - %li= link_to p.title.value, site_post_path(site, p.id) + - site.indexed_posts.where(post_id: metadata.value).find_each do |p| + %li= link_to p.title, site_post_path(site, p.path) From ce299cc9d3906fbbe0c76e987b7f8a9279b1e927 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 12:24:41 -0300 Subject: [PATCH 02/18] feat: modificar relaciones en PostService (cherry picked from commit 3e88a98f5e9e1b31de8c058b7b5a41dfc4222f7c) --- app/services/post_service.rb | 47 ++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/app/services/post_service.rb b/app/services/post_service.rb index dc04dda6..1d7e1a99 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -20,7 +20,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do added_paths << post.path.value # Recorrer todas las asociaciones y agregarse donde corresponda - update_associations_forward(post) + update_associations(post) associated_posts_to_save.each do |associated_post| next unless associated_post.save(validate: false) @@ -65,7 +65,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do added_paths << post.path.value # Recorrer todas las asociaciones y agregarse donde corresponda - update_associations_forward(post) + update_associations(post) associated_posts_to_save.each do |associated_post| next unless associated_post.save(validate: false) @@ -181,12 +181,8 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do site.indexed_posts.where(post_id: post_ids).map(&:post) end - # Modificar las asociaciones. - # - # Si el valor actual es una String, es un BelongsTo - # - # - def update_associations_forward(post) + # Modificar las asociaciones en cascada + def update_associations(post) association_attributes(post).each do |attribute| metadata = post[attribute] @@ -194,11 +190,10 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do inverse_attribute = post[attribute].inverse value_was = metadata.value_was.dup - value = metadata.value.dup + value = metadata.value.dup case metadata.type when 'has_and_belongs_to_many' - binding.pry associated_posts(value_was - value).each do |remove_post| remove_post[inverse_attribute].value.delete(post.uuid.value) @@ -212,7 +207,39 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do associated_posts_to_save << add_post end when 'has_many' + associated_posts(value_was - value).each do |remove_post| + # @todo por qué no podemos usar nil para deshabilitar un valor? + remove_post[inverse_attribute].value = '' + + associated_posts_to_save << remove_post + end + + associated_posts(value - value_was).each do |add_post| + associated_posts(add_post[inverse_attribute].value_was).each do |remove_post| + remove_post[attribute].value.delete(add_post.uuid.value) + + associated_posts_to_save << remove_post + end + + add_post[inverse_attribute].value = post.uuid.value + + associated_posts_to_save << add_post + end when 'belongs_to' + if value_was.present? + associated_posts(value_was).each do |remove_post| + remove_post[inverse_attribute].value.delete(value_was) + + associated_posts_to_save << remove_post + end + end + + associated_posts(value).each do |add_post| + add_post[inverse_attribute].value << post.uuid.value + add_post[inverse_attribute].value.uniq! + + associated_posts_to_save << add_post + end when 'locales' end end From fabcabc10057946d38f17297897d19d563a4ccd5 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 16:27:01 -0300 Subject: [PATCH 03/18] refactor: eliminar relaciones (cherry picked from commit b81e38e9ea830f1517f79f90ac99187a0ee9902e) --- app/services/post_service.rb | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 1d7e1a99..c82953e3 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -195,9 +195,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do case metadata.type when 'has_and_belongs_to_many' associated_posts(value_was - value).each do |remove_post| - remove_post[inverse_attribute].value.delete(post.uuid.value) - - associated_posts_to_save << remove_post + remove_relation_from(remove_post[inverse_attribute], post.uuid.value) end associated_posts(value - value_was).each do |add_post| @@ -208,17 +206,12 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end when 'has_many' associated_posts(value_was - value).each do |remove_post| - # @todo por qué no podemos usar nil para deshabilitar un valor? - remove_post[inverse_attribute].value = '' - - associated_posts_to_save << remove_post + remove_relation_from(remove_post[inverse_attribute], '') end associated_posts(value - value_was).each do |add_post| associated_posts(add_post[inverse_attribute].value_was).each do |remove_post| - remove_post[attribute].value.delete(add_post.uuid.value) - - associated_posts_to_save << remove_post + remove_relation_from(remove_post[attribute], add_post.uuid.value) end add_post[inverse_attribute].value = post.uuid.value @@ -228,9 +221,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do when 'belongs_to' if value_was.present? associated_posts(value_was).each do |remove_post| - remove_post[inverse_attribute].value.delete(value_was) - - associated_posts_to_save << remove_post + remove_relation_from(remove_post[inverse_attribute], value_was) end end @@ -244,4 +235,15 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end end end + + # @todo por qué no podemos usar nil para deshabilitar un valor? + def remove_relation_from(metadata, value) + case metadata.value + when Array then metadata.value.delete(value) + when String then metadata.value = '' + end + + associated_posts_to_save << metadata.post + nil + end end From 0eee4f17d34e510a9503752f37247cbb21eaf226 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 16:29:58 -0300 Subject: [PATCH 04/18] refactor: agregar relaciones (cherry picked from commit 66472d4b6f6f74197ad9e884dff3b716491cd33e) --- app/services/post_service.rb | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/app/services/post_service.rb b/app/services/post_service.rb index c82953e3..83d30a50 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -199,10 +199,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end associated_posts(value - value_was).each do |add_post| - add_post[inverse_attribute].value << post.uuid.value - add_post[inverse_attribute].value.uniq! - - associated_posts_to_save << add_post + add_relation_to(add_post[inverse_attribute], post.uuid.value) end when 'has_many' associated_posts(value_was - value).each do |remove_post| @@ -214,9 +211,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do remove_relation_from(remove_post[attribute], add_post.uuid.value) end - add_post[inverse_attribute].value = post.uuid.value - - associated_posts_to_save << add_post + add_relation_to(add_post[inverse_attribute], post.uuid.value) end when 'belongs_to' if value_was.present? @@ -226,10 +221,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end associated_posts(value).each do |add_post| - add_post[inverse_attribute].value << post.uuid.value - add_post[inverse_attribute].value.uniq! - - associated_posts_to_save << add_post + add_relation_to(add_post[inverse_attribute], post.uuid.value) end when 'locales' end @@ -246,4 +238,17 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do associated_posts_to_save << metadata.post nil end + + # @todo El validador ya debería eliminar valores duplicados + def add_relation_to(metadata, value) + case metadata.value + when Array + metadata.value << value + metadata.uniq! + when String then metadata.value = value + end + + associated_posts_to_save << metadata.post + nil + end end From 10610857b4f8a1fc42c3430d65bf0313e85ba64c Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 16:31:34 -0300 Subject: [PATCH 05/18] refactor: guardar las relaciones luego de actualizarlas (cherry picked from commit 67bb6e45df3f0540fb11576f68f41a627b1687ca) --- app/services/post_service.rb | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 83d30a50..3e77896c 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -22,12 +22,6 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do # Recorrer todas las asociaciones y agregarse donde corresponda update_associations(post) - associated_posts_to_save.each do |associated_post| - next unless associated_post.save(validate: false) - - added_paths << associated_post.path.value - end - commit(action: :created, add: added_paths) end @@ -67,12 +61,6 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do # Recorrer todas las asociaciones y agregarse donde corresponda update_associations(post) - associated_posts_to_save.each do |associated_post| - next unless associated_post.save(validate: false) - - added_paths << associated_post.path.value - end - commit(action: :updated, add: added_paths, rm: rm) end @@ -226,6 +214,12 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do when 'locales' end end + + associated_posts_to_save.each do |associated_post| + next unless associated_post.save(validate: false) + + added_paths << associated_post.path.value + end end # @todo por qué no podemos usar nil para deshabilitar un valor? From 381467ecc06bae999ba21d3a28120f9b20141994 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 16:36:29 -0300 Subject: [PATCH 06/18] =?UTF-8?q?fix:=20la=20relaci=C3=B3n=20de=20traducci?= =?UTF-8?q?=C3=B3n=20es=20como=20habtm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 685845a558a5f9ba0ecbee9eee84cc63393cdc51) --- app/services/post_service.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 3e77896c..4b7c2d7d 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -181,7 +181,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do value = metadata.value.dup case metadata.type - when 'has_and_belongs_to_many' + when 'has_and_belongs_to_many', 'locales' associated_posts(value_was - value).each do |remove_post| remove_relation_from(remove_post[inverse_attribute], post.uuid.value) end @@ -211,7 +211,6 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do associated_posts(value).each do |add_post| add_relation_to(add_post[inverse_attribute], post.uuid.value) end - when 'locales' end end @@ -223,6 +222,9 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end # @todo por qué no podemos usar nil para deshabilitar un valor? + # @param :metadata [MetadataTemplate] + # @param :value [String] + # @return [nil] def remove_relation_from(metadata, value) case metadata.value when Array then metadata.value.delete(value) @@ -234,6 +236,9 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end # @todo El validador ya debería eliminar valores duplicados + # @param :metadata [MetadataTemplate] + # @param :value [String] + # @return [nil] def add_relation_to(metadata, value) case metadata.value when Array From 84d7b8f7c0eac7874c15e072d86e205e70ea85d3 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 16:41:33 -0300 Subject: [PATCH 07/18] docs: relaciones (cherry picked from commit 82fdf2518e7b3465e03fb4ba1a7074610aae70e7) --- app/services/post_service.rb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 4b7c2d7d..da7673d0 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -169,7 +169,21 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do site.indexed_posts.where(post_id: post_ids).map(&:post) end - # Modificar las asociaciones en cascada + # Modificar las asociaciones en cascada, manteniendo reciprocidad + # y guardando los archivos correspondientes. + # + # HABTM, Locales: si se rompe de un lado se elimina en el otro y lo + # mismo si se agrega. + # + # HasMany: la relación es de uno a muchos. Al quitar uno, se elimina + # la relación inversa. Al agregar uno, se elimina su relación + # anterior en el tercer Post y se actualiza con la nueva. + # + # BelongsTo: la inversa de HasMany. Al cambiarla, se quita de la + # relación anterior y se agrega en la nueva. + # + # @param :post [Post] + # @return [nil] def update_associations(post) association_attributes(post).each do |attribute| metadata = post[attribute] @@ -219,6 +233,8 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do added_paths << associated_post.path.value end + + nil end # @todo por qué no podemos usar nil para deshabilitar un valor? From ed1fd9cdef67a2e9687d77d626a983e159cb1973 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 16:43:16 -0300 Subject: [PATCH 08/18] =?UTF-8?q?fix:=20mantener=20una=20lista=20=C3=BAnic?= =?UTF-8?q?a=20de=20archivos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 0dc3a69a93c53eb8a59181503ddb12dca2e5a698) --- app/services/post_service.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/services/post_service.rb b/app/services/post_service.rb index da7673d0..6c10b044 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -16,7 +16,6 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end if post.update(post_params) - added_paths = [] added_paths << post.path.value # Recorrer todas las asociaciones y agregarse donde corresponda @@ -55,7 +54,6 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do rm = [] rm << post.path.value_was if post.path.changed? - added_paths = [] added_paths << post.path.value # Recorrer todas las asociaciones y agregarse donde corresponda @@ -148,11 +146,16 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end end - # @return [Array] + # @return [Set] def associated_posts_to_save @associated_posts_to_save ||= Set.new end + # @return [Set] + def added_paths + @added_paths ||= Set.new + end + # Recolectar campos asociados que no estén vacíos # # @param [Post] From d6e49bbd1e0d5ac82fbcc28f75ff44b31abc762a Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 16:46:23 -0300 Subject: [PATCH 09/18] fix: reordenar (cherry picked from commit f5f66bd95f86e5f85f4ef3fab5a7453c49c1f61f) --- app/services/post_service.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 6c10b044..91d8b8f6 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -67,6 +67,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do post end + # @todo Eliminar relaciones def destroy post.destroy! @@ -82,7 +83,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do # { uuid => 2, uuid => 1, uuid => 0 } def reorder reorder = params.require(:post).permit(reorder: {})&.dig(:reorder)&.transform_values(&:to_i) - posts = site.indexed_posts(locale: locale).where(post_id: reorder.keys).map(&:post) + posts = site.indexed_posts.where(locale: locale, post_id: reorder.keys).map(&:post) files = posts.map do |post| next unless post.attribute? :order @@ -98,8 +99,11 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do return if files.empty? # TODO: Implementar transacciones! - posts.save_all(validate: false) && - commit(action: :reorder, add: files) + posts.map do |post| + post.save(validate: false) + end + + commit(action: :reorder, add: files) end private From cd613b053df85bbcd3b7fad961b68d569166a6b4 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 18:05:58 -0300 Subject: [PATCH 10/18] fix: validar presencia de atributos (cherry picked from commit 2282acfc6e1b11f8ef23b3879d6c9c45ba6a1e7f) --- app/models/indexed_post.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/indexed_post.rb b/app/models/indexed_post.rb index 02679ec0..992c07c8 100644 --- a/app/models/indexed_post.rb +++ b/app/models/indexed_post.rb @@ -48,6 +48,8 @@ class IndexedPost < ApplicationRecord .flatten.uniq end + validates_presence_of :layout, :path, :locale + belongs_to :site # La ubicación del Post en el disco From 43ba7d78f0de49d569ca9187274c3667509cc1b2 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 18:06:43 -0300 Subject: [PATCH 11/18] =?UTF-8?q?fix:=20notificar=20la=20excepci=C3=B3n=20?= =?UTF-8?q?correctamente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 6e5195ca90490cf18fbf6457d4da70336aded108) --- app/models/post.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/post.rb b/app/models/post.rb index ab91bd43..84f18386 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -30,6 +30,10 @@ class Post # a demanda? def find_layout(path) File.foreach(path).lazy.grep(/^layout: /).take(1).first&.split(' ')&.last&.tr('\'', '')&.tr('"', '')&.to_sym + rescue Errno::ENOENT => e + ExceptionNotifier.notify_exception(e, data: { path: path }) + + :post end # Genera un Post nuevo From c07eb9773505279de9c6a90d36bc08263ef2d49b Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 18:07:03 -0300 Subject: [PATCH 12/18] fix: valor por defecto correcto (cherry picked from commit 9531a2004c8b8c1c83b44ea1110f31e9672fca2f) --- db/migrate/20210504224343_create_indexed_posts.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20210504224343_create_indexed_posts.rb b/db/migrate/20210504224343_create_indexed_posts.rb index 9cf21538..a88db1f3 100644 --- a/db/migrate/20210504224343_create_indexed_posts.rb +++ b/db/migrate/20210504224343_create_indexed_posts.rb @@ -27,7 +27,7 @@ class CreateIndexedPosts < ActiveRecord::Migration[6.1] # Queremos mostrar el título por separado t.string :title, default: '' # También vamos a mostrar las categorías - t.jsonb :front_matter, default: '{}' + t.jsonb :front_matter, default: {} t.string :content, default: '' t.tsvector :indexed_content From bbcfc701b4f7248e26d420f8abd6292e816bf112 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 18:08:14 -0300 Subject: [PATCH 13/18] feat: poder pasar la ruta a Post.build (cherry picked from commit b0c21f989e7b6cf77b41b732e0f08e88d676c4d8) --- app/models/post.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/post.rb b/app/models/post.rb index 84f18386..af2b48d4 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -38,19 +38,22 @@ class Post # Genera un Post nuevo # + # @todo Mergear en Post#initialize + # @params :path [String] # @params :site [Site] # @params :locale [String, Symbol] # @params :document [Jekyll::Document] # @params :layout [String,Symbol] # @return [Post] def build(**args) + args[:path] ||= '' args[:document] ||= begin site = args[:site] collection = site.collections[args[:locale].to_s] - Jekyll::Document.new('', site: site.jekyll, collection: collection).tap do |doc| - doc.data['date'] = Date.today.to_time + Jekyll::Document.new(args[:path], site: site.jekyll, collection: collection).tap do |doc| + doc.data['date'] ||= Date.today.to_time end end From dc83dfe00ab0d564d8c070b64730e52977fe1e41 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 18:08:44 -0300 Subject: [PATCH 14/18] =?UTF-8?q?feat:=20poder=20pasar=20el=20layout=20com?= =?UTF-8?q?o=20s=C3=ADmbolo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit e1d80e485369fbf031643f93f05a3410adba45b1) --- app/models/post.rb | 2 ++ app/models/site/index.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/post.rb b/app/models/post.rb index af2b48d4..5f6d5ff4 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -57,6 +57,8 @@ class Post end end + args[:layout] = args[:site].layouts[args[:layout]] if args[:layout].is_a? Symbol + Post.new(**args) end end diff --git a/app/models/site/index.rb b/app/models/site/index.rb index 2cb950cb..7a070e6d 100644 --- a/app/models/site/index.rb +++ b/app/models/site/index.rb @@ -20,7 +20,7 @@ class Site jekyll.documents.each do |doc| doc.read! - Post.new(document: doc, site: self, layout: layouts[doc['layout'].to_sym]).index! + Post.new(document: doc, site: self, layout: doc['layout'].to_sym).index! end update(last_indexed_commit: repository.head_commit.oid) From f5b1217a02f8e5b76a2a166aa547fcb825b0d84c Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 18:09:20 -0300 Subject: [PATCH 15/18] fix: indexar cambios sin leer todo el sitio (cherry picked from commit e34392e77c78d26a3fdb6afadbebdc9d72a77fca) --- app/models/site/index.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/site/index.rb b/app/models/site/index.rb index 7a070e6d..dc9f266f 100644 --- a/app/models/site/index.rb +++ b/app/models/site/index.rb @@ -104,9 +104,10 @@ class Site indexable_posts.select do |delta| MODIFIED_STATUSES.include? delta.status end.each do |delta| - locale, path = locale_and_path_from(delta.new_file[:path]) + locale, _ = locale_and_path_from(delta.new_file[:path]) + full_path = File.join(self.path, delta.new_file[:path]) - posts(lang: locale).find(path).index! + Post.build(path: full_path, site: self, layout: Post.find_layout(full_path), locale: locale).index! end end From 8c507752189474b57c43f6cb89d679904ca58961 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 18:10:58 -0300 Subject: [PATCH 16/18] fix: obtener el post indexado a partir del post (cherry picked from commit 3b656f190852c9649cffa7cf86a50dedb592b7a2) --- app/models/post.rb | 5 ----- app/models/post/indexable.rb | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models/post.rb b/app/models/post.rb index 5f6d5ff4..26611a41 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -63,11 +63,6 @@ class Post end end - # @return [IndexedPost,nil] - def indexed_post - site.indexed_posts.find_by(locale: lang.value, path: id) - end - # Redefinir el inicializador de OpenStruct # # @param :site [Site] el sitio en Sutty diff --git a/app/models/post/indexable.rb b/app/models/post/indexable.rb index 3aa8ce59..40be9b1c 100644 --- a/app/models/post/indexable.rb +++ b/app/models/post/indexable.rb @@ -10,6 +10,11 @@ class Post after_save :index! after_destroy :remove_from_index! + # @return [IndexedPost,nil] + def indexed_post + site.indexed_posts.find_by_post_id(uuid.value) + end + # Devuelve una versión indexable del Post # # @return [IndexedPost] From 0d781b6a157d6fe8c772c1f2440112312106793e Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 18:12:30 -0300 Subject: [PATCH 17/18] =?UTF-8?q?fix:=20garantizar=20que=20el=20sitio=20es?= =?UTF-8?q?t=C3=A1=20indexado=20antes=20de=20verlo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 39ea2d25fc34674d566f4a5de63be39adc24cc20) --- app/controllers/application_controller.rb | 4 +++- app/models/post/indexable.rb | 3 --- app/services/site_service.rb | 9 ++------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2746ab10..218e1e55 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -96,7 +96,9 @@ class ApplicationController < ActionController::Base end def site - @site ||= find_site + @site ||= find_site.tap do |s| + s.reindex_changes! + end end protected diff --git a/app/models/post/indexable.rb b/app/models/post/indexable.rb index 40be9b1c..38a98c2b 100644 --- a/app/models/post/indexable.rb +++ b/app/models/post/indexable.rb @@ -6,9 +6,6 @@ class Post extend ActiveSupport::Concern included do - # Indexa o reindexa el Post - after_save :index! - after_destroy :remove_from_index! # @return [IndexedPost,nil] def indexed_post diff --git a/app/services/site_service.rb b/app/services/site_service.rb index 34d5d43f..53165e1a 100644 --- a/app/services/site_service.rb +++ b/app/services/site_service.rb @@ -76,16 +76,11 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do commit_config(action: :tor) end - # Trae cambios desde la rama remota y reindexa los artículos. + # Trae cambios desde la rama remota # # @return [Boolean] def merge - result = site.repository.merge(usuarie) - - # TODO: Implementar callbacks - site.try(:index_posts!) if result - - result.present? + site.repository.merge(usuarie).present? end private From fca074bb0ec9cd78dfb02602ea94f77a79911c6b Mon Sep 17 00:00:00 2001 From: f Date: Thu, 26 Oct 2023 18:17:42 -0300 Subject: [PATCH 18/18] fix: deprecar DeployReindex ya no es necesario porque estamos reindexando a demanda (cherry picked from commit a171aa24e3aa06f781a9b38c1db985239b73f39a) --- app/models/deploy_reindex.rb | 38 ------------------- ...20231026211607_deprecate_deploy_reindex.rb | 10 +++++ 2 files changed, 10 insertions(+), 38 deletions(-) delete mode 100644 app/models/deploy_reindex.rb create mode 100644 db/migrate/20231026211607_deprecate_deploy_reindex.rb diff --git a/app/models/deploy_reindex.rb b/app/models/deploy_reindex.rb deleted file mode 100644 index f3eb3d23..00000000 --- a/app/models/deploy_reindex.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -# Reindexa los artículos al terminar la compilación -class DeployReindex < Deploy - def deploy(**) - time_start - - site.reset - - Site.transaction do - site.indexed_posts.destroy_all - site.index_posts! - end - - time_stop - - build_stats.create action: 'reindex', - log: 'Reindex', - seconds: time_spent_in_seconds, - bytes: size, - status: true - site.touch - end - - def size - 0 - end - - def limit - 1 - end - - def hostname; end - - def url; end - - def destination; end -end diff --git a/db/migrate/20231026211607_deprecate_deploy_reindex.rb b/db/migrate/20231026211607_deprecate_deploy_reindex.rb new file mode 100644 index 00000000..945d01b4 --- /dev/null +++ b/db/migrate/20231026211607_deprecate_deploy_reindex.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# Ya no es necesario reindexar por la fuerza +class DeprecateDeployReindex < ActiveRecord::Migration[6.1] + def up + Deploy.where(type: 'DeployReindex').destroy_all + end + + def down;end +end