From e01940c1665718567328ac8cd1340908d6239fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Sat, 24 Jul 2021 02:39:26 +0200 Subject: [PATCH] Move image/document attachments code to a concern This way we reduce some of the duplication in these classes. --- app/models/concerns/attachable.rb | 75 +++++++++++++++++++++++++++++++ app/models/document.rb | 69 +++------------------------- app/models/image.rb | 68 +++------------------------- 3 files changed, 89 insertions(+), 123 deletions(-) create mode 100644 app/models/concerns/attachable.rb diff --git a/app/models/concerns/attachable.rb b/app/models/concerns/attachable.rb new file mode 100644 index 000000000..fbc29a055 --- /dev/null +++ b/app/models/concerns/attachable.rb @@ -0,0 +1,75 @@ +module Attachable + extend ActiveSupport::Concern + + included do + attr_accessor :cached_attachment + + # Disable paperclip security validation due to polymorphic configuration + # Paperclip do not allow to use Procs on valiations definition + do_not_validate_attachment_file_type :attachment + validate :attachment_presence + validate :validate_attachment_content_type, if: -> { attachment.present? } + validate :validate_attachment_size, if: -> { attachment.present? } + + before_save :set_attachment_from_cached_attachment, if: -> { cached_attachment.present? } + + Paperclip.interpolates :prefix do |attachment, style| + attachment.instance.prefix(attachment, style) + end + end + + def association_class + type = send("#{association_name}_type") + + type.constantize if type.present? + end + + def set_cached_attachment_from_attachment + self.cached_attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem + attachment.path + else + attachment.url + end + end + + def set_attachment_from_cached_attachment + self.attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem + File.open(cached_attachment) + else + URI.parse(cached_attachment) + end + end + + def prefix(attachment, _style) + if attachment.instance.persisted? + ":attachment/:id_partition" + else + "cached_attachments/user/#{attachment.instance.user_id}" + end + end + + private + + def validate_attachment_size + if association_class && attachment_file_size > max_file_size.megabytes + errors.add(:attachment, I18n.t("#{model_name.plural}.errors.messages.in_between", + min: "0 Bytes", + max: "#{max_file_size} MB")) + end + end + + def validate_attachment_content_type + if association_class && !accepted_content_types.include?(attachment_content_type) + message = I18n.t("#{model_name.plural}.errors.messages.wrong_content_type", + content_type: attachment_content_type, + accepted_content_types: self.class.humanized_accepted_content_types) + errors.add(:attachment, message) + end + end + + def attachment_presence + if attachment.blank? && cached_attachment.blank? + errors.add(:attachment, I18n.t("errors.messages.blank")) + end + end +end diff --git a/app/models/document.rb b/app/models/document.rb index f610ba61c..cc24ef871 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -1,48 +1,21 @@ class Document < ApplicationRecord + include Attachable + has_attached_file :attachment, url: "/system/:class/:prefix/:style/:hash.:extension", hash_data: ":class/:style/:custom_hash_data", use_timestamp: false, hash_secret: Rails.application.secrets.secret_key_base - attr_accessor :cached_attachment belongs_to :user belongs_to :documentable, polymorphic: true - # Disable paperclip security validation due to polymorphic configuration - # Paperclip do not allow to use Procs on valiations definition - do_not_validate_attachment_file_type :attachment - validate :attachment_presence - validate :validate_attachment_content_type, if: -> { attachment.present? } - validate :validate_attachment_size, if: -> { attachment.present? } validates :title, presence: true validates :user_id, presence: true validates :documentable_id, presence: true, if: -> { persisted? } validates :documentable_type, presence: true, if: -> { persisted? } - before_save :set_attachment_from_cached_attachment, if: -> { cached_attachment.present? } - scope :admin, -> { where(admin: true) } - def set_cached_attachment_from_attachment - self.cached_attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem - attachment.path - else - attachment.url - end - end - - def set_attachment_from_cached_attachment - self.attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem - File.open(cached_attachment) - else - URI.parse(cached_attachment) - end - end - - Paperclip.interpolates :prefix do |attachment, style| - attachment.instance.prefix(attachment, style) - end - Paperclip.interpolates :custom_hash_data do |attachment, _style| attachment.instance.custom_hash_data(attachment) end @@ -51,14 +24,6 @@ class Document < ApplicationRecord Setting.accepted_content_types_for("documents").join(", ") end - def prefix(attachment, _style) - if attachment.instance.persisted? - ":attachment/:id_partition" - else - "cached_attachments/user/#{attachment.instance.user_id}" - end - end - def custom_hash_data(attachment) original_filename = if attachment.instance.persisted? attachment.instance.title @@ -82,31 +47,11 @@ class Document < ApplicationRecord private + def association_name + :documentable + end + def documentable_class - documentable_type.constantize if documentable_type.present? - end - - def validate_attachment_size - if documentable_class.present? && - attachment_file_size > max_file_size.megabytes - errors.add(:attachment, I18n.t("documents.errors.messages.in_between", - min: "0 Bytes", - max: "#{max_file_size} MB")) - end - end - - def validate_attachment_content_type - if documentable_class && !accepted_content_types.include?(attachment_content_type) - message = I18n.t("documents.errors.messages.wrong_content_type", - content_type: attachment_content_type, - accepted_content_types: self.class.humanized_accepted_content_types) - errors.add(:attachment, message) - end - end - - def attachment_presence - if attachment.blank? && cached_attachment.blank? - errors.add(:attachment, I18n.t("errors.messages.blank")) - end + association_class end end diff --git a/app/models/image.rb b/app/models/image.rb index 781b51da3..19825d629 100644 --- a/app/models/image.rb +++ b/app/models/image.rb @@ -1,4 +1,6 @@ class Image < ApplicationRecord + include Attachable + has_attached_file :attachment, styles: { large: "x#{Setting["uploads.images.min_height"]}", medium: "300x300#", @@ -8,17 +10,10 @@ class Image < ApplicationRecord hash_data: ":class/:style", use_timestamp: false, hash_secret: Rails.application.secrets.secret_key_base - attr_accessor :cached_attachment belongs_to :user belongs_to :imageable, polymorphic: true - # Disable paperclip security validation due to polymorphic configuration - # Paperclip do not allow to use Procs on valiations definition - do_not_validate_attachment_file_type :attachment - validate :attachment_presence - validate :validate_attachment_content_type, if: -> { attachment.present? } - validate :validate_attachment_size, if: -> { attachment.present? } validates :title, presence: true validate :validate_title_length validates :user_id, presence: true @@ -26,8 +21,6 @@ class Image < ApplicationRecord validates :imageable_type, presence: true, if: -> { persisted? } validate :validate_image_dimensions, if: -> { attachment.present? && attachment.dirty? } - before_save :set_attachment_from_cached_attachment, if: -> { cached_attachment.present? } - def self.max_file_size Setting["uploads.images.max_size"].to_i end @@ -48,38 +41,14 @@ class Image < ApplicationRecord self.class.accepted_content_types end - def set_cached_attachment_from_attachment - self.cached_attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem - attachment.path - else - attachment.url - end - end - - def set_attachment_from_cached_attachment - self.attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem - File.open(cached_attachment) - else - URI.parse(cached_attachment) - end - end - - Paperclip.interpolates :prefix do |attachment, style| - attachment.instance.prefix(attachment, style) - end - - def prefix(attachment, _style) - if attachment.instance.persisted? - ":attachment/:id_partition" - else - "cached_attachments/user/#{attachment.instance.user_id}" - end - end - private + def association_name + :imageable + end + def imageable_class - imageable_type.constantize if imageable_type.present? + association_class end def validate_image_dimensions @@ -94,14 +63,6 @@ class Image < ApplicationRecord end end - def validate_attachment_size - if imageable_class && attachment_file_size > max_file_size.megabytes - errors.add(:attachment, I18n.t("images.errors.messages.in_between", - min: "0 Bytes", - max: "#{max_file_size} MB")) - end - end - def validate_title_length if title.present? title_min_length = Setting["uploads.images.title.min_length"].to_i @@ -117,21 +78,6 @@ class Image < ApplicationRecord end end - def validate_attachment_content_type - if imageable_class && !attachment_of_valid_content_type? - message = I18n.t("images.errors.messages.wrong_content_type", - content_type: attachment_content_type, - accepted_content_types: self.class.humanized_accepted_content_types) - errors.add(:attachment, message) - end - end - - def attachment_presence - if attachment.blank? && cached_attachment.blank? - errors.add(:attachment, I18n.t("errors.messages.blank")) - end - end - def attachment_of_valid_content_type? attachment.present? && accepted_content_types.include?(attachment_content_type) end