carrierwave 0.11.2 → 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +452 -178
  3. data/lib/carrierwave/compatibility/paperclip.rb +4 -4
  4. data/lib/carrierwave/downloader/base.rb +101 -0
  5. data/lib/carrierwave/downloader/remote_file.rb +68 -0
  6. data/lib/carrierwave/error.rb +1 -0
  7. data/lib/carrierwave/locale/en.yml +11 -5
  8. data/lib/carrierwave/mount.rb +217 -182
  9. data/lib/carrierwave/mounter.rb +255 -0
  10. data/lib/carrierwave/orm/activerecord.rb +29 -35
  11. data/lib/carrierwave/processing/mini_magick.rb +140 -84
  12. data/lib/carrierwave/processing/rmagick.rb +72 -21
  13. data/lib/carrierwave/processing/vips.rb +284 -0
  14. data/lib/carrierwave/processing.rb +1 -1
  15. data/lib/carrierwave/sanitized_file.rb +83 -84
  16. data/lib/carrierwave/storage/abstract.rb +16 -3
  17. data/lib/carrierwave/storage/file.rb +71 -3
  18. data/lib/carrierwave/storage/fog.rb +215 -57
  19. data/lib/carrierwave/storage.rb +1 -9
  20. data/lib/carrierwave/test/matchers.rb +88 -19
  21. data/lib/carrierwave/uploader/cache.rb +75 -45
  22. data/lib/carrierwave/uploader/callbacks.rb +1 -3
  23. data/lib/carrierwave/uploader/configuration.rb +80 -16
  24. data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
  25. data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
  26. data/lib/carrierwave/uploader/default_url.rb +3 -5
  27. data/lib/carrierwave/uploader/dimension.rb +66 -0
  28. data/lib/carrierwave/uploader/download.rb +4 -74
  29. data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
  30. data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
  31. data/lib/carrierwave/uploader/file_size.rb +43 -0
  32. data/lib/carrierwave/uploader/mountable.rb +13 -8
  33. data/lib/carrierwave/uploader/processing.rb +48 -13
  34. data/lib/carrierwave/uploader/proxy.rb +20 -9
  35. data/lib/carrierwave/uploader/remove.rb +0 -2
  36. data/lib/carrierwave/uploader/serialization.rb +2 -4
  37. data/lib/carrierwave/uploader/store.rb +59 -28
  38. data/lib/carrierwave/uploader/url.rb +8 -7
  39. data/lib/carrierwave/uploader/versions.rb +171 -123
  40. data/lib/carrierwave/uploader.rb +12 -10
  41. data/lib/carrierwave/utilities/file_name.rb +47 -0
  42. data/lib/carrierwave/utilities/uri.rb +14 -12
  43. data/lib/carrierwave/utilities.rb +1 -3
  44. data/lib/carrierwave/validations/active_model.rb +7 -11
  45. data/lib/carrierwave/version.rb +1 -1
  46. data/lib/carrierwave.rb +39 -21
  47. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +6 -10
  48. data/lib/generators/uploader_generator.rb +3 -3
  49. metadata +135 -83
  50. data/lib/carrierwave/locale/cs.yml +0 -11
  51. data/lib/carrierwave/locale/de.yml +0 -11
  52. data/lib/carrierwave/locale/el.yml +0 -11
  53. data/lib/carrierwave/locale/es.yml +0 -11
  54. data/lib/carrierwave/locale/fr.yml +0 -11
  55. data/lib/carrierwave/locale/ja.yml +0 -11
  56. data/lib/carrierwave/locale/nb.yml +0 -11
  57. data/lib/carrierwave/locale/nl.yml +0 -11
  58. data/lib/carrierwave/locale/pl.yml +0 -11
  59. data/lib/carrierwave/locale/pt-BR.yml +0 -11
  60. data/lib/carrierwave/locale/pt-PT.yml +0 -11
  61. data/lib/carrierwave/locale/ru.yml +0 -11
  62. data/lib/carrierwave/locale/sk.yml +0 -11
  63. data/lib/carrierwave/locale/tr.yml +0 -11
  64. data/lib/carrierwave/processing/mime_types.rb +0 -74
  65. data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
  66. data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
  67. data/lib/carrierwave/uploader/extension_blacklist.rb +0 -47
  68. data/lib/carrierwave/uploader/extension_whitelist.rb +0 -49
  69. data/lib/carrierwave/utilities/deprecation.rb +0 -18
