mirror of
https://0xacab.org/sutty/sutty
synced 2025-03-14 17:58:16 +00:00
Merge branch 'issue-7537' into 'rails'
usar más la base de datos #7537 See merge request sutty/sutty!212
This commit is contained in:
commit
dd9d2a9d57
29 changed files with 410 additions and 574 deletions
|
@ -86,7 +86,9 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def site
|
||||
@site ||= find_site
|
||||
@site ||= find_site.tap do |s|
|
||||
s.reindex_changes!
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -55,9 +55,11 @@ class PostsController < ApplicationController
|
|||
|
||||
def new
|
||||
authorize Post
|
||||
@post = site.posts(lang: locale).build(layout: params[:layout])
|
||||
|
||||
breadcrumb I18n.t('loaf.breadcrumbs.posts.new', layout: @post.layout.humanized_name.downcase), ''
|
||||
layout = site.layouts[params[:layout].to_sym]
|
||||
@post = Post.build(locale: locale, layout: layout, site: site)
|
||||
|
||||
breadcrumb I18n.t('loaf.breadcrumbs.posts.new', layout: layout.humanized_name.downcase), ''
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -161,7 +163,7 @@ class PostsController < ApplicationController
|
|||
end
|
||||
|
||||
def post
|
||||
@post ||= site.posts(lang: locale).find(params[:post_id] || params[:id])
|
||||
@post ||= site.indexed_posts.find_by!(locale: locale, path: params[:post_id] || params[:id]).post
|
||||
end
|
||||
|
||||
# Recuerda el nombre del servicio de subida de archivos
|
||||
|
|
23
app/models/concerns/metadata/inverse_concern.rb
Normal file
23
app/models/concerns/metadata/inverse_concern.rb
Normal file
|
@ -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
|
|
@ -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
|
|
@ -34,15 +34,66 @@ class IndexedPost < ApplicationRecord
|
|||
scope :in_category, ->(category) { where("front_matter->'categories' ? :category", category: category.to_s) }
|
||||
scope :by_usuarie, ->(usuarie) { where("front_matter->'usuaries' @> :usuarie::jsonb", usuarie: usuarie.to_s) }
|
||||
|
||||
# Trae todos los valores únicos para un atributo
|
||||
#
|
||||
# @param :attribute [String,Symbol]
|
||||
# @return [Array]
|
||||
scope :everything_of, ->(attribute) do
|
||||
where('front_matter ? :attribute', attribute: attribute)
|
||||
.pluck(
|
||||
Arel.sql(
|
||||
ActiveRecord::Base::sanitize_sql(['front_matter -> :attribute', attribute: attribute])
|
||||
)
|
||||
)
|
||||
.flatten.uniq
|
||||
end
|
||||
|
||||
validates_presence_of :layout, :path, :locale
|
||||
|
||||
belongs_to :site
|
||||
|
||||
# Encuentra el post original
|
||||
# La ubicación del Post en el disco
|
||||
#
|
||||
# @return [nil,Post]
|
||||
def post
|
||||
return if post_id.blank?
|
||||
# @return [String]
|
||||
def full_path
|
||||
@full_path ||= File.join(site.path, "_#{locale}", "#{path}.markdown")
|
||||
end
|
||||
|
||||
@post ||= site.posts(lang: locale).find(post_id, uuid: true)
|
||||
# La colección
|
||||
#
|
||||
# @return [Jekyll::Collection]
|
||||
def collection
|
||||
site.collections[locale.to_s]
|
||||
end
|
||||
|
||||
# Obtiene el documento
|
||||
#
|
||||
# @return [Jekyll::Document]
|
||||
def document
|
||||
@document ||= Jekyll::Document.new(full_path, site: site.jekyll, collection: collection)
|
||||
end
|
||||
|
||||
# El Post
|
||||
#
|
||||
# @todo Decidir qué pasa si el archivo ya no existe
|
||||
# @return [Post]
|
||||
def post
|
||||
@post ||= Post.new(document: document, site: site, layout: schema)
|
||||
end
|
||||
|
||||
# Devuelve el esquema de datos
|
||||
#
|
||||
# @todo Renombrar
|
||||
# @return [Layout]
|
||||
def schema
|
||||
site.layouts[layout.to_sym]
|
||||
end
|
||||
|
||||
# Existe físicamente?
|
||||
#
|
||||
# @return [Boolean]
|
||||
def exist?
|
||||
File.exist?(full_path)
|
||||
end
|
||||
|
||||
# Convertir locale a direccionario de PG
|
||||
|
|
|
@ -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,82 +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
|
||||
def belongs_to
|
||||
posts.find(value, uuid: true) if value.present?
|
||||
end
|
||||
|
||||
# El artículo relacionado anterior
|
||||
def belonged_to
|
||||
posts.find(value_was, uuid: true) 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,55 +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
|
||||
def has_many
|
||||
return default_value if value.blank?
|
||||
|
||||
posts.where(uuid: value)
|
||||
end
|
||||
|
||||
# La relación anterior
|
||||
def had_many
|
||||
return default_value if value_was.blank?
|
||||
|
||||
posts.where(uuid: value_was)
|
||||
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
|
||||
|
|
3
app/models/metadata_has_one.rb
Normal file
3
app/models/metadata_has_one.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class MetadataHasOne < MetadataBelongsTo; end
|
|
@ -6,11 +6,16 @@ class MetadataLocales < MetadataHasAndBelongsToMany
|
|||
#
|
||||
# @return { lang: { title: uuid } }
|
||||
def values
|
||||
@values ||= site.locales.map do |locale|
|
||||
[locale, posts.where(lang: locale).map do |post|
|
||||
[title(post), post.uuid.value]
|
||||
end.to_h]
|
||||
end.to_h
|
||||
@values ||= other_locales.to_h do |other_locale|
|
||||
[
|
||||
other_locale,
|
||||
posts.where(locale: other_locale).pluck(:title, :layout, :post_id).to_h do |row|
|
||||
row.tap do |value|
|
||||
value[0] = "#{value[0]} (#{site.layouts[value.delete_at(1)].humanized_name})"
|
||||
end
|
||||
end
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
# Siempre hay una relación inversa
|
||||
|
@ -33,17 +38,13 @@ class MetadataLocales < MetadataHasAndBelongsToMany
|
|||
#
|
||||
# @return [Array]
|
||||
def other_locales
|
||||
site.locales.reject do |locale|
|
||||
locale == post.lang.value.to_sym
|
||||
end
|
||||
@other_locales ||= site.locales - [locale]
|
||||
end
|
||||
|
||||
# Obtiene todos los posts de los otros locales con el mismo layout
|
||||
#
|
||||
# @return [PostRelation]
|
||||
# @return [IndexedPost::ActiveRecord_AssociationRelation]
|
||||
def posts
|
||||
other_locales.map do |locale|
|
||||
site.posts(lang: locale).where(layout: post.layout.value)
|
||||
end.reduce(&:concat) || PostRelation.new(site: site, lang: 'any')
|
||||
site.indexed_posts(locale: other_locales).where(layout: post.layout.value).where.not(post_id: post.uuid.value)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class MetadataOrder < MetadataTemplate
|
|||
# El valor según la posición del post en la relación ordenada por
|
||||
# fecha, a fecha más alta, posición más alta
|
||||
def default_value
|
||||
super || site.posts(lang: lang).sort_by(:date).index(post)
|
||||
super || ((site.indexed_posts.where(locale: locale).first&.order || 0) + 1)
|
||||
end
|
||||
|
||||
def save
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
# Devuelve una lista de títulos y UUID de todos los posts del mismo
|
||||
# idioma que el actual, para usar con input-map.js
|
||||
class MetadataRelatedPosts < MetadataArray
|
||||
# Genera un Hash de { title | slug => uuid } y excluye el Post actual
|
||||
# Genera un Hash de { title (schema) => uuid } para usar en
|
||||
# options_for_select
|
||||
#
|
||||
# @return [Hash]
|
||||
def values
|
||||
@values ||= posts.map do |p|
|
||||
next if p.uuid.value == post.uuid.value
|
||||
|
||||
[title(p), p.uuid.value]
|
||||
end.compact.to_h
|
||||
@values ||= posts.pluck(:title, :created_at, :layout, :post_id).to_h do |row|
|
||||
row.tap do |value|
|
||||
value[0] = "#{value[0]} #{value.delete_at(1).strftime('%F')} (#{site.layouts[value.delete_at(1)].humanized_name})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Las relaciones nunca son privadas
|
||||
|
@ -23,23 +25,23 @@ class MetadataRelatedPosts < MetadataArray
|
|||
end
|
||||
|
||||
def indexable_values
|
||||
posts.where(uuid: value).map(&:title).map(&:value)
|
||||
posts.where(post_id: value).pluck(:title)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Obtiene todos los posts y opcionalmente los filtra
|
||||
# Obtiene todos los posts menos el actual y opcionalmente los filtra
|
||||
#
|
||||
# @return [IndexedPost::ActiveRecord_AssociationRelation]
|
||||
def posts
|
||||
site.posts(lang: lang).where(**filter)
|
||||
site.indexed_posts.where(locale: locale).where.not(post_id: post.uuid.value).where(filter)
|
||||
end
|
||||
|
||||
def title(post)
|
||||
"#{post&.title&.value || post&.slug&.value} #{post&.date&.value.strftime('%F')} (#{post.layout.humanized_name})"
|
||||
end
|
||||
|
||||
# Encuentra el filtro
|
||||
# Encuentra el filtro desde el esquema del atributo
|
||||
#
|
||||
# @return [Hash,nil]
|
||||
def filter
|
||||
layout.metadata.dig(name, 'filter')&.to_h&.symbolize_keys || {}
|
||||
layout.metadata.dig(name, 'filter')&.to_h&.symbolize_keys
|
||||
end
|
||||
|
||||
def sanitize(uuid)
|
||||
|
|
|
@ -62,20 +62,28 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
|
|||
end
|
||||
|
||||
# Trae el idioma actual del sitio o del panel
|
||||
#
|
||||
# @deprecated Empezar a usar locale
|
||||
# @return [String]
|
||||
def lang
|
||||
@lang ||= post&.lang&.value || I18n.locale
|
||||
@lang ||= post&.lang&.value || I18n.locale.to_s
|
||||
end
|
||||
|
||||
# El valor por defecto
|
||||
alias_method :locale, :lang
|
||||
|
||||
# El valor por defecto desde el esquema de datos
|
||||
#
|
||||
# @return [any]
|
||||
def default_value
|
||||
layout.metadata.dig(name, 'default', lang.to_s)
|
||||
layout.metadata.dig(name, 'default', lang)
|
||||
end
|
||||
|
||||
# Valores posibles, busca todos los valores actuales en otros
|
||||
# artículos del mismo sitio
|
||||
#
|
||||
# @return [Array]
|
||||
def values
|
||||
site.everything_of(name, lang: lang)
|
||||
site.indexed_posts.everything_of(name)
|
||||
end
|
||||
|
||||
# Valor actual o por defecto. Al memoizarlo podemos modificarlo
|
||||
|
|
|
@ -30,15 +30,44 @@ 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
|
||||
#
|
||||
# @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(args[:path], site: site.jekyll, collection: collection).tap do |doc|
|
||||
doc.data['date'] = Date.today.to_time if args[:path].blank?
|
||||
end
|
||||
end
|
||||
|
||||
args[:layout] = args[:site].layouts[args[:layout]] if args[:layout].is_a? Symbol
|
||||
|
||||
Post.new(**args)
|
||||
end
|
||||
end
|
||||
|
||||
# Redefinir el inicializador de OpenStruct
|
||||
#
|
||||
# @param site: [Site] el sitio en Sutty
|
||||
# @param document: [Jekyll::Document] el documento leído por Jekyll
|
||||
# @param layout: [Layout] la plantilla
|
||||
#
|
||||
# @param :site [Site] el sitio en Sutty
|
||||
# @param :document [Jekyll::Document] el documento leído por Jekyll
|
||||
# @param :layout [Layout] la plantilla
|
||||
def initialize(**args)
|
||||
default_attributes_missing(**args)
|
||||
|
||||
|
@ -289,8 +318,6 @@ class Post
|
|||
def destroy
|
||||
run_callbacks :destroy do
|
||||
FileUtils.rm_f path.absolute
|
||||
|
||||
site.delete_post self
|
||||
end
|
||||
end
|
||||
alias destroy! destroy
|
||||
|
|
|
@ -6,9 +6,11 @@ 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
|
||||
site.indexed_posts.find_by_post_id(uuid.value)
|
||||
end
|
||||
|
||||
# Devuelve una versión indexable del Post
|
||||
#
|
||||
|
@ -40,16 +42,19 @@ class Post
|
|||
|
||||
private
|
||||
|
||||
# Los metadatos que se almacenan como objetos JSON. Empezamos con
|
||||
# las categorías porque se usan para filtrar en el listado de
|
||||
# artículos.
|
||||
# Los metadatos que se almacenan como objetos JSON.
|
||||
#
|
||||
# @return [Hash]
|
||||
def indexable_front_matter
|
||||
{}.tap do |ifm|
|
||||
ifm[:usuaries] = usuaries.map(&:id)
|
||||
ifm[:draft] = attribute?(:draft) ? draft.value : false
|
||||
ifm[:categories] = categories.indexable_values if attribute? :categories
|
||||
|
||||
indexable_attributes.select do |attr|
|
||||
self[attr].front_matter?
|
||||
end.each do |attr|
|
||||
ifm[attr] = self[attr].try(:indexable_values)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# La relación de un sitio con sus artículos, esto nos permite generar
|
||||
# artículos como si estuviésemos usando ActiveRecord.
|
||||
class PostRelation < Array
|
||||
# No necesitamos cambiar el sitio
|
||||
attr_reader :site, :lang
|
||||
|
||||
def initialize(site:, lang:)
|
||||
@site = site
|
||||
@lang = lang
|
||||
# Proseguimos la inicialización sin valores por defecto
|
||||
super()
|
||||
end
|
||||
|
||||
# Genera un artículo nuevo con los parámetros que le pasemos y lo suma
|
||||
# al array
|
||||
def build(**args)
|
||||
args[:lang] = lang
|
||||
args[:document] ||= build_document(collection: args[:lang])
|
||||
args[:layout] = build_layout(args[:layout])
|
||||
|
||||
post = Post.new(site: site, **args)
|
||||
|
||||
self << post
|
||||
post
|
||||
end
|
||||
|
||||
def create(**args)
|
||||
post = build(**args)
|
||||
post.save
|
||||
post
|
||||
end
|
||||
|
||||
alias sort_by_generic sort_by
|
||||
alias sort_by_generic! sort_by!
|
||||
|
||||
# Permite ordenar los artículos por sus atributos
|
||||
#
|
||||
# XXX: Prestar atención cuando estamos mezclando artículos con
|
||||
# diferentes tipos de atributos.
|
||||
def sort_by(*attrs)
|
||||
sort_by_generic do |post|
|
||||
attrs.map do |attr|
|
||||
# TODO: detectar el tipo de atributo faltante y obtener el valor
|
||||
# por defecto para hacer la comparación
|
||||
if post.attributes.include? attr
|
||||
post.public_send(attr).value
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sort_by!(*attrs)
|
||||
replace sort_by(*attrs)
|
||||
end
|
||||
|
||||
alias find_generic find
|
||||
|
||||
# Encontrar un post por su UUID
|
||||
def find(id, uuid: false)
|
||||
find_generic do |p|
|
||||
if uuid
|
||||
p.uuid.value == id
|
||||
else
|
||||
p.id == id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Encuentra el primer post por el valor de los atributos
|
||||
#
|
||||
# @param [Hash]
|
||||
# @return [Post]
|
||||
def find_by(**args)
|
||||
find_generic do |post|
|
||||
args.map do |attr, value|
|
||||
post.attribute?(attr) &&
|
||||
post.public_send(attr).value == value
|
||||
end.all?
|
||||
end
|
||||
end
|
||||
|
||||
# Encuentra todos los Post que cumplan las condiciones
|
||||
#
|
||||
# TODO: Implementar caché
|
||||
#
|
||||
# @param [Hash] Mapa de atributo => valor. Valor puede ser un Array
|
||||
# de valores
|
||||
# @return [PostRelation]
|
||||
def where(**args)
|
||||
return self if args.empty?
|
||||
|
||||
begin
|
||||
PostRelation.new(site: site, lang: lang).concat(select do |post|
|
||||
result = args.map do |attr, value|
|
||||
next unless post.attribute?(attr)
|
||||
|
||||
attribute = post[attr]
|
||||
|
||||
# TODO: Si el valor del atributo también es un Array deberíamos
|
||||
# cruzar ambas.
|
||||
case value
|
||||
when Array then value.include? attribute.value
|
||||
else
|
||||
case attribute.value
|
||||
when Array then attribute.value.include? value
|
||||
else attribute.value == value
|
||||
end
|
||||
end
|
||||
end.compact
|
||||
|
||||
# Un Array vacío devuelve true para all?
|
||||
result.present? && result.all?
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
# Como Array#select devolviendo una relación
|
||||
#
|
||||
# @return [PostRelation]
|
||||
alias array_select select
|
||||
def select(&block)
|
||||
PostRelation.new(site: site, lang: lang).concat array_select(&block)
|
||||
end
|
||||
|
||||
# Intenta guardar todos y devuelve true si pudo
|
||||
def save_all(validate: true)
|
||||
map do |post|
|
||||
post.save(validate: validate)
|
||||
end.all?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_layout(layout = nil)
|
||||
return layout if layout.is_a? Layout
|
||||
|
||||
site.layouts[layout&.to_sym || :post]
|
||||
end
|
||||
|
||||
# Devuelve una colección Jekyll que hace pasar el documento
|
||||
def build_collection(label:)
|
||||
Jekyll::Collection.new(site.jekyll, label.to_s)
|
||||
end
|
||||
|
||||
# Un documento borrador con algunas propiedades por defecto
|
||||
def build_document(collection:)
|
||||
col = build_collection(label: collection)
|
||||
doc = Jekyll::Document.new('', site: site.jekyll, collection: col)
|
||||
doc.data['date'] = Date.today.to_time
|
||||
doc
|
||||
end
|
||||
end
|
|
@ -230,15 +230,10 @@ class Site < ApplicationRecord
|
|||
jekyll.data
|
||||
end
|
||||
|
||||
# Traer las colecciones. Todos los artículos van a estar dentro de
|
||||
# colecciones.
|
||||
# Trae las colecciones desde el sitio, sin leer su contenido
|
||||
#
|
||||
# @return [Hash]
|
||||
def collections
|
||||
unless @read
|
||||
jekyll.reader.read_collections
|
||||
|
||||
@read = true
|
||||
end
|
||||
|
||||
jekyll.collections
|
||||
end
|
||||
|
||||
|
@ -247,55 +242,6 @@ class Site < ApplicationRecord
|
|||
@config ||= Site::Config.new(self)
|
||||
end
|
||||
|
||||
# Los posts en el idioma actual o en uno en particular
|
||||
#
|
||||
# @param lang: [String|Symbol] traer los artículos de este idioma
|
||||
def posts(lang: nil)
|
||||
# Traemos los posts del idioma actual por defecto o el que haya
|
||||
lang ||= locales.include?(I18n.locale) ? I18n.locale : default_locale
|
||||
lang = lang.to_sym
|
||||
|
||||
# Crea un Struct dinámico con los valores de los locales, si
|
||||
# llegamos a pasar un idioma que no existe vamos a tener una
|
||||
# excepción NoMethodError
|
||||
@posts ||= Struct.new(*locales).new
|
||||
|
||||
return @posts[lang] unless @posts[lang].blank?
|
||||
|
||||
@posts[lang] = PostRelation.new site: self, lang: lang
|
||||
|
||||
# No fallar si no existe colección para este idioma
|
||||
# XXX: queremos fallar silenciosamente?
|
||||
(collections[lang.to_s]&.docs || []).each do |doc|
|
||||
layout = layouts[Post.find_layout(doc.path)]
|
||||
|
||||
@posts[lang].build(document: doc, layout: layout, lang: lang)
|
||||
rescue TypeError => e
|
||||
ExceptionNotifier.notify_exception(e, data: { site: name, site_id: id, path: doc.path })
|
||||
end
|
||||
|
||||
@posts[lang]
|
||||
end
|
||||
|
||||
# Todos los Post del sitio para poder buscar en todos.
|
||||
#
|
||||
# @return PostRelation
|
||||
def docs
|
||||
@docs ||= PostRelation.new(site: self, lang: :docs).push(locales.flat_map do |locale|
|
||||
posts(lang: locale)
|
||||
end).flatten!
|
||||
end
|
||||
|
||||
# Elimina un artículo de la colección
|
||||
def delete_post(post)
|
||||
lang = post.lang.value
|
||||
|
||||
collections[lang.to_s].docs.delete(post.document) &&
|
||||
posts(lang: lang).delete(post)
|
||||
|
||||
post
|
||||
end
|
||||
|
||||
# Obtiene todas las plantillas de artículos
|
||||
#
|
||||
# @return [Hash] { post: Layout }
|
||||
|
@ -334,24 +280,6 @@ class Site < ApplicationRecord
|
|||
jekyll.reader.read_layouts
|
||||
end
|
||||
|
||||
# Trae todos los valores disponibles para un campo
|
||||
#
|
||||
# TODO: Traer recursivamente, si el campo contiene Hash
|
||||
#
|
||||
# TODO: Mover a PostRelation#pluck
|
||||
#
|
||||
# @param attr [Symbol|String] El atributo a buscar
|
||||
# @return Array
|
||||
def everything_of(attr, lang: nil)
|
||||
Rails.cache.fetch("#{cache_key_with_version}/everything_of/#{lang}/#{attr}", expires_in: 1.hour) do
|
||||
attr = attr.to_sym
|
||||
|
||||
posts(lang: lang).flat_map do |p|
|
||||
p[attr].value if p.attribute? attr
|
||||
end.uniq.compact
|
||||
end
|
||||
end
|
||||
|
||||
# Poner en la cola de compilación
|
||||
def enqueue!
|
||||
update(status: 'enqueued') if waiting?
|
||||
|
@ -468,7 +396,6 @@ class Site < ApplicationRecord
|
|||
@incompatible_layouts = nil
|
||||
@jekyll = nil
|
||||
@config = nil
|
||||
@posts = nil
|
||||
@docs = nil
|
||||
end
|
||||
|
||||
|
|
|
@ -16,7 +16,12 @@ class Site
|
|||
|
||||
def index_posts!
|
||||
Site.transaction do
|
||||
docs.each(&:index!)
|
||||
jekyll.read
|
||||
jekyll.documents.each do |doc|
|
||||
doc.read!
|
||||
|
||||
Post.build(document: doc, site: self, layout: doc['layout'].to_sym).index!
|
||||
end
|
||||
|
||||
update(last_indexed_commit: repository.head_commit.oid)
|
||||
end
|
||||
|
@ -99,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
|
||||
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
# 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
|
||||
self.post = site.posts(lang: locale)
|
||||
.build(layout: layout)
|
||||
self.post = Post.build(site: site, locale: locale, layout: layout)
|
||||
post.usuaries << usuarie
|
||||
params[:post][:draft] = true if site.invitade? usuarie
|
||||
|
||||
|
@ -16,42 +15,13 @@ 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)
|
||||
|
||||
update_site_license!
|
||||
|
||||
# Devolver el post aunque no se haya salvado para poder rescatar los
|
||||
# errores
|
||||
post
|
||||
end
|
||||
|
||||
# Crear un post anónimo, con opciones más limitadas. No usamos post.
|
||||
def create_anonymous
|
||||
# XXX: Confiamos en el parámetro de idioma porque estamos
|
||||
# verificándolos en Site#posts
|
||||
self.post = site.posts(lang: locale)
|
||||
.build(layout: layout)
|
||||
# Los artículos anónimos siempre son borradores
|
||||
params[:draft] = true
|
||||
|
||||
commit(action: :created, add: [post.path.absolute]) if post.update(anon_post_params)
|
||||
post
|
||||
end
|
||||
|
||||
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)
|
||||
rm = []
|
||||
rm << post.path.value_was if post.path.changed?
|
||||
added_paths << post.path.value
|
||||
|
||||
# Es importante que el artículo se guarde primero y luego los
|
||||
# relacionados.
|
||||
commit(action: :updated, add: update_related_posts, rm: rm)
|
||||
# Recorrer todas las asociaciones y agregarse donde corresponda
|
||||
update_associations(post)
|
||||
|
||||
update_site_license!
|
||||
commit(action: :created, add: added_paths)
|
||||
end
|
||||
|
||||
# Devolver el post aunque no se haya salvado para poder rescatar los
|
||||
|
@ -59,6 +29,45 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
post
|
||||
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
|
||||
self.post = Post.build(site: site, locale: locale, layout: layouts)
|
||||
# Los artículos anónimos siempre son borradores
|
||||
params[:draft] = true
|
||||
|
||||
commit(action: :created, add: [post.path.absolute]) if post.update(anon_post_params)
|
||||
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
|
||||
|
||||
if post.update(post_params)
|
||||
# Eliminar ("mover") el archivo si cambió de ubicación.
|
||||
rm = []
|
||||
rm << post.path.value_was if post.path.changed?
|
||||
|
||||
added_paths << post.path.value
|
||||
|
||||
# Recorrer todas las asociaciones y agregarse donde corresponda
|
||||
update_associations(post)
|
||||
|
||||
commit(action: :updated, add: added_paths, rm: rm)
|
||||
end
|
||||
|
||||
# Devolver el post aunque no se haya salvado para poder rescatar los
|
||||
# errores
|
||||
post
|
||||
end
|
||||
|
||||
# @todo Eliminar relaciones
|
||||
def destroy
|
||||
post.destroy!
|
||||
|
||||
|
@ -74,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.posts(lang: locale).where(uuid: reorder.keys)
|
||||
posts = site.indexed_posts.where(locale: locale, post_id: reorder.keys).map(&:post)
|
||||
|
||||
files = posts.map do |post|
|
||||
next unless post.attribute? :order
|
||||
|
@ -90,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
|
||||
|
@ -118,36 +130,16 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
end
|
||||
end
|
||||
|
||||
# @return [Symbol]
|
||||
def locale
|
||||
params.dig(:post, :lang)&.to_sym || I18n.locale
|
||||
end
|
||||
|
||||
# @return [Layout]
|
||||
def layout
|
||||
params.dig(:post, :layout) || params[:layout]
|
||||
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
|
||||
site.layouts[
|
||||
(params.dig(:post, :layout) || params[:layout]).to_sym
|
||||
]
|
||||
end
|
||||
|
||||
# Si les usuaries modifican o crean una licencia, considerarla
|
||||
|
@ -157,4 +149,128 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
site.update licencia: Licencia.find_by_icons('custom')
|
||||
end
|
||||
end
|
||||
|
||||
# @return [Set<String>]
|
||||
def associated_posts_to_save
|
||||
@associated_posts_to_save ||= Set.new
|
||||
end
|
||||
|
||||
# @return [Set<String>]
|
||||
def added_paths
|
||||
@added_paths ||= Set.new
|
||||
end
|
||||
|
||||
# Recolectar campos asociados que no estén vacíos
|
||||
#
|
||||
# @param [Post]
|
||||
# @return [Array<Symbol>]
|
||||
def association_attributes(post)
|
||||
post.attributes.select do |attribute|
|
||||
post[attribute].try(:inverse?)
|
||||
end
|
||||
end
|
||||
|
||||
# @param :post_ids [Array<String>]
|
||||
# @return [Association]
|
||||
def associated_posts(post_ids)
|
||||
site.indexed_posts.where(post_id: post_ids).map(&:post)
|
||||
end
|
||||
|
||||
# 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]
|
||||
|
||||
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', 'locales'
|
||||
associated_posts(value_was - value).each do |remove_post|
|
||||
remove_relation_from(remove_post[inverse_attribute], post.uuid.value)
|
||||
end
|
||||
|
||||
associated_posts(value - value_was).each do |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|
|
||||
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_relation_from(remove_post[attribute], add_post.uuid.value)
|
||||
end
|
||||
|
||||
add_relation_to(add_post[inverse_attribute], post.uuid.value)
|
||||
end
|
||||
when 'belongs_to', 'has_one'
|
||||
if value_was.present?
|
||||
associated_posts(value_was).each do |remove_post|
|
||||
remove_relation_from(remove_post[inverse_attribute], post.uuid.value)
|
||||
end
|
||||
end
|
||||
|
||||
associated_posts(value).each do |add_post|
|
||||
add_relation_to(add_post[inverse_attribute], post.uuid.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
associated_posts_to_save.each do |associated_post|
|
||||
next unless associated_post.save(validate: false)
|
||||
|
||||
added_paths << associated_post.path.value
|
||||
end
|
||||
|
||||
nil
|
||||
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)
|
||||
when String then metadata.value = ''
|
||||
end
|
||||
|
||||
associated_posts_to_save << metadata.post
|
||||
nil
|
||||
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
|
||||
metadata.value << value
|
||||
metadata.value.uniq!
|
||||
when String then metadata.value = value
|
||||
end
|
||||
|
||||
associated_posts_to_save << metadata.post
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -82,16 +82,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
|
||||
|
@ -146,7 +141,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
|
|||
return true if site.licencia.custom?
|
||||
|
||||
with_all_locales do |locale|
|
||||
post = site.posts(lang: locale).find_by(layout: 'license')
|
||||
post = site.indexed_posts(locale: locale).find_by(layout: 'license')&.post
|
||||
|
||||
change_licencia(post: post) if post
|
||||
end.compact.map(&:valid?).all?
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
6
app/views/posts/attribute_ro/_has_one.haml
Normal file
6
app/views/posts/attribute_ro/_has_one.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td{ dir: dir, lang: locale }
|
||||
- p = site.indexed_posts.find_by_post_id(metadata.value)
|
||||
- if p
|
||||
= link_to p.title, site_post_path(site, p.post_id)
|
|
@ -2,10 +2,9 @@
|
|||
%th= post_label_t(attribute, post: post)
|
||||
%td
|
||||
%ul{ dir: dir, lang: locale }
|
||||
- metadata.value.each do |v|
|
||||
- p = site.posts(lang: post.lang.value).find(v, uuid: true)
|
||||
- site.indexed_posts.where(locale: post.lang.value, post_id: metadata.value).find_each do |p|
|
||||
-#
|
||||
XXX: Ignorar todos los posts no encontrados (ej: fueron
|
||||
borrados o el uuid cambió)
|
||||
- next unless p
|
||||
%li= link_to p.title.value, site_post_path(site, p.id)
|
||||
%li= link_to p.title, site_post_path(site, p.path)
|
||||
|
|
7
app/views/posts/attributes/_has_one.haml
Normal file
7
app/views/posts/attributes/_has_one.haml
Normal file
|
@ -0,0 +1,7 @@
|
|||
.form-group
|
||||
= label_tag "#{base}_#{attribute}", post_label_t(attribute, post: post)
|
||||
= select_tag(plain_field_name_for(base, attribute),
|
||||
options_for_select(metadata.values, metadata.value),
|
||||
**field_options(attribute, metadata), include_blank: t('.empty'))
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: attribute, metadata: metadata
|
|
@ -6,7 +6,6 @@
|
|||
post: post, attribute: attribute, metadata: metadata
|
||||
|
||||
- site.locales.each do |locale|
|
||||
- next if post.lang.value == locale
|
||||
- locale_t = t("locales.#{locale}.name", default: locale.to_s.humanize)
|
||||
- value = metadata.value.find do |v|
|
||||
- metadata.values[locale].values.include? v
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
10
db/migrate/20231026211607_deprecate_deploy_reindex.rb
Normal file
10
db/migrate/20231026211607_deprecate_deploy_reindex.rb
Normal file
|
@ -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
|
Loading…
Reference in a new issue