# frozen_string_literal: true require 'yaml' require 'jekyll/utils' module Sutty module Models # Un post class Post attr_accessor :content, :front_matter attr_reader :post, :site, :errors REJECT_FROM_DATA = %w[excerpt slug draft date ext].freeze def initialize(site, post = nil, front_matter = {}) @site = site @post = post @errors = [] @front_matter = front_matter load_data! end def new? !@post.is_a? ::Jekyll::Document end # Guarda los cambios def save merge_data_with_front_matter! clean_content! return unless write return unless detect_file_rename! # Vuelve a leer el post para tomar los cambios @post.read true end private # Cambiar el nombre del archivo si cambió el título o la fecha. # Como Jekyll no tiene métodos para modificar un Document, lo # engañamos eliminando la instancia de @post y recargando otra. def detect_file_rename! basename = basename_from_front_matter path = File.join(File.dirname(@post.path), basename) return true if basename == @post.basename if File.exist? path @errors << 'El archivo destino ya existe' return end FileUtils.mv @post.path, path replace_post! path end def replace_post!(path) opts = { site: @site, collection: @site.posts } @site.posts.docs.delete @post @post = ::Jekyll::Document.new(path, opts) @site.posts.docs << @post @site.posts.docs.sort! end def basename_from_front_matter date = @front_matter[:date].strftime('%F') title = ::Jekyll::Utils.slugify(@front_matter[:title]) "#{date}-#{title}.markdown" end def merge_data_with_front_matter! @data.merge! Hash[@front_matter.map { |k, v| [k.to_s, v] }] end def load_data! @data ||= @post.data.reject do |key, _| REJECT_FROM_DATA.include? key end end # Aplica limpiezas básicas del contenido def clean_content! @content = @content.delete("\r") end # Guarda los cambios en el archivo destino def write r = File.open(@post.path, File::RDWR | File::CREAT, 0o640) do |f| # Bloquear el archivo para que no sea accedido por otro # proceso u otra editora f.flock(File::LOCK_EX) # Empezar por el principio f.rewind # Escribir f.write(full_content) # Eliminar el resto f.flush f.truncate(f.pos) end return true if r.zero? @errors << 'No se pudo escribir el archivo' false end def full_content "#{@data.to_yaml}---\n\n#{@content}" end end end end