@@ -0,0 +1,255 @@
1
+ module CarrierWave
2
+
3
+ # this is an internal class, used by CarrierWave::Mount so that
4
+ # we don't pollute the model with a lot of methods.
5
+ class Mounter # :nodoc:
6
+ class Single < Mounter # :nodoc
7
+ def identifier
8
+ uploaders.first&.identifier
9
+ end
10
+
11
+ def temporary_identifier
12
+ temporary_identifiers.first
13
+ end
14
+ end
15
+
16
+ class Multiple < Mounter # :nodoc
17
+ def identifier
18
+ uploaders.map(&:identifier).presence
19
+ end
20
+
21
+ def temporary_identifier
22
+ temporary_identifiers.presence
23
+ end
24
+ end
25
+
26
+ def self.build(record, column)
27
+ if record.class.uploader_options[column][:multiple]
28
+ Multiple.new(record, column)
29
+ else
30
+ Single.new(record, column)
31
+ end
32
+ end
33
+
34
+ attr_reader :column, :record, :remote_urls, :remove,
35
+ :integrity_errors, :processing_errors, :download_errors
36
+ attr_accessor :remote_request_headers, :uploader_options
37
+
38
+ def initialize(record, column)
39
+ @record = record
40
+ @column = column
41
+ @options = record.class.uploader_options[column]
42
+ @download_errors = []
43
+ @processing_errors = []
44
+ @integrity_errors = []
45
+
46
+ @removed_uploaders = []
47
+ @added_uploaders = []
48
+ end
49
+
50
+ def uploader_class
51
+ record.class.uploaders[column]
52
+ end
53
+
54
+ def blank_uploader
55
+ uploader_class.new(record, column)
56
+ end
57
+
58
+ def identifiers
59
+ uploaders.map(&:identifier)
60
+ end
61
+
62
+ def read_identifiers
63
+ [record.read_uploader(serialization_column)].flatten.reject(&:blank?)
64
+ end
65
+
66
+ def uploaders
67
+ @uploaders ||= read_identifiers.map do |identifier|
68
+ uploader = blank_uploader
69
+ uploader.retrieve_from_store!(identifier)
70
+ uploader
71
+ end
72
+ end
73
+
74
+ def cache(new_files)
75
+ return if !new_files.is_a?(Array) && new_files.blank?
76
+ old_uploaders = uploaders
77
+ @uploaders = new_files.map do |new_file|
78
+ handle_error do
79
+ if new_file.is_a?(String)
80
+ if (uploader = old_uploaders.detect { |old_uploader| old_uploader.identifier == new_file })
81
+ uploader.staged = true
82
+ uploader
83
+ else
84
+ begin
85
+ uploader = blank_uploader
86
+ uploader.retrieve_from_cache!(new_file)
87
+ uploader
88
+ rescue CarrierWave::InvalidParameter
89
+ nil
90
+ end
91
+ end
92
+ else
93
+ uploader = blank_uploader
94
+ uploader.cache!(new_file)
95
+ uploader
96
+ end
97
+ end
98
+ end.reject(&:blank?)
99
+ @removed_uploaders += (old_uploaders - @uploaders)
100
+ write_temporary_identifier
101
+ end
102
+
103
+ def cache_names
104
+ uploaders.map(&:cache_name).compact
105
+ end
106
+
107
+ def cache_names=(cache_names)
108
+ cache_names = cache_names.reject(&:blank?)
109
+ return if cache_names.blank?
110
+ clear_unstaged
111
+ cache_names.each do |cache_name|
112
+ uploader = blank_uploader
113
+ uploader.retrieve_from_cache!(cache_name)
114
+ @uploaders << uploader
115
+ rescue CarrierWave::InvalidParameter
116
+ # ignore
117
+ end
118
+ write_temporary_identifier
119
+ end
120
+
121
+ def remote_urls=(urls)
122
+ if urls.nil?
123
+ urls = []
124
+ else
125
+ urls = Array.wrap(urls).reject(&:blank?)
126
+ return if urls.blank?
127
+ end
128
+ @remote_urls = urls
129
+
130
+ clear_unstaged
131
+ @remote_urls.zip(remote_request_headers || []) do |url, header|
132
+ handle_error do
133
+ uploader = blank_uploader
134
+ uploader.download!(url, header || {})
135
+ @uploaders << uploader
136
+ end
137
+ end
138
+ write_temporary_identifier
139
+ end
140
+
141
+ def store!
142
+ uploaders.each(&:store!)
143
+ end
144
+
145
+ def write_identifier
146
+ return if record.frozen?
147
+
148
+ clear! if remove?
149
+
150
+ additions, remains = uploaders.partition(&:cached?)
151
+ existing_identifiers = (@removed_uploaders + remains).map(&:identifier)
152
+ additions.each do |uploader|
153
+ uploader.deduplicate(existing_identifiers)
154
+ existing_identifiers << uploader.identifier
155
+ end
156
+ @added_uploaders += additions
157
+
158
+ record.write_uploader(serialization_column, identifier)
159
+ end
160
+
161
+ def urls(*args)
162
+ uploaders.map { |u| u.url(*args) }
163
+ end
164
+
165
+ def blank?
166
+ uploaders.none?(&:present?)
167
+ end
168
+
169
+ def remove=(value)
170
+ @remove = value
171
+ write_temporary_identifier
172
+ end
173
+
174
+ def remove?
175
+ remove.present? && (remove.to_s !~ /\A0|false$\z/)
176
+ end
177
+
178
+ def remove!
179
+ uploaders.each(&:remove!)
180
+ clear!
181
+ end
182
+
183
+ def clear!
184
+ @removed_uploaders += uploaders
185
+ @remove = nil
186
+ @uploaders = []
187
+ end
188
+
189
+ def reset_changes!
190
+ @removed_uploaders = []
191
+ @added_uploaders = []
192
+ end
193
+
194
+ def serialization_column
195
+ option(:mount_on) || column
196
+ end
197
+
198
+ def remove_previous
199
+ current_paths = uploaders.map(&:path)
200
+ @removed_uploaders
201
+ .reject {|uploader| current_paths.include?(uploader.path) }
202
+ .each { |uploader| uploader.remove! if uploader.remove_previously_stored_files_after_update }
203
+ reset_changes!
204
+ end
205
+
206
+ def remove_added
207
+ current_paths = (@removed_uploaders + uploaders.select(&:staged)).map(&:path)
208
+ @added_uploaders
209
+ .reject {|uploader| current_paths.include?(uploader.path) }
210
+ .each { |uploader| uploader.remove! }
211
+ reset_changes!
212
+ end
213
+
214
+ private
215
+
216
+ def option(name)
217
+ self.uploader_options ||= {}
218
+ self.uploader_options[name] ||= record.class.uploader_option(column, name)
219
+ end
220
+
221
+ def clear_unstaged
222
+ @uploaders ||= []
223
+ staged, unstaged = @uploaders.partition(&:staged)
224
+ @uploaders = staged
225
+ @removed_uploaders += unstaged
226
+ end
227
+
228
+ def handle_error
229
+ yield
230
+ rescue CarrierWave::DownloadError => e
231
+ @download_errors << e
232
+ raise e unless option(:ignore_download_errors)
233
+ rescue CarrierWave::ProcessingError => e
234
+ @processing_errors << e
235
+ raise e unless option(:ignore_processing_errors)
236
+ rescue CarrierWave::IntegrityError => e
237
+ @integrity_errors << e
238
+ raise e unless option(:ignore_integrity_errors)
239
+ end
240
+
241
+ def write_temporary_identifier
242
+ return if record.frozen?
243
+
244
+ record.write_uploader(serialization_column, temporary_identifier)
245
+ end
246
+
247
+ def temporary_identifiers
248
+ if remove?
249
+ []
250
+ else
251
+ uploaders.map { |uploader| uploader.temporary_identifier }
252
+ end
253
+ end
254
+ end # Mounter
255
+ end # CarrierWave
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  require 'active_record'
4
2
  require 'carrierwave/validations/active_model'
5
3
 
@@ -8,10 +6,9 @@ module CarrierWave
8
6
 
9
7
  include CarrierWave::Mount
10
8
 
11
- ##
12
- # See +CarrierWave::Mount#mount_uploader+ for documentation
13
- #
14
- def mount_uploader(column, uploader=nil, options={}, &block)
9
+ private
10
+
11
+ def mount_base(column, uploader=nil, options={}, &block)
15
12
  super
16
13
 
17
14
  alias_method :read_uploader, :read_attribute
@@ -27,45 +24,42 @@ module CarrierWave
27
24
 
28
25
  after_save :"store_#{column}!"
29
26
  before_save :"write_#{column}_identifier"
27
+ if ::ActiveRecord.try(:run_after_transaction_callbacks_in_order_defined)
28
+ after_commit :"remove_previously_stored_#{column}", :on => :update
29
+ after_commit :"reset_previous_changes_for_#{column}"
30
+ after_commit :"mark_remove_#{column}_false", :on => :update
31
+ else
32
+ after_commit :"mark_remove_#{column}_false", :on => :update
33
+ after_commit :"reset_previous_changes_for_#{column}"
34
+ after_commit :"remove_previously_stored_#{column}", :on => :update
35
+ end
30
36
  after_commit :"remove_#{column}!", :on => :destroy
