diff --git a/Dockerfile b/Dockerfile index bc7b0be..789f355 100644 --- a/Dockerfile +++ b/Dockerfile @@ -63,7 +63,9 @@ COPY --chown=app:www-data ./public/packs-production ./public/packs # assets ya están pre-compilados. RUN sed -re "/(sassc|uglifier|bootstrap|coffee-rails)/d" -i Gemfile RUN bundle clean -RUN rm -rf ./node_modules ./tmp/cache ./.git +RUN rm -rf ./node_modules ./tmp/cache ./.git ./test ./doc +# Eliminar archivos innecesarios +RUN find /srv/http/vendor/ruby/2.7.0/ -maxdepth 3 -type d -name test -o -name spec -o name rubocop | xargs -r rm -rf # Contenedor final FROM sutty/monit:latest diff --git a/Gemfile b/Gemfile index df6bd4a..f5a211b 100644 --- a/Gemfile +++ b/Gemfile @@ -48,7 +48,8 @@ gem 'hiredis' gem 'image_processing' gem 'icalendar' gem 'inline_svg' -gem 'jekyll' +gem 'jekyll', git: 'https://0xacab.org/sutty/jekyll/jekyll.git', + branch: 'master' gem 'jekyll-data', require: 'jekyll-data', git: 'https://0xacab.org/sutty/jekyll/jekyll-data.git' gem 'lockbox' diff --git a/Gemfile.lock b/Gemfile.lock index 09be6cc..cd83cac 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,6 +5,27 @@ GIT jekyll-data (1.1.0) jekyll (>= 3.3, < 5.0.0) +GIT + remote: https://0xacab.org/sutty/jekyll/jekyll.git + revision: 7aff6e245eae2ddbf081567f1ebaa8458d73079d + branch: master + specs: + jekyll (4.1.1) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 1.0) + jekyll-sass-converter (~> 2.0) + jekyll-watch (~> 2.0) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.0) + liquid (~> 4.0) + mercenary (~> 0.4.0) + pathutil (~> 0.9) + rouge (~> 3.0) + safe_yaml (~> 1.0) + terminal-table (~> 1.8) + GIT remote: https://0xacab.org/sutty/yaml_db.git revision: 40e44c29ce4290dfe4013ff4fce1be5a936fedf4 @@ -214,21 +235,6 @@ GEM nokogiri (>= 1.6) jbuilder (2.10.1) activesupport (>= 5.0.0) - jekyll (4.1.1) - addressable (~> 2.4) - colorator (~> 1.0) - em-websocket (~> 0.5) - i18n (~> 1.0) - jekyll-sass-converter (~> 2.0) - jekyll-watch (~> 2.0) - kramdown (~> 2.1) - kramdown-parser-gfm (~> 1.0) - liquid (~> 4.0) - mercenary (~> 0.4.0) - pathutil (~> 0.9) - rouge (~> 3.0) - safe_yaml (~> 1.0) - terminal-table (~> 1.8) jekyll-feed (0.15.0) jekyll (>= 3.7, < 5.0) jekyll-images (0.2.7) @@ -578,7 +584,7 @@ DEPENDENCIES image_processing inline_svg jbuilder (~> 2.5) - jekyll + jekyll! jekyll-data! letter_opener listen (>= 3.0.5, < 3.2) diff --git a/app/controllers/api/v1/protected_controller.rb b/app/controllers/api/v1/protected_controller.rb index b4e4db5..4e49f3a 100644 --- a/app/controllers/api/v1/protected_controller.rb +++ b/app/controllers/api/v1/protected_controller.rb @@ -50,7 +50,7 @@ module Api # puede ser que haya tardado más de media hora en enviar el # formulario. def cookie_is_valid? - return if (site_cookie.try(:[], 'expires') || 0) > Time.now.to_i + return if (site_cookie&.dig('expires') || 0) > Time.now.to_i @reason = 'expired_or_invalid_cookie' render plain: Rails.env.production? ? nil : @reason, status: :precondition_required diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6786491..abf1229 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -13,7 +13,7 @@ class ApplicationController < ActionController::Base rescue_from ActionController::RoutingError, with: :page_not_found before_action do - Rack::MiniProfiler.authorize_request if current_usuarie.try(:ends_with?, '@' + ENV.fetch('SUTTY', 'sutty.nl')) + Rack::MiniProfiler.authorize_request if current_usuarie&.email&.ends_with?('@' + ENV.fetch('SUTTY', 'sutty.nl')) end # No tenemos índice de sutty, vamos directamente a ver el listado de @@ -45,7 +45,7 @@ class ApplicationController < ActionController::Base # corresponde con el idioma de los artículos, porque puede querer # traducirlos. def set_locale(&action) - I18n.with_locale(current_usuarie.try(:lang) || I18n.default_locale, &action) + I18n.with_locale(current_usuarie&.lang || I18n.default_locale, &action) end # Muestra una página 404 diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 395a85d..ecf5af8 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -9,7 +9,7 @@ class PostsController < ApplicationController # Las URLs siempre llevan el idioma actual o el de le usuarie def default_url_options - { locale: params[:locale] || current_usuarie.try(:lang) || I18n.locale } + { locale: params[:locale] || current_usuarie&.lang || I18n.locale } end def index @@ -145,7 +145,7 @@ class PostsController < ApplicationController # solicite a le usuarie crear el nuevo idioma y que esto lo agregue al # _config.yml del sitio en lugar de mezclar idiomas. def locale - @site.try(:locales).try(:find, -> { I18n.locale.to_s }) do |l| + @site&.locales&.find(-> { I18n.locale.to_s }) do |l| l == params[:locale] end end diff --git a/app/models/deploy.rb b/app/models/deploy.rb index a5dd153..98e6d0a 100644 --- a/app/models/deploy.rb +++ b/app/models/deploy.rb @@ -65,7 +65,7 @@ class Deploy < ApplicationRecord stat.bytes = size stat.save - r.try :success? + r&.success? end # rubocop:enable Metrics/MethodLength end diff --git a/app/models/metadata_factory.rb b/app/models/metadata_factory.rb index d7a2e03..8cac1c8 100644 --- a/app/models/metadata_factory.rb +++ b/app/models/metadata_factory.rb @@ -3,6 +3,8 @@ # Devuelve metadatos de cierto tipo class MetadataFactory def self.build(**args) - "Metadata#{args[:type].camelcase}".constantize.new(args) + @@factory_cache ||= {} + @@factory_cache[args[:type]] ||= ('Metadata' + args[:type].to_s.camelcase).constantize + @@factory_cache[args[:type]].new(args) end end diff --git a/app/models/metadata_path.rb b/app/models/metadata_path.rb index 7f38692..5dc25c8 100644 --- a/app/models/metadata_path.rb +++ b/app/models/metadata_path.rb @@ -8,7 +8,8 @@ class MetadataPath < MetadataTemplate end def value - default_value + @value_was ||= default_value + self[:value] = default_value end alias absolute value alias to_s value diff --git a/app/models/metadata_related_posts.rb b/app/models/metadata_related_posts.rb index 5b73857..e38538a 100644 --- a/app/models/metadata_related_posts.rb +++ b/app/models/metadata_related_posts.rb @@ -23,12 +23,12 @@ class MetadataRelatedPosts < MetadataArray end def title(post) - post.try(:title).try(:value) || post.try(:slug).try(:value) + post&.title&.value || post&.slug&.value end # TODO: Traer el idioma actual de otra forma def lang - post.try(:lang).try(:value) || I18n.locale + post&.lang&.value || I18n.locale end # Encuentra el filtro diff --git a/app/models/metadata_slug.rb b/app/models/metadata_slug.rb index 7798c7c..1f12475 100644 --- a/app/models/metadata_slug.rb +++ b/app/models/metadata_slug.rb @@ -40,6 +40,6 @@ class MetadataSlug < MetadataTemplate # Devuelve el título a menos que sea privado y no esté vacío def title - post.title.try(:value).try(:to_s) unless post.title.private? && !post.title.try(:value).try(:blank?) + post.title&.value&.to_s unless post.title.private? && !post.title&.value&.blank? end end diff --git a/app/models/metadata_template.rb b/app/models/metadata_template.rb index 81ee17f..521a02a 100644 --- a/app/models/metadata_template.rb +++ b/app/models/metadata_template.rb @@ -10,6 +10,17 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type, :layout, keyword_init: true) do include ActionText::ContentHelper + attr_reader :value_was + + def value=(new_value) + @value_was = value + self[:value] = new_value + end + + def changed? + !value_was.nil? && value_was != value + end + # El valor por defecto def default_value raise NotImplementedError @@ -18,7 +29,7 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type, # Valores posibles, busca todos los valores actuales en otros # artículos del mismo sitio def values - site.everything_of(name, lang: post.try(:lang).try(:value)) + site.everything_of(name, lang: post&.lang&.value) end # Valor actual o por defecto. Al memoizarlo podemos modificarlo diff --git a/app/models/post.rb b/app/models/post.rb index affec14..4a80cab 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -11,15 +11,15 @@ class Post < OpenStruct # Otros atributos que no vienen en los metadatos PRIVATE_ATTRIBUTES = %i[path slug attributes errors].freeze PUBLIC_ATTRIBUTES = %i[lang date uuid].freeze + ATTR_SUFFIXES = %w[? =].freeze class << self # Obtiene el layout sin leer el Document # # TODO: Reemplazar cuando leamos el contenido del Document # a demanda? - def find_layout(doc) - IO.foreach(doc.path).lazy.grep(/^layout: /).take(1).first - .try(:split, ' ').try(:last).try(:to_sym) + def find_layout(path) + IO.foreach(path).lazy.grep(/^layout: /).take(1).first&.split(' ')&.last&.to_sym end end @@ -118,10 +118,6 @@ class Post < OpenStruct method: mid) end - # Definir los attribute_* - new_attribute_was(name) - new_attribute_changed(name) - # OpenStruct super(mid, *args) @@ -194,7 +190,7 @@ class Post < OpenStruct def save(validate: true) return false if validate && !valid? # 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 @@ -226,7 +222,7 @@ class Post < OpenStruct # existe el destino def update_path! !File.exist?(path.absolute) && - FileUtils.mv(path_was, path.absolute) && + FileUtils.mv(path.value_was, path.absolute) && document.path = path.absolute end @@ -300,38 +296,11 @@ class Post < OpenStruct document.data.fetch('usuaries', []) end - def new_attribute_was(method) - attr_was = "#{method}_was".to_sym - return attr_was if singleton_class.method_defined? attr_was - - define_singleton_method(attr_was) do - name = attribute_name(attr_was) - if document.respond_to?(name) - document.send(name) - else - document.data[name.to_s] - end - end - end - - # Pregunta si el atributo cambió - def new_attribute_changed(method) - attr_changed = "#{method}_changed?".to_sym - - return attr_changed if singleton_class.method_defined? attr_changed - - define_singleton_method(attr_changed) do - name = attribute_name(attr_changed) - name_was = (name.to_s + '_was').to_sym - - (send(name).try(:value) || send(name)) != send(name_was) - end - end - # Obtiene el nombre del atributo a partir del nombre del método def attribute_name(attr) # XXX: Los simbolos van al final - %w[_was _changed? ? =].reduce(attr.to_s) do |a, suffix| + @attribute_name_cache ||= {} + @attribute_name_cache[attr] ||= ATTR_SUFFIXES.reduce(attr.to_s) do |a, suffix| a.chomp suffix end.to_sym end diff --git a/app/models/post/template_field.rb b/app/models/post/template_field.rb index 8b4b692..08da933 100644 --- a/app/models/post/template_field.rb +++ b/app/models/post/template_field.rb @@ -305,7 +305,7 @@ class Post # Procesamos el valor, buscando : como separador de campos que # queremos encontrar y luego los unimos - _value = (values.try(:split, ':', 2) || []).map do |v| + _value = (values&.split(':', 2) || []).map do |v| # Tenemos hasta tres niveles de búsqueda collection, attr, subattr = v.split('/', 3) diff --git a/app/models/post_relation.rb b/app/models/post_relation.rb index 1e5c528..acf9058 100644 --- a/app/models/post_relation.rb +++ b/app/models/post_relation.rb @@ -135,7 +135,7 @@ class PostRelation < Array def build_layout(layout = nil) return layout if layout.is_a? Layout - site.layouts[layout.try(:to_sym) || :post] + site.layouts[layout&.to_sym || :post] end # Devuelve una colección Jekyll que hace pasar el documento diff --git a/app/models/site.rb b/app/models/site.rb index 9392eff..4a6dfbf 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -221,8 +221,8 @@ class Site < ApplicationRecord # No fallar si no existe colección para este idioma # XXX: queremos fallar silenciosamente? - (collections[lang.to_s].try(:docs) || []).each do |doc| - layout = layouts[Post.find_layout(doc)] + (collections[lang.to_s]&.docs || []).each do |doc| + layout = layouts[Post.find_layout(doc.path)] @posts[lang].build(document: doc, layout: layout, lang: lang) end @@ -288,7 +288,7 @@ class Site < ApplicationRecord attr = attr.to_sym posts(lang: lang).flat_map do |p| - p.send(attr).try(:value) if p.attribute? attr + p[attr].value if p.attribute? attr end.uniq.compact end diff --git a/app/models/site/forms.rb b/app/models/site/forms.rb index b65be16..8abd6cc 100644 --- a/app/models/site/forms.rb +++ b/app/models/site/forms.rb @@ -37,7 +37,7 @@ class Site # # @return Array Formularios disponibles para este sitio def forms - @forms ||= data.dig('forms').try(:keys) || [] + @forms ||= data.dig('forms')&.keys || [] end # El nombre del formulario está disponible diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 5ee1f2f..40562a8 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -58,7 +58,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do # # { uuid => 2, uuid => 1, uuid => 0 } def reorder - reorder = params.require(:post).permit(reorder: {}).try(:[], :reorder).transform_values(&:to_i) + reorder = params.require(:post).permit(reorder: {})&.dig(:reorder)&.transform_values(&:to_i) posts = site.posts(lang: locale).where(uuid: reorder.keys) files = posts.map do |post| @@ -86,7 +86,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do usuarie: usuarie, remove: action == :destroyed, message: I18n.t("post_service.#{action}", - title: post.try(:title).try(:value))) + title: post&.title&.value)) end # Solo permitir cambiar estos atributos de cada articulo diff --git a/app/services/site_service.rb b/app/services/site_service.rb index a9b5af4..24e5bc0 100644 --- a/app/services/site_service.rb +++ b/app/services/site_service.rb @@ -11,7 +11,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do add_role temporal: false, rol: 'usuarie' - I18n.with_locale(usuarie.try(:lang) || I18n.default_locale) do + I18n.with_locale(usuarie&.lang || I18n.default_locale) do site.save && site.config.write && commit_config(action: :create) @@ -24,7 +24,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do # Actualiza el sitio y guarda los cambios en la configuración def update - I18n.with_locale(usuarie.try(:lang) || I18n.default_locale) do + I18n.with_locale(usuarie&.lang || I18n.default_locale) do site.update(params) && site.config.write && commit_config(action: :update) diff --git a/app/views/active_storage/blobs/_blob.html.erb b/app/views/active_storage/blobs/_blob.html.erb index 49ba357..a515cf7 100644 --- a/app/views/active_storage/blobs/_blob.html.erb +++ b/app/views/active_storage/blobs/_blob.html.erb @@ -4,7 +4,7 @@ <% end %>
- <% if caption = blob.try(:caption) %> + <% if caption = blob&.caption %> <%= caption %> <% else %> <%= blob.filename %> diff --git a/app/views/devise/mailer/email_changed.html.haml b/app/views/devise/mailer/email_changed.html.haml index 8ae4d38..6b505fa 100644 --- a/app/views/devise/mailer/email_changed.html.haml +++ b/app/views/devise/mailer/email_changed.html.haml @@ -1,5 +1,5 @@ %p= t('.greeting', recipient: @email) -- if @resource.try(:unconfirmed_email?) +- if @resource&.unconfirmed_email? %p= t('.message', email: @resource.unconfirmed_email) - else %p= t('.message', email: @resource.email) diff --git a/app/views/devise/mailer/email_changed.text.haml b/app/views/devise/mailer/email_changed.text.haml index e5216a5..4df926a 100644 --- a/app/views/devise/mailer/email_changed.text.haml +++ b/app/views/devise/mailer/email_changed.text.haml @@ -1,6 +1,6 @@ = t('.greeting', recipient: @email) \ -- if @resource.try(:unconfirmed_email?) +- if @resource&.unconfirmed_email? = t('.message', email: @resource.unconfirmed_email) - else = t('.message', email: @resource.email) diff --git a/app/views/posts/_attribute_feedback.haml b/app/views/posts/_attribute_feedback.haml index d2345f6..9852c7b 100644 --- a/app/views/posts/_attribute_feedback.haml +++ b/app/views/posts/_attribute_feedback.haml @@ -3,5 +3,5 @@ .invalid-feedback{ id: id_for_feedback(*attribute) } - if metadata.required = t('posts.attributes.required.feedback') - - metadata.errors.try :each do |error| + - metadata.errors&.each do |error| = error diff --git a/app/views/posts/attributes/_lang.haml b/app/views/posts/attributes/_lang.haml index a41fbb3..b748480 100644 --- a/app/views/posts/attributes/_lang.haml +++ b/app/views/posts/attributes/_lang.haml @@ -1 +1 @@ -= hidden_field_tag 'post[lang]', post.try(:lang).try(:value) += hidden_field_tag 'post[lang]', post&.lang&.value diff --git a/app/views/posts/index.haml b/app/views/posts/index.haml index d9327e0..a11ff81 100644 --- a/app/views/posts/index.haml +++ b/app/views/posts/index.haml @@ -86,7 +86,7 @@ %td = post.date.value.strftime('%F') %br/ - = post.try(:order).try(:value) + = post&.order&.value %td - if @usuarie || policy(post).edit? = link_to t('posts.edit'), diff --git a/test/jobs/deploy_job_test.rb b/test/jobs/deploy_job_test.rb index 6d1d321..a0fabfc 100644 --- a/test/jobs/deploy_job_test.rb +++ b/test/jobs/deploy_job_test.rb @@ -13,7 +13,7 @@ class DeployJobTest < ActiveSupport::TestCase assert_not ActionMailer::Base.deliveries.empty? site.deploys.each do |d| - assert File.exist?(d.try(:path) || d.try(:destination)) + assert File.exist?(d.respond_to?(:path) ? d.path : d.destination) end site.destroy diff --git a/test/models/post_test.rb b/test/models/post_test.rb index 0235970..67dd1a2 100644 --- a/test/models/post_test.rb +++ b/test/models/post_test.rb @@ -98,36 +98,32 @@ class PostTest < ActiveSupport::TestCase test 'attribute_name' do assert_equal :hola, @post.send(:attribute_name, :hola) assert_equal :hola, @post.send(:attribute_name, :hola?) - assert_equal :hola, @post.send(:attribute_name, :hola_was) - assert_equal :hola, @post.send(:attribute_name, :hola_changed?) end test 'se puede cambiar el slug' do @post.title.value = SecureRandom.hex - assert_equal @post.slug_was, @post.slug.value - assert_not @post.slug_changed? + assert_not @post.slug.changed? assert @post.slug.valid? ex_slug = @post.slug.value @post.slug.value = SecureRandom.hex assert_not_equal ex_slug, @post.slug.value - assert_equal ex_slug, @post.slug_was - assert @post.slug_changed? + assert_equal ex_slug, @post.slug.value_was + assert @post.slug.changed? assert @post.slug.valid? end test 'se puede cambiar la fecha' do - assert_equal @post.date_was, @post.date.value - assert_not @post.date_changed? + assert_not @post.date.changed? assert @post.date.valid? ex_date = @post.date.value @post.date.value = 2.days.ago assert_not_equal ex_date, @post.date.value - assert_equal ex_date, @post.date_was - assert @post.date_changed? + assert_equal ex_date, @post.date.value_was + assert @post.date.changed? assert @post.date.valid? end @@ -137,7 +133,7 @@ class PostTest < ActiveSupport::TestCase @post.slug.value = title = SecureRandom.hex @post.date.value = hoy - assert @post.path_changed? + assert @post.path.changed? assert_equal "_es/#{hoy.strftime('%F')}-#{title}.markdown", @post.path.relative @@ -165,7 +161,7 @@ class PostTest < ActiveSupport::TestCase assert_not @post.save assert File.exist?(@post.path.absolute) - assert File.exist?(@post.path_was) + assert File.exist?(@post.path.value_was) end test 'se pueden crear nuevos' do diff --git a/test/system/site_test.rb b/test/system/site_test.rb new file mode 100644 index 0000000..052db4e --- /dev/null +++ b/test/system/site_test.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'test_helper' + +class SiteTest < ActiveSupport::TestCase + def site + @site ||= Site.new name: 'recursero', design_id: Design.find_by_gem('recursero-jekyll-theme').id + end + + test 'tiene una performance' do + skip unless ENV['TEST_PERFORMANCE'].present? + + site.read + + profile = StackProf.run mode: :object do + site.posts + end + + StackProf::Report.new(profile).print_text + end +end