mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-15 02:41:41 +00:00
ver posts y sitios
This commit is contained in:
parent
6a1240308d
commit
8c9ed4c083
7 changed files with 292 additions and 0 deletions
|
@ -1,7 +1,22 @@
|
|||
# Forma de ingreso a Sutty
|
||||
class ApplicationController < ActionController::Base
|
||||
protect_from_forgery with: :exception
|
||||
|
||||
# No tenemos índice de sutty, vamos directamente a ver el listado de
|
||||
# sitios
|
||||
def index
|
||||
redirect_to sites_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Encontrar un sitio por su ID
|
||||
# TODO volverlo más natural a rails
|
||||
def find_site
|
||||
id = params[:site_id] || params[:id]
|
||||
|
||||
current_user.sites.find do |s|
|
||||
s.id == id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
7
app/controllers/posts_controller.rb
Normal file
7
app/controllers/posts_controller.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class PostsController < ApplicationController
|
||||
before_action :authenticate!
|
||||
|
||||
def index
|
||||
@site = find_site
|
||||
end
|
||||
end
|
|
@ -1,6 +1,17 @@
|
|||
# Controlador de sitios
|
||||
class SitesController < ApplicationController
|
||||
before_action :authenticate!
|
||||
|
||||
# Ver un listado de sitios
|
||||
def index
|
||||
@sites = current_user.sites
|
||||
end
|
||||
|
||||
# No tenemos propiedades de un sitio aún, así que vamos al listado de
|
||||
# artículos
|
||||
def show
|
||||
site = find_site
|
||||
|
||||
redirect_to site_posts_path(site)
|
||||
end
|
||||
end
|
||||
|
|
169
app/models/post.rb
Normal file
169
app/models/post.rb
Normal file
|
@ -0,0 +1,169 @@
|
|||
# frozen_string_literal: true
|
||||
require 'jekyll/utils'
|
||||
|
||||
# Esta clase representa un post en un sitio jekyll e incluye métodos
|
||||
# para modificarlos y crear nuevos
|
||||
class Post
|
||||
attr_accessor :content, :front_matter
|
||||
attr_reader :post, :site, :errors
|
||||
|
||||
REJECT_FROM_DATA = %w[excerpt slug draft date ext].freeze
|
||||
|
||||
# Trabajar con posts. Si estamos creando uno nuevo, el **site** y
|
||||
# el **front_matter** son necesarios, sino, **site** y **post**.
|
||||
# XXX chequear que se den las condiciones
|
||||
def initialize(site:, post:, front_matter: {})
|
||||
@site = site
|
||||
@post = post
|
||||
@errors = []
|
||||
@front_matter = front_matter
|
||||
|
||||
new_post! unless @post
|
||||
load_data! unless new?
|
||||
end
|
||||
|
||||
# Si el archivo destino no existe, el post es nuevo
|
||||
def new?
|
||||
!File.exist? @post.path
|
||||
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
|
||||
alias :save! :save
|
||||
|
||||
def title
|
||||
get_metadata 'title'
|
||||
end
|
||||
|
||||
def date
|
||||
get_metadata 'date'
|
||||
end
|
||||
|
||||
def tags
|
||||
get_metadata 'tags'
|
||||
end
|
||||
|
||||
def categories
|
||||
get_metadata 'categories'
|
||||
end
|
||||
alias :category :categories
|
||||
|
||||
def path
|
||||
@post.try :path || File.join(@site.path, '_posts', basename_from_front_matter)
|
||||
end
|
||||
|
||||
def id
|
||||
get_metadata 'slug'
|
||||
end
|
||||
alias :slug :id
|
||||
alias :to_s :id
|
||||
|
||||
def basename_changed?
|
||||
@post.basename != basename_from_front_matter
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def new_post
|
||||
opts = { site: @site, collection: @site.posts }
|
||||
@post = ::Jekyll::Document.new(path, opts)
|
||||
@site.posts.docs << @post
|
||||
@site.posts.docs.sort!
|
||||
|
||||
@post
|
||||
end
|
||||
|
||||
# Obtiene metadatos
|
||||
def get_metadata(name)
|
||||
new? ? @front_matter.dig(name) : @post.data[name]
|
||||
end
|
||||
|
||||
# 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!
|
||||
return true unless basename_changed?
|
||||
|
||||
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)
|
||||
@site.posts.docs.delete @post
|
||||
|
||||
new_post
|
||||
end
|
||||
|
||||
# Obtiene el nombre del archivo a partir de los datos que le
|
||||
# pasemos
|
||||
def basename_from_front_matter
|
||||
date = @front_matter[:date].strftime('%F')
|
||||
title = ::Jekyll::Utils.slugify(@front_matter[:title])
|
||||
ext = @post.try :ext || 'markdown'
|
||||
|
||||
"#{date}-#{title}.#{ext}"
|
||||
end
|
||||
|
||||
# Toma los datos del front matter local y los mueve a los datos
|
||||
# que van a ir al post. Si hay símbolos se convierten a cadenas,
|
||||
# porque Jekyll trabaja con cadenas.
|
||||
def merge_data_with_front_matter!
|
||||
@data.merge! Hash[@front_matter.map { |k, v| [k.to_s, v] }]
|
||||
end
|
||||
|
||||
# Carga una copia de los datos del post original excluyendo datos
|
||||
# que no nos interesan
|
||||
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
|
76
app/models/site.rb
Normal file
76
app/models/site.rb
Normal file
|
@ -0,0 +1,76 @@
|
|||
# Un sitio es un directorio dentro del directorio de sitios de cada
|
||||
# Usuaria
|
||||
class Site
|
||||
|
||||
attr_accessor :jekyll
|
||||
|
||||
def initialize(jekyll:)
|
||||
@jekyll = jekyll
|
||||
end
|
||||
|
||||
# Obtener el nombre del sitio
|
||||
def name
|
||||
@name ||= @jekyll.config['source'].split('/').last
|
||||
end
|
||||
|
||||
# El id es el sitio sin puntos, para no confundir al routeador de
|
||||
# rails
|
||||
def id
|
||||
@id ||= name.tr('.', '-')
|
||||
end
|
||||
alias :to_s :id
|
||||
|
||||
# Los posts de este sitio
|
||||
def posts
|
||||
return @posts if @posts
|
||||
|
||||
@jekyll.read if @jekyll.posts.docs.empty?
|
||||
|
||||
# Los convertimos a una clase intermedia capaz de acceder a sus
|
||||
# datos y modificarlos
|
||||
@posts = @jekyll.posts.docs.map do |post|
|
||||
Post.new(site: @jekyll, post: post)
|
||||
end
|
||||
end
|
||||
|
||||
# El directorio donde se almacenan los sitios
|
||||
def self.site_path
|
||||
File.join(Rails.root, '_sites')
|
||||
end
|
||||
|
||||
# El directorio de los sitios de una usuaria
|
||||
#
|
||||
# Los sitios se organizan por usuaria, entonces los sitios que
|
||||
# administra pueden encontrarse directamente en su directorio.
|
||||
#
|
||||
# Si comparten gestión con otras usuarias, se hacen links simbólicos
|
||||
# entre sí.
|
||||
def self.site_path_for(usuaria)
|
||||
File.join(Site.site_path, usuaria.username)
|
||||
end
|
||||
|
||||
# Comprueba que el directorio parezca ser de jekyll
|
||||
def self.jekyll?(dir)
|
||||
File.directory?(dir) && File.exist?(File.join(dir, '_config.yml'))
|
||||
end
|
||||
|
||||
# Obtener todos los directorios de sitios asociados a esta usuaria
|
||||
def self.all_for(usuaria)
|
||||
@sites ||= Pathname.new(Site.site_path_for(usuaria))
|
||||
.children.map(&:expand_path).map(&:to_s).map do |j|
|
||||
|
||||
next unless Site.jekyll? j
|
||||
|
||||
Dir.chdir(j) do
|
||||
config = ::Jekyll.configuration('source' => Dir.pwd)
|
||||
|
||||
# No necesitamos cargar plugins en este momento
|
||||
%w[plugins gems].each do |unneeded|
|
||||
config[unneeded] = [] if config.key? unneeded
|
||||
end
|
||||
|
||||
Site.new(jekyll: ::Jekyll::Site.new(config))
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
end
|
8
app/views/posts/index.haml
Normal file
8
app/views/posts/index.haml
Normal file
|
@ -0,0 +1,8 @@
|
|||
%h1= @site.name
|
||||
|
||||
%table.table.table-condensed.table-striped
|
||||
%tbody
|
||||
- @site.posts.each do |post|
|
||||
%tr
|
||||
%td= link_to post.title, site_post_path(@site, post)
|
||||
%td= post.date
|
|
@ -1 +1,7 @@
|
|||
%h1= t('sites.title')
|
||||
|
||||
%table.table.table-striped.table-condensed
|
||||
%tbody
|
||||
- @sites.each do |site|
|
||||
%tr
|
||||
%td= link_to site.name, site_path(site)
|
||||
|
|
Loading…
Reference in a new issue