From 10580d2b4f169be9643f13c2335d44e3f95a9ef6 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 10 May 2021 10:56:08 -0300 Subject: [PATCH 01/13] compilar el sitio de a uno por vez MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit estamos deshabilitando el botón en el panel, pero gracias a la caché (o si le usuarie tiene el botón ya abierto y habilitado) es posible correr dos o más tareas de compilación a la vez, lo que produce errores. por ejemplo, que una compilación falle porque quiere comprimir archivos que ya no están. con este cambio, si un sitio está compilando, la tarea se aplaza un minuto. si hay muchas tareas, eventualmente se harán todas pero no se pisan entre sí. quizás lo correcto sería hacerlo cuando se lanza la compilación también. --- app/jobs/deploy_job.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/jobs/deploy_job.rb b/app/jobs/deploy_job.rb index 98e474ac..e5e051c7 100644 --- a/app/jobs/deploy_job.rb +++ b/app/jobs/deploy_job.rb @@ -8,13 +8,20 @@ class DeployJob < ApplicationJob def perform(site, notify = true) ActiveRecord::Base.connection_pool.with_connection do @site = Site.find(site) - @site.update_attribute :status, 'building' + + # Si ya hay una tarea corriendo, aplazar esta + if @site.status == 'building' + DeployJob.perform_in(60, site, notify) + return + end + + @site.update status: 'building' # Asegurarse que DeployLocal sea el primero! @deployed = { deploy_local: deploy_locally } # No es opcional unless @deployed[:deploy_local] - @site.update_attribute :status, 'waiting' + @site.update status: 'waiting' notify_usuaries if notify # Hacer fallar la tarea @@ -23,7 +30,7 @@ class DeployJob < ApplicationJob deploy_others notify_usuaries if notify - @site.update_attribute :status, 'waiting' + @site.update status: 'waiting' end end # rubocop:enable Metrics/MethodLength From 39eb584a97ebca66c83e591d29e52fd4c6d9c7ad Mon Sep 17 00:00:00 2001 From: f Date: Mon, 10 May 2021 13:14:02 -0300 Subject: [PATCH 02/13] no compilar varias veces desde el compilador MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit esto evita que se espamee el botón de publicar --- app/jobs/deploy_job.rb | 2 +- app/models/site.rb | 13 ++++++++++++- test/controllers/sites_controller_test.rb | 7 +++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/jobs/deploy_job.rb b/app/jobs/deploy_job.rb index e5e051c7..f1ceca9e 100644 --- a/app/jobs/deploy_job.rb +++ b/app/jobs/deploy_job.rb @@ -10,7 +10,7 @@ class DeployJob < ApplicationJob @site = Site.find(site) # Si ya hay una tarea corriendo, aplazar esta - if @site.status == 'building' + if @site.building? DeployJob.perform_in(60, site, notify) return end diff --git a/app/models/site.rb b/app/models/site.rb index 7a8b4b4b..7aeb8a9b 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -315,14 +315,25 @@ class Site < ApplicationRecord # Poner en la cola de compilación def enqueue! - !enqueued? && update_attribute(:status, 'enqueued') + waiting? && update(status: 'enqueued') end # Está en la cola de compilación? + # + # TODO: definir todos estos métodos dinámicamente, aunque todavía no + # tenemos una máquina de estados propiamente dicha. def enqueued? status == 'enqueued' end + def waiting? + status == 'waiting' + end + + def building? + status == 'building' + end + # Cargar el sitio Jekyll # # TODO: En lugar de leer todo junto de una vez, extraer la carga de diff --git a/test/controllers/sites_controller_test.rb b/test/controllers/sites_controller_test.rb index 5f67092a..a7e2f68b 100644 --- a/test/controllers/sites_controller_test.rb +++ b/test/controllers/sites_controller_test.rb @@ -102,6 +102,13 @@ class SitesControllerTest < ActionDispatch::IntegrationTest 'index.html')) end + test 'no se pueden encolar varias veces seguidas' do + assert_enqueued_jobs 2 do + post site_enqueue_url(@site), headers: @authorization + post site_enqueue_url(@site), headers: @authorization + end + end + test 'se pueden actualizar' do name = SecureRandom.hex design = Design.all.where.not(id: @site.design_id).sample From 8d7e4d2b6406ee9a34204e94454ff4c4a84e7ad8 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 10 May 2021 13:55:36 -0300 Subject: [PATCH 03/13] solo poner en cola una sola vez MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit el cambio anterior no permitía que se encolen varias compilaciones y la versión actual pone el sitio en cola mientras se está compilando. con este cambio el sitio se puede encolar varias veces pero el estado se cambia una sola vez, para no abrirle la puerta a un loop infinito de compilaciones. lo correcto sería generar un modelo de cola con su propio estado (y probablemente a partir de cuál commit se está compilando). --- app/controllers/sites_controller.rb | 3 ++- app/models/site.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/sites_controller.rb b/app/controllers/sites_controller.rb index d7d2f9f6..22429f35 100644 --- a/app/controllers/sites_controller.rb +++ b/app/controllers/sites_controller.rb @@ -63,7 +63,8 @@ class SitesController < ApplicationController authorize site # XXX: Convertir en una máquina de estados? - DeployJob.perform_async site.id if site.enqueue! + site.enqueue! + DeployJob.perform_async site.id redirect_to site_posts_path(site) end diff --git a/app/models/site.rb b/app/models/site.rb index 7aeb8a9b..683541e4 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -315,7 +315,7 @@ class Site < ApplicationRecord # Poner en la cola de compilación def enqueue! - waiting? && update(status: 'enqueued') + update(status: 'enqueued') if waiting? end # Está en la cola de compilación? From 8a5e965e617142250debe2a0ae3bf04a223e54e8 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 13 May 2021 11:44:09 -0300 Subject: [PATCH 04/13] validar la fecha MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit una fecha en el futuro como 20213-01-01 es válida para ruby, pero jekyll valida las fechas con cuatro digitos nada mas (issue pendiente :P), así que tomaba el archivo con fecha en el futurísimo como un archivo sin fecha. fixes #1573 fixes #1572 --- app/models/metadata_document_date.rb | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/app/models/metadata_document_date.rb b/app/models/metadata_document_date.rb index 39e68735..f6ad7d0d 100644 --- a/app/models/metadata_document_date.rb +++ b/app/models/metadata_document_date.rb @@ -13,11 +13,25 @@ class MetadataDocumentDate < MetadataTemplate # El valor puede ser un Date, Time o una String en el formato # "yyyy-mm-dd" + # + # XXX: Date.iso8601 acepta fechas en el futuro lejano, como 20000, + # pero Jekyll las limita a cuatro cifras, así que vamos a mantener + # eso. + # + # @see {https://github.com/jekyll/jekyll/blob/master/lib/jekyll/document.rb#L15} def value - return (self[:value] = value_from_document || default_value) if self[:value].nil? + self[:value] = + case self[:value] + when String + begin + raise Date::Error unless /\A\d{2,4}-\d{1,2}-\d{1,2}\z/ ~= self[:value] - self[:value] = Date.iso8601(self[:value]).to_time if self[:value].is_a? String - - self[:value] + Date.iso8601(self[:value]).to_time + rescue Date::Error + value_from_document || default_value + end + else + value_from_document || default_value + end end end From f5834b5c1bbd647530b7ca160343832aeeedcb4e Mon Sep 17 00:00:00 2001 From: f Date: Thu, 13 May 2021 12:52:49 -0300 Subject: [PATCH 05/13] en lugar de asumir un valor, informar el error --- app/models/metadata_document_date.rb | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/app/models/metadata_document_date.rb b/app/models/metadata_document_date.rb index f6ad7d0d..82126ba3 100644 --- a/app/models/metadata_document_date.rb +++ b/app/models/metadata_document_date.rb @@ -11,6 +11,19 @@ class MetadataDocumentDate < MetadataTemplate document.date end + # Siempre es obligatorio + def required + true + end + + def validate + super + + errors << I18n.t('metadata.date.invalid_format') unless valid_format? + + errors.empty? + end + # El valor puede ser un Date, Time o una String en el formato # "yyyy-mm-dd" # @@ -24,8 +37,6 @@ class MetadataDocumentDate < MetadataTemplate case self[:value] when String begin - raise Date::Error unless /\A\d{2,4}-\d{1,2}-\d{1,2}\z/ ~= self[:value] - Date.iso8601(self[:value]).to_time rescue Date::Error value_from_document || default_value @@ -34,4 +45,13 @@ class MetadataDocumentDate < MetadataTemplate value_from_document || default_value end end + + private + + def valid_format? + return true if self[:value].is_a?(Time) + + @valid_format_re ||= /\A\d{2,4}-\d{1,2}-\d{1,2}\z/ + @valid_format_re =~ self[:value].to_s + end end From 42923c4e04a3701468c97caa22bd459205b07694 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 13 May 2021 19:41:39 -0300 Subject: [PATCH 06/13] sitios de testeo --- .../site_with_relationships/README.md | 2 ++ .../site_with_relationships/_config.yml | 2 ++ .../_data/layouts/author.yml | 9 ++++++++ .../_data/layouts/post.yml | 23 +++++++++++++++++++ .../site_with_relationships/_en/.keep | 0 test/fixtures/site_with_relationships/_posts | 1 + test/models/metadata_test.rb | 19 +++++++++++++++ 7 files changed, 56 insertions(+) create mode 100644 test/fixtures/site_with_relationships/README.md create mode 100644 test/fixtures/site_with_relationships/_config.yml create mode 100644 test/fixtures/site_with_relationships/_data/layouts/author.yml create mode 100644 test/fixtures/site_with_relationships/_data/layouts/post.yml create mode 100644 test/fixtures/site_with_relationships/_en/.keep create mode 120000 test/fixtures/site_with_relationships/_posts create mode 100644 test/models/metadata_test.rb diff --git a/test/fixtures/site_with_relationships/README.md b/test/fixtures/site_with_relationships/README.md new file mode 100644 index 00000000..89b5b892 --- /dev/null +++ b/test/fixtures/site_with_relationships/README.md @@ -0,0 +1,2 @@ +This is site where posts can have many authors and viceversa and posts +can be replies to others. diff --git a/test/fixtures/site_with_relationships/_config.yml b/test/fixtures/site_with_relationships/_config.yml new file mode 100644 index 00000000..da2d25c8 --- /dev/null +++ b/test/fixtures/site_with_relationships/_config.yml @@ -0,0 +1,2 @@ +locales: +- en diff --git a/test/fixtures/site_with_relationships/_data/layouts/author.yml b/test/fixtures/site_with_relationships/_data/layouts/author.yml new file mode 100644 index 00000000..afe620e2 --- /dev/null +++ b/test/fixtures/site_with_relationships/_data/layouts/author.yml @@ -0,0 +1,9 @@ +--- +title: + type: 'string' + required: true +posts: + type: 'has_and_belongs_to_many' + inverse: 'authors' + filter: + layout: 'post' diff --git a/test/fixtures/site_with_relationships/_data/layouts/post.yml b/test/fixtures/site_with_relationships/_data/layouts/post.yml new file mode 100644 index 00000000..c98baf7d --- /dev/null +++ b/test/fixtures/site_with_relationships/_data/layouts/post.yml @@ -0,0 +1,23 @@ +--- +title: + type: 'string' + required: true +authors: + type: 'has_and_belongs_to_many' + inverse: 'posts' + filter: + layout: 'author' +posts: + type: 'has_many' + inverse: 'in_reply_to' + filter: + layout: 'post' +in_reply_to: + type: 'belongs_to' + inverse: 'posts' + filter: + layout: 'post' +recommended_posts: + type: 'related_posts' + filter: + layout: 'post' diff --git a/test/fixtures/site_with_relationships/_en/.keep b/test/fixtures/site_with_relationships/_en/.keep new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/site_with_relationships/_posts b/test/fixtures/site_with_relationships/_posts new file mode 120000 index 00000000..3da1d67b --- /dev/null +++ b/test/fixtures/site_with_relationships/_posts @@ -0,0 +1 @@ +_en \ No newline at end of file diff --git a/test/models/metadata_test.rb b/test/models/metadata_test.rb new file mode 100644 index 00000000..24d955ae --- /dev/null +++ b/test/models/metadata_test.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module MetadataTest + extend ActiveSupport::Concern + + included do + setup do + name = SecureRandom.hex + # TODO: Poder cambiar el nombre + FileUtils.cp_r(Rails.root.join('test', 'fixtures', 'site_with_relationships'), Rails.root.join('_sites', name)) + + @site = create :site, name: name + end + + teardown do + @site&.destroy + end + end +end From 38c5cdef813c9989b3c5d05ca1e7cae0a1414c6d Mon Sep 17 00:00:00 2001 From: f Date: Thu, 13 May 2021 19:44:21 -0300 Subject: [PATCH 07/13] =?UTF-8?q?testear=20la=20relaci=C3=B3n=20belongs=5F?= =?UTF-8?q?to?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit belongs_to indica que un artículo pertenece a otro, la relación inversa es un has_many. en este caso nos fuimos dando cuenta que las memoizaciones nos juegan en contra, así que las vamos eliminando del código cuando no hacen falta. utilizamos `value = value.filtrado` para aprovechar `MetadataTemplate#value=` que guarda el valor anterior en `MetadataTemplate#value_was` y nos permite comparar entre el valor anterior y el actual. --- app/models/metadata_belongs_to.rb | 26 +++++--------- test/models/metadata_belongs_to_test.rb | 45 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 test/models/metadata_belongs_to_test.rb diff --git a/app/models/metadata_belongs_to.rb b/app/models/metadata_belongs_to.rb index 0626ba0c..ee182a50 100644 --- a/app/models/metadata_belongs_to.rb +++ b/app/models/metadata_belongs_to.rb @@ -3,13 +3,6 @@ # Almacena el UUID de otro Post y actualiza el valor en el Post # relacionado. class MetadataBelongsTo < MetadataRelatedPosts - def value_was=(new_value) - @belongs_to = nil - @belonged_to = nil - - super(new_value) - end - # TODO: Convertir algunos tipos de valores en módulos para poder # implementar varios tipos de campo sin repetir código # @@ -39,10 +32,14 @@ class MetadataBelongsTo < MetadataRelatedPosts # Si estamos cambiando la relación, tenemos que eliminar la relación # anterior - belonged_to[inverse].value.delete post.uuid.value if changed? && belonged_to.present? + 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 << post.uuid.value unless belongs_to.blank? || included? + belongs_to[inverse].value = (belongs_to[inverse].value.dup << post.uuid.value) unless belongs_to.blank? || included? true end @@ -63,20 +60,13 @@ class MetadataBelongsTo < MetadataRelatedPosts end # El Post relacionado con este artículo - # - # XXX: Memoizamos usando el valor para tener el valor siempre - # actualizado. def belongs_to - return if value.blank? - - @belongs_to ||= posts.find(value, uuid: true) + posts.find(value, uuid: true) if value.present? end # El artículo relacionado anterior def belonged_to - return if value_was.blank? - - @belonged_to ||= posts.find(value_was, uuid: true) + posts.find(value_was, uuid: true) if value_was.present? end def related_posts? diff --git a/test/models/metadata_belongs_to_test.rb b/test/models/metadata_belongs_to_test.rb new file mode 100644 index 00000000..bf3f7b67 --- /dev/null +++ b/test/models/metadata_belongs_to_test.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'test_helper' +require_relative 'metadata_test' + +class MetadataBelongsToTest < ActiveSupport::TestCase + include MetadataTest + + test 'se pueden relacionar artículos' do + post = @site.posts.create(layout: :post, title: SecureRandom.hex) + reply = @site.posts.create(layout: :post, title: SecureRandom.hex, in_reply_to: post.uuid.value) + + assert_equal post, reply.in_reply_to.belongs_to + assert_includes post.posts.has_many, reply + end + + test 'se puede eliminar la relación' do + post = @site.posts.create(layout: :post, title: SecureRandom.hex) + reply = @site.posts.create(layout: :post, title: SecureRandom.hex, in_reply_to: post.uuid.value) + + reply.in_reply_to.value = '' + reply.save + + assert_not_equal post, reply.in_reply_to.belongs_to + assert_equal post, reply.in_reply_to.belonged_to + assert_nil reply.in_reply_to.belongs_to + assert_not_includes post.posts.has_many, reply + end + + test 'se puede cambiar la relación' do + post1 = @site.posts.create(layout: :post, title: SecureRandom.hex) + post2 = @site.posts.create(layout: :post, title: SecureRandom.hex) + reply = @site.posts.create(layout: :post, title: SecureRandom.hex, in_reply_to: post1.uuid.value) + + reply.in_reply_to.value = post2.uuid.value + reply.save + + assert_not_equal post1, reply.in_reply_to.belongs_to + assert_equal post1, reply.in_reply_to.belonged_to + assert_not_includes post1.posts.has_many, reply + + assert_equal post2, reply.in_reply_to.belongs_to + assert_includes post2.posts.has_many, reply + end +end From bda56e3c1075554b0272f84b1a4b9a8a83bad593 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 13 May 2021 19:47:00 -0300 Subject: [PATCH 08/13] =?UTF-8?q?eliminar=20memoizaci=C3=B3n=20en=20las=20?= =?UTF-8?q?b=C3=BAsquedas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/metadata_related_posts.rb | 2 +- app/models/post_relation.rb | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/models/metadata_related_posts.rb b/app/models/metadata_related_posts.rb index 9a73dea9..855ea73d 100644 --- a/app/models/metadata_related_posts.rb +++ b/app/models/metadata_related_posts.rb @@ -22,7 +22,7 @@ class MetadataRelatedPosts < MetadataArray # Obtiene todos los posts y opcionalmente los filtra def posts - @posts ||= site.posts(lang: lang).where(**filter) + site.posts(lang: lang).where(**filter) end def title(post) diff --git a/app/models/post_relation.rb b/app/models/post_relation.rb index 850a83dc..531d3cc4 100644 --- a/app/models/post_relation.rb +++ b/app/models/post_relation.rb @@ -93,8 +93,7 @@ class PostRelation < Array def where(**args) return self if args.empty? - @where ||= {} - @where[args.hash.to_s] ||= begin + begin PostRelation.new(site: site, lang: lang).concat(select do |post| result = args.map do |attr, value| next unless post.attribute?(attr) From 817d5650f846e4ce5f965686067d667d08e8b20e Mon Sep 17 00:00:00 2001 From: f Date: Thu, 13 May 2021 19:48:41 -0300 Subject: [PATCH 09/13] =?UTF-8?q?testear=20la=20relaci=C3=B3n=20has=5Fmany?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit un artículo puede tener varios artículos, es la inversa de belongs_to también eliminamos la memoización --- app/models/metadata_has_many.rb | 16 +++------- test/models/metadata_has_many_test.rb | 45 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 test/models/metadata_has_many_test.rb diff --git a/app/models/metadata_has_many.rb b/app/models/metadata_has_many.rb index 61354011..a24a5f1b 100644 --- a/app/models/metadata_has_many.rb +++ b/app/models/metadata_has_many.rb @@ -6,14 +6,6 @@ # Localmente tenemos un Array de UUIDs. Remotamente tenemos una String # apuntando a un Post, que se mantiene actualizado como el actual. class MetadataHasMany < MetadataRelatedPosts - # Invalidar la relación anterior - def value_was=(new_value) - @had_many = nil - @has_many = nil - - super(new_value) - end - def validate super @@ -24,14 +16,16 @@ class MetadataHasMany < MetadataRelatedPosts # Todos los Post relacionados def has_many - @has_many ||= posts.where(uuid: value) + return default_value if value.blank? + + posts.where(uuid: value) end # La relación anterior def had_many - return [] if value_was.blank? + return default_value if value_was.blank? - @had_many ||= posts.where(uuid: value_was) + posts.where(uuid: value_was) end def inverse? diff --git a/test/models/metadata_has_many_test.rb b/test/models/metadata_has_many_test.rb new file mode 100644 index 00000000..637c9425 --- /dev/null +++ b/test/models/metadata_has_many_test.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'test_helper' +require_relative 'metadata_test' + +class MetadataHasManyTest < ActiveSupport::TestCase + include MetadataTest + + test 'se pueden relacionar artículos' do + reply = @site.posts.create(layout: :post, title: SecureRandom.hex) + post = @site.posts.create(layout: :post, title: SecureRandom.hex, posts: [reply.uuid.value]) + + assert_equal post, reply.in_reply_to.belongs_to + assert_includes post.posts.has_many, reply + end + + test 'se puede eliminar la relación' do + reply = @site.posts.create(layout: :post, title: SecureRandom.hex) + post = @site.posts.create(layout: :post, title: SecureRandom.hex, posts: [reply.uuid.value]) + + post.posts.value = [] + post.save + + assert_not_equal post, reply.in_reply_to.belongs_to + assert_equal post, reply.in_reply_to.belonged_to + assert_nil reply.in_reply_to.belongs_to + assert_not_includes post.posts.has_many, reply + end + + test 'se puede cambiar la relación' do + reply = @site.posts.create(layout: :post, title: SecureRandom.hex) + post1 = @site.posts.create(layout: :post, title: SecureRandom.hex, posts: [reply.uuid.value]) + post2 = @site.posts.create(layout: :post, title: SecureRandom.hex) + + reply.in_reply_to.value = post2.uuid.value + reply.save + + assert_not_equal post1, reply.in_reply_to.belongs_to + assert_equal post1, reply.in_reply_to.belonged_to + assert_not_includes post1.posts.has_many, reply + + assert_equal post2, reply.in_reply_to.belongs_to + assert_includes post2.posts.has_many, reply + end +end From fc6e7da5d698c95f10ad10749e9c703a4c6f86ea Mon Sep 17 00:00:00 2001 From: f Date: Thu, 13 May 2021 19:50:41 -0300 Subject: [PATCH 10/13] =?UTF-8?q?testear=20relaci=C3=B3n=20has=5Fand=5Fbel?= =?UTF-8?q?ongs=5Fto=5Fmany?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit un artículo puede pertenecer y tener muchos artículos (una relación poliamorosa de muchos a muchos) también utilizamos asignación para aprovechar `value=`. --- .../metadata_has_and_belongs_to_many.rb | 19 +++--- .../metadata_has_and_belongs_to_many_test.rb | 58 +++++++++++++++++++ 2 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 test/models/metadata_has_and_belongs_to_many_test.rb diff --git a/app/models/metadata_has_and_belongs_to_many.rb b/app/models/metadata_has_and_belongs_to_many.rb index f14827ec..2c4f3d43 100644 --- a/app/models/metadata_has_and_belongs_to_many.rb +++ b/app/models/metadata_has_and_belongs_to_many.rb @@ -18,6 +18,7 @@ class MetadataHasAndBelongsToMany < MetadataHasMany # # 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 @@ -25,27 +26,21 @@ class MetadataHasAndBelongsToMany < MetadataHasMany 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&.delete post.uuid.value + 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 << post.uuid.value + add[inverse].value = (add[inverse].value.dup << post.uuid.value) end true end - - private - - # Igual que en MetadataRelatedPosts - # TODO: Mover a un módulo - def sanitize(uuid) - super(uuid.map do |u| - u.to_s.gsub(/[^a-f0-9\-]/i, '') - end) - end end diff --git a/test/models/metadata_has_and_belongs_to_many_test.rb b/test/models/metadata_has_and_belongs_to_many_test.rb new file mode 100644 index 00000000..1404e145 --- /dev/null +++ b/test/models/metadata_has_and_belongs_to_many_test.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'test_helper' +require_relative 'metadata_test' + +class MetadataHasAndBelongsManyTest < ActiveSupport::TestCase + include MetadataTest + + test 'se pueden relacionar artículos' do + author = @site.posts.create(layout: :author, title: SecureRandom.hex) + post = @site.posts.create(layout: :post, title: SecureRandom.hex) + + post.authors.value = [author.uuid.value] + assert post.save + + assert_includes author.posts.has_many, post + assert_includes post.authors.has_many, author + end + + test 'se puede eliminar la relación' do + author = @site.posts.create(layout: :author, title: SecureRandom.hex) + post = @site.posts.create(layout: :post, title: SecureRandom.hex, authors: [author.uuid.value]) + + assert_includes post.authors.value, author.uuid.value + assert_includes author.posts.value, post.uuid.value + + post.authors.value = [] + ENV['HOLA'] = 'hola' + assert post.save + + assert_not_includes author.posts.has_many, post + assert_not_includes post.authors.has_many, author + + assert_includes author.posts.had_many, post + assert_includes post.authors.had_many, author + end + + test 'se puede cambiar la relación' do + author = @site.posts.create(layout: :author, title: SecureRandom.hex) + post1 = @site.posts.create(layout: :post, title: SecureRandom.hex, authors: [author.uuid.value]) + post2 = @site.posts.create(layout: :post, title: SecureRandom.hex) + + author.posts.value = [post2.uuid.value] + assert author.save + + assert_not_includes author.posts.has_many, post1 + assert_not_includes post1.authors.has_many, author + + assert_includes author.posts.had_many, post1 + assert_includes post1.authors.had_many, author + + assert_not_includes author.posts.had_many, post2 + assert_not_includes post2.authors.had_many, author + + assert_includes author.posts.has_many, post2 + assert_includes post2.authors.has_many, author + end +end From 3dacdf82bf528fbb380ad623e00c9c4e470cd6b4 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 14 May 2021 11:15:32 -0300 Subject: [PATCH 11/13] testear que lo que diga sutty termine en jekyll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit agregué el método `Jekyll::Document#reset` para que no queden datos de la versión anterior, porque jekyll mergea la información. además, para evitar bugs en las plantillas, se mantienen los arrays vacíos en el front matter para que se puedan seguir usando métodos de arrays, como each y sort. --- app/models/post.rb | 7 +++++-- config/initializers/core_extensions.rb | 11 +++++++++- test/models/metadata_belongs_to_test.rb | 21 +++++++++++++++++-- .../metadata_has_and_belongs_to_many_test.rb | 20 +++++++++++++++++- test/models/metadata_has_many_test.rb | 21 +++++++++++++++++-- 5 files changed, 72 insertions(+), 8 deletions(-) diff --git a/app/models/post.rb b/app/models/post.rb index 461733f9..d5f77092 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -230,12 +230,14 @@ class Post template = public_send attr unless template.front_matter? - body += "\n\n" + body += "\n\n" if body.present? body += template.value next end - next if template.empty? + # Queremos mantener los Array en el resultado final para que + # siempre respondan a {% for %} en Liquid. + next if template.empty? && !template.value.is_a?(Array) [attr.to_s, template.value] end.compact.to_h @@ -285,6 +287,7 @@ class Post return false unless write # Vuelve a leer el post para tomar los cambios + document.reset read written? diff --git a/config/initializers/core_extensions.rb b/config/initializers/core_extensions.rb index 8bbcbe71..d43a5116 100644 --- a/config/initializers/core_extensions.rb +++ b/config/initializers/core_extensions.rb @@ -52,7 +52,7 @@ module Jekyll # Solo lee los datos def read_data - @site.data = DataReader.new(site).read(site.config["data_dir"]) + @site.data = DataReader.new(site).read(site.config['data_dir']) end # Lee todos los artículos del sitio @@ -73,6 +73,15 @@ module Jekyll Document.class_eval do alias_method :read!, :read def read; end + + # Permitir restablecer el documento sin crear uno nuevo + def reset + @path = @extname = @has_yaml_header = @relative_path = nil + @basename_without_ext = @data = @basename = nil + @renderer = @url_placeholders = @url = nil + @to_liquid = @write_p = @excerpt_separator = @id = nil + @related_posts = @cleaned_relative_path = self.content = nil + end end # Prevenir la instanciación de Time diff --git a/test/models/metadata_belongs_to_test.rb b/test/models/metadata_belongs_to_test.rb index bf3f7b67..09b9714f 100644 --- a/test/models/metadata_belongs_to_test.rb +++ b/test/models/metadata_belongs_to_test.rb @@ -12,6 +12,11 @@ class MetadataBelongsToTest < ActiveSupport::TestCase assert_equal post, reply.in_reply_to.belongs_to assert_includes post.posts.has_many, reply + + assert post.save + + assert_equal reply.document.data['in_reply_to'], post.document.data['uuid'] + assert_includes post.document.data['posts'], reply.document.data['uuid'] end test 'se puede eliminar la relación' do @@ -19,12 +24,17 @@ class MetadataBelongsToTest < ActiveSupport::TestCase reply = @site.posts.create(layout: :post, title: SecureRandom.hex, in_reply_to: post.uuid.value) reply.in_reply_to.value = '' - reply.save + assert reply.save assert_not_equal post, reply.in_reply_to.belongs_to assert_equal post, reply.in_reply_to.belonged_to assert_nil reply.in_reply_to.belongs_to assert_not_includes post.posts.has_many, reply + + assert post.save + + assert_nil reply.document.data['in_reply_to'] + assert_not_includes post.document.data['posts'], reply.document.data['uuid'] end test 'se puede cambiar la relación' do @@ -33,7 +43,7 @@ class MetadataBelongsToTest < ActiveSupport::TestCase reply = @site.posts.create(layout: :post, title: SecureRandom.hex, in_reply_to: post1.uuid.value) reply.in_reply_to.value = post2.uuid.value - reply.save + assert reply.save assert_not_equal post1, reply.in_reply_to.belongs_to assert_equal post1, reply.in_reply_to.belonged_to @@ -41,5 +51,12 @@ class MetadataBelongsToTest < ActiveSupport::TestCase assert_equal post2, reply.in_reply_to.belongs_to assert_includes post2.posts.has_many, reply + + assert post1.save + assert post2.save + + assert_equal post2.document.data['uuid'], reply.document.data['in_reply_to'] + assert_includes post2.document.data['posts'], reply.document.data['uuid'] + assert_not_includes post1.document.data['posts'], reply.document.data['uuid'] end end diff --git a/test/models/metadata_has_and_belongs_to_many_test.rb b/test/models/metadata_has_and_belongs_to_many_test.rb index 1404e145..4887a96e 100644 --- a/test/models/metadata_has_and_belongs_to_many_test.rb +++ b/test/models/metadata_has_and_belongs_to_many_test.rb @@ -15,6 +15,11 @@ class MetadataHasAndBelongsManyTest < ActiveSupport::TestCase assert_includes author.posts.has_many, post assert_includes post.authors.has_many, author + + assert author.save + + assert_includes author.document.data['posts'], post.document.data['uuid'] + assert_includes post.document.data['authors'], author.document.data['uuid'] end test 'se puede eliminar la relación' do @@ -25,7 +30,6 @@ class MetadataHasAndBelongsManyTest < ActiveSupport::TestCase assert_includes author.posts.value, post.uuid.value post.authors.value = [] - ENV['HOLA'] = 'hola' assert post.save assert_not_includes author.posts.has_many, post @@ -33,6 +37,11 @@ class MetadataHasAndBelongsManyTest < ActiveSupport::TestCase assert_includes author.posts.had_many, post assert_includes post.authors.had_many, author + + assert author.save + + assert_not_includes author.document.data['posts'], post.document.data['uuid'] + assert_not_includes post.document.data['authors'], author.document.data['uuid'] end test 'se puede cambiar la relación' do @@ -54,5 +63,14 @@ class MetadataHasAndBelongsManyTest < ActiveSupport::TestCase assert_includes author.posts.has_many, post2 assert_includes post2.authors.has_many, author + + assert post1.save + assert post2.save + + assert_not_includes author.document.data['posts'], post1.document.data['uuid'] + assert_not_includes post1.document.data['authors'], author.document.data['uuid'] + + assert_includes author.document.data['posts'], post2.document.data['uuid'] + assert_includes post2.document.data['authors'], author.document.data['uuid'] end end diff --git a/test/models/metadata_has_many_test.rb b/test/models/metadata_has_many_test.rb index 637c9425..38b4d46a 100644 --- a/test/models/metadata_has_many_test.rb +++ b/test/models/metadata_has_many_test.rb @@ -12,6 +12,11 @@ class MetadataHasManyTest < ActiveSupport::TestCase assert_equal post, reply.in_reply_to.belongs_to assert_includes post.posts.has_many, reply + + assert reply.save + + assert_equal reply.document.data['in_reply_to'], post.document.data['uuid'] + assert_includes post.document.data['posts'], reply.document.data['uuid'] end test 'se puede eliminar la relación' do @@ -19,12 +24,17 @@ class MetadataHasManyTest < ActiveSupport::TestCase post = @site.posts.create(layout: :post, title: SecureRandom.hex, posts: [reply.uuid.value]) post.posts.value = [] - post.save + assert post.save assert_not_equal post, reply.in_reply_to.belongs_to assert_equal post, reply.in_reply_to.belonged_to assert_nil reply.in_reply_to.belongs_to assert_not_includes post.posts.has_many, reply + + assert reply.save + + assert_nil reply.document.data['in_reply_to'] + assert_not_includes post.document.data['posts'], reply.document.data['uuid'] end test 'se puede cambiar la relación' do @@ -33,7 +43,7 @@ class MetadataHasManyTest < ActiveSupport::TestCase post2 = @site.posts.create(layout: :post, title: SecureRandom.hex) reply.in_reply_to.value = post2.uuid.value - reply.save + assert reply.save assert_not_equal post1, reply.in_reply_to.belongs_to assert_equal post1, reply.in_reply_to.belonged_to @@ -41,5 +51,12 @@ class MetadataHasManyTest < ActiveSupport::TestCase assert_equal post2, reply.in_reply_to.belongs_to assert_includes post2.posts.has_many, reply + + assert post1.save + assert post2.save + + assert_equal post2.document.data['uuid'], reply.document.data['in_reply_to'] + assert_includes post2.document.data['posts'], reply.document.data['uuid'] + assert_not_includes post1.document.data['posts'], reply.document.data['uuid'] end end From 64b59bfc4dc996f9a0e2391dc328cba9a13a9631 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 14 May 2021 17:41:44 -0300 Subject: [PATCH 12/13] si el post ya tiene fecha, no cambiarla --- app/models/metadata_document_date.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/metadata_document_date.rb b/app/models/metadata_document_date.rb index 82126ba3..312bb3af 100644 --- a/app/models/metadata_document_date.rb +++ b/app/models/metadata_document_date.rb @@ -8,6 +8,8 @@ class MetadataDocumentDate < MetadataTemplate end def value_from_document + return nil unless document.path + document.date end @@ -42,7 +44,7 @@ class MetadataDocumentDate < MetadataTemplate value_from_document || default_value end else - value_from_document || default_value + self[:value] || value_from_document || default_value end end From e19713f5df355150a99b07c2d20f3a5be2693815 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 17 May 2021 12:43:23 -0300 Subject: [PATCH 13/13] =?UTF-8?q?usar=20Post#new=3F=20que=20ya=20sabe=20cu?= =?UTF-8?q?=C3=A1ndo=20el=20documento=20no=20existe=20aun?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/metadata_document_date.rb | 2 +- app/models/post.rb | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/metadata_document_date.rb b/app/models/metadata_document_date.rb index 312bb3af..7512cbfb 100644 --- a/app/models/metadata_document_date.rb +++ b/app/models/metadata_document_date.rb @@ -8,7 +8,7 @@ class MetadataDocumentDate < MetadataTemplate end def value_from_document - return nil unless document.path + return nil if post.new? document.date end diff --git a/app/models/post.rb b/app/models/post.rb index 461733f9..13e8a919 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -49,9 +49,7 @@ class Post public_send(attr)&.value = args[attr] if args.key?(attr) end - # XXX: No usamos Post#read porque a esta altura todavía no sabemos - # nada del Document - document.read! if File.exist? document.path + document.read! unless new? end def inspect