diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..b0bc1626 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 + +[Makefile] +indent_style = tab diff --git a/app/models/site/index.rb b/app/models/site/index.rb index c2c55ade..a0910194 100644 --- a/app/models/site/index.rb +++ b/app/models/site/index.rb @@ -1,20 +1,122 @@ # frozen_string_literal: true -# Indexa todos los artículos de un sitio -# -# TODO: Hacer opcional class Site + # Indexa todos los artículos de un sitio + # + # TODO: Hacer opcional module Index extend ActiveSupport::Concern included do has_many :indexed_posts, dependent: :destroy + MODIFIED_STATUSES = %i[added modified].freeze + DELETED_STATUSES = %i[deleted].freeze + LOCALE_FROM_PATH = /\A_/.freeze + def index_posts! Site.transaction do docs.each(&:index!) + + update(last_indexed_commit: repository.head_commit.oid) end end + + # Encuentra los artículos modificados entre dos commits y los + # reindexa. + def reindex_changes! + return unless reindexable? + + Site.transaction do + remove_deleted_posts! + reindex_modified_posts! + + update(last_indexed_commit: repository.head_commit.oid) + end + end + + # No hacer nada si el repositorio no cambió o no hubo cambios + # necesarios + def reindexable? + return false if last_indexed_commit.blank? + return false if last_indexed_commit == repository.head_commit.oid + + !indexable_posts.empty? + end + + private + + # Trae el último commit indexado desde el repositorio + # + # @return [Rugged::Commit] + def indexed_commit + @indexed_commit ||= repository.rugged.lookup(last_indexed_commit) + end + + # Calcula la diferencia entre el último commit indexado y el + # actual + # + # XXX: Esto no tiene en cuenta modificaciones en la historia como + # cambio de ramas, reverts y etc, solo asume que se mueve hacia + # adelante en la misma rama o las dos ramas están relacionadas. + # + # @return [Rugged::Diff] + def diff_with_head + @diff_with_head ||= indexed_commit.diff(repository.head_commit) + end + + # Obtiene todos los archivos a reindexar + # + # @return [Array] + def indexable_posts + @indexable_posts ||= + diff_with_head.each_delta.select do |delta| + locales.any? do |locale| + delta.old_file[:path].start_with? "_#{locale}/" + end + end + end + + # Elimina los artículos eliminados o que cambiaron de ubicación + # del índice + def remove_deleted_posts! + indexable_posts.select do |delta| + DELETED_STATUSES.include? delta.status + end.each do |delta| + locale, path = locale_and_path_from(delta.old_file[:path]) + + indexed_posts.destroy_by(locale: locale, path: path).tap do |destroyed_posts| + next unless destroyed_posts.empty? + + Rails.logger.info I18n.t('indexed_posts.deleted', site: name, path: path, records: destroyed_posts.count) + end + end + end + + # Reindexa artículos que cambiaron de ubicación, se agregaron + # o fueron modificados + def reindex_modified_posts! + indexable_posts.select do |delta| + MODIFIED_STATUSES.include? delta.status + end.each do |delta| + locale, path = locale_and_path_from(delta.new_file[:path]) + + site.posts(lang: locale).find(path).index! + end + end + + # Obtiene el idioma y la ruta del post a partir de la ubicación en + # el disco + # + # @return [Array] + def locale_and_path_from(path) + locale, path = path.split(File::SEPARATOR, 2) + + [ + locale.sub(LOCALE_FROM_PATH, ''), + File.basename(path, '.*') + ] + end end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 7ef07df8..6a07bb86 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -168,6 +168,7 @@ en: usuarie: User licencia: License design: Design + indexed_post: Indexed post attributes: usuarie: email: 'E-mail address' @@ -730,3 +731,5 @@ en: filter: filter: 'Filter' remove: 'Back' + indexed_posts: + deleted: "Deleted indexed post %{path} from %{site} (records: %{records})" diff --git a/config/locales/es.yml b/config/locales/es.yml index a62e18ad..3151812f 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -168,6 +168,7 @@ es: usuarie: Usuarie licencia: Licencia design: Diseño + indexed_post: Artículo indexado attributes: usuarie: email: 'Correo electrónico' @@ -738,3 +739,5 @@ es: filter: filter: 'Filtrar' remove: 'Volver' + indexed_posts: + deleted: "Eliminado artículo %{path} de %{site} (filas: %{records})" diff --git a/db/migrate/20230927153926_add_last_indexed_commit_to_sites.rb b/db/migrate/20230927153926_add_last_indexed_commit_to_sites.rb new file mode 100644 index 00000000..2d22cbd7 --- /dev/null +++ b/db/migrate/20230927153926_add_last_indexed_commit_to_sites.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Almacenar el último commit indexado +class AddLastIndexedCommitToSites < ActiveRecord::Migration[6.1] + def change + add_column :sites, :last_indexed_commit, :string, null: true + end +end