Skip to content

Commit

Permalink
Merge pull request #3501 from filippoliverani/add-active-storage-adapter
Browse files Browse the repository at this point in the history
Add ActiveStorage adapter
  • Loading branch information
kennyadsl authored Apr 20, 2020
2 parents 9b1f3c8 + 0ac3904 commit 138bf61
Show file tree
Hide file tree
Showing 22 changed files with 448 additions and 67 deletions.
5 changes: 3 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,12 @@ jobs:
- setup
- test

postgres_rails_master:
postgres_rails_master_activestorage:
executor: postgres
parallelism: *parallelism
environment:
RAILS_VERSION: 'master'
ENABLE_ACTIVE_STORAGE: true
steps:
- setup
- test
Expand Down Expand Up @@ -141,7 +142,7 @@ workflows:
- mysql
- postgres_rails52
- mysql_rails52
- postgres_rails_master
- postgres_rails_master_activestorage
- stoplight/push:
project: solidus/solidus-api
git_token: $STOPLIGHT_GIT_TOKEN
Expand Down
1 change: 1 addition & 0 deletions api/app/controllers/spree/api/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class BaseController < ActionController::Base
include Spree::Core::ControllerHelpers::Store
include Spree::Core::ControllerHelpers::Pricing
include Spree::Core::ControllerHelpers::StrongParameters
include ActiveStorage::SetCurrent if Spree::Config.active_storage_enabled?

class_attribute :admin_line_item_attributes
self.admin_line_item_attributes = [:price, :variant_id, :sku]
Expand Down
1 change: 1 addition & 0 deletions core/app/controllers/spree/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Spree::BaseController < ApplicationController
include Spree::Core::ControllerHelpers::Search
include Spree::Core::ControllerHelpers::Store
include Spree::Core::ControllerHelpers::StrongParameters
include ActiveStorage::SetCurrent if Spree::Config.active_storage_enabled?

respond_to :html
end
116 changes: 116 additions & 0 deletions core/app/models/concerns/spree/active_storage_adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# frozen_string_literal: true

module Spree
# Adapts ActiveStorage interface to make it compliant with Solidus'
# Paperclip-oriented attachment support.
module ActiveStorageAdapter
extend ActiveSupport::Concern
include Spree::ActiveStorageAdapter::Normalization

included do
next if Rails.gem_version >= Gem::Version.new('6.1.0.alpha')

abort <<~MESSAGE
Configuration Error: Solidus ActiveStorage attachment adpater requires Rails >= 6.1.0.
Spree::Config.image_attachment_module preference is set to #{Spree::Config.image_attachment_module}
Spree::Config.taxon_attachment_module preference is set to #{Spree::Config.taxon_attachment_module}
Rails version is #{Rails.gem_version}
To solve the problem you can upgrade to a Rails version greater than or equal to 6.1.0
or use legacy Paperclip attachment adapter by editing `config/initialiers/spree/rb`:
config.image_attachment_module = 'Spree::Image::PaperclipAttachment'
config.taxon_attachment_module = 'Spree::Taxon::PaperclipAttachment'
MESSAGE
end

class_methods do
attr_reader :attachment_name
attr_reader :attachment_definition

# Specifies the relation between a single attachment and the model
def has_attachment(name, definition)
@attachment_name = name.to_sym
@attachment_definition = definition

has_one_attached attachment_name

override_reader
override_writer
define_image_validation
define_presence_reader
end

def attachment_definitions
{ attachment_name => attachment_definition }
end

private

def override_reader
method_name = attachment_name
override = Module.new do
define_method method_name do |*args|
attachment = Attachment.new(super(), styles: styles)
if args.empty?
attachment
else
style = args.first || default_style
attachment.url(style)
end
end
end
prepend override

alias_method :attachment, method_name if method_name != :attachment
end

def override_writer
method_name = :"#{attachment_name}="
override = Module.new do
define_method method_name do |attachable|
no_other_changes = persisted? && !changed?
super(normalize_attachable(attachable))
save if no_other_changes
end
end
prepend override
end

def define_image_validation
define_method :"#{attachment_name}_is_an_image" do
return unless attachment.attached?
return if attachment.image?

errors.add(self.class.attachment_name, 'is not an image')
end
end

def define_presence_reader
define_method :"#{attachment_name}_present?" do
attachment.attached?
end
end
end

def styles
self.class.attachment_definition[:styles]
end

def default_style
self.class.attachment_definition[:default_style]
end

def filename
attachment.filename
end

def url(style = default_style)
attachment.url(style)
end

def destroy_attachment(_name)
attachment.destroy
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

require 'mini_magick'

module Spree
module ActiveStorageAdapter
# Decorares AtiveStorage attachment to add methods exptected by Solidus'
# Paperclip-oriented attachment support.
class Attachment
delegate_missing_to :@attachment

DEFAULT_SIZE = '100%'

def initialize(attachment, styles: {})
@attachment = attachment
@styles = styles
end

def exists?
attached?
end

def filename
blob.filename.to_s
end

def url(style = nil)
variant(style).url
end

def variant(style = nil)
size = style_to_size(style&.to_sym)
@attachment.variant(
resize: size,
strip: true,
'auto-orient': true,
colorspace: 'sRGB',
).processed
end

def height
metadata[:height]
end

def width
metadata[:width]
end

def destroy
return false unless attached?

