# frozen_string_literal: true class Site # Indexa todos los artículos de un sitio # # TODO: Hacer opcional module Index extend ActiveSupport::Concern included do # TODO: Debería ser un Job? after_create :index_posts! 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]) 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. # # Las rutas vienen en ASCII-9BIT desde Rugged, pero en realidad # son UTF-8 # # @return [Array] def locale_and_path_from(path) locale, path = path.force_encoding('utf-8').split(File::SEPARATOR, 2) [ locale.sub(LOCALE_FROM_PATH, ''), File.basename(path, '.*') ] end end end end