sutty/app/models/metadata_file.rb

171 lines
4.3 KiB
Ruby
Raw Normal View History

2019-11-07 16:08:14 +00:00
# frozen_string_literal: true
2020-08-05 15:29:11 +00:00
require 'filemagic'
2019-11-07 16:08:14 +00:00
# Define un campo de archivo
class MetadataFile < MetadataTemplate
# Una ruta vacía a la imagen con una descripción vacía
def default_value
super || { 'path' => nil, 'description' => nil }
2019-11-07 16:08:14 +00:00
end
def empty?
value == default_value
end
def validate
super
errors << I18n.t("metadata.#{type}.path_required") if path_missing?
errors << I18n.t("metadata.#{type}.no_file_for_description") if no_file_for_description?
2019-11-07 16:08:14 +00:00
errors.compact!
errors.empty?
end
# Determina si necesitamos la imagen pero no la tenemos
def path_missing?
required && !path?
2019-11-07 16:08:14 +00:00
end
# Determina si el archivo ya fue subido
def uploaded?
value['path'].is_a?(String)
2019-11-07 16:08:14 +00:00
end
# Determina si la ruta es opcional pero deja pasar si la ruta se
# especifica
def path_optional?
!required && !path?
2019-11-07 16:08:14 +00:00
end
# Asociar la imagen subida al sitio y obtener la ruta
#
# XXX: Si evitamos guardar cambios con changed? no tenemos forma de
# saber que un archivo subido manualmente se convirtió en
# un Attachment y cada vez que lo editemos vamos a subir una imagen
# repetida.
2019-11-07 16:08:14 +00:00
def save
value['description'] = sanitize value['description']
if path?
hardlink
value['path'] = relative_destination_path
else
value['path'] = nil
end
2019-11-07 16:08:14 +00:00
true
end
# Almacena el archivo en el sitio y lo devuelve o lo obtiene de la
# base de datos.
#
# Existen tres casos:
#
# * El archivo fue subido a través de HTTP
# * El archivo es una ruta que apunta a un archivo asociado al sitio
# * El archivo es una ruta a un archivo dentro del repositorio
2019-11-07 16:08:14 +00:00
#
# XXX: La última opción provoca archivos duplicados, pero es lo mejor
# que tenemos hasta que resolvamos https://0xacab.org/sutty/sutty/-/issues/213
#
# @return [ActiveStorage::Attachment]
2019-11-07 16:08:14 +00:00
def static_file
return unless path?
2019-11-14 17:27:24 +00:00
@static_file ||=
case value['path']
when ActionDispatch::Http::UploadedFile
site.static_files.last if site.static_files.attach(value['path'])
when String
if (blob = ActiveStorage::Blob.where(key: key_from_path).pluck(:id).first)
site.static_files.find_by(blob_id: blob)
elsif site.static_files.attach(io: path.open, filename: path.basename)
site.static_files.last
end
2019-11-07 16:08:14 +00:00
end
end
2021-02-16 21:08:42 +00:00
def key_from_path
path.dirname.basename.to_s
end
def path?
2021-02-16 21:08:42 +00:00
value['path'].present?
end
private
2020-08-05 15:29:11 +00:00
def filemagic
@filemagic ||= FileMagic.new(FileMagic::MAGIC_MIME)
end
# @return [Pathname]
2019-11-14 17:27:24 +00:00
def path
@path ||= Pathname.new(File.join(site.path, value['path']))
2019-11-14 17:27:24 +00:00
end
2020-08-05 15:29:11 +00:00
def file
return unless path?
@file ||=
case value['path']
when ActionDispatch::Http::UploadedFile then value['path'].tempfile.path
when String then File.join(site.path, value['path'])
end
2020-08-05 15:29:11 +00:00
end
2019-11-07 16:08:14 +00:00
# Hacemos un link duro para colocar el archivo dentro del repositorio
# y no duplicar el espacio que ocupan. Esto requiere que ambos
# directorios estén dentro del mismo punto de montaje.
#
# XXX: Asumimos que el archivo destino no existe porque siempre
# contiene una key única.
#
# @return [Boolean]
2019-11-07 16:08:14 +00:00
def hardlink
return if hardlink?
return if File.exist? destination_path
2021-02-23 22:16:58 +00:00
FileUtils.mkdir_p(File.dirname(destination_path))
FileUtils.ln(uploaded_path, destination_path).zero?
2019-11-07 16:08:14 +00:00
end
2021-02-23 22:16:58 +00:00
def hardlink?
File.stat(uploaded_path).ino == File.stat(destination_path).ino
rescue Errno::ENOENT
false
end
2019-11-07 16:08:14 +00:00
# Obtener la ruta al archivo
# https://stackoverflow.com/a/53908358
def uploaded_relative_path
ActiveStorage::Blob.service.path_for(static_file.key)
end
# @return [String]
2019-11-07 16:08:14 +00:00
def uploaded_path
Rails.root.join uploaded_relative_path
end
# La ruta del archivo mantiene el nombre original pero contiene el
# nombre interno y único del archivo para poder relacionarlo con el
# archivo subido en Sutty.
#
# @return [String]
2019-11-07 16:08:14 +00:00
def relative_destination_path
@relative_destination_path ||= File.join('public', static_file.key, static_file.filename.to_s)
2019-11-07 16:08:14 +00:00
end
# @return [String]
2019-11-07 16:08:14 +00:00
def destination_path
@destination_path ||= File.join(site.path, relative_destination_path)
2019-11-07 16:08:14 +00:00
end
# No hay archivo pero se lo describió
def no_file_for_description?
value['description'].present? && value['path'].blank?
end
2019-11-07 16:08:14 +00:00
end