paperclip 4.3.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of paperclip might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +14 -13
- data/Appraisals +22 -9
- data/CONTRIBUTING.md +16 -5
- data/Gemfile +2 -8
- data/LICENSE +1 -1
- data/NEWS +49 -2
- data/README.md +126 -102
- data/UPGRADING +12 -9
- data/features/basic_integration.feature +1 -0
- data/features/migration.feature +0 -24
- data/features/step_definitions/attachment_steps.rb +20 -20
- data/features/step_definitions/html_steps.rb +2 -2
- data/features/step_definitions/rails_steps.rb +11 -17
- data/features/step_definitions/s3_steps.rb +2 -2
- data/features/step_definitions/web_steps.rb +1 -103
- data/features/support/file_helpers.rb +2 -2
- data/gemfiles/4.2.awsv2.0.gemfile +17 -0
- data/gemfiles/4.2.awsv2.1.gemfile +17 -0
- data/gemfiles/{4.2.gemfile → 4.2.awsv2.gemfile} +1 -1
- data/gemfiles/5.0.awsv2.0.gemfile +17 -0
- data/gemfiles/5.0.awsv2.1.gemfile +17 -0
- data/gemfiles/{4.1.gemfile → 5.0.awsv2.gemfile} +2 -2
- data/lib/paperclip/attachment.rb +18 -17
- data/lib/paperclip/attachment_registry.rb +2 -1
- data/lib/paperclip/callbacks.rb +8 -6
- data/lib/paperclip/content_type_detector.rb +3 -2
- data/lib/paperclip/errors.rb +3 -1
- data/lib/paperclip/geometry_parser_factory.rb +1 -1
- data/lib/paperclip/glue.rb +1 -1
- data/lib/paperclip/has_attached_file.rb +7 -1
- data/lib/paperclip/helpers.rb +14 -10
- data/lib/paperclip/interpolations/plural_cache.rb +6 -5
- data/lib/paperclip/interpolations.rb +18 -13
- data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +1 -1
- data/lib/paperclip/io_adapters/uri_adapter.rb +3 -1
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
- data/lib/paperclip/media_type_spoof_detector.rb +1 -1
- data/lib/paperclip/rails_environment.rb +1 -1
- data/lib/paperclip/schema.rb +3 -9
- data/lib/paperclip/storage/fog.rb +17 -8
- data/lib/paperclip/storage/s3.rb +51 -49
- data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
- data/lib/paperclip/version.rb +3 -1
- data/lib/paperclip.rb +2 -1
- data/lib/tasks/paperclip.rake +1 -1
- data/paperclip.gemspec +15 -11
- data/spec/paperclip/attachment_processing_spec.rb +2 -4
- data/spec/paperclip/attachment_registry_spec.rb +28 -0
- data/spec/paperclip/attachment_spec.rb +36 -14
- data/spec/paperclip/geometry_spec.rb +1 -1
- data/spec/paperclip/glue_spec.rb +44 -0
- data/spec/paperclip/has_attached_file_spec.rb +24 -8
- data/spec/paperclip/integration_spec.rb +4 -3
- data/spec/paperclip/interpolations_spec.rb +14 -4
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +12 -0
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +27 -0
- data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +10 -0
- data/spec/paperclip/media_type_spoof_detector_spec.rb +12 -3
- data/spec/paperclip/paperclip_spec.rb +3 -28
- data/spec/paperclip/plural_cache_spec.rb +17 -16
- data/spec/paperclip/storage/fog_spec.rb +31 -1
- data/spec/paperclip/storage/s3_live_spec.rb +8 -4
- data/spec/paperclip/storage/s3_spec.rb +213 -156
- data/spec/paperclip/validators/attachment_size_validator_spec.rb +26 -20
- data/spec/paperclip/validators_spec.rb +3 -3
- data/spec/spec_helper.rb +6 -1
- data/spec/support/assertions.rb +7 -0
- data/spec/support/model_reconstruction.rb +9 -1
- data/spec/support/reporting.rb +11 -0
- metadata +74 -47
- data/cucumber/paperclip_steps.rb +0 -6
- data/gemfiles/3.2.gemfile +0 -20
- data/lib/paperclip/locales/de.yml +0 -18
- data/lib/paperclip/locales/es.yml +0 -18
- data/lib/paperclip/locales/ja.yml +0 -18
- data/lib/paperclip/locales/pt-BR.yml +0 -18
- data/lib/paperclip/locales/zh-CN.yml +0 -18
- data/lib/paperclip/locales/zh-HK.yml +0 -18
- data/lib/paperclip/locales/zh-TW.yml +0 -18
- data/spec/support/rails_helpers.rb +0 -7
data/lib/paperclip/errors.rb
CHANGED
@@ -19,7 +19,9 @@ module Paperclip
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# Will be thrown when ImageMagic cannot determine the uploaded file's
|
22
|
-
# metadata, usually this would mean the file is not an image.
|
22
|
+
# metadata, usually this would mean the file is not an image. If you are
|
23
|
+
# consistently receiving this error on PDFs make sure that you have
|
24
|
+
# installed Ghostscript.
|
23
25
|
class NotIdentifiedByImageMagickError < Paperclip::Error
|
24
26
|
end
|
25
27
|
|
data/lib/paperclip/glue.rb
CHANGED
@@ -8,7 +8,7 @@ module Paperclip
|
|
8
8
|
base.extend ClassMethods
|
9
9
|
base.send :include, Callbacks
|
10
10
|
base.send :include, Validators
|
11
|
-
base.send :include, Schema
|
11
|
+
base.send :include, Schema if defined? ActiveRecord::Base
|
12
12
|
|
13
13
|
locale_path = Dir.glob(File.dirname(__FILE__) + "/locales/*.{rb,yml}")
|
14
14
|
I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
|
@@ -91,7 +91,13 @@ module Paperclip
|
|
91
91
|
name = @name
|
92
92
|
@klass.send(:after_save) { send(name).send(:save) }
|
93
93
|
@klass.send(:before_destroy) { send(name).send(:queue_all_for_delete) }
|
94
|
-
@klass.
|
94
|
+
if @klass.respond_to?(:after_commit)
|
95
|
+
@klass.send(:after_commit, on: :destroy) do
|
96
|
+
send(name).send(:flush_deletes)
|
97
|
+
end
|
98
|
+
else
|
99
|
+
@klass.send(:after_destroy) { send(name).send(:flush_deletes) }
|
100
|
+
end
|
95
101
|
end
|
96
102
|
|
97
103
|
def add_paperclip_callbacks
|
data/lib/paperclip/helpers.rb
CHANGED
@@ -8,23 +8,27 @@ module Paperclip
|
|
8
8
|
Paperclip::Interpolations[key] = block
|
9
9
|
end
|
10
10
|
|
11
|
-
# The run method takes the name of a binary to run, the arguments
|
12
|
-
# and some options
|
11
|
+
# The run method takes the name of a binary to run, the arguments
|
12
|
+
# to that binary, the values to interpolate and some local options.
|
13
13
|
#
|
14
|
-
#
|
15
|
-
# on the filesystem. Colon-separated, just like $PATH.
|
14
|
+
# :cmd -> The name of a binary to run.
|
16
15
|
#
|
17
|
-
#
|
18
|
-
# of the binary. Defaults to [0].
|
16
|
+
# :arguments -> The command line arguments to that binary.
|
19
17
|
#
|
20
|
-
#
|
21
|
-
# This will only log if logging in general is set to true as well.
|
18
|
+
# :interpolation_values -> Values to be interpolated into the arguments.
|
22
19
|
#
|
23
|
-
#
|
20
|
+
# :local_options -> The options to be used by Cocain::CommandLine.
|
21
|
+
# These could be: runner
|
22
|
+
# logger
|
23
|
+
# swallow_stderr
|
24
|
+
# expected_outcodes
|
25
|
+
# environment
|
26
|
+
# runner_options
|
24
27
|
#
|
25
28
|
def run(cmd, arguments = "", interpolation_values = {}, local_options = {})
|
26
29
|
command_path = options[:command_path]
|
27
|
-
|
30
|
+
cocaine_path_array = Cocaine::CommandLine.path.try(:split, Cocaine::OS.path_separator)
|
31
|
+
Cocaine::CommandLine.path = [cocaine_path_array, command_path].flatten.compact.uniq
|
28
32
|
if logging? && (options[:log_command] || local_options[:log_command])
|
29
33
|
local_options = local_options.merge(:logger => logger)
|
30
34
|
end
|
@@ -2,15 +2,16 @@ module Paperclip
|
|
2
2
|
module Interpolations
|
3
3
|
class PluralCache
|
4
4
|
def initialize
|
5
|
-
@
|
5
|
+
@symbol_cache = {}.compare_by_identity
|
6
|
+
@klass_cache = {}.compare_by_identity
|
6
7
|
end
|
7
8
|
|
8
|
-
def
|
9
|
-
@
|
9
|
+
def pluralize_symbol(symbol)
|
10
|
+
@symbol_cache[symbol] ||= symbol.to_s.downcase.pluralize
|
10
11
|
end
|
11
12
|
|
12
|
-
def
|
13
|
-
@
|
13
|
+
def underscore_and_pluralize_class(klass)
|
14
|
+
@klass_cache[klass] ||= klass.name.underscore.pluralize
|
14
15
|
end
|
15
16
|
end
|
16
17
|
end
|
@@ -10,6 +10,7 @@ module Paperclip
|
|
10
10
|
# and is not intended for normal use.
|
11
11
|
def self.[]= name, block
|
12
12
|
define_method(name, &block)
|
13
|
+
@interpolators_cache = nil
|
13
14
|
end
|
14
15
|
|
15
16
|
# Hash access of interpolations. Included only for compatibility,
|
@@ -20,7 +21,7 @@ module Paperclip
|
|
20
21
|
|
21
22
|
# Returns a sorted list of all interpolations.
|
22
23
|
def self.all
|
23
|
-
self.instance_methods(false).sort
|
24
|
+
self.instance_methods(false).sort!
|
24
25
|
end
|
25
26
|
|
26
27
|
# Perform the actual interpolation. Takes the pattern to interpolate
|
@@ -29,11 +30,15 @@ module Paperclip
|
|
29
30
|
# an interpolation pattern for Paperclip to use.
|
30
31
|
def self.interpolate pattern, *args
|
31
32
|
pattern = args.first.instance.send(pattern) if pattern.kind_of? Symbol
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
33
|
+
result = pattern.dup
|
34
|
+
interpolators_cache.each do |method, token|
|
35
|
+
result.gsub!(token) { send(method, *args) } if result.include?(token)
|
36
36
|
end
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.interpolators_cache
|
41
|
+
@interpolators_cache ||= all.reverse!.map! { |method| [method, ":#{method}"] }
|
37
42
|
end
|
38
43
|
|
39
44
|
def self.plural_cache
|
@@ -42,7 +47,7 @@ module Paperclip
|
|
42
47
|
|
43
48
|
# Returns the filename, the same way as ":basename.:extension" would.
|
44
49
|
def filename attachment, style_name
|
45
|
-
[ basename(attachment, style_name), extension(attachment, style_name) ].
|
50
|
+
[ basename(attachment, style_name), extension(attachment, style_name) ].delete_if(&:empty?).join(".".freeze)
|
46
51
|
end
|
47
52
|
|
48
53
|
# Returns the interpolated URL. Will raise an error if the url itself
|
@@ -85,12 +90,12 @@ module Paperclip
|
|
85
90
|
# all class names. Calling #class will return the expected class.
|
86
91
|
def class attachment = nil, style_name = nil
|
87
92
|
return super() if attachment.nil? && style_name.nil?
|
88
|
-
plural_cache.
|
93
|
+
plural_cache.underscore_and_pluralize_class(attachment.instance.class)
|
89
94
|
end
|
90
95
|
|
91
96
|
# Returns the basename of the file. e.g. "file" for "file.jpg"
|
92
97
|
def basename attachment, style_name
|
93
|
-
|
98
|
+
File.basename(attachment.original_filename, ".*".freeze)
|
94
99
|
end
|
95
100
|
|
96
101
|
# Returns the extension of the file. e.g. "jpg" for "file.jpg"
|
@@ -98,7 +103,7 @@ module Paperclip
|
|
98
103
|
# of the actual extension.
|
99
104
|
def extension attachment, style_name
|
100
105
|
((style = attachment.styles[style_name.to_s.to_sym]) && style[:format]) ||
|
101
|
-
File.extname(attachment.original_filename).
|
106
|
+
File.extname(attachment.original_filename).sub(/\A\.+/, "".freeze)
|
102
107
|
end
|
103
108
|
|
104
109
|
# Returns the dot+extension of the file. e.g. ".jpg" for "file.jpg"
|
@@ -106,7 +111,7 @@ module Paperclip
|
|
106
111
|
# of the actual extension. If the extension is empty, no dot is added.
|
107
112
|
def dotextension attachment, style_name
|
108
113
|
ext = extension(attachment, style_name)
|
109
|
-
ext.empty? ?
|
114
|
+
ext.empty? ? ext : ".#{ext}"
|
110
115
|
end
|
111
116
|
|
112
117
|
# Returns an extension based on the content type. e.g. "jpeg" for
|
@@ -170,9 +175,9 @@ module Paperclip
|
|
170
175
|
def id_partition attachment, style_name
|
171
176
|
case id = attachment.instance.id
|
172
177
|
when Integer
|
173
|
-
("%09d" % id).scan(/\d{3}/).join("/")
|
178
|
+
("%09d".freeze % id).scan(/\d{3}/).join("/".freeze)
|
174
179
|
when String
|
175
|
-
id.scan(/.{3}/).first(3).join("/")
|
180
|
+
id.scan(/.{3}/).first(3).join("/".freeze)
|
176
181
|
else
|
177
182
|
nil
|
178
183
|
end
|
@@ -181,7 +186,7 @@ module Paperclip
|
|
181
186
|
# Returns the pluralized form of the attachment name. e.g.
|
182
187
|
# "avatars" for an attachment of :avatar
|
183
188
|
def attachment attachment, style_name
|
184
|
-
plural_cache.
|
189
|
+
plural_cache.pluralize_symbol(attachment.name)
|
185
190
|
end
|
186
191
|
|
187
192
|
# Returns the style, or the default style if nil is supplied.
|
@@ -40,9 +40,9 @@ module Paperclip
|
|
40
40
|
|
41
41
|
def failure_message
|
42
42
|
"#{expected_attachment}\n".tap do |message|
|
43
|
-
message << accepted_types_and_failures
|
43
|
+
message << accepted_types_and_failures.to_s
|
44
44
|
message << "\n\n" if @allowed_types.present? && @rejected_types.present?
|
45
|
-
message << rejected_types_and_failures
|
45
|
+
message << rejected_types_and_failures.to_s
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -55,7 +55,7 @@ module Paperclip
|
|
55
55
|
def accepted_types_and_failures
|
56
56
|
if @allowed_types.present?
|
57
57
|
"Accept content types: #{@allowed_types.join(", ")}\n".tap do |message|
|
58
|
-
if @missing_allowed_types.
|
58
|
+
if @missing_allowed_types.present?
|
59
59
|
message << " #{@missing_allowed_types.join(", ")} were rejected."
|
60
60
|
else
|
61
61
|
message << " All were accepted successfully."
|
@@ -66,7 +66,7 @@ module Paperclip
|
|
66
66
|
def rejected_types_and_failures
|
67
67
|
if @rejected_types.present?
|
68
68
|
"Reject content types: #{@rejected_types.join(", ")}\n".tap do |message|
|
69
|
-
if @missing_rejected_types.
|
69
|
+
if @missing_rejected_types.present?
|
70
70
|
message << " #{@missing_rejected_types.join(", ")} were accepted."
|
71
71
|
else
|
72
72
|
message << " All were rejected successfully."
|
@@ -12,7 +12,7 @@ module Paperclip
|
|
12
12
|
|
13
13
|
def spoofed?
|
14
14
|
if has_name? && has_extension? && media_type_mismatch? && mapping_override_mismatch?
|
15
|
-
Paperclip.log("Content Type Spoof: Filename #{File.basename(@name)} (#{supplied_content_type} from Headers, #{content_types_from_name} from Extension), content type discovered from file command: #{calculated_content_type}. See documentation to allow this combination.")
|
15
|
+
Paperclip.log("Content Type Spoof: Filename #{File.basename(@name)} (#{supplied_content_type} from Headers, #{content_types_from_name.map(&:to_s)} from Extension), content type discovered from file command: #{calculated_content_type}. See documentation to allow this combination.")
|
16
16
|
true
|
17
17
|
else
|
18
18
|
false
|
data/lib/paperclip/schema.rb
CHANGED
@@ -12,10 +12,7 @@ module Paperclip
|
|
12
12
|
ActiveRecord::ConnectionAdapters::Table.send :include, TableDefinition
|
13
13
|
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, TableDefinition
|
14
14
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, Statements
|
15
|
-
|
16
|
-
if defined?(ActiveRecord::Migration::CommandRecorder) # Rails 3.1+
|
17
|
-
ActiveRecord::Migration::CommandRecorder.send :include, CommandRecorder
|
18
|
-
end
|
15
|
+
ActiveRecord::Migration::CommandRecorder.send :include, CommandRecorder
|
19
16
|
end
|
20
17
|
|
21
18
|
module Statements
|
@@ -35,12 +32,9 @@ module Paperclip
|
|
35
32
|
def remove_attachment(table_name, *attachment_names)
|
36
33
|
raise ArgumentError, "Please specify attachment name in your remove_attachment call in your migration." if attachment_names.empty?
|
37
34
|
|
38
|
-
options = attachment_names.extract_options!
|
39
|
-
|
40
35
|
attachment_names.each do |attachment_name|
|
41
|
-
COLUMNS.
|
42
|
-
|
43
|
-
remove_column(table_name, "#{attachment_name}_#{column_name}", column_type, column_options)
|
36
|
+
COLUMNS.keys.each do |column_name|
|
37
|
+
remove_column(table_name, "#{attachment_name}_#{column_name}")
|
44
38
|
end
|
45
39
|
end
|
46
40
|
end
|
@@ -33,6 +33,10 @@ module Paperclip
|
|
33
33
|
# that is the alias to the S3 domain of your bucket, e.g.
|
34
34
|
# 'http://images.example.com'. This can also be used in
|
35
35
|
# conjunction with Cloudfront (http://aws.amazon.com/cloudfront)
|
36
|
+
# * +fog_options+: (optional) A hash of options that are passed
|
37
|
+
# to fog when the file is created. For example, you could set
|
38
|
+
# the multipart-chunk size to 100MB with a hash:
|
39
|
+
# { :multipart_chunk_size => 104857600 }
|
36
40
|
|
37
41
|
module Fog
|
38
42
|
def self.extended base
|
@@ -98,12 +102,14 @@ module Paperclip
|
|
98
102
|
log("saving #{path(style)}")
|
99
103
|
retried = false
|
100
104
|
begin
|
101
|
-
|
105
|
+
attributes = fog_file.merge(
|
102
106
|
:body => file,
|
103
107
|
:key => path(style),
|
104
108
|
:public => fog_public(style),
|
105
109
|
:content_type => file.content_type
|
106
|
-
)
|
110
|
+
)
|
111
|
+
attributes.merge!(@options[:fog_options]) if @options[:fog_options]
|
112
|
+
directory.files.create(attributes)
|
107
113
|
rescue Excon::Errors::NotFound
|
108
114
|
raise if retried
|
109
115
|
retried = true
|
@@ -164,6 +170,7 @@ module Paperclip
|
|
164
170
|
log("copying #{path(style)} to local file #{local_dest_path}")
|
165
171
|
::File.open(local_dest_path, 'wb') do |local_file|
|
166
172
|
file = directory.files.get(path(style))
|
173
|
+
return false unless file
|
167
174
|
local_file.write(file.body)
|
168
175
|
end
|
169
176
|
rescue ::Fog::Errors::Error => e
|
@@ -189,10 +196,10 @@ module Paperclip
|
|
189
196
|
end
|
190
197
|
|
191
198
|
def host_name_for_directory
|
192
|
-
if
|
193
|
-
"#{
|
199
|
+
if directory_name.to_s =~ Fog::AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX
|
200
|
+
"#{directory_name}.s3.amazonaws.com"
|
194
201
|
else
|
195
|
-
"s3.amazonaws.com/#{
|
202
|
+
"s3.amazonaws.com/#{directory_name}"
|
196
203
|
end
|
197
204
|
end
|
198
205
|
|
@@ -218,13 +225,15 @@ module Paperclip
|
|
218
225
|
end
|
219
226
|
|
220
227
|
def directory
|
221
|
-
|
228
|
+
@directory ||= connection.directories.new(key: directory_name)
|
229
|
+
end
|
230
|
+
|
231
|
+
def directory_name
|
232
|
+
if @options[:fog_directory].respond_to?(:call)
|
222
233
|
@options[:fog_directory].call(self)
|
223
234
|
else
|
224
235
|
@options[:fog_directory]
|
225
236
|
end
|
226
|
-
|
227
|
-
@directory ||= connection.directories.new(:key => dir)
|
228
237
|
end
|
229
238
|
|
230
239
|
def scheme
|
data/lib/paperclip/storage/s3.rb
CHANGED
@@ -4,7 +4,7 @@ module Paperclip
|
|
4
4
|
# distribution. You can find out more about it at http://aws.amazon.com/s3
|
5
5
|
#
|
6
6
|
# To use Paperclip with S3, include the +aws-sdk+ gem in your Gemfile:
|
7
|
-
# gem 'aws-sdk'
|
7
|
+
# gem 'aws-sdk'
|
8
8
|
# There are a few S3-specific options for has_attached_file:
|
9
9
|
# * +s3_credentials+: Takes a path, a File, a Hash or a Proc. The path (or File) must point
|
10
10
|
# to a YAML file containing the +access_key_id+ and +secret_access_key+ that Amazon
|
@@ -41,7 +41,7 @@ module Paperclip
|
|
41
41
|
# * +s3_permissions+: This is a String that should be one of the "canned" access
|
42
42
|
# policies that S3 provides (more information can be found here:
|
43
43
|
# http://docs.aws.amazon.com/AmazonS3/latest/dev/ACLOverview.html)
|
44
|
-
# The default for Paperclip is
|
44
|
+
# The default for Paperclip is public-read.
|
45
45
|
#
|
46
46
|
# You can set permission on a per style bases by doing the following:
|
47
47
|
# :s3_permissions => {
|
@@ -61,7 +61,7 @@ module Paperclip
|
|
61
61
|
# * +bucket+: This is the name of the S3 bucket that will store your files. Remember
|
62
62
|
# that the bucket must be unique across all of Amazon S3. If the bucket does not exist
|
63
63
|
# Paperclip will attempt to create it. The bucket name will not be interpolated.
|
64
|
-
# You can define the bucket as a Proc if you want to determine
|
64
|
+
# You can define the bucket as a Proc if you want to determine its name at runtime.
|
65
65
|
# Paperclip will call that Proc with attachment as the only argument.
|
66
66
|
# * +s3_host_alias+: The fully-qualified domain name (FQDN) that is the alias to the
|
67
67
|
# S3 domain of your bucket. Used with the :s3_alias_url url interpolation. See the
|
@@ -93,6 +93,7 @@ module Paperclip
|
|
93
93
|
# S3 (strictly speaking) does not support directories, you can still use a / to
|
94
94
|
# separate parts of your file name.
|
95
95
|
# * +s3_host_name+: If you are using your bucket in Tokyo region etc, write host_name.
|
96
|
+
# * +s3_region+: For aws-sdk v2, s3_region is required.
|
96
97
|
# * +s3_metadata+: These key/value pairs will be stored with the
|
97
98
|
# object. This option works by prefixing each key with
|
98
99
|
# "x-amz-meta-" before sending it as a header on the object
|
@@ -117,21 +118,10 @@ module Paperclip
|
|
117
118
|
rescue LoadError => e
|
118
119
|
e.message << " (You may need to install the aws-sdk gem)"
|
119
120
|
raise e
|
120
|
-
end
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
AWS::Core::LogFormatter.class_eval do
|
125
|
-
def summarize_hash(hash)
|
126
|
-
hash.map { |key, value| ":#{key}=>#{summarize_value(value)}".force_encoding('UTF-8') }.sort.join(',')
|
127
|
-
end
|
128
|
-
end
|
129
|
-
elsif defined?(AWS::Core::ClientLogging)
|
130
|
-
AWS::Core::ClientLogging.class_eval do
|
131
|
-
def sanitize_hash(hash)
|
132
|
-
hash.map { |key, value| "#{sanitize_value(key)}=>#{sanitize_value(value)}".force_encoding('UTF-8') }.sort.join(',')
|
133
|
-
end
|
134
|
-
end
|
121
|
+
end
|
122
|
+
if Gem::Version.new(Aws::VERSION) >= Gem::Version.new(2) &&
|
123
|
+
Gem::Version.new(Aws::VERSION) <= Gem::Version.new("2.0.33")
|
124
|
+
raise LoadError, "paperclip does not support aws-sdk versions 2.0.0 - 2.0.33. Please upgrade aws-sdk to a newer version."
|
135
125
|
end
|
136
126
|
|
137
127
|
base.instance_eval do
|
@@ -141,7 +131,7 @@ module Paperclip
|
|
141
131
|
Proc.new do |style, attachment|
|
142
132
|
permission = (@s3_permissions[style.to_s.to_sym] || @s3_permissions[:default])
|
143
133
|
permission = permission.call(attachment, style) if permission.respond_to?(:call)
|
144
|
-
(permission == :
|
134
|
+
(permission == :"public-read") ? 'http'.freeze : 'https'.freeze
|
145
135
|
end
|
146
136
|
@s3_metadata = @options[:s3_metadata] || {}
|
147
137
|
@s3_headers = {}
|
@@ -149,7 +139,7 @@ module Paperclip
|
|
149
139
|
|
150
140
|
@s3_storage_class = set_storage_class(@options[:s3_storage_class])
|
151
141
|
|
152
|
-
@s3_server_side_encryption =
|
142
|
+
@s3_server_side_encryption = "AES256"
|
153
143
|
if @options[:s3_server_side_encryption].blank?
|
154
144
|
@s3_server_side_encryption = false
|
155
145
|
end
|
@@ -157,9 +147,9 @@ module Paperclip
|
|
157
147
|
@s3_server_side_encryption = @options[:s3_server_side_encryption]
|
158
148
|
end
|
159
149
|
|
160
|
-
unless @options[:url].to_s.match(/\A:s3.*url\Z/) || @options[:url] == ":asset_host"
|
161
|
-
@options[:path] = path_option.gsub(/:url/, @options[:url]).
|
162
|
-
@options[:url] = ":s3_path_url"
|
150
|
+
unless @options[:url].to_s.match(/\A:s3.*url\Z/) || @options[:url] == ":asset_host".freeze
|
151
|
+
@options[:path] = path_option.gsub(/:url/, @options[:url]).sub(/\A:rails_root\/public\/system/, "".freeze)
|
152
|
+
@options[:url] = ":s3_path_url".freeze
|
163
153
|
end
|
164
154
|
@options[:url] = @options[:url].inspect if @options[:url].is_a?(Symbol)
|
165
155
|
|
@@ -167,23 +157,26 @@ module Paperclip
|
|
167
157
|
end
|
168
158
|
|
169
159
|
Paperclip.interpolates(:s3_alias_url) do |attachment, style|
|
170
|
-
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_alias}/#{attachment.path(style).
|
160
|
+
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_alias}/#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
|
171
161
|
end unless Paperclip::Interpolations.respond_to? :s3_alias_url
|
172
162
|
Paperclip.interpolates(:s3_path_url) do |attachment, style|
|
173
|
-
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_name}/#{attachment.bucket_name}/#{attachment.path(style).
|
163
|
+
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_name}/#{attachment.bucket_name}/#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
|
174
164
|
end unless Paperclip::Interpolations.respond_to? :s3_path_url
|
175
165
|
Paperclip.interpolates(:s3_domain_url) do |attachment, style|
|
176
|
-
"#{attachment.s3_protocol(style, true)}//#{attachment.bucket_name}.#{attachment.s3_host_name}/#{attachment.path(style).
|
166
|
+
"#{attachment.s3_protocol(style, true)}//#{attachment.bucket_name}.#{attachment.s3_host_name}/#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
|
177
167
|
end unless Paperclip::Interpolations.respond_to? :s3_domain_url
|
178
168
|
Paperclip.interpolates(:asset_host) do |attachment, style|
|
179
|
-
"#{attachment.path(style).
|
169
|
+
"#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
|
180
170
|
end unless Paperclip::Interpolations.respond_to? :asset_host
|
181
171
|
end
|
182
172
|
|
183
173
|
def expiring_url(time = 3600, style_name = default_style)
|
184
174
|
if path(style_name)
|
185
|
-
base_options = { :
|
186
|
-
s3_object(style_name).
|
175
|
+
base_options = { expires_in: time }
|
176
|
+
s3_object(style_name).presigned_url(
|
177
|
+
:get,
|
178
|
+
base_options.merge(s3_url_options),
|
179
|
+
).to_s
|
187
180
|
else
|
188
181
|
url(style_name)
|
189
182
|
end
|
@@ -197,7 +190,14 @@ module Paperclip
|
|
197
190
|
host_name = @options[:s3_host_name]
|
198
191
|
host_name = host_name.call(self) if host_name.is_a?(Proc)
|
199
192
|
|
200
|
-
host_name || s3_credentials[:s3_host_name] || "s3.amazonaws.com"
|
193
|
+
host_name || s3_credentials[:s3_host_name] || "s3.amazonaws.com".freeze
|
194
|
+
end
|
195
|
+
|
196
|
+
def s3_region
|
197
|
+
region = @options[:s3_region]
|
198
|
+
region = region.call(self) if region.is_a?(Proc)
|
199
|
+
|
200
|
+
region || s3_credentials[:s3_region]
|
201
201
|
end
|
202
202
|
|
203
203
|
def s3_host_alias
|
@@ -220,7 +220,7 @@ module Paperclip
|
|
220
220
|
|
221
221
|
def s3_interface
|
222
222
|
@s3_interface ||= begin
|
223
|
-
config = { :
|
223
|
+
config = { region: s3_region }
|
224
224
|
|
225
225
|
if using_http_proxy?
|
226
226
|
|
@@ -234,7 +234,7 @@ module Paperclip
|
|
234
234
|
config[:proxy_uri] = URI::HTTP.build(proxy_opts)
|
235
235
|
end
|
236
236
|
|
237
|
-
[:access_key_id, :secret_access_key, :credential_provider].each do |opt|
|
237
|
+
[:access_key_id, :secret_access_key, :credential_provider, :credentials].each do |opt|
|
238
238
|
config[opt] = s3_credentials[opt] if s3_credentials[opt]
|
239
239
|
end
|
240
240
|
|
@@ -244,15 +244,19 @@ module Paperclip
|
|
244
244
|
|
245
245
|
def obtain_s3_instance_for(options)
|
246
246
|
instances = (Thread.current[:paperclip_s3_instances] ||= {})
|
247
|
-
instances[options] ||=
|
247
|
+
instances[options] ||= ::Aws::S3::Resource.new(options)
|
248
248
|
end
|
249
249
|
|
250
250
|
def s3_bucket
|
251
|
-
@s3_bucket ||= s3_interface.
|
251
|
+
@s3_bucket ||= s3_interface.bucket(bucket_name)
|
252
|
+
end
|
253
|
+
|
254
|
+
def style_name_as_path(style_name)
|
255
|
+
path(style_name).sub(%r{\A/},'')
|
252
256
|
end
|
253
257
|
|
254
258
|
def s3_object style_name = default_style
|
255
|
-
s3_bucket.
|
259
|
+
s3_bucket.object style_name_as_path(style_name)
|
256
260
|
end
|
257
261
|
|
258
262
|
def using_http_proxy?
|
@@ -277,7 +281,7 @@ module Paperclip
|
|
277
281
|
|
278
282
|
def set_permissions permissions
|
279
283
|
permissions = { :default => permissions } unless permissions.respond_to?(:merge)
|
280
|
-
permissions.merge :default => (permissions[:default] || :
|
284
|
+
permissions.merge :default => (permissions[:default] || :"public-read")
|
281
285
|
end
|
282
286
|
|
283
287
|
def set_storage_class(storage_class)
|
@@ -286,7 +290,7 @@ module Paperclip
|
|
286
290
|
end
|
287
291
|
|
288
292
|
def parse_credentials creds
|
289
|
-
creds = creds.respond_to?(
|
293
|
+
creds = creds.respond_to?(:call) ? creds.call(self) : creds
|
290
294
|
creds = find_credentials(creds).stringify_keys
|
291
295
|
(creds[RailsEnvironment.get] || creds).symbolize_keys
|
292
296
|
end
|
@@ -297,7 +301,7 @@ module Paperclip
|
|
297
301
|
else
|
298
302
|
false
|
299
303
|
end
|
300
|
-
rescue
|
304
|
+
rescue Aws::Errors::ServiceError => e
|
301
305
|
false
|
302
306
|
end
|
303
307
|
|
@@ -323,7 +327,7 @@ module Paperclip
|
|
323
327
|
end
|
324
328
|
|
325
329
|
def create_bucket
|
326
|
-
s3_interface.
|
330
|
+
s3_interface.bucket(bucket_name).create
|
327
331
|
end
|
328
332
|
|
329
333
|
def flush_writes #:nodoc:
|
@@ -331,11 +335,9 @@ module Paperclip
|
|
331
335
|
retries = 0
|
332
336
|
begin
|
333
337
|
log("saving #{path(style)}")
|
334
|
-
acl = @s3_permissions[style] || @s3_permissions[:default]
|
335
|
-
acl = acl.call(self, style) if acl.respond_to?(:call)
|
336
338
|
write_options = {
|
337
339
|
:content_type => file.content_type,
|
338
|
-
:acl =>
|
340
|
+
:acl => s3_permissions(style)
|
339
341
|
}
|
340
342
|
|
341
343
|
# add storage class for this style if defined
|
@@ -356,11 +358,11 @@ module Paperclip
|
|
356
358
|
write_options[:metadata] = @s3_metadata unless @s3_metadata.empty?
|
357
359
|
write_options.merge!(@s3_headers)
|
358
360
|
|
359
|
-
s3_object(style).
|
360
|
-
rescue
|
361
|
+
s3_object(style).upload_file(file.path, write_options)
|
362
|
+
rescue ::Aws::S3::Errors::NoSuchBucket
|
361
363
|
create_bucket
|
362
364
|
retry
|
363
|
-
rescue
|
365
|
+
rescue ::Aws::S3::Errors::SlowDown
|
364
366
|
retries += 1
|
365
367
|
if retries <= 5
|
366
368
|
sleep((2 ** retries) * 0.5)
|
@@ -382,8 +384,8 @@ module Paperclip
|
|
382
384
|
@queued_for_delete.each do |path|
|
383
385
|
begin
|
384
386
|
log("deleting #{path}")
|
385
|
-
s3_bucket.
|
386
|
-
rescue
|
387
|
+
s3_bucket.object(path.sub(%r{\A/}, "")).delete
|
388
|
+
rescue Aws::Errors::ServiceError => e
|
387
389
|
# Ignore this.
|
388
390
|
end
|
389
391
|
end
|
@@ -393,11 +395,11 @@ module Paperclip
|
|
393
395
|
def copy_to_local_file(style, local_dest_path)
|
394
396
|
log("copying #{path(style)} to local file #{local_dest_path}")
|
395
397
|
::File.open(local_dest_path, 'wb') do |local_file|
|
396
|
-
s3_object(style).
|
398
|
+
s3_object(style).get do |chunk|
|
397
399
|
local_file.write(chunk)
|
398
400
|
end
|
399
401
|
end
|
400
|
-
rescue
|
402
|
+
rescue Aws::Errors::ServiceError => e
|
401
403
|
warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
|
402
404
|
false
|
403
405
|
end
|
@@ -71,13 +71,7 @@ module Paperclip
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def human_size(size)
|
74
|
-
|
75
|
-
ActiveSupport::NumberHelper.number_to_human_size(size)
|
76
|
-
else
|
77
|
-
storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
|
78
|
-
unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => size.to_i, :raise => true)
|
79
|
-
storage_units_format.gsub(/%n/, size.to_i.to_s).gsub(/%u/, unit).html_safe
|
80
|
-
end
|
74
|
+
ActiveSupport::NumberHelper.number_to_human_size(size)
|
81
75
|
end
|
82
76
|
|
83
77
|
def min_value_in_human_size(record)
|