mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-15 07:31:42 +00:00
WIP: Subida de archivos
This commit is contained in:
parent
80457588dd
commit
0a1cbb9fc2
18 changed files with 265 additions and 43 deletions
4
Gemfile
4
Gemfile
|
@ -35,9 +35,6 @@ gem 'bcrypt', '~> 3.1.7'
|
||||||
# gem 'capistrano-rails', group: :development
|
# gem 'capistrano-rails', group: :development
|
||||||
|
|
||||||
gem 'bootstrap', '~> 4'
|
gem 'bootstrap', '~> 4'
|
||||||
gem 'carrierwave'
|
|
||||||
gem 'carrierwave-bombshelter'
|
|
||||||
gem 'carrierwave-i18n'
|
|
||||||
gem 'commonmarker'
|
gem 'commonmarker'
|
||||||
gem 'devise'
|
gem 'devise'
|
||||||
gem 'devise-i18n'
|
gem 'devise-i18n'
|
||||||
|
@ -48,6 +45,7 @@ gem 'font-awesome-rails'
|
||||||
gem 'friendly_id'
|
gem 'friendly_id'
|
||||||
gem 'hamlit-rails'
|
gem 'hamlit-rails'
|
||||||
gem 'hiredis'
|
gem 'hiredis'
|
||||||
|
gem 'image_processing'
|
||||||
gem 'jekyll'
|
gem 'jekyll'
|
||||||
gem 'jquery-rails'
|
gem 'jquery-rails'
|
||||||
gem 'mini_magick'
|
gem 'mini_magick'
|
||||||
|
|
22
Gemfile.lock
22
Gemfile.lock
|
@ -80,15 +80,6 @@ GEM
|
||||||
rack (>= 1.0.0)
|
rack (>= 1.0.0)
|
||||||
rack-test (>= 0.5.4)
|
rack-test (>= 0.5.4)
|
||||||
xpath (>= 2.0, < 4.0)
|
xpath (>= 2.0, < 4.0)
|
||||||
carrierwave (1.3.1)
|
|
||||||
activemodel (>= 4.0.0)
|
|
||||||
activesupport (>= 4.0.0)
|
|
||||||
mime-types (>= 1.16)
|
|
||||||
carrierwave-bombshelter (0.2.2)
|
|
||||||
activesupport (>= 3.2.0)
|
|
||||||
carrierwave
|
|
||||||
fastimage
|
|
||||||
carrierwave-i18n (0.2.0)
|
|
||||||
childprocess (0.9.0)
|
childprocess (0.9.0)
|
||||||
ffi (~> 1.0, >= 1.0.11)
|
ffi (~> 1.0, >= 1.0.11)
|
||||||
coderay (1.1.2)
|
coderay (1.1.2)
|
||||||
|
@ -132,7 +123,6 @@ GEM
|
||||||
factory_bot_rails (5.0.2)
|
factory_bot_rails (5.0.2)
|
||||||
factory_bot (~> 5.0.2)
|
factory_bot (~> 5.0.2)
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
fastimage (2.1.5)
|
|
||||||
ffi (1.11.1)
|
ffi (1.11.1)
|
||||||
font-awesome-rails (4.7.0.4)
|
font-awesome-rails (4.7.0.4)
|
||||||
railties (>= 3.2, < 6.0)
|
railties (>= 3.2, < 6.0)
|
||||||
|
@ -165,6 +155,9 @@ GEM
|
||||||
http_parser.rb (0.6.0)
|
http_parser.rb (0.6.0)
|
||||||
i18n (0.9.5)
|
i18n (0.9.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
|
image_processing (1.9.1)
|
||||||
|
mini_magick (>= 4.9.3, < 5)
|
||||||
|
ruby-vips (>= 2.0.13, < 3)
|
||||||
jaro_winkler (1.5.3)
|
jaro_winkler (1.5.3)
|
||||||
jbuilder (2.8.0)
|
jbuilder (2.8.0)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
|
@ -209,9 +202,6 @@ GEM
|
||||||
mimemagic (~> 0.3.2)
|
mimemagic (~> 0.3.2)
|
||||||
mercenary (0.3.6)
|
mercenary (0.3.6)
|
||||||
method_source (0.9.2)
|
method_source (0.9.2)
|
||||||
mime-types (3.2.2)
|
|
||||||
mime-types-data (~> 3.2015)
|
|
||||||
mime-types-data (3.2018.0812)
|
|
||||||
mimemagic (0.3.3)
|
mimemagic (0.3.3)
|
||||||
mini_magick (4.9.4)
|
mini_magick (4.9.4)
|
||||||
mini_mime (1.0.1)
|
mini_mime (1.0.1)
|
||||||
|
@ -319,6 +309,8 @@ GEM
|
||||||
ruby-enum (0.7.2)
|
ruby-enum (0.7.2)
|
||||||
i18n
|
i18n
|
||||||
ruby-progressbar (1.10.1)
|
ruby-progressbar (1.10.1)
|
||||||
|
ruby-vips (2.0.14)
|
||||||
|
ffi (~> 1.9)
|
||||||
ruby_dep (1.5.0)
|
ruby_dep (1.5.0)
|
||||||
rubyzip (1.2.2)
|
rubyzip (1.2.2)
|
||||||
rugged (0.28.2)
|
rugged (0.28.2)
|
||||||
|
@ -416,9 +408,6 @@ DEPENDENCIES
|
||||||
capistrano-rails
|
capistrano-rails
|
||||||
capistrano-rbenv
|
capistrano-rbenv
|
||||||
capybara (~> 2.13)
|
capybara (~> 2.13)
|
||||||
carrierwave
|
|
||||||
carrierwave-bombshelter
|
|
||||||
carrierwave-i18n
|
|
||||||
commonmarker
|
commonmarker
|
||||||
database_cleaner
|
database_cleaner
|
||||||
devise
|
devise
|
||||||
|
@ -434,6 +423,7 @@ DEPENDENCIES
|
||||||
haml-lint
|
haml-lint
|
||||||
hamlit-rails
|
hamlit-rails
|
||||||
hiredis
|
hiredis
|
||||||
|
image_processing
|
||||||
jbuilder (~> 2.5)
|
jbuilder (~> 2.5)
|
||||||
jekyll
|
jekyll
|
||||||
jquery-rails
|
jquery-rails
|
||||||
|
|
11
app/assets/javascripts/image_preview.js
Normal file
11
app/assets/javascripts/image_preview.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
$(document).on('turbolinks:load', function() {
|
||||||
|
$('input[type=file]').on('change', function(event) {
|
||||||
|
if (event.target.files.length == 0) return;
|
||||||
|
|
||||||
|
var input = $(event.target);
|
||||||
|
var preview = $(`#${input.data('preview')}`);
|
||||||
|
|
||||||
|
preview.attr('src',
|
||||||
|
window.URL.createObjectURL(event.target.files[0]));
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,6 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Define un campo de imagen
|
# Define un campo de imagen
|
||||||
|
# TODO: Validar que sea una imagen
|
||||||
class MetadataImage < MetadataTemplate
|
class MetadataImage < MetadataTemplate
|
||||||
# Una ruta vacía a la imagen con una descripción vacía
|
# Una ruta vacía a la imagen con una descripción vacía
|
||||||
def default_value
|
def default_value
|
||||||
|
@ -11,7 +12,73 @@ class MetadataImage < MetadataTemplate
|
||||||
value == default_value
|
value == default_value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Asociar la imagen subida al sitio y obtener la ruta
|
||||||
|
# rubocop:disable Metrics/CyclomaticComplexity
|
||||||
|
def save
|
||||||
|
return true if !required && value['path'].blank?
|
||||||
|
return false if required && value['path'].blank?
|
||||||
|
return true if value['path'].is_a? String
|
||||||
|
return false unless hardlink.zero?
|
||||||
|
|
||||||
|
# Modificar el valor actual
|
||||||
|
value['path'] = relative_destination_path
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
# rubocop:enable Metrics/CyclomaticComplexity
|
||||||
|
|
||||||
def to_param
|
def to_param
|
||||||
{ name => %i[description path] }
|
{ name => %i[description path] }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Almacena el archivo en el sitio y lo devuelve
|
||||||
|
# XXX: ActiveStorage devuelve un Array al guardar
|
||||||
|
#
|
||||||
|
# @return ActiveStorage::Attachment
|
||||||
|
def static_file
|
||||||
|
if value['path'].is_a? String
|
||||||
|
blob = ActiveStorage::Blob.find_by(key: key_from_path)
|
||||||
|
@static_file ||= site.static_files.find_by(blob_id: blob.id)
|
||||||
|
else
|
||||||
|
@static_file ||= site.static_files.attach(value['path']).first
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def key_from_path
|
||||||
|
# XXX: No podemos usar self#extension porque en este punto todavía
|
||||||
|
# no sabemos el static_file
|
||||||
|
File.basename(value['path'], '.*')
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
def hardlink
|
||||||
|
FileUtils.mkdir_p File.dirname(destination_path)
|
||||||
|
FileUtils.ln uploaded_path, destination_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def extension
|
||||||
|
static_file.blob.content_type.split('/').last
|
||||||
|
end
|
||||||
|
|
||||||
|
# Obtener la ruta al archivo
|
||||||
|
# https://stackoverflow.com/a/53908358
|
||||||
|
def uploaded_relative_path
|
||||||
|
ActiveStorage::Blob.service.path_for(static_file.key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def uploaded_path
|
||||||
|
Rails.root.join uploaded_relative_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def relative_destination_path
|
||||||
|
File.join('public', [static_file.key, extension].join('.'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def destination_path
|
||||||
|
File.join(site.path, relative_destination_path)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -55,6 +55,12 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
|
||||||
type == 'array'
|
type == 'array'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# En caso de que algún campo necesite realizar acciones antes de ser
|
||||||
|
# guardado
|
||||||
|
def save
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Si es obligatorio no puede estar vacío
|
# Si es obligatorio no puede estar vacío
|
||||||
|
|
|
@ -149,10 +149,12 @@ class Post < OpenStruct
|
||||||
alias destroy! destroy
|
alias destroy! destroy
|
||||||
|
|
||||||
# Guarda los cambios
|
# Guarda los cambios
|
||||||
|
# rubocop:disable Metrics/CyclomaticComplexity
|
||||||
def save
|
def save
|
||||||
return false unless valid?
|
return false unless valid?
|
||||||
# Salir si tenemos que cambiar el nombre del archivo y no pudimos
|
# Salir si tenemos que cambiar el nombre del archivo y no pudimos
|
||||||
return false if !new? && path_changed? && !update_path!
|
return false if !new? && path_changed? && !update_path!
|
||||||
|
return false unless save_attributes!
|
||||||
return false unless write
|
return false unless write
|
||||||
|
|
||||||
# Vuelve a leer el post para tomar los cambios
|
# Vuelve a leer el post para tomar los cambios
|
||||||
|
@ -160,6 +162,7 @@ class Post < OpenStruct
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics/CyclomaticComplexity
|
||||||
alias save! save
|
alias save! save
|
||||||
|
|
||||||
# Lee el documento a menos que estemos trabajando con un documento en
|
# Lee el documento a menos que estemos trabajando con un documento en
|
||||||
|
@ -282,6 +285,13 @@ class Post < OpenStruct
|
||||||
type: :path, post: self,
|
type: :path, post: self,
|
||||||
required: true)
|
required: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Ejecuta la acción de guardado en cada atributo
|
||||||
|
def save_attributes!
|
||||||
|
attributes.map do |attr|
|
||||||
|
send(attr).save
|
||||||
|
end.all?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
# rubocop:enable Metrics/ClassLength
|
# rubocop:enable Metrics/ClassLength
|
||||||
# rubocop:enable Style/MethodMissingSuper
|
# rubocop:enable Style/MethodMissingSuper
|
||||||
|
|
|
@ -25,6 +25,9 @@ class Site < ApplicationRecord
|
||||||
has_many :invitades, -> { where('roles.rol = ?', 'invitade') },
|
has_many :invitades, -> { where('roles.rol = ?', 'invitade') },
|
||||||
through: :roles, source: :usuarie
|
through: :roles, source: :usuarie
|
||||||
|
|
||||||
|
# Mantenemos el nombre que les da Jekyll
|
||||||
|
has_many_attached :static_files
|
||||||
|
|
||||||
# Clonar el directorio de esqueleto antes de crear el sitio
|
# Clonar el directorio de esqueleto antes de crear el sitio
|
||||||
before_create :clone_skel!
|
before_create :clone_skel!
|
||||||
# Elimina el directorio al destruir un sitio
|
# Elimina el directorio al destruir un sitio
|
||||||
|
|
|
@ -3,5 +3,7 @@
|
||||||
%td
|
%td
|
||||||
- if metadata.value['path'].present?
|
- if metadata.value['path'].present?
|
||||||
%figure
|
%figure
|
||||||
= image_tag metadata.value['path'], alt: metadata.value['description']
|
= image_tag url_for(metadata.static_file),
|
||||||
|
alt: metadata.value['description'],
|
||||||
|
class: 'img-fluid'
|
||||||
%figcaption= metadata.value['description']
|
%figcaption= metadata.value['description']
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
.form-group{ class: invalid(post, attribute) }
|
.form-group{ class: invalid(post, attribute) }
|
||||||
- if metadata.value['path'].present?
|
- if metadata.value['path'].present?
|
||||||
= image_tag metadata.value[:path], alt: metadata.value['description']
|
= image_tag url_for(metadata.static_file),
|
||||||
|
alt: metadata.value['description'],
|
||||||
|
class: 'img-fluid',
|
||||||
|
id: "#{attribute}-preview"
|
||||||
|
|
||||||
|
-#
|
||||||
|
Mantener el valor si no enviamos ninguna imagen
|
||||||
|
TODO: Agregar checkbox para eliminarla
|
||||||
|
= hidden_field_tag "post[#{attribute}][path]", metadata.value['path']
|
||||||
|
|
||||||
.custom-file
|
.custom-file
|
||||||
= file_field(*field_name_for('post', attribute, :path),
|
= file_field(*field_name_for('post', attribute, :path),
|
||||||
**field_options(attribute, metadata), class: 'custom-file-input')
|
**field_options(attribute, metadata),
|
||||||
|
class: 'custom-file-input', accept: 'image/*',
|
||||||
|
data: { preview: "#{attribute}-preview" })
|
||||||
= label_tag "post_#{attribute}_path",
|
= label_tag "post_#{attribute}_path",
|
||||||
post_label_t(attribute, :path, post: post), class: 'custom-file-label'
|
post_label_t(attribute, :path, post: post), class: 'custom-file-label'
|
||||||
= render 'posts/attribute_feedback',
|
= render 'posts/attribute_feedback',
|
||||||
|
|
|
@ -13,6 +13,7 @@ require 'action_view/railtie'
|
||||||
# require "action_cable/engine"
|
# require "action_cable/engine"
|
||||||
require 'sprockets/railtie'
|
require 'sprockets/railtie'
|
||||||
require 'rails/test_unit/railtie'
|
require 'rails/test_unit/railtie'
|
||||||
|
require 'active_storage/engine'
|
||||||
|
|
||||||
# Require the gems listed in Gemfile, including any gems
|
# Require the gems listed in Gemfile, including any gems
|
||||||
# you've limited to :test, :development, or :production.
|
# you've limited to :test, :development, or :production.
|
||||||
|
@ -32,5 +33,7 @@ module Sutty
|
||||||
config.action_dispatch
|
config.action_dispatch
|
||||||
.rescue_responses['Pundit::NotAuthorizedError'] = :forbidden
|
.rescue_responses['Pundit::NotAuthorizedError'] = :forbidden
|
||||||
config.active_record.sqlite3.represent_boolean_as_integer = true
|
config.active_record.sqlite3.represent_boolean_as_integer = true
|
||||||
|
|
||||||
|
config.active_storage.variant_processor = :vips
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,15 +57,11 @@ Rails.application.configure do
|
||||||
# listen gem.
|
# listen gem.
|
||||||
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
||||||
|
|
||||||
CarrierWave.configure do |config|
|
|
||||||
config.ignore_integrity_errors = false
|
|
||||||
config.ignore_processing_errors = false
|
|
||||||
config.ignore_download_errors = false
|
|
||||||
end
|
|
||||||
|
|
||||||
config.action_mailer.perform_caching = false
|
config.action_mailer.perform_caching = false
|
||||||
config.action_mailer.delivery_method = :letter_opener
|
config.action_mailer.delivery_method = :letter_opener
|
||||||
config.action_mailer.perform_deliveries = true
|
config.action_mailer.perform_deliveries = true
|
||||||
config.action_mailer.default_url_options = { host: 'localhost',
|
config.action_mailer.default_url_options = { host: 'localhost',
|
||||||
port: 3000 }
|
port: 3000 }
|
||||||
|
|
||||||
|
config.active_storage.service = :local
|
||||||
end
|
end
|
||||||
|
|
|
@ -107,4 +107,5 @@ Rails.application.configure do
|
||||||
sender_address: ENV['DEFAULT_FROM'],
|
sender_address: ENV['DEFAULT_FROM'],
|
||||||
exception_recipients: ENV['EXCEPTION_TO']
|
exception_recipients: ENV['EXCEPTION_TO']
|
||||||
}
|
}
|
||||||
|
config.active_storage.service = :local
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,4 +45,5 @@ Rails.application.configure do
|
||||||
|
|
||||||
# Raises error for missing translations
|
# Raises error for missing translations
|
||||||
# config.action_view.raise_on_missing_translations = true
|
# config.action_view.raise_on_missing_translations = true
|
||||||
|
config.active_storage.service = :test
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
CarrierWave.configure do |config|
|
|
||||||
config.permissions = 0o640
|
|
||||||
config.directory_permissions = 0o750
|
|
||||||
config.storage = :file
|
|
||||||
end
|
|
15
config/storage.yml
Normal file
15
config/storage.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# XXX: Dirty hack
|
||||||
|
#
|
||||||
|
# Almacenamos los archivos dentro del directorio de sitios para poder
|
||||||
|
# hacer links duros. Si montamos en Docker los dos directorios por
|
||||||
|
# separado, aunque en el servidor estén los directorios dentro del mismo
|
||||||
|
# sistema de archivos, para Docker son dos distintos.
|
||||||
|
#
|
||||||
|
# Ver app/models/metadata_image.rb
|
||||||
|
local:
|
||||||
|
service: Disk
|
||||||
|
root: <%= Rails.root.join('_sites/_storage') %>
|
||||||
|
|
||||||
|
test:
|
||||||
|
service: Disk
|
||||||
|
root: <%= Rails.root.join('tmp/storage') %>
|
|
@ -0,0 +1,29 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# This migration comes from active_storage (originally 20170806125915)
|
||||||
|
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
create_table :active_storage_blobs do |t|
|
||||||
|
t.string :key, null: false
|
||||||
|
t.string :filename, null: false
|
||||||
|
t.string :content_type
|
||||||
|
t.text :metadata
|
||||||
|
t.bigint :byte_size, null: false
|
||||||
|
t.string :checksum, null: false
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
|
||||||
|
t.index [:key], unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :active_storage_attachments do |t|
|
||||||
|
t.string :name, null: false
|
||||||
|
t.references :record, null: false, polymorphic: true, index: false
|
||||||
|
t.references :blob, null: false
|
||||||
|
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
|
||||||
|
t.index %i[record_type record_id name blob_id], name: 'index_active_storage_attachments_uniqueness', unique: true
|
||||||
|
t.foreign_key :active_storage_blobs, column: :blob_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
db/schema.rb
23
db/schema.rb
|
@ -12,7 +12,28 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20_190_730_211_756) do
|
ActiveRecord::Schema.define(version: 20_190_820_225_238) do
|
||||||
|
create_table 'active_storage_attachments', force: :cascade do |t|
|
||||||
|
t.string 'name', null: false
|
||||||
|
t.string 'record_type', null: false
|
||||||
|
t.integer 'record_id', null: false
|
||||||
|
t.integer 'blob_id', null: false
|
||||||
|
t.datetime 'created_at', null: false
|
||||||
|
t.index ['blob_id'], name: 'index_active_storage_attachments_on_blob_id'
|
||||||
|
t.index %w[record_type record_id name blob_id], name: 'index_active_storage_attachments_uniqueness', unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table 'active_storage_blobs', force: :cascade do |t|
|
||||||
|
t.string 'key', null: false
|
||||||
|
t.string 'filename', null: false
|
||||||
|
t.string 'content_type'
|
||||||
|
t.text 'metadata'
|
||||||
|
t.bigint 'byte_size', null: false
|
||||||
|
t.string 'checksum', null: false
|
||||||
|
t.datetime 'created_at', null: false
|
||||||
|
t.index ['key'], name: 'index_active_storage_blobs_on_key', unique: true
|
||||||
|
end
|
||||||
|
|
||||||
create_table 'build_stats', force: :cascade do |t|
|
create_table 'build_stats', force: :cascade do |t|
|
||||||
t.datetime 'created_at', null: false
|
t.datetime 'created_at', null: false
|
||||||
t.datetime 'updated_at', null: false
|
t.datetime 'updated_at', null: false
|
||||||
|
|
|
@ -5,17 +5,83 @@ plantilla:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
cover: image
|
cover:
|
||||||
attachment: document
|
type: image
|
||||||
video: video
|
attachment:
|
||||||
audio: audio
|
type: document
|
||||||
|
video:
|
||||||
|
type: video
|
||||||
|
audio:
|
||||||
|
type: audio
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
Donde `image` solo admite imágenes, `document` cualquier tipo de documento y
|
Donde `image` solo admite imágenes, `document` cualquier tipo de
|
||||||
`video` y `audio` medios.
|
documento y `video` y `audio` medios.
|
||||||
|
|
||||||
Al subir los archivos, se guardan en el directorio `public/` en la raíz
|
Al subir los archivos, se guardan en el directorio `public/` en la raíz
|
||||||
del proyecto Jekyll.
|
del sitio.
|
||||||
|
|
||||||
En el frontmatter solo sale la URL del archivo asociado.
|
En el frontmatter solo sale la URL del archivo asociado.
|
||||||
|
|
||||||
|
## ActiveStorage
|
||||||
|
|
||||||
|
ActiveStorage es el método por defecto para subida de archivos en Ruby
|
||||||
|
on Rails. Sin embargo, no nos permite decidir dónde queremos guardar
|
||||||
|
los archivos, sino que los guarda directamente relacionados a un modelo
|
||||||
|
de la base de datos, en una ruta generada automáticamente.
|
||||||
|
|
||||||
|
Podríamos implementar un proveedor de ActiveStorage, pero la
|
||||||
|
documentación para hacerlo es escasa.
|
||||||
|
|
||||||
|
Como tenemos que asociarlo a un modelo, lo correcto sería usar el modelo
|
||||||
|
Site, de forma que tengamos acceso a todas las imágenes. Esto abriría
|
||||||
|
la posibilidad de tener una galería de imágenes y poder seleccionar
|
||||||
|
imágenes entre las ya subidas o subir nuevas.
|
||||||
|
|
||||||
|
Pero como los archivos físicos se guardan directamente en una ruta
|
||||||
|
indicada por ActiveStorage, empezamos a utilizar más espacio porque para
|
||||||
|
que los sitios sean autocontenidos, deberíamos copiarlos al directorio
|
||||||
|
del sitio. También podemos utilizar enlaces duros (_hardlinks_) para no
|
||||||
|
ocupar espacio extra.
|
||||||
|
|
||||||
|
Ya que las imágenes van a estar dentro del directorio de Sutty en lugar
|
||||||
|
de cada archivo, es más fácil poder visualizarlas, sin tener que hacer
|
||||||
|
hacks enviando el archivo a través de Rails. Con AS podemos vincularlas
|
||||||
|
directamente desde el sistema de archivos.
|
||||||
|
|
||||||
|
## CarrierWave
|
||||||
|
|
||||||
|
CarrierWave es la forma en que estamos subiendo los archivos hasta el
|
||||||
|
momento. Puede transformar las imágenes a distintas resoluciones
|
||||||
|
automáticamente (aunque quisiéramos hacer esto durante la generación del
|
||||||
|
sitio). También hemos tenido problemas con los nombres de los archivos
|
||||||
|
y encontrado errores no documentados con respecto a cuándo cambia el
|
||||||
|
nombre del archivo o no, con lo que usar CW se nos hizo un poco endeble.
|
||||||
|
|
||||||
|
## Subida
|
||||||
|
|
||||||
|
Entonces:
|
||||||
|
|
||||||
|
* Activamos ActiveStorage local (ofrece nubes, pero no queremos ninguna
|
||||||
|
nube)
|
||||||
|
|
||||||
|
* Asociamos archivos al modelo Site
|
||||||
|
|
||||||
|
* Al subir un archivo desde el editor de Post, se asocia al Site, no al
|
||||||
|
Post.
|
||||||
|
|
||||||
|
* Para asociar el archivo al Post, generamos un hardlink con la misma
|
||||||
|
ruta, dentro del directorio del sitio. Esto nos permite vincular
|
||||||
|
ambos archivos sin agregar metadatos adicionales.
|
||||||
|
|
||||||
|
* Para generar variantes del archivo, lo hacemos con un plugin Jekyll
|
||||||
|
que las genera dentro del sitio si no existen y que agrega los
|
||||||
|
atributos srcset para imágenes responsive.
|
||||||
|
|
||||||
|
* Si encontramos forma, aplicamos `oxipng` y otros optimizadores. Sino,
|
||||||
|
lo haremos desde el plugin.
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
Usamos VIPS para procesar imágenes con bajo consumo de recursos
|
||||||
|
|
Loading…
Reference in a new issue