diff --git a/app/models/metadata_belongs_to.rb b/app/models/metadata_belongs_to.rb index 2119547..2be7b3b 100644 --- a/app/models/metadata_belongs_to.rb +++ b/app/models/metadata_belongs_to.rb @@ -16,7 +16,7 @@ class MetadataBelongsTo < MetadataRelatedPosts def validate 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? end @@ -30,24 +30,27 @@ class MetadataBelongsTo < MetadataRelatedPosts # nos ahorra recursos en la búsqueda al cachear la información. En # una relación HABTM también vamos a hacer lo mismo. 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 - belonged_to&.dig(inverse)&.value&.delete post.uuid.value if belonged_to != belongs_to + # 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? - 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 end # El Post actual está incluido en la relación inversa? def included? - belongs_to[inverse].value.include? post.uuid.value + belongs_to[inverse].value.include?(post.uuid.value) end # Hay una relación inversa y el artículo existe? def inverse? - inverse.present? && belongs_to.present? + inverse.present? end # 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) end - # El anterior artículo relacionado + # El artículo relacionado anterior 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 def related_posts? @@ -88,6 +91,6 @@ class MetadataBelongsTo < MetadataRelatedPosts end def post_exists? - !value.blank? && posts.find(sanitize(value), uuid: true) + posts.find(sanitize(value), uuid: true).present? end end diff --git a/app/models/metadata_template.rb b/app/models/metadata_template.rb index 0d8669d..7fa9ba7 100644 --- a/app/models/metadata_template.rb +++ b/app/models/metadata_template.rb @@ -7,8 +7,6 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type, :value, :help, :required, :errors, :post, :layout, keyword_init: true) do - attr_reader :value_was - # Queremos que los artículos nuevos siempre cacheen, si usamos el UUID # siempre vamos a obtener un item nuevo. def cache_key @@ -30,8 +28,20 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type, self[:value] = new_value 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? - !value_was.nil? && value_was != value + value_was != value end # 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 # usando otros métodos que el de asignación. def value - self[:value] ||= if (data = document.data[name.to_s]).present? + self[:value] ||= if (data = value_from_document).present? private? ? decrypt(data) : data else default_value @@ -123,7 +133,7 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type, end def related_methods - raise NotImplementedError + @related_methods ||= [].freeze end # Determina si el campo es privado y debería ser cifrado diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 571588c..bc8def1 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -36,6 +36,8 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do post.usuaries << 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) # 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 # 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 - files = [post.path.absolute] + posts = Set.new post.attributes.each do |a| - next unless post[a].related_posts? - post[a].related_methods.each do |m| next unless post[a].respond_to? m # La respuesta puede ser una PostRelation también - [post[a].public_send(m)].flatten.compact.uniq.each do |p| - files << p.path.absolute if p.save(validate: false) - end + posts.merge [post[a].public_send(m)].flatten.compact end end - files + posts.map do |p| + p.path.absolute if p.save(validate: false) + end.compact << post.path.absolute end end diff --git a/app/views/posts/attributes/_belongs_to.haml b/app/views/posts/attributes/_belongs_to.haml index 0659474..2d17c3f 100644 --- a/app/views/posts/attributes/_belongs_to.haml +++ b/app/views/posts/attributes/_belongs_to.haml @@ -1,13 +1,7 @@ .form-group = label_tag "#{base}_#{attribute}", post_label_t(attribute, post: post) - = text_field base, attribute, value: metadata.value, - dir: dir, lang: locale, list: id_for_datalist(attribute), - pattern: metadata.values.values.join('|'), autocomplete: 'off', - **field_options(attribute, metadata) + = select_tag(plain_field_name_for(base, attribute), + options_for_select(metadata.values, metadata.value), + **field_options(attribute, metadata), include_blank: true) = render 'posts/attribute_feedback', 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