mostrar el valor de la relación y actualizarla bidireccionalmente

closes #226
This commit is contained in:
f 2021-02-11 15:27:44 -03:00
parent 69159e6749
commit 5fdc170db0
4 changed files with 42 additions and 32 deletions

View file

@ -16,7 +16,7 @@ class MetadataBelongsTo < MetadataRelatedPosts
def validate def validate
super super
errors << I18n.t('metadata.belongs_to.missing_post') unless post_exists? errors << I18n.t('metadata.belongs_to.missing_post') if value.present? && !post_exists?
errors.empty? errors.empty?
end end
@ -30,24 +30,27 @@ class MetadataBelongsTo < MetadataRelatedPosts
# nos ahorra recursos en la búsqueda al cachear la información. En # nos ahorra recursos en la búsqueda al cachear la información. En
# una relación HABTM también vamos a hacer lo mismo. # una relación HABTM también vamos a hacer lo mismo.
def save def save
return super unless inverse? && !included? # Si no hay relación inversa, no hacer nada más
return super unless inverse?
# Evitar que se cambie el orden de la relación # Si estamos cambiando la relación, tenemos que eliminar la relación
belonged_to&.dig(inverse)&.value&.delete post.uuid.value if belonged_to != belongs_to # anterior
belonged_to[inverse].value.delete post.uuid.value if changed? && belonged_to.present?
belongs_to[inverse].value << post.uuid.value unless belongs_to[inverse].value.include? post.uuid.value # No duplicar las relaciones
belongs_to[inverse].value << post.uuid.value unless belongs_to.blank? || included?
true true
end end
# El Post actual está incluido en la relación inversa? # El Post actual está incluido en la relación inversa?
def included? def included?
belongs_to[inverse].value.include? post.uuid.value belongs_to[inverse].value.include?(post.uuid.value)
end end
# Hay una relación inversa y el artículo existe? # Hay una relación inversa y el artículo existe?
def inverse? def inverse?
inverse.present? && belongs_to.present? inverse.present?
end end
# El campo que es la relación inversa de este # El campo que es la relación inversa de este
@ -66,11 +69,11 @@ class MetadataBelongsTo < MetadataRelatedPosts
@belongs_to[value] ||= posts.find(value, uuid: true) @belongs_to[value] ||= posts.find(value, uuid: true)
end end
# El anterior artículo relacionado # El artículo relacionado anterior
def belonged_to def belonged_to
return if document.data[name.to_s].blank? return if value_was.blank?
@belonged_to ||= posts.find(document.data[name.to_s], uuid: true) @belonged_to ||= posts.find(value_was, uuid: true)
end end
def related_posts? def related_posts?
@ -88,6 +91,6 @@ class MetadataBelongsTo < MetadataRelatedPosts
end end
def post_exists? def post_exists?
!value.blank? && posts.find(sanitize(value), uuid: true) posts.find(sanitize(value), uuid: true).present?
end end
end end

View file

@ -7,8 +7,6 @@
MetadataTemplate = Struct.new(:site, :document, :name, :label, :type, MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
:value, :help, :required, :errors, :post, :value, :help, :required, :errors, :post,
:layout, keyword_init: true) do :layout, keyword_init: true) do
attr_reader :value_was
# Queremos que los artículos nuevos siempre cacheen, si usamos el UUID # Queremos que los artículos nuevos siempre cacheen, si usamos el UUID
# siempre vamos a obtener un item nuevo. # siempre vamos a obtener un item nuevo.
def cache_key def cache_key
@ -30,8 +28,20 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
self[:value] = new_value self[:value] = new_value
end end
# Siempre obtener el valor actual y solo obtenerlo del documento una
# vez.
def value_was
return @value_was if instance_variable_defined? '@value_was'
@value_was = value_from_document
end
def value_from_document
@value_from_document ||= document.data[name.to_s]
end
def changed? def changed?
!value_was.nil? && value_was != value value_was != value
end end
# Obtiene el valor del JekyllDocument # Obtiene el valor del JekyllDocument
@ -59,7 +69,7 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
# Valor actual o por defecto. Al memoizarlo podemos modificarlo # Valor actual o por defecto. Al memoizarlo podemos modificarlo
# usando otros métodos que el de asignación. # usando otros métodos que el de asignación.
def value def value
self[:value] ||= if (data = document.data[name.to_s]).present? self[:value] ||= if (data = value_from_document).present?
private? ? decrypt(data) : data private? ? decrypt(data) : data
else else
default_value default_value
@ -123,7 +133,7 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
end end
def related_methods def related_methods
raise NotImplementedError @related_methods ||= [].freeze
end end
# Determina si el campo es privado y debería ser cifrado # Determina si el campo es privado y debería ser cifrado

View file

@ -36,6 +36,8 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
post.usuaries << usuarie post.usuaries << usuarie
params[:post][:draft] = true if site.invitade? usuarie params[:post][:draft] = true if site.invitade? usuarie
# Es importante que el artículo se guarde primero y luego los
# relacionados.
commit(action: :updated, file: update_related_posts) if post.update(post_params) commit(action: :updated, file: update_related_posts) if post.update(post_params)
# Devolver el post aunque no se haya salvado para poder rescatar los # Devolver el post aunque no se haya salvado para poder rescatar los
@ -111,23 +113,24 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
# Actualiza los artículos relacionados según los métodos que los # Actualiza los artículos relacionados según los métodos que los
# metadatos declaren. # metadatos declaren.
# #
# Este método se asegura que todos los artículos se guardan una sola
# vez.
#
# @return [Array] Lista de archivos modificados # @return [Array] Lista de archivos modificados
def update_related_posts def update_related_posts
files = [post.path.absolute] posts = Set.new
post.attributes.each do |a| post.attributes.each do |a|
next unless post[a].related_posts?
post[a].related_methods.each do |m| post[a].related_methods.each do |m|
next unless post[a].respond_to? m next unless post[a].respond_to? m
# La respuesta puede ser una PostRelation también # La respuesta puede ser una PostRelation también
[post[a].public_send(m)].flatten.compact.uniq.each do |p| posts.merge [post[a].public_send(m)].flatten.compact
files << p.path.absolute if p.save(validate: false)
end
end end
end end
files posts.map do |p|
p.path.absolute if p.save(validate: false)
end.compact << post.path.absolute
end end
end end

View file

@ -1,13 +1,7 @@
.form-group .form-group
= label_tag "#{base}_#{attribute}", post_label_t(attribute, post: post) = label_tag "#{base}_#{attribute}", post_label_t(attribute, post: post)
= text_field base, attribute, value: metadata.value, = select_tag(plain_field_name_for(base, attribute),
dir: dir, lang: locale, list: id_for_datalist(attribute), options_for_select(metadata.values, metadata.value),
pattern: metadata.values.values.join('|'), autocomplete: 'off', **field_options(attribute, metadata), include_blank: true)
**field_options(attribute, metadata)
= render 'posts/attribute_feedback', = render 'posts/attribute_feedback',
post: post, attribute: attribute, metadata: metadata post: post, attribute: attribute, metadata: metadata
-# TODO: Ocultar el UUID
%datalist{ id: id_for_datalist(attribute) }
- metadata.values.each_pair do |key, value|
%option{ value: value }= key