Skip to content

Commit 697f4a9

Browse files
Extract transformers
1 parent 924f443 commit 697f4a9

File tree

6 files changed

+146
-70
lines changed

6 files changed

+146
-70
lines changed

activestorage/app/models/active_storage/variant.rb

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,8 @@ def process
101101
end
102102
end
103103

104-
def transform(image)
105-
result = variation.transform(image, format: format)
106-
107-
begin
108-
yield result
109-
ensure
110-
result.close!
111-
end
104+
def transform(image, &block)
105+
variation.transform(image, format: format, &block)
112106
end
113107

114108
def upload(file)

activestorage/app/models/active_storage/variation.rb

Lines changed: 17 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,9 @@ def initialize(transformations)
4747
# saves the transformed image into a temporary file. If +format+ is specified
4848
# it will be the format of the result image, otherwise the result image
4949
# retains the source format.
50-
def transform(file, format: nil)
50+
def transform(file, format: nil, &block)
5151
ActiveSupport::Notifications.instrument("transform.active_storage") do
52-
if processor
53-
image_processing_transform(file, format)
54-
else
55-
mini_magick_transform(file, format)
56-
end
52+
transformer.transform(file, format: format, &block)
5753
end
5854
end
5955

@@ -63,63 +59,22 @@ def key
6359
end
6460

6561
private
66-
# Applies image transformations using the ImageProcessing gem.
67-
def image_processing_transform(file, format)
68-
operations = transformations.each_with_object([]) do |(name, argument), list|
69-
if name.to_s == "combine_options"
70-
ActiveSupport::Deprecation.warn("The ImageProcessing ActiveStorage variant backend doesn't need :combine_options, as it already generates a single MiniMagick command. In Rails 6.1 :combine_options will not be supported anymore.")
71-
list.concat argument.keep_if { |key, value| value.present? }.to_a
72-
elsif argument.present?
73-
list << [name, argument]
62+
def transformer
63+
if ActiveStorage.variant_processor
64+
begin
65+
require "image_processing"
66+
rescue LoadError
67+
ActiveSupport::Deprecation.warn <<~WARNING
68+
Generating image variants will require the image_processing gem in Rails 6.1.
69+
Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.
70+
WARNING
71+
72+
ActiveStorage::Transformers::MiniMagickTransformer.new(transformations)
73+
else
74+
ActiveStorage::Transformers::ImageProcessingTransformer.new(transformations)
7475
end
75-
end
76-
77-
processor
78-
.source(file)
79-
.loader(page: 0)
80-
.convert(format)
81-
.apply(operations)
82-
.call
83-
end
84-
85-
# Applies image transformations using the MiniMagick gem.
86-
def mini_magick_transform(file, format)
87-
image = MiniMagick::Image.new(file.path, file)
88-
89-
transformations.each do |name, argument_or_subtransformations|
90-
image.mogrify do |command|
91-
if name.to_s == "combine_options"
92-
argument_or_subtransformations.each do |subtransformation_name, subtransformation_argument|
93-
pass_transform_argument(command, subtransformation_name, subtransformation_argument)
94-
end
95-
else
96-
pass_transform_argument(command, name, argument_or_subtransformations)
97-
end
98-
end
99-
end
100-
101-
image.format(format) if format
102-
103-
image.tempfile.tap(&:open)
104-
end
105-
106-
# Returns the ImageProcessing processor class specified by `ActiveStorage.variant_processor`.
107-
def processor
108-
begin
109-
require "image_processing"
110-
rescue LoadError
111-
ActiveSupport::Deprecation.warn("Using mini_magick gem directly is deprecated and will be removed in Rails 6.1. Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.")
112-
return nil
113-
end
114-
115-
ImageProcessing.const_get(ActiveStorage.variant_processor.to_s.camelize) if ActiveStorage.variant_processor
116-
end
117-
118-
def pass_transform_argument(command, method, argument)
119-
if argument == true
120-
command.public_send(method)
121-
elsif argument.present?
122-
command.public_send(method, argument)
76+
else
77+
ActiveStorage::Transformers::MiniMagickTransformer.new(transformations)
12378
end
12479
end
12580
end