purge
true
end

private

def metadata
analyze unless analyzed?

@attachment.metadata
end

def style_to_size(style)
@styles.fetch(style) { DEFAULT_SIZE }
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

module Spree
module ActiveStorageAdapter
# Contains normalization methods to make objects compliant with
# ActiveStorage API.
module Normalization
# Normalizes an attachable
def normalize_attachable(attachable)
case attachable
when ActiveStorage::Blob, ActionDispatch::Http::UploadedFile,
Rack::Test::UploadedFile, Hash, String
attachable
when Attachment, ActiveStorage::Attached
attachable_blob(attachable)
else # assume it's an IO
attachable_io(attachable)
end
end

private

def attachable_blob(attachable)
attachable.blob
end

def attachable_io(attachable)
filename = if attachable.respond_to?(:to_path)
attachable.to_path
else
SecureRandom.uuid
end
attachable.rewind

{ io: attachable, filename: filename }
end
end
end
end
21 changes: 21 additions & 0 deletions core/app/models/spree/image/active_storage_attachment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Spree::Image::ActiveStorageAttachment
extend ActiveSupport::Concern
include Spree::ActiveStorageAdapter

delegate :width, :height, to: :attachment, prefix: true

included do
has_attachment :attachment,
styles: {
mini: '48x48>',
small: '400x400>',
product: '680x680>',
large: '1200x1200>'
},
default_style: :product
validates :attachment, presence: true
validate :attachment_is_an_image
end
end
19 changes: 19 additions & 0 deletions core/app/models/spree/taxon/active_storage_attachment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

module Spree::Taxon::ActiveStorageAttachment
extend ActiveSupport::Concern
include Spree::ActiveStorageAdapter

included do
has_attachment :icon,
styles: { mini: '32x32>', normal: '128x128>' },
default_style: :mini
validate :icon_is_an_image


end

def attachment_partial_name
'paperclip'
end
end
6 changes: 6 additions & 0 deletions core/lib/spree/app_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -585,5 +585,11 @@ def admin_vat_location
country: Spree::Country.find_by(iso: admin_vat_country_iso)
)
end

def active_storage_enabled?
@active_storage_enabled ||=
Spree::Config.image_attachment_module == Spree::Image::ActiveStorageAttachment ||
Spree::Config.taxon_attachment_module == Spree::Taxon::ActiveStorageAttachment
end
end
end
56 changes: 37 additions & 19 deletions core/lib/spree/awesome_nested_set_override.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
# frozen_string_literal: true

module AwesomeNestedSetOvveride
# Add :polimorphic key option only when used to make it work with Rails 6.1+
# https://github.com/osmaelo/rails/commit/2c008d9f6311d92b3660193285230505e74a114f
# Add :polimorphic key option only when used to make it work with Rails 6.1+,
# required since rails/rails@2c008d9
# This can be removed when upgrading to an awesome_nested_set version
# compliant with Rails 6.1+.
def acts_as_nested_set_relate_parent!
# Disable Rubocop to keep original code for diffs
# rubocop:disable
options = {
:class_name => self.base_class.to_s,
:foreign_key => parent_column_name,
:primary_key => primary_column_name,
:counter_cache => acts_as_nested_set_options[:counter_cache],
:inverse_of => (:children unless acts_as_nested_set_options[:polymorphic]),
:touch => acts_as_nested_set_options[:touch]
}
options[:polymorphic] = true if acts_as_nested_set_options[:polymorphic]
options[:optional] = true if ActiveRecord::VERSION::MAJOR >= 5
belongs_to :parent, options
# rubocop:disable
# compliant with Rails 6.1+, already addressed in
# collectiveidea/awesome_nested_set#421
module RelateParent
def acts_as_nested_set_relate_parent!
# Disable Rubocop to keep original code for diffs
# rubocop:disable
options = {
:class_name => self.base_class.to_s,
:foreign_key => parent_column_name,
:primary_key => primary_column_name,
:counter_cache => acts_as_nested_set_options[:counter_cache],
:inverse_of => (:children unless acts_as_nested_set_options[:polymorphic]),
:touch => acts_as_nested_set_options[:touch]
}
options[:polymorphic] = true if acts_as_nested_set_options[:polymorphic]
options[:optional] = true if ActiveRecord::VERSION::MAJOR >= 5
belongs_to :parent, options
# rubocop:enable
end

CollectiveIdea::Acts::NestedSet.prepend self
end

CollectiveIdea::Acts::NestedSet.prepend self
# Skip breaking model reload before update depth. Already addressed in
# collectiveidea/awesome_nested_set#413
# This can be removed when a new version of awesome_nested_set is released.
module Model
def set_depth!
return unless has_depth_column?

in_tenacious_transaction do
update_depth(level)
end
end

CollectiveIdea::Acts::NestedSet::Model.prepend self
end
end
2 changes: 1 addition & 1 deletion core/lib/spree/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,6 @@ class GatewayError < RuntimeError; end
require 'spree/preferences/static_model_preferences'
require 'spree/preferences/scoped_store'

if Gem::Version.new(Rails.version) >= Gem::Version.new('6.1.0.alpha')
if Rails.gem_version >= Gem::Version.new('6.1.0.alpha')
require 'spree/awesome_nested_set_override'
end
Loading

0 comments on commit 138bf61

Please sign in to comment.