carrierwave 1.3.4 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +278 -86
- data/lib/carrierwave/compatibility/paperclip.rb +4 -2
- data/lib/carrierwave/downloader/base.rb +101 -0
- data/lib/carrierwave/downloader/remote_file.rb +68 -0
- data/lib/carrierwave/locale/en.yml +9 -6
- data/lib/carrierwave/mount.rb +53 -61
- data/lib/carrierwave/mounter.rb +169 -77
- data/lib/carrierwave/orm/activerecord.rb +23 -58
- data/lib/carrierwave/processing/mini_magick.rb +137 -123
- data/lib/carrierwave/processing/rmagick.rb +47 -20
- data/lib/carrierwave/processing/vips.rb +315 -0
- data/lib/carrierwave/processing.rb +1 -0
- data/lib/carrierwave/sanitized_file.rb +83 -70
- data/lib/carrierwave/storage/abstract.rb +5 -5
- data/lib/carrierwave/storage/file.rb +6 -5
- data/lib/carrierwave/storage/fog.rb +114 -62
- data/lib/carrierwave/storage.rb +1 -0
- data/lib/carrierwave/test/matchers.rb +11 -7
- data/lib/carrierwave/uploader/cache.rb +40 -24
- data/lib/carrierwave/uploader/callbacks.rb +1 -1
- data/lib/carrierwave/uploader/configuration.rb +42 -19
- data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
- data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
- data/lib/carrierwave/uploader/dimension.rb +66 -0
- data/lib/carrierwave/uploader/download.rb +2 -123
- data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
- data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
- data/lib/carrierwave/uploader/file_size.rb +2 -2
- data/lib/carrierwave/uploader/mountable.rb +6 -0
- data/lib/carrierwave/uploader/processing.rb +45 -7
- data/lib/carrierwave/uploader/proxy.rb +17 -4
- data/lib/carrierwave/uploader/serialization.rb +1 -1
- data/lib/carrierwave/uploader/store.rb +73 -7
- data/lib/carrierwave/uploader/url.rb +7 -4
- data/lib/carrierwave/uploader/versions.rb +161 -111
- data/lib/carrierwave/uploader.rb +10 -17
- data/lib/carrierwave/utilities/file_name.rb +47 -0
- data/lib/carrierwave/utilities/uri.rb +14 -11
- data/lib/carrierwave/utilities.rb +1 -0
- data/lib/carrierwave/validations/active_model.rb +7 -9
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +22 -17
- data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +5 -4
- data/lib/generators/uploader_generator.rb +3 -3
- metadata +94 -47
- data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
- data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
- data/lib/carrierwave/uploader/extension_blacklist.rb +0 -51
- data/lib/carrierwave/uploader/extension_whitelist.rb +0 -52
|
@@ -18,7 +18,7 @@ module CarrierWave
|
|
|
18
18
|
# Adds a processor callback which applies operations as a file is uploaded.
|
|
19
19
|
# The argument may be the name of any method of the uploader, expressed as a symbol,
|
|
20
20
|
# or a list of such methods, or a hash where the key is a method and the value is
|
|
21
|
-
# an array of arguments to call the method with
|
|
21
|
+
# an array of arguments to call the method with. Also accepts an :if or :unless condition
|
|
22
22
|
#
|
|
23
23
|
# === Parameters
|
|
24
24
|
#
|
|
@@ -31,6 +31,7 @@ module CarrierWave
|
|
|
31
31
|
# process :sepiatone, :vignette
|
|
32
32
|
# process :scale => [200, 200]
|
|
33
33
|
# process :scale => [200, 200], :if => :image?
|
|
34
|
+
# process :scale => [200, 200], :unless => :disallowed_image_type?
|
|
34
35
|
# process :sepiatone, :if => :image?
|
|
35
36
|
#
|
|
36
37
|
# def sepiatone
|
|
@@ -49,6 +50,10 @@ module CarrierWave
|
|
|
49
50
|
# ...
|
|
50
51
|
# end
|
|
51
52
|
#
|
|
53
|
+
# def disallowed_image_type?
|
|
54
|
+
# ...
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
52
57
|
# end
|
|
53
58
|
#
|
|
54
59
|
def process(*args)
|
|
@@ -57,12 +62,20 @@ module CarrierWave
|
|
|
57
62
|
hash.merge!(arg)
|
|
58
63
|
end
|
|
59
64
|
|
|
60
|
-
|
|
65
|
+
condition_type = new_processors.keys.detect { |key| [:if, :unless].include?(key) }
|
|
66
|
+
condition = new_processors.delete(:if) || new_processors.delete(:unless)
|
|
61
67
|
new_processors.each do |processor, processor_args|
|
|
62
|
-
self.processors += [[processor, processor_args, condition]]
|
|
68
|
+
self.processors += [[processor, processor_args, condition, condition_type]]
|
|
69
|
+
|
|
70
|
+
if processor == :convert
|
|
71
|
+
# Treat :convert specially, since it should trigger the file extension change
|
|
72
|
+
force_extension processor_args
|
|
73
|
+
if condition
|
|
74
|
+
warn "Use of 'process convert: format' with conditionals has an issue and doesn't work correctly. See https://github.com/carrierwaveuploader/carrierwave/issues/2723 for details. "
|
|
75
|
+
end
|
|
76
|
+
end
|
|
63
77
|
end
|
|
64
78
|
end
|
|
65
|
-
|
|
66
79
|
end # ClassMethods
|
|
67
80
|
|
|
68
81
|
##
|
|
@@ -72,19 +85,44 @@ module CarrierWave
|
|
|
72
85
|
return unless enable_processing
|
|
73
86
|
|
|
74
87
|
with_callbacks(:process, new_file) do
|
|
75
|
-
self.class.processors.each do |method, args, condition|
|
|
76
|
-
if
|
|
88
|
+
self.class.processors.each do |method, args, condition, condition_type|
|
|
89
|
+
if condition && condition_type == :if
|
|
77
90
|
if condition.respond_to?(:call)
|
|
78
91
|
next unless condition.call(self, :args => args, :method => method, :file => new_file)
|
|
79
92
|
else
|
|
80
93
|
next unless self.send(condition, new_file)
|
|
81
94
|
end
|
|
95
|
+
elsif condition && condition_type == :unless
|
|
96
|
+
if condition.respond_to?(:call)
|
|
97
|
+
next if condition.call(self, :args => args, :method => method, :file => new_file)
|
|
98
|
+
elsif self.send(condition, new_file)
|
|
99
|
+
next
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if args.is_a? Array
|
|
104
|
+
kwargs, args = args.partition { |arg| arg.is_a? Hash }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
if kwargs.present?
|
|
108
|
+
kwargs = kwargs.reduce(:merge)
|
|
109
|
+
self.send(method, *args, **kwargs)
|
|
110
|
+
else
|
|
111
|
+
self.send(method, *args)
|
|
82
112
|
end
|
|
83
|
-
self.send(method, *args)
|
|
84
113
|
end
|
|
85
114
|
end
|
|
86
115
|
end
|
|
87
116
|
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def forcing_extension(filename)
|
|
120
|
+
if force_extension && filename
|
|
121
|
+
Pathname.new(filename).sub_ext(".#{force_extension.to_s.delete_prefix('.')}").to_s
|
|
122
|
+
else
|
|
123
|
+
filename
|
|
124
|
+
end
|
|
125
|
+
end
|
|
88
126
|
end # Processing
|
|
89
127
|
end # Uploader
|
|
90
128
|
end # CarrierWave
|
|
@@ -23,14 +23,27 @@ module CarrierWave
|
|
|
23
23
|
alias_method :path, :current_path
|
|
24
24
|
|
|
25
25
|
##
|
|
26
|
-
# Returns a string that uniquely identifies the last stored file
|
|
26
|
+
# Returns a string that uniquely identifies the retrieved or last stored file
|
|
27
27
|
#
|
|
28
28
|
# === Returns
|
|
29
29
|
#
|
|
30
30
|
# [String] uniquely identifies a file
|
|
31
31
|
#
|
|
32
32
|
def identifier
|
|
33
|
-
storage.try(:identifier)
|
|
33
|
+
@identifier || (file && storage.try(:identifier))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# Returns a String which is to be used as a temporary value which gets assigned to the column.
|
|
38
|
+
# The purpose is to mark the column that it will be updated. Finally before the save it will be
|
|
39
|
+
# overwritten by the #identifier value, which is usually #filename.
|
|
40
|
+
#
|
|
41
|
+
# === Returns
|
|
42
|
+
#
|
|
43
|
+
# [String] a temporary_identifier, by default the value of #cache_name is used
|
|
44
|
+
#
|
|
45
|
+
def temporary_identifier
|
|
46
|
+
cache_name || @identifier
|
|
34
47
|
end
|
|
35
48
|
|
|
36
49
|
##
|
|
@@ -40,8 +53,8 @@ module CarrierWave
|
|
|
40
53
|
#
|
|
41
54
|
# [String] contents of the file
|
|
42
55
|
#
|
|
43
|
-
def read
|
|
44
|
-
file.try(:read)
|
|
56
|
+
def read(*args)
|
|
57
|
+
file.try(:read, *args)
|
|
45
58
|
end
|
|
46
59
|
|
|
47
60
|
##
|
|
@@ -7,7 +7,7 @@ module CarrierWave
|
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
8
|
|
|
9
9
|
def serializable_hash(options = nil)
|
|
10
|
-
{"url" => url}.merge Hash[versions.map { |name, version| [name, { "url" => version.url }] }]
|
|
10
|
+
{"url" => url}.merge Hash[versions.map { |name, version| [name.to_s, { "url" => version.url }] }]
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def as_json(options=nil)
|
|
@@ -11,9 +11,23 @@ module CarrierWave
|
|
|
11
11
|
prepend Module.new {
|
|
12
12
|
def initialize(*)
|
|
13
13
|
super
|
|
14
|
-
@file, @filename, @cache_id = nil
|
|
14
|
+
@file, @filename, @cache_id, @identifier, @deduplication_index = nil
|
|
15
15
|
end
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
after :store, :show_warning_when_filename_is_unavailable
|
|
19
|
+
|
|
20
|
+
class_attribute :filename_safeguard_checked
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module ClassMethods
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def inherited(subclass)
|
|
27
|
+
# To perform the filename safeguard check once per a class
|
|
28
|
+
self.filename_safeguard_checked = false
|
|
29
|
+
super
|
|
30
|
+
end
|
|
17
31
|
end
|
|
18
32
|
|
|
19
33
|
##
|
|
@@ -34,9 +48,26 @@ module CarrierWave
|
|
|
34
48
|
@filename
|
|
35
49
|
end
|
|
36
50
|
|
|
51
|
+
##
|
|
52
|
+
# Returns a filename which doesn't conflict with already-stored files.
|
|
53
|
+
#
|
|
54
|
+
# === Returns
|
|
55
|
+
#
|
|
56
|
+
# [String] the filename with suffix added for deduplication
|
|
57
|
+
#
|
|
58
|
+
def deduplicated_filename
|
|
59
|
+
return unless filename
|
|
60
|
+
return filename unless @deduplication_index
|
|
61
|
+
|
|
62
|
+
parts = filename.split('.')
|
|
63
|
+
basename = parts.shift
|
|
64
|
+
basename.sub!(/ ?\(\d+\)\z/, '')
|
|
65
|
+
([basename.to_s + (@deduplication_index > 1 ? "(#{@deduplication_index})" : '')] + parts).join('.')
|
|
66
|
+
end
|
|
67
|
+
|
|
37
68
|
##
|
|
38
69
|
# Calculates the path where the file should be stored. If +for_file+ is given, it will be
|
|
39
|
-
# used as the
|
|
70
|
+
# used as the identifier, otherwise +CarrierWave::Uploader#identifier+ is assumed.
|
|
40
71
|
#
|
|
41
72
|
# === Parameters
|
|
42
73
|
#
|
|
@@ -46,7 +77,7 @@ module CarrierWave
|
|
|
46
77
|
#
|
|
47
78
|
# [String] the store path
|
|
48
79
|
#
|
|
49
|
-
def store_path(for_file=
|
|
80
|
+
def store_path(for_file=identifier)
|
|
50
81
|
File.join([store_dir, full_filename(for_file)].compact)
|
|
51
82
|
end
|
|
52
83
|
|
|
@@ -60,8 +91,8 @@ module CarrierWave
|
|
|
60
91
|
# [new_file (File, IOString, Tempfile)] any kind of file object
|
|
61
92
|
#
|
|
62
93
|
def store!(new_file=nil)
|
|
63
|
-
cache!(new_file) if new_file &&
|
|
64
|
-
if !cache_only
|
|
94
|
+
cache!(new_file) if new_file && !cached?
|
|
95
|
+
if !cache_only && @file && @cache_id
|
|
65
96
|
with_callbacks(:store, new_file) do
|
|
66
97
|
new_file = storage.store!(@file)
|
|
67
98
|
if delete_tmp_file_after_storage
|
|
@@ -69,7 +100,9 @@ module CarrierWave
|
|
|
69
100
|
cache_storage.delete_dir!(cache_path(nil))
|
|
70
101
|
end
|
|
71
102
|
@file = new_file
|
|
72
|
-
@
|
|
103
|
+
@identifier = storage.identifier
|
|
104
|
+
@original_filename = @cache_id = @deduplication_index = nil
|
|
105
|
+
@staged = false
|
|
73
106
|
end
|
|
74
107
|
end
|
|
75
108
|
end
|
|
@@ -84,13 +117,46 @@ module CarrierWave
|
|
|
84
117
|
def retrieve_from_store!(identifier)
|
|
85
118
|
with_callbacks(:retrieve_from_store, identifier) do
|
|
86
119
|
@file = storage.retrieve!(identifier)
|
|
120
|
+
@identifier = identifier
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
##
|
|
125
|
+
# Look for an identifier which doesn't collide with the given already-stored identifiers.
|
|
126
|
+
# It is done by adding a index number as the suffix.
|
|
127
|
+
# For example, if there's 'image.jpg' and the @deduplication_index is set to 2,
|
|
128
|
+
# The stored file will be named as 'image(2).jpg'.
|
|
129
|
+
#
|
|
130
|
+
# === Parameters
|
|
131
|
+
#
|
|
132
|
+
# [current_identifiers (Array[String])] List of identifiers for already-stored files
|
|
133
|
+
#
|
|
134
|
+
def deduplicate(current_identifiers)
|
|
135
|
+
@deduplication_index = nil
|
|
136
|
+
return unless current_identifiers.include?(identifier)
|
|
137
|
+
|
|
138
|
+
(1..current_identifiers.size + 1).each do |i|
|
|
139
|
+
@deduplication_index = i
|
|
140
|
+
break unless current_identifiers.include?(identifier)
|
|
87
141
|
end
|
|
88
142
|
end
|
|
89
143
|
|
|
90
144
|
private
|
|
91
145
|
|
|
92
146
|
def full_filename(for_file)
|
|
93
|
-
for_file
|
|
147
|
+
forcing_extension(for_file)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def show_warning_when_filename_is_unavailable(_)
|
|
151
|
+
return if self.class.filename_safeguard_checked
|
|
152
|
+
self.class.filename_safeguard_checked = true
|
|
153
|
+
return if filename
|
|
154
|
+
|
|
155
|
+
warn <<~MESSAGE
|
|
156
|
+
[WARNING] Your uploader's #filename method defined at #{method(:filename).source_location.join(':')} didn't return value after storing the file.
|
|
157
|
+
It's likely that the method is safeguarded with `if original_filename`, which were necessary for pre-3.x CarrierWave but is no longer needed.
|
|
158
|
+
Removing it is recommended, as it is known to cause issues depending on the use case: https://github.com/carrierwaveuploader/carrierwave/issues/2708
|
|
159
|
+
MESSAGE
|
|
94
160
|
end
|
|
95
161
|
|
|
96
162
|
def storage
|
|
@@ -15,12 +15,15 @@ module CarrierWave
|
|
|
15
15
|
# [String] the location where this file is accessible via a url
|
|
16
16
|
#
|
|
17
17
|
def url(options = {})
|
|
18
|
-
if file.respond_to?(:url)
|
|
19
|
-
file.method(:url).arity
|
|
20
|
-
|
|
18
|
+
if file.respond_to?(:url)
|
|
19
|
+
tmp_url = file.method(:url).arity.zero? ? file.url : file.url(options)
|
|
20
|
+
return tmp_url if tmp_url.present?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if file.respond_to?(:path)
|
|
21
24
|
path = encode_path(file.path.sub(File.expand_path(root), ''))
|
|
22
25
|
|
|
23
|
-
if host = asset_host
|
|
26
|
+
if (host = asset_host)
|
|
24
27
|
if host.respond_to? :call
|
|
25
28
|
"#{host.call(file)}#{path}"
|
|
26
29
|
else
|
|
@@ -1,6 +1,89 @@
|
|
|
1
|
+
require "active_support/core_ext/object/deep_dup"
|
|
2
|
+
|
|
1
3
|
module CarrierWave
|
|
2
4
|
module Uploader
|
|
3
5
|
module Versions
|
|
6
|
+
class Builder
|
|
7
|
+
def initialize(name)
|
|
8
|
+
@name = name
|
|
9
|
+
@options = {}
|
|
10
|
+
@blocks = []
|
|
11
|
+
@klass = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def configure(options, &block)
|
|
15
|
+
@options.merge!(options)
|
|
16
|
+
@blocks << block if block
|
|
17
|
+
@klass = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def build(superclass)
|
|
21
|
+
return @klass if @klass
|
|
22
|
+
@klass = Class.new(superclass)
|
|
23
|
+
superclass.const_set("VersionUploader#{@name.to_s.camelize}", @klass)
|
|
24
|
+
|
|
25
|
+
@klass.version_names += [@name]
|
|
26
|
+
@klass.versions = {}
|
|
27
|
+
@klass.processors = []
|
|
28
|
+
@klass.version_options = @options
|
|
29
|
+
@klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
30
|
+
# Define the enable_processing method for versions so they get the
|
|
31
|
+
# value from the parent class unless explicitly overwritten
|
|
32
|
+
def self.enable_processing(value=nil)
|
|
33
|
+
self.enable_processing = value if value
|
|
34
|
+
if defined?(@enable_processing) && !@enable_processing.nil?
|
|
35
|
+
@enable_processing
|
|
36
|
+
else
|
|
37
|
+
superclass.enable_processing
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Regardless of what is set in the parent uploader, do not enforce the
|
|
42
|
+
# move_to_cache config option on versions because it moves the original
|
|
43
|
+
# file to the version's target file.
|
|
44
|
+
#
|
|
45
|
+
# If you want to enforce this setting on versions, override this method
|
|
46
|
+
# in each version:
|
|
47
|
+
#
|
|
48
|
+
# version :thumb do
|
|
49
|
+
# def move_to_cache
|
|
50
|
+
# true
|
|
51
|
+
# end
|
|
52
|
+
# end
|
|
53
|
+
#
|
|
54
|
+
def move_to_cache
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Need to rely on the parent version's identifier, as versions don't have its own one.
|
|
59
|
+
def identifier
|
|
60
|
+
parent_version.identifier
|
|
61
|
+
end
|
|
62
|
+
RUBY
|
|
63
|
+
@blocks.each { |block| @klass.class_eval(&block) }
|
|
64
|
+
@klass
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def deep_dup
|
|
68
|
+
other = dup
|
|
69
|
+
other.instance_variable_set(:@blocks, @blocks.dup)
|
|
70
|
+
other
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def method_missing(name, *args)
|
|
74
|
+
super
|
|
75
|
+
rescue NoMethodError => e
|
|
76
|
+
raise e.exception <<~ERROR
|
|
77
|
+
#{e.message}
|
|
78
|
+
If you're trying to configure a version, do it inside a block like `version(:thumb) { self.#{name} #{args.map(&:inspect).join(', ')} }`.
|
|
79
|
+
ERROR
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def respond_to_missing?(*)
|
|
83
|
+
super
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
4
87
|
extend ActiveSupport::Concern
|
|
5
88
|
|
|
6
89
|
include CarrierWave::Uploader::Callbacks
|
|
@@ -11,9 +94,8 @@ module CarrierWave
|
|
|
11
94
|
self.versions = {}
|
|
12
95
|
self.version_names = []
|
|
13
96
|
|
|
14
|
-
attr_accessor :
|
|
97
|
+
attr_accessor :parent_version
|
|
15
98
|
|
|
16
|
-
after :cache, :assign_parent_cache_id
|
|
17
99
|
after :cache, :cache_versions!
|
|
18
100
|
after :store, :store_versions!
|
|
19
101
|
after :remove, :remove_versions!
|
|
@@ -51,80 +133,33 @@ module CarrierWave
|
|
|
51
133
|
# process :scale => [200, 200]
|
|
52
134
|
# end
|
|
53
135
|
#
|
|
136
|
+
# version :square, :unless => :invalid_image_type? do
|
|
137
|
+
# process :scale => [100, 100]
|
|
138
|
+
# end
|
|
139
|
+
#
|
|
54
140
|
# end
|
|
55
141
|
#
|
|
56
142
|
def version(name, options = {}, &block)
|
|
57
143
|
name = name.to_sym
|
|
58
|
-
|
|
144
|
+
versions[name] ||= Builder.new(name)
|
|
145
|
+
versions[name].configure(options, &block)
|
|
59
146
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
147
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
148
|
+
def #{name}
|
|
149
|
+
versions[:#{name}]
|
|
150
|
+
end
|
|
151
|
+
RUBY
|
|
63
152
|
|
|
64
|
-
|
|
65
|
-
versions.each do |name, version|
|
|
66
|
-
version.class_eval(&block)
|
|
67
|
-
version.recursively_apply_block_to_versions(&block)
|
|
68
|
-
end
|
|
153
|
+
versions[name]
|
|
69
154
|
end
|
|
70
155
|
|
|
71
156
|
private
|
|
72
157
|
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
uploader.version_names += [name]
|
|
78
|
-
uploader.versions = {}
|
|
79
|
-
uploader.processors = []
|
|
80
|
-
uploader.version_options = options
|
|
81
|
-
|
|
82
|
-
uploader.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
83
|
-
# Define the enable_processing method for versions so they get the
|
|
84
|
-
# value from the parent class unless explicitly overwritten
|
|
85
|
-
def self.enable_processing(value=nil)
|
|
86
|
-
self.enable_processing = value if value
|
|
87
|
-
if defined?(@enable_processing) && !@enable_processing.nil?
|
|
88
|
-
@enable_processing
|
|
89
|
-
else
|
|
90
|
-
superclass.enable_processing
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Regardless of what is set in the parent uploader, do not enforce the
|
|
95
|
-
# move_to_cache config option on versions because it moves the original
|
|
96
|
-
# file to the version's target file.
|
|
97
|
-
#
|
|
98
|
-
# If you want to enforce this setting on versions, override this method
|
|
99
|
-
# in each version:
|
|
100
|
-
#
|
|
101
|
-
# version :thumb do
|
|
102
|
-
# def move_to_cache
|
|
103
|
-
# true
|
|
104
|
-
# end
|
|
105
|
-
# end
|
|
106
|
-
#
|
|
107
|
-
def move_to_cache
|
|
108
|
-
false
|
|
109
|
-
end
|
|
110
|
-
RUBY
|
|
111
|
-
|
|
112
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
113
|
-
def #{name}
|
|
114
|
-
versions[:#{name}]
|
|
115
|
-
end
|
|
116
|
-
RUBY
|
|
117
|
-
else
|
|
118
|
-
uploader = Class.new(versions[name])
|
|
119
|
-
const_set("Uploader#{uploader.object_id}".tr('-', '_'), uploader)
|
|
120
|
-
uploader.processors = []
|
|
121
|
-
uploader.version_options = uploader.version_options.merge(options)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Add the current version hash to class attribute :versions
|
|
125
|
-
self.versions = versions.merge(name => uploader)
|
|
158
|
+
def inherited(subclass)
|
|
159
|
+
# To prevent subclass version changes affecting superclass versions
|
|
160
|
+
subclass.versions = versions.deep_dup
|
|
161
|
+
super
|
|
126
162
|
end
|
|
127
|
-
|
|
128
163
|
end # ClassMethods
|
|
129
164
|
|
|
130
165
|
##
|
|
@@ -138,7 +173,7 @@ module CarrierWave
|
|
|
138
173
|
return @versions if @versions
|
|
139
174
|
@versions = {}
|
|
140
175
|
self.class.versions.each do |name, version|
|
|
141
|
-
@versions[name] = version.new(model, mounted_as)
|
|
176
|
+
@versions[name] = version.build(self.class).new(model, mounted_as)
|
|
142
177
|
@versions[name].parent_version = self
|
|
143
178
|
end
|
|
144
179
|
@versions
|
|
@@ -161,24 +196,45 @@ module CarrierWave
|
|
|
161
196
|
#
|
|
162
197
|
# === Returns
|
|
163
198
|
#
|
|
164
|
-
# [Boolean] True when the version
|
|
199
|
+
# [Boolean] True when the version satisfy its :if or :unless condition
|
|
165
200
|
#
|
|
166
|
-
def
|
|
201
|
+
def version_active?(name)
|
|
167
202
|
name = name.to_sym
|
|
168
203
|
|
|
169
|
-
return false unless
|
|
204
|
+
return false unless versions.has_key?(name)
|
|
170
205
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
206
|
+
if_condition = versions[name].class.version_options[:if]
|
|
207
|
+
unless_condition = versions[name].class.version_options[:unless]
|
|
208
|
+
|
|
209
|
+
if if_condition
|
|
210
|
+
if if_condition.respond_to?(:call)
|
|
211
|
+
if_condition.call(self, :version => name, :file => file)
|
|
212
|
+
else
|
|
213
|
+
send(if_condition, file)
|
|
214
|
+
end
|
|
215
|
+
elsif unless_condition
|
|
216
|
+
if unless_condition.respond_to?(:call)
|
|
217
|
+
!unless_condition.call(self, :version => name, :file => file)
|
|
175
218
|
else
|
|
176
|
-
send(
|
|
219
|
+
!send(unless_condition, file)
|
|
177
220
|
end
|
|
178
221
|
else
|
|
179
222
|
true
|
|
180
223
|
end
|
|
181
224
|
end
|
|
225
|
+
alias_method :version_exists?, :version_active?
|
|
226
|
+
CarrierWave.deprecator.deprecate_methods(self, version_exists?: :version_active?)
|
|
227
|
+
|
|
228
|
+
##
|
|
229
|
+
# Copies the parent's cache_id when caching a version file.
|
|
230
|
+
# This behavior is not essential but it makes easier to understand
|
|
231
|
+
# that the cached files are generated by the single upload attempt.
|
|
232
|
+
#
|
|
233
|
+
def cache!(*args)
|
|
234
|
+
self.cache_id = parent_version.cache_id if parent_version
|
|
235
|
+
|
|
236
|
+
super
|
|
237
|
+
end
|
|
182
238
|
|
|
183
239
|
##
|
|
184
240
|
# When given a version name as a parameter, will return the url for that version
|
|
@@ -220,45 +276,47 @@ module CarrierWave
|
|
|
220
276
|
# Recreate versions and reprocess them. This can be used to recreate
|
|
221
277
|
# versions if their parameters somehow have changed.
|
|
222
278
|
#
|
|
223
|
-
def recreate_versions!(*
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
else
|
|
234
|
-
cache! if !cached?
|
|
235
|
-
store!
|
|
279
|
+
def recreate_versions!(*names)
|
|
280
|
+
# As well as specified versions, we need to reprocess versions
|
|
281
|
+
# that are the source of another version.
|
|
282
|
+
|
|
283
|
+
self.cache_id = CarrierWave.generate_cache_id
|
|
284
|
+
derived_versions.each_value do |v|
|
|
285
|
+
v.cache!(file) if names.empty? || !(v.descendant_version_names & names).empty?
|
|
286
|
+
end
|
|
287
|
+
active_versions.each do |name, v|
|
|
288
|
+
v.store! if names.empty? || names.include?(name)
|
|
236
289
|
end
|
|
290
|
+
ensure
|
|
291
|
+
@cache_id = nil
|
|
237
292
|
end
|
|
238
293
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
294
|
+
protected
|
|
295
|
+
|
|
296
|
+
def descendant_version_names
|
|
297
|
+
[version_name] + derived_versions.flat_map do |name, version|
|
|
298
|
+
version.descendant_version_names
|
|
243
299
|
end
|
|
244
300
|
end
|
|
245
301
|
|
|
246
302
|
def active_versions
|
|
247
303
|
versions.select do |name, uploader|
|
|
248
|
-
|
|
304
|
+
version_active?(name)
|
|
249
305
|
end
|
|
250
306
|
end
|
|
251
307
|
|
|
252
|
-
|
|
308
|
+
private
|
|
309
|
+
|
|
310
|
+
def derived_versions
|
|
253
311
|
active_versions.reject do |name, v|
|
|
254
312
|
v.class.version_options[:from_version]
|
|
255
|
-
end.
|
|
313
|
+
end.merge(active_sibling_versions.select do |name, v|
|
|
256
314
|
v.class.version_options[:from_version] == self.class.version_names.last
|
|
257
|
-
end
|
|
315
|
+
end)
|
|
258
316
|
end
|
|
259
317
|
|
|
260
|
-
def
|
|
261
|
-
parent_version
|
|
318
|
+
def active_sibling_versions
|
|
319
|
+
parent_version&.active_versions || {}
|
|
262
320
|
end
|
|
263
321
|
|
|
264
322
|
def full_filename(for_file)
|
|
@@ -270,31 +328,23 @@ module CarrierWave
|
|
|
270
328
|
end
|
|
271
329
|
|
|
272
330
|
def cache_versions!(new_file)
|
|
273
|
-
|
|
274
|
-
v.send(:cache_id=, @cache_id)
|
|
275
|
-
v.cache!(new_file)
|
|
276
|
-
end
|
|
331
|
+
derived_versions.each_value { |v| v.cache!(new_file) }
|
|
277
332
|
end
|
|
278
333
|
|
|
279
|
-
def store_versions!(new_file
|
|
280
|
-
|
|
281
|
-
active = Hash[active_versions]
|
|
282
|
-
versions.each { |v| active[v].try(:store!, new_file) } unless active.empty?
|
|
283
|
-
else
|
|
284
|
-
active_versions.each { |name, v| v.store!(new_file) }
|
|
285
|
-
end
|
|
334
|
+
def store_versions!(new_file)
|
|
335
|
+
active_versions.each_value { |v| v.store!(new_file) }
|
|
286
336
|
end
|
|
287
337
|
|
|
288
338
|
def remove_versions!
|
|
289
|
-
versions.
|
|
339
|
+
versions.each_value { |v| v.remove! }
|
|
290
340
|
end
|
|
291
341
|
|
|
292
342
|
def retrieve_versions_from_cache!(cache_name)
|
|
293
|
-
active_versions.
|
|
343
|
+
active_versions.each_value { |v| v.retrieve_from_cache!(cache_name) }
|
|
294
344
|
end
|
|
295
345
|
|
|
296
346
|
def retrieve_versions_from_store!(identifier)
|
|
297
|
-
active_versions.
|
|
347
|
+
active_versions.each_value { |v| v.retrieve_from_store!(identifier) }
|
|
298
348
|
end
|
|
299
349
|
|
|
300
350
|
end # Versions
|