activestorage/lib/active_storage.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,12 @@ module ActiveStorage
5050
mattr_accessor :variable_content_types, default: []
5151
mattr_accessor :content_types_to_serve_as_binary, default: []
5252
mattr_accessor :service_urls_expire_in, default: 5.minutes
53+
54+
module Transformers
55+
extend ActiveSupport::Autoload
56+
57+
autoload :Transformer
58+
autoload :ImageProcessingTransformer
59+
autoload :MiniMagickTransformer
60+
end
5361
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# frozen_string_literal: true
2+
3+
require "image_processing"
4+
5+
module ActiveStorage
6+
module Transformers
7+
class ImageProcessingTransformer < Transformer
8+
private
9+
def process(file, format:)
10+
processor.
11+
source(file).
12+
loader(page: 0).
13+
convert(format).
14+
apply(operations).
15+
call
16+
end
17+
18+
def processor
19+
ImageProcessing.const_get(ActiveStorage.variant_processor.to_s.camelize)
20+
end
21+
22+
def operations
23+
transformations.each_with_object([]) do |(name, argument), list|
24+
if name.to_s == "combine_options"
25+
ActiveSupport::Deprecation.warn <<~WARNING
26+
Active Storage's ImageProcessing transformer doesn't support :combine_options,
27+
as it always generates a single ImageMagick command. Passing :combine_options will
28+
not be supported in Rails 6.1.
29+
WARNING
30+
31+
list.concat argument.keep_if { |key, value| value.present? }.to_a
32+
elsif argument.present?
33+
list << [ name, argument ]
34+
end
35+
end
36+
end
37+
end
38+
end
39+
end
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# frozen_string_literal: true
2+
3+
require "mini_magick"
4+
5+
module ActiveStorage
6+
module Transformers
7+
class MiniMagickTransformer < Transformer
8+
private
9+
def process(file, format:)
10+
image = MiniMagick::Image.new(file.path, file)
11+
12+
transformations.each do |name, argument_or_subtransformations|
13+
image.mogrify do |command|
14+
if name.to_s == "combine_options"
15+
argument_or_subtransformations.each do |subtransformation_name, subtransformation_argument|
16+
pass_transform_argument(command, subtransformation_name, subtransformation_argument)
17+
end
18+
else
19+
pass_transform_argument(command, name, argument_or_subtransformations)
20+
end
21+
end
22+
end
23+
24+
image.format(format) if format
25+
26+
image.tempfile.tap(&:open)
27+
end
28+
29+
def pass_transform_argument(command, method, argument)
30+
if argument == true
31+
command.public_send(method)
32+
elsif argument.present?
33+
command.public_send(method, argument)
34+
end
35+
end
36+
end
37+
end
38+
end
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# frozen_string_literal: true
2+
3+
module ActiveStorage
4+
module Transformers
5+
# A Transformer applies a set of transformations to an image.
6+
#
7+
# The following concrete subclasses are included in Active Storage:
8+
#
9+
# * ActiveStorage::Transformers::ImageProcessingTransformer:
10+
# backed by ImageProcessing, a common interface for MiniMagick and ruby-vips
11+
#
12+
# * ActiveStorage::Transformers::MiniMagickTransformer:
13+
# backed by MiniMagick, a wrapper around the ImageMagick CLI
14+
class Transformer
15+
attr_reader :transformations
16+
17+
def initialize(transformations)
18+
@transformations = transformations
19+
end
20+
21+
# Applies the transformations to the source image in +file+, producing a target image in the
22+
# specified +format+. Yields an open Tempfile containing the target image. Closes and unlinks
23+
# the output tempfile after yielding to the given block. Returns the result of the block.
24+
def transform(file, format:)
25+
output = process(file, format: format)
26+
27+
begin
28+
yield output
29+
ensure
30+
output.close!
31+
end
32+
end
33+
34+
private
35+
# Returns an open Tempfile containing a transformed image in the given +format+.
36+
# All subclasses implement this method.
37+
def process(file, format:) #:doc:
38+
raise NotImplementedError
39+
end
40+
end
41+
end
42+
end

0 commit comments

Comments
 (0)