-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ActiveStorage support #2974
ActiveStorage support #2974
Changes from all commits
a219287
1153395
808d93b
1789677
0b4d540
fe995c4
f5db2d1
2435b84
47b377e
3032e4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# frozen_string_literal: true | ||
|
||
module Spree | ||
elia marked this conversation as resolved.
Show resolved
Hide resolved
|
||
module ActiveStorageAttachment | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you explain why you added a second module for active storage support? Why not have these methods in the other module as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This module is included by both AS implementations (Image and Taxon) and is an extraction of repeated code. The call between extracting/DRYing and leaving the duplication was, admittedly, close. But in the end I ended up refactoring in order to avoid the risk of leaving one of the two behind when fixing stuff. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since ActiveStorage is meant to add attachments to any model I like the idea here. |
||
extend ActiveSupport::Concern | ||
|
||
# @private | ||
def self.attachment_variant(attachment, style:, default_style:, styles:) | ||
return unless attachment && attachment.attachment | ||
|
||
if style.nil? || style == default_style | ||
attachment_variant = attachment | ||
else | ||
attachment_variant = attachment.variant( | ||
resize: styles[style.to_sym], | ||
strip: true, | ||
'auto-orient': true, | ||
colorspace: 'sRGB', | ||
).processed | ||
end | ||
|
||
attachment_variant | ||
end | ||
|
||
class_methods do | ||
def redefine_attachment_writer_with_legacy_io_support(name) | ||
define_method :"#{name}=" do |attachable| | ||
attachment = public_send(name) | ||
|
||
case attachable | ||
when ActiveStorage::Blob, ActionDispatch::Http::UploadedFile, | ||
Rack::Test::UploadedFile, Hash, String | ||
attachment.attach(attachable) | ||
when ActiveStorage::Attached | ||
attachment.attach(attachable.blob) | ||
else # assume it's an IO | ||
if attachable.respond_to?(:to_path) | ||
filename = attachable.to_path | ||
else | ||
filename = SecureRandom.uuid | ||
end | ||
attachable.rewind | ||
|
||
attachment.attach( | ||
io: attachable, | ||
filename: filename | ||
) | ||
end | ||
end | ||
end | ||
|
||
def validate_attachment_to_be_an_image(name) | ||
method_name = :"attached_#{name}_is_an_image" | ||
|
||
define_method method_name do | ||
attachment = public_send(name) | ||
next if attachment.nil? || attachment.attachment.nil? | ||
|
||
errors.add name, 'is not an image' unless attachment.attachment.image? | ||
end | ||
|
||
validate method_name | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'active_storage' | ||
|
||
module Spree::Image::ActiveStorageAttachment | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use to redefine modules/classes into existing ones by repeating their inheritances on each level, see #2572 for example for more information. In this case, it would be:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can do that, but used this way there's no risk of incurring in the problem described in #2572 (at least I don't see it). The problem there was using the Sometimes it was Let me know if the change is still needed 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if this module is someway loaded before There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will autoload There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I think it can work but I'd still change it for consistency. I also found this related PR #2098 and I think the goal there was to explicitly inform the type of all the modules/classes involved in the inheritance. |
||
extend ActiveSupport::Concern | ||
include Spree::ActiveStorageAttachment | ||
|
||
module DeprecatedPaperclipAPI | ||
def attachment(*args) | ||
if args.size == 1 | ||
# TODO: deprecation | ||
style = args.first | ||
Spree::ActiveStorageAttachment.attachment_variant( | ||
super(), | ||
style: style, | ||
default_style: default_style, | ||
styles: ATTACHMENT_STYLES | ||
) | ||
else | ||
# With 0 args will be ok, otherwise will raise an ArgumentError | ||
super | ||
end | ||
end | ||
end | ||
|
||
included do | ||
has_one_attached :attachment | ||
redefine_attachment_writer_with_legacy_io_support :attachment | ||
validate_attachment_to_be_an_image :attachment | ||
validates :attachment, presence: true | ||
prepend DeprecatedPaperclipAPI | ||
end | ||
|
||
class_methods do | ||
def attachment_definitions | ||
{ attachment: { styles: ATTACHMENT_STYLES } } | ||
end | ||
end | ||
|
||
ATTACHMENT_STYLES = { | ||
mini: '48x48>', | ||
small: '100x100>', | ||
product: '240x240>', | ||
large: '600x600>', | ||
} | ||
|
||
def default_style | ||
:original | ||
end | ||
|
||
def url(style = default_style, options = {}) | ||
options = normalize_url_options(options) | ||
|
||
Spree::ActiveStorageAttachment.attachment_variant( | ||
attachment, | ||
style: style, | ||
default_style: default_style, | ||
styles: ATTACHMENT_STYLES | ||
)&.service_url(options) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lint/Syntax: unexpected token error |
||
end | ||
|
||
def filename | ||
attachment.blob.filename.to_s | ||
end | ||
|
||
def attachment_width | ||
attachment.metadata[:width] | ||
end | ||
|
||
def attachment_height | ||
attachment.metadata[:height] | ||
end | ||
|
||
def attachment_present? | ||
attachment.attached? | ||
end | ||
|
||
private | ||
|
||
def normalize_url_options(options) | ||
if [true, false].include? options # Paperclip backwards compatibility. | ||
Spree::Deprecation.warn( | ||
"Using #{self.class}#url with true/false as second parameter is deprecated, if you "\ | ||
"want to enable/disable timestamps pass `timestamps: true` (or `false`)." | ||
) | ||
options = { timestamp: options } | ||
end | ||
|
||
options | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# frozen_string_literal: true | ||
|
||
module Spree::Image::PaperclipAttachment | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
validate :no_attachment_errors | ||
|
||
has_attached_file :attachment, | ||
styles: { mini: '48x48>', small: '100x100>', product: '240x240>', large: '600x600>' }, | ||
default_style: :product, | ||
default_url: 'noimage/:style.png', | ||
url: '/spree/products/:id/:style/:basename.:extension', | ||
path: ':rails_root/public/spree/products/:id/:style/:basename.:extension', | ||
convert_options: { all: '-strip -auto-orient -colorspace sRGB' } | ||
validates_attachment :attachment, | ||
presence: true, | ||
content_type: { content_type: %w[image/jpeg image/jpg image/png image/gif] } | ||
|
||
# save the w,h of the original image (from which others can be calculated) | ||
# we need to look at the write-queue for images which have not been saved yet | ||
after_post_process :find_dimensions, if: :valid? | ||
end | ||
|
||
def url(size) | ||
attachment.url(size) | ||
end | ||
|
||
def filename | ||
attachment_file_name | ||
end | ||
|
||
def attachment_present? | ||
attachment.present? | ||
end | ||
|
||
def find_dimensions | ||
temporary = attachment.queued_for_write[:original] | ||
filename = temporary.path unless temporary.nil? | ||
filename = attachment.path if filename.blank? | ||
geometry = Paperclip::Geometry.from_file(filename) | ||
self.attachment_width = geometry.width | ||
self.attachment_height = geometry.height | ||
end | ||
|
||
# if there are errors from the plugin, then add a more meaningful message | ||
def no_attachment_errors | ||
unless attachment.errors.empty? | ||
# uncomment this to get rid of the less-than-useful interim messages | ||
# errors.clear | ||
errors.add :attachment, "Paperclip returned errors for file '#{attachment_file_name}' - check ImageMagick installation or image source file." | ||
false | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,15 +25,27 @@ class Taxon < Spree::Base | |
after_save :touch_ancestors_and_taxonomy | ||
after_touch :touch_ancestors_and_taxonomy | ||
|
||
has_attached_file :icon, | ||
styles: { mini: '32x32>', normal: '128x128>' }, | ||
default_style: :mini, | ||
url: '/spree/taxons/:id/:style/:basename.:extension', | ||
path: ':rails_root/public/spree/taxons/:id/:style/:basename.:extension', | ||
default_url: '/assets/default_taxon.png' | ||
|
||
validates_attachment :icon, | ||
content_type: { content_type: ["image/jpg", "image/jpeg", "image/png", "image/gif"] } | ||
if ::Spree::Config.taxon_attachment_module.blank? | ||
Spree::Deprecation.warn <<-MESSAGE.strip_heredoc + "\n\n" | ||
Using Paperclip as taxon_attachment_module for Taxon. | ||
|
||
Please configure Spree::Config.taxon_attachment_module in your store | ||
initializer. | ||
|
||
To use the ActiveStorage adapter (recommended): | ||
Spree.config do |config| | ||
config.image_attachment_module = 'Spree::Taxon::ActiveStorageAttachment' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks good catch! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this still unsolved? |
||
end | ||
|
||
To use the Paperclip adapter (legacy, deprecated): | ||
Spree.config do |config| | ||
config.taxon_attachment_module = 'Spree::Taxon::PaperclipAttachment' | ||
end | ||
MESSAGE | ||
::Spree::Config.taxon_attachment_module = 'Spree::Taxon::PaperclipAttachment' | ||
end | ||
|
||
include ::Spree::Config.taxon_attachment_module.to_s.constantize | ||
|
||
self.whitelisted_ransackable_attributes = %w[name] | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about extracting it into a method? It could be reused in that way (I noticed it's used elsewhere)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to stick to how it's done within AS, I think it will probably need to be revised once Rails 5.1 support is dropped