Merge branch 'belongs-to' into staging

This commit is contained in:
f 2021-05-14 17:22:44 -03:00
commit 0f92cf193f
17 changed files with 292 additions and 46 deletions

View file

@ -3,13 +3,6 @@
# Almacena el UUID de otro Post y actualiza el valor en el Post
# relacionado.
class MetadataBelongsTo < MetadataRelatedPosts
def value_was=(new_value)
@belongs_to = nil
@belonged_to = nil
super(new_value)
end
# TODO: Convertir algunos tipos de valores en módulos para poder
# implementar varios tipos de campo sin repetir código
#
@ -39,10 +32,14 @@ class MetadataBelongsTo < MetadataRelatedPosts
# 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?
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 << post.uuid.value unless belongs_to.blank? || included?
belongs_to[inverse].value = (belongs_to[inverse].value.dup << post.uuid.value) unless belongs_to.blank? || included?
true
end
@ -63,20 +60,13 @@ class MetadataBelongsTo < MetadataRelatedPosts
end
# El Post relacionado con este artículo
#
# XXX: Memoizamos usando el valor para tener el valor siempre
# actualizado.
def belongs_to
return if value.blank?
@belongs_to ||= posts.find(value, uuid: true)
posts.find(value, uuid: true) if value.present?
end
# El artículo relacionado anterior
def belonged_to
return if value_was.blank?
@belonged_to ||= posts.find(value_was, uuid: true)
posts.find(value_was, uuid: true) if value_was.present?
end
def related_posts?

View file

@ -18,6 +18,7 @@ class MetadataHasAndBelongsToMany < MetadataHasMany
#
# 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
@ -25,27 +26,21 @@ class MetadataHasAndBelongsToMany < MetadataHasMany
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&.delete post.uuid.value
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 << post.uuid.value
add[inverse].value = (add[inverse].value.dup << post.uuid.value)
end
true
end
private
# Igual que en MetadataRelatedPosts
# TODO: Mover a un módulo
def sanitize(uuid)
super(uuid.map do |u|
u.to_s.gsub(/[^a-f0-9\-]/i, '')
end)
end
end

View file

@ -6,14 +6,6 @@
# Localmente tenemos un Array de UUIDs. Remotamente tenemos una String
# apuntando a un Post, que se mantiene actualizado como el actual.
class MetadataHasMany < MetadataRelatedPosts
# Invalidar la relación anterior
def value_was=(new_value)
@had_many = nil
@has_many = nil
super(new_value)
end
def validate
super
@ -24,14 +16,16 @@ class MetadataHasMany < MetadataRelatedPosts
# Todos los Post relacionados
def has_many
@has_many ||= posts.where(uuid: value)
return default_value if value.blank?
posts.where(uuid: value)
end
# La relación anterior
def had_many
return [] if value_was.blank?
return default_value if value_was.blank?
@had_many ||= posts.where(uuid: value_was)
posts.where(uuid: value_was)
end
def inverse?

View file

@ -30,7 +30,7 @@ class MetadataRelatedPosts < MetadataArray
# Obtiene todos los posts y opcionalmente los filtra
def posts
@posts ||= site.posts(lang: lang).where(**filter)
site.posts(lang: lang).where(**filter)
end
def title(post)

View file

@ -238,12 +238,14 @@ class Post
template = public_send attr
unless template.front_matter?
body += "\n\n"
body += "\n\n" if body.present?
body += template.value
next
end
next if template.empty?
# Queremos mantener los Array en el resultado final para que
# siempre respondan a {% for %} en Liquid.
next if template.empty? && !template.value.is_a?(Array)
[attr.to_s, template.value]
end.compact.to_h
@ -301,6 +303,7 @@ class Post
end
# Vuelve a leer el post para tomar los cambios
document.reset
read
written?

View file