31
- after_commit :"mark_remove_#{column}_false", :on => :update
32
- before_update :"store_previous_model_for_#{column}"
33
- after_save :"remove_previously_stored_#{column}"
34
-
35
- class_eval <<-RUBY, __FILE__, __LINE__+1
36
- def #{column}=(new_file)
37
- column = _mounter(:#{column}).serialization_column
38
- send(:"\#{column}_will_change!")
37
+ after_rollback :"remove_rolled_back_#{column}"
38
+
39
+ mod = Module.new
40
+ prepend mod
41
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
42
+ # Reset cached mounter on record reload
43
+ def reload(*)
44
+ @_mounters = nil
39
45
  super
40
46
  end
41
47
 
42
- def remote_#{column}_url=(url)
43
- column = _mounter(:#{column}).serialization_column
44
- send(:"\#{column}_will_change!")
48
+ # Reset cached mounter on record dup
49
+ def initialize_dup(other)
50
+ old_uploaders = _mounter(:"#{column}").uploaders
45
51
  super
52
+ @_mounters[:"#{column}"] = nil
53
+ # The attribute needs to be cleared to prevent it from picked up as identifier
54
+ write_attribute(_mounter(:#{column}).serialization_column, nil)
55
+ _mounter(:"#{column}").cache(old_uploaders)
46
56
  end
47
57
 
48
- def remove_#{column}!
58
+ def write_#{column}_identifier
59
+ return unless has_attribute?(_mounter(:#{column}).serialization_column)
49
60
  super
50
- _mounter(:#{column}).remove = true
51
- _mounter(:#{column}).write_identifier
52
- end
53
-
54
- def serializable_hash(options=nil)
55
- hash = {}
56
-
57
- except = options && options[:except] && Array.wrap(options[:except]).map(&:to_s)
58
- only = options && options[:only] && Array.wrap(options[:only]).map(&:to_s)
59
-
60
- self.class.uploaders.each do |column, uploader|
61
- if (!only && !except) || (only && only.include?(column.to_s)) || (!only && except && !except.include?(column.to_s))
62
- hash[column.to_s] = _mounter(column).uploader.serializable_hash
63
- end
64
- end
65
- super(options).merge(hash)
66
61
  end
67
62
  RUBY
68
-
69
63
  end
70
64
 
71
65
  end # ActiveRecord
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module CarrierWave
4
2
 
5
3
  ##
@@ -23,9 +21,11 @@ module CarrierWave
23
21
  # process :resize_to_fit => [200, 200]
24
22
  # end
25
23
  #
26
- # Or create your own helpers with the powerful manipulate! method. Check
27
- # out the ImageMagick docs at http://www.imagemagick.org/script/command-line-options.php for more
28
- # info
24
+ # Or create your own helpers with the powerful minimagick! method, which
25
+ # yields an ImageProcessing::Builder object. Check out the ImageProcessing
26
+ # docs at http://github.com/janko-m/image_processing and the list of all
27
+ # available ImageMagick options at
28
+ # http://www.imagemagick.org/script/command-line-options.php for more info.
29
29
  #
30
30
  # class MyUploader < CarrierWave::Uploader::Base
31
31
  # include CarrierWave::MiniMagick
@@ -33,35 +33,30 @@ module CarrierWave
33
33
  # process :radial_blur => 10
34
34
  #
35
35
  # def radial_blur(amount)
36
- # manipulate! do |img|
37
- # img.radial_blur(amount)
38
- # img = yield(img) if block_given?
39
- # img
36
+ # minimagick! do |builder|
37
+ # builder.radial_blur(amount)
38
+ # builder = yield(builder) if block_given?
39
+ # builder
40
40
  # end
41
41
  # end
42
+ # end
42
43
  #
43
44
  # === Note
44
45
  #
45
- # MiniMagick is a mini replacement for RMagick that uses the command line
46
- # tool "mogrify" for image manipulation.
46
+ # The ImageProcessing gem uses MiniMagick, a mini replacement for RMagick
47
+ # that uses ImageMagick command-line tools, to build a "convert" command that
48
+ # performs the processing.
47
49
  #
48
50
  # You can find more information here:
49
51
  #
50
- # http://mini_magick.rubyforge.org/
51
- # and
52
- # https://github.com/minimagic/minimagick/
52
+ # https://github.com/minimagick/minimagick/
53
53
  #
54
54
  #
55
55
  module MiniMagick
56
56
  extend ActiveSupport::Concern
57
57
 
58
58
  included do
59
- begin
60
- require "mini_magick"
61
- rescue LoadError => e
62
- e.message << " (You may need to install the mini_magick gem)"
63
- raise e
64
- end
59
+ require "image_processing/mini_magick"
65
60
  end
66
61
 
67
62
  module ClassMethods
@@ -81,7 +76,7 @@ module CarrierWave
81
76
  process :resize_to_fill => [width, height, gravity]
82
77
  end
83
78
 
84
- def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
79
+ def resize_and_pad(width, height, background=:transparent, gravity='Center')
85
80
  process :resize_and_pad => [width, height, background, gravity]
86
81
  end
87
82
  end
@@ -93,7 +88,7 @@ module CarrierWave
93
88
  #
94
89
  # === Parameters
95
90
  #
96
- # [format (#to_s)] an abreviation of the format
91
+ # [format (#to_s)] an abbreviation of the format
97
92
  #
98
93
  # === Yields
99
94
  #
@@ -103,12 +98,11 @@ module CarrierWave
103
98
  #
104
99
  # image.convert(:png)
105
100
  #
106
- def convert(format)
107
- @format = format
108
- manipulate! do |img|
109
- img.format(format.to_s.downcase)
110
- img = yield(img) if block_given?
111
- img
101
+ def convert(format, page=nil, &block)
102
+ minimagick!(block) do |builder|
103
+ builder = builder.convert(format)
104
+ builder = builder.loader(page: page) if page
105
+ builder
112
106
  end
113
107
  end
114
108
 
@@ -122,16 +116,18 @@ module CarrierWave
122
116
  #
123
117
  # [width (Integer)] the width to scale the image to
124
118
  # [height (Integer)] the height to scale the image to
119
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
125
120
  #
126
121
  # === Yields
127
122
  #
128
123
  # [MiniMagick::Image] additional manipulations to perform
129
124
  #
130
- def resize_to_limit(width, height)
131
- manipulate! do |img|
132
- img.resize "#{width}x#{height}>"
133
- img = yield(img) if block_given?
134
- img
125
+ def resize_to_limit(width, height, combine_options: {}, &block)
126
+ width, height = resolve_dimensions(width, height)
127
+
128
+ minimagick!(block) do |builder|
129
+ builder.resize_to_limit(width, height)
130
+ .apply(combine_options)
135
131
  end
136
132
  end
137
133
 
@@ -144,16 +140,18 @@ module CarrierWave
144
140
  #
145
141
  # [width (Integer)] the width to scale the image to
146
142
  # [height (Integer)] the height to scale the image to
143
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
147
144
  #
148
145
  # === Yields
149
146
  #
150
147
  # [MiniMagick::Image] additional manipulations to perform
151
148
  #
152
- def resize_to_fit(width, height)
153
- manipulate! do |img|
154
- img.resize "#{width}x#{height}"
155
- img = yield(img) if block_given?
156
- img
149
+ def resize_to_fit(width, height, combine_options: {}, &block)
150
+ width, height = resolve_dimensions(width, height)
151
+
152
+ minimagick!(block) do |builder|
153
+ builder.resize_to_fit(width, height)
154
+ .apply(combine_options)
157
155
  end
158
156
  end
159
157
 
@@ -167,34 +165,18 @@ module CarrierWave
167
165
  # [width (Integer)] the width to scale the image to
168
166
  # [height (Integer)] the height to scale the image to
169
167
  # [gravity (String)] the current gravity suggestion (default: 'Center'; options: 'NorthWest', 'North', 'NorthEast', 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast')
168
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
170
169
  #
171
170
  # === Yields
172
171
  #
173
172
  # [MiniMagick::Image] additional manipulations to perform
174
173
  #
175
- def resize_to_fill(width, height, gravity = 'Center')
176
- manipulate! do |img|
177
- cols, rows = img[:dimensions]
178
- img.combine_options do |cmd|
179
- if width != cols || height != rows
180
- scale_x = width/cols.to_f
181
- scale_y = height/rows.to_f
182
- if scale_x >= scale_y
183
- cols = (scale_x * (cols + 0.5)).round
184
- rows = (scale_x * (rows + 0.5)).round
185
- cmd.resize "#{cols}"
186
- else
187
- cols = (scale_y * (cols + 0.5)).round
188
- rows = (scale_y * (rows + 0.5)).round
189
- cmd.resize "x#{rows}"
190
- end
191
- end
192
- cmd.gravity gravity
193
- cmd.background "rgba(255,255,255,0.0)"
194
- cmd.extent "#{width}x#{height}" if cols != width || rows != height
195
- end
196
- img = yield(img) if block_given?
197
- img
174
+ def resize_to_fill(width, height, gravity = 'Center', combine_options: {}, &block)
175
+ width, height = resolve_dimensions(width, height)
176
+
177
+ minimagick!(block) do |builder|
178
+ builder.resize_to_fill(width, height, gravity: gravity)
179
+ .apply(combine_options)
198
180
  end
199
181
  end
200
182
 
@@ -213,33 +195,51 @@ module CarrierWave
213
195
  # [height (Integer)] the height to scale the image to
214
196
  # [background (String, :transparent)] the color of the background as a hexcode, like "#ff45de"
215
197
  # [gravity (String)] how to position the image
198
+ # [combine_options (Hash)] additional ImageMagick options to apply before resizing
216
199
  #
217
200
  # === Yields
218
201
  #
219
202
  # [MiniMagick::Image] additional manipulations to perform
220
203
  #
221
- def resize_and_pad(width, height, background=:transparent, gravity='Center')
222
- manipulate! do |img|
223
- img.combine_options do |cmd|
224
- cmd.thumbnail "#{width}x#{height}>"
225
- if background == :transparent
226
- cmd.background "rgba(255, 255, 255, 0.0)"
227
- else
228
- cmd.background background
229
- end
230
- cmd.gravity gravity
231
- cmd.extent "#{width}x#{height}"
232
- end
233
- img = yield(img) if block_given?
234
- img
204
+ def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {}, &block)
205
+ width, height = resolve_dimensions(width, height)
206
+
207
+ minimagick!(block) do |builder|
208
+ builder.resize_and_pad(width, height, background: background, gravity: gravity)
209
+ .apply(combine_options)
235
210
  end
236
211
  end
237
212
 
213
+ ##
214
+ # Returns the width of the image in pixels.
215
+ #
216
+ # === Returns
217
+ #
218
+ # [Integer] the image's width in pixels
219
+ #
220
+ def width
221
+ mini_magick_image[:width]
222
+ end
223
+
224
+ ##
225
+ # Returns the height of the image in pixels.
226
+ #
227
+ # === Returns
228
+ #
229
+ # [Integer] the image's height in pixels
230
+ #
231
+ def height
232
+ mini_magick_image[:height]
233
+ end
234
+
238
235
  ##
239
236
  # Manipulate the image with MiniMagick. This method will load up an image
240
237
  # and then pass each of its frames to the supplied block. It will then
241
238
  # save the image to disk.
242
239
  #
240
+ # NOTE: This method exists mostly for backwards compatibility, you should
241
+ # probably use #minimagick!.
242
+ #
243
243
  # === Gotcha
244
244
  #
245
245
  # This method assumes that the object responds to +current_path+.
@@ -259,19 +259,75 @@ module CarrierWave
259
259
  cache_stored_file! if !cached?
260
260
  image = ::MiniMagick::Image.open(current_path)
261
261
 
262
- begin
263
- image.format(@format.to_s.downcase) if @format
264
- image = yield(image)
265
- image.write(current_path)
266
- image.run_command("identify", current_path)
267
- ensure
268
- image.destroy!
262
+ image = yield(image)
263
+ FileUtils.mv image.path, current_path
264
+
265
+ image.run_command("identify", current_path)
266
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
267
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
268
+ message = I18n.translate(:"errors.messages.processing_error")
269
+ raise CarrierWave::ProcessingError, message
270
+ ensure
271
+ image.destroy! if image
272
+ end
273
+
274
+ # Process the image with MiniMagick, using the ImageProcessing gem. This
275
+ # method will build a "convert" ImageMagick command and execute it on the
276
+ # current image.
277
+ #
278
+ # === Gotcha
279
+ #
280
+ # This method assumes that the object responds to +current_path+.
281
+ # Any class that this module is mixed into must have a +current_path+ method.
282
+ # CarrierWave::Uploader does, so you won't need to worry about this in
283
+ # most cases.
284
+ #
285
+ # === Yields
286
+ #
287
+ # [ImageProcessing::Builder] use it to define processing to be performed
288
+ #
289
+ # === Raises
290
+ #
291
+ # [CarrierWave::ProcessingError] if processing failed.
292
+ def minimagick!(block = nil)
293
+ builder = ImageProcessing::MiniMagick.source(current_path)
294
+ builder = yield(builder)
295
+
296
+ result = builder.call
297
+ result.close
298
+
299
+ # backwards compatibility (we want to eventually move away from MiniMagick::Image)
300
+ if block
301
+ image = ::MiniMagick::Image.new(result.path, result)
302
+ image = block.call(image)
303
+ result = image.instance_variable_get(:@tempfile)
304
+ end
305
+
306
+ FileUtils.mv result.path, current_path
307
+
308
+ if File.extname(result.path) != File.extname(current_path)
309
+ move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
310
+ file.content_type = Marcel::Magic.by_path(move_to).try(:type)
311
+ file.move_to(move_to, permissions, directory_permissions)
269
312
  end
270
313
  rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
271
- default = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :locale => :en)
272
- message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :default => default)
314
+ raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
315
+ message = I18n.translate(:"errors.messages.processing_error")
273
316
  raise CarrierWave::ProcessingError, message
274
317
  end
275
318
 
319
+ private
320
+
321
+ def resolve_dimensions(*dimensions)
322
+ dimensions.map do |value|
323
+ next value unless value.instance_of?(Proc)
324
+ value.arity >= 1 ? value.call(self) : value.call
325
+ end
326
+ end
327
+
328
+ def mini_magick_image
329
+ ::MiniMagick::Image.read(read)
330
+ end
331
+
276
332
  end # MiniMagick
277
333
  end # CarrierWave