carrierwave 0.11.2 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of carrierwave might be problematic. Click here for more details.

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 +212 -182
  9. data/lib/carrierwave/mounter.rb +255 -0
  10. data/lib/carrierwave/orm/activerecord.rb +22 -33
  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 +170 -122
  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} +5 -9
  48. data/lib/generators/uploader_generator.rb +3 -3
  49. metadata +132 -80
  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
@@ -29,43 +26,35 @@ module CarrierWave
29
26
  before_save :"write_#{column}_identifier"
30
27
  after_commit :"remove_#{column}!", :on => :destroy
31
28
  after_commit :"mark_remove_#{column}_false", :on => :update
32
- before_update :"store_previous_model_for_#{column}"
33
- after_save :"remove_previously_stored_#{column}"
34
29
 
35
- class_eval <<-RUBY, __FILE__, __LINE__+1
36
- def #{column}=(new_file)
37
- column = _mounter(:#{column}).serialization_column
38
- send(:"\#{column}_will_change!")
39
- super
40
- end
30
+ after_commit :"reset_previous_changes_for_#{column}"
31
+ after_commit :"remove_previously_stored_#{column}", :on => :update
32
+ after_rollback :"remove_rolled_back_#{column}"
41
33
 
42
- def remote_#{column}_url=(url)
43
- column = _mounter(:#{column}).serialization_column
44
- send(:"\#{column}_will_change!")
34
+ mod = Module.new
35
+ prepend mod
36
+ mod.class_eval <<-RUBY, __FILE__, __LINE__+1
37
+ # Reset cached mounter on record reload
38
+ def reload(*)
39
+ @_mounters = nil
45
40
  super
46
41
  end
47
42
 
48
- def remove_#{column}!
43
+ # Reset cached mounter on record dup
44
+ def initialize_dup(other)
45
+ old_uploaders = _mounter(:"#{column}").uploaders
46
+ @_mounters[:"#{column}"] = nil
49
47
  super
50
- _mounter(:#{column}).remove = true
51
- _mounter(:#{column}).write_identifier
48
+ # The attribute needs to be cleared to prevent it from picked up as identifier
49
+ write_attribute(:"#{column}", nil)
50
+ _mounter(:"#{column}").cache(old_uploaders)
52
51
  end
53
52
 
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)
53
+ def write_#{column}_identifier
54
+ return unless has_attribute?(_mounter(:#{column}).serialization_column)
55
+ super
66
56
  end
67
57
  RUBY
68
-
69
58
  end
70
59
 
71
60
  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