@ -93,8 +93,7 @@ class PostRelation < Array
def where(**args)
return self if args.empty?
@where ||= {}
@where[args.hash.to_s] ||= begin
begin
PostRelation.new(site: site, lang: lang).concat(select do |post|
result = args.map do |attr, value|
next unless post.attribute?(attr)

View file

@ -73,6 +73,15 @@ module Jekyll
Document.class_eval do
alias_method :read!, :read
def read; end
# Permitir restablecer el documento sin crear uno nuevo
def reset
@path = @extname = @has_yaml_header = @relative_path = nil
@basename_without_ext = @data = @basename = nil
@renderer = @url_placeholders = @url = nil
@to_liquid = @write_p = @excerpt_separator = @id = nil
@related_posts = @cleaned_relative_path = self.content = nil
end
end
# Prevenir la instanciación de Time

View file

@ -0,0 +1,2 @@
This is site where posts can have many authors and viceversa and posts
can be replies to others.

View file

@ -0,0 +1,2 @@
locales:
- en

View file

@ -0,0 +1,9 @@
---
title:
type: 'string'
required: true
posts:
type: 'has_and_belongs_to_many'
inverse: 'authors'
filter:
layout: 'post'

View file

@ -0,0 +1,23 @@
---
title:
type: 'string'
required: true
authors:
type: 'has_and_belongs_to_many'
inverse: 'posts'
filter:
layout: 'author'
posts:
type: 'has_many'
inverse: 'in_reply_to'
filter:
layout: 'post'
in_reply_to:
type: 'belongs_to'
inverse: 'posts'
filter:
layout: 'post'
recommended_posts:
type: 'related_posts'
filter:
layout: 'post'

View file

View file

@ -0,0 +1 @@
_en

View file

@ -0,0 +1,62 @@
# frozen_string_literal: true
require 'test_helper'
require_relative 'metadata_test'
class MetadataBelongsToTest < ActiveSupport::TestCase
include MetadataTest
test 'se pueden relacionar artículos' do
post = @site.posts.create(layout: :post, title: SecureRandom.hex)
reply = @site.posts.create(layout: :post, title: SecureRandom.hex, in_reply_to: post.uuid.value)
assert_equal post, reply.in_reply_to.belongs_to
assert_includes post.posts.has_many, reply
assert post.save
assert_equal reply.document.data['in_reply_to'], post.document.data['uuid']
assert_includes post.document.data['posts'], reply.document.data['uuid']
end
test 'se puede eliminar la relación' do
post = @site.posts.create(layout: :post, title: SecureRandom.hex)
reply = @site.posts.create(layout: :post, title: SecureRandom.hex, in_reply_to: post.uuid.value)
reply.in_reply_to.value = ''
assert reply.save
assert_not_equal post, reply.in_reply_to.belongs_to
assert_equal post, reply.in_reply_to.belonged_to
assert_nil reply.in_reply_to.belongs_to
assert_not_includes post.posts.has_many, reply
assert post.save
assert_nil reply.document.data['in_reply_to']
assert_not_includes post.document.data['posts'], reply.document.data['uuid']
end
test 'se puede cambiar la relación' do
post1 = @site.posts.create(layout: :post, title: SecureRandom.hex)
post2 = @site.posts.create(layout: :post, title: SecureRandom.hex)
reply = @site.posts.create(layout: :post, title: SecureRandom.hex, in_reply_to: post1.uuid.value)
reply.in_reply_to.value = post2.uuid.value
assert reply.save
assert_not_equal post1, reply.in_reply_to.belongs_to
assert_equal post1, reply.in_reply_to.belonged_to
assert_not_includes post1.posts.has_many, reply
assert_equal post2, reply.in_reply_to.belongs_to
assert_includes post2.posts.has_many, reply
assert post1.save
assert post2.save
assert_equal post2.document.data['uuid'], reply.document.data['in_reply_to']
assert_includes post2.document.data['posts'], reply.document.data['uuid']
assert_not_includes post1.document.data['posts'], reply.document.data['uuid']
end
end

View file

@ -0,0 +1,76 @@
# frozen_string_literal: true
require 'test_helper'
require_relative 'metadata_test'
class MetadataHasAndBelongsManyTest < ActiveSupport::TestCase
include MetadataTest
test 'se pueden relacionar artículos' do
author = @site.posts.create(layout: :author, title: SecureRandom.hex)
post = @site.posts.create(layout: :post, title: SecureRandom.hex)
post.authors.value = [author.uuid.value]
assert post.save
assert_includes author.posts.has_many, post
assert_includes post.authors.has_many, author
assert author.save
assert_includes author.document.data['posts'], post.document.data['uuid']
assert_includes post.document.data['authors'], author.document.data['uuid']
end
test 'se puede eliminar la relación' do
author = @site.posts.create(layout: :author, title: SecureRandom.hex)
post = @site.posts.create(layout: :post, title: SecureRandom.hex, authors: [author.uuid.value])
assert_includes post.authors.value, author.uuid.value
assert_includes author.posts.value, post.uuid.value
post.authors.value = []
assert post.save
assert_not_includes author.posts.has_many, post
assert_not_includes post.authors.has_many, author
assert_includes author.posts.had_many, post
assert_includes post.authors.had_many, author
assert author.save
assert_not_includes author.document.data['posts'], post.document.data['uuid']
assert_not_includes post.document.data['authors'], author.document.data['uuid']
end
test 'se puede cambiar la relación' do
author = @site.posts.create(layout: :author, title: SecureRandom.hex)
post1 = @site.posts.create(layout: :post, title: SecureRandom.hex, authors: [author.uuid.value])
post2 = @site.posts.create(layout: :post, title: SecureRandom.hex)
author.posts.value = [post2.uuid.value]
assert author.save
assert_not_includes author.posts.has_many, post1
assert_not_includes post1.authors.has_many, author
assert_includes author.posts.had_many, post1
assert_includes post1.authors.had_many, author
assert_not_includes author.posts.had_many, post2
assert_not_includes post2.authors.had_many, author
assert_includes author.posts.has_many, post2
assert_includes post2.authors.has_many, author
assert post1.save
assert post2.save
assert_not_includes author.document.data['posts'], post1.document.data['uuid']
assert_not_includes post1.document.data['authors'], author.document.data['uuid']
assert_includes author.document.data['posts'], post2.document.data['uuid']
assert_includes post2.document.data['authors'], author.document.data['uuid']
end
end

View file

@ -0,0 +1,62 @@
# frozen_string_literal: true
require 'test_helper'
require_relative 'metadata_test'
class MetadataHasManyTest < ActiveSupport::TestCase
include MetadataTest
test 'se pueden relacionar artículos' do
reply = @site.posts.create(layout: :post, title: SecureRandom.hex)
post = @site.posts.create(layout: :post, title: SecureRandom.hex, posts: [reply.uuid.value])
assert_equal post, reply.in_reply_to.belongs_to
assert_includes post.posts.has_many, reply
assert reply.save
assert_equal reply.document.data['in_reply_to'], post.document.data['uuid']
assert_includes post.document.data['posts'], reply.document.data['uuid']
end
test 'se puede eliminar la relación' do
reply = @site.posts.create(layout: :post, title: SecureRandom.hex)
post = @site.posts.create(layout: :post, title: SecureRandom.hex, posts: [reply.uuid.value])
post.posts.value = []
assert post.save
assert_not_equal post, reply.in_reply_to.belongs_to
assert_equal post, reply.in_reply_to.belonged_to
assert_nil reply.in_reply_to.belongs_to
assert_not_includes post.posts.has_many, reply
assert reply.save
assert_nil reply.document.data['in_reply_to']
assert_not_includes post.document.data['posts'], reply.document.data['uuid']
end
test 'se puede cambiar la relación' do
reply = @site.posts.create(layout: :post, title: SecureRandom.hex)
post1 = @site.posts.create(layout: :post, title: SecureRandom.hex, posts: [reply.uuid.value])
post2 = @site.posts.create(layout: :post, title: SecureRandom.hex)
reply.in_reply_to.value = post2.uuid.value
assert reply.save
assert_not_equal post1, reply.in_reply_to.belongs_to
assert_equal post1, reply.in_reply_to.belonged_to
assert_not_includes post1.posts.has_many, reply
assert_equal post2, reply.in_reply_to.belongs_to
assert_includes post2.posts.has_many, reply
assert post1.save
assert post2.save
assert_equal post2.document.data['uuid'], reply.document.data['in_reply_to']
assert_includes post2.document.data['posts'], reply.document.data['uuid']
assert_not_includes post1.document.data['posts'], reply.document.data['uuid']
end
end

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
module MetadataTest
extend ActiveSupport::Concern
included do
setup do
name = SecureRandom.hex
# TODO: Poder cambiar el nombre
FileUtils.cp_r(Rails.root.join('test', 'fixtures', 'site_with_relationships'), Rails.root.join('_sites', name))
@site = create :site, name: name
end
teardown do
@site&.destroy
end
end
end