paperclip 3.5.4 → 4.3.7
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/.gitignore +0 -6
- data/.hound.yml +1066 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +11 -17
- data/Appraisals +6 -14
- data/CONTRIBUTING.md +13 -8
- data/Gemfile +16 -3
- data/LICENSE +1 -3
- data/NEWS +167 -49
- data/README.md +294 -75
- data/RELEASING.md +17 -0
- data/Rakefile +6 -8
- data/features/basic_integration.feature +24 -6
- data/features/step_definitions/attachment_steps.rb +30 -22
- data/features/step_definitions/html_steps.rb +2 -2
- data/features/step_definitions/rails_steps.rb +44 -14
- data/features/step_definitions/web_steps.rb +1 -103
- data/features/support/env.rb +2 -2
- data/features/support/file_helpers.rb +2 -2
- data/features/support/fixtures/gemfile.txt +1 -1
- data/features/support/rails.rb +2 -1
- data/gemfiles/3.2.gemfile +14 -6
- data/gemfiles/4.1.gemfile +19 -0
- data/gemfiles/4.2.gemfile +19 -0
- data/lib/generators/paperclip/paperclip_generator.rb +0 -2
- data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +1 -1
- data/lib/paperclip/attachment.rb +132 -38
- data/lib/paperclip/attachment_registry.rb +1 -1
- data/lib/paperclip/callbacks.rb +11 -1
- data/lib/paperclip/content_type_detector.rb +25 -22
- data/lib/paperclip/deprecations.rb +42 -0
- data/lib/paperclip/errors.rb +5 -0
- data/lib/paperclip/file_command_content_type_detector.rb +6 -8
- data/lib/paperclip/geometry_detector_factory.rb +3 -1
- data/lib/paperclip/geometry_parser_factory.rb +1 -1
- data/lib/paperclip/has_attached_file.rb +10 -0
- data/lib/paperclip/interpolations/plural_cache.rb +6 -5
- data/lib/paperclip/interpolations.rb +25 -12
- data/lib/paperclip/io_adapters/abstract_adapter.rb +3 -1
- data/lib/paperclip/io_adapters/attachment_adapter.rb +4 -4
- data/lib/paperclip/io_adapters/data_uri_adapter.rb +5 -10
- data/lib/paperclip/io_adapters/stringio_adapter.rb +6 -10
- data/lib/paperclip/io_adapters/uri_adapter.rb +30 -11
- data/lib/paperclip/locales/de.yml +18 -0
- data/lib/paperclip/locales/en.yml +1 -0
- data/lib/paperclip/locales/es.yml +18 -0
- data/lib/paperclip/locales/ja.yml +18 -0
- data/lib/paperclip/locales/pt-BR.yml +18 -0
- data/lib/paperclip/locales/zh-CN.yml +18 -0
- data/lib/paperclip/locales/zh-HK.yml +18 -0
- data/lib/paperclip/locales/zh-TW.yml +18 -0
- data/lib/paperclip/matchers/have_attached_file_matcher.rb +2 -1
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +2 -1
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +2 -1
- data/lib/paperclip/media_type_spoof_detector.rb +89 -0
- data/lib/paperclip/processor.rb +0 -37
- data/lib/paperclip/processor_helpers.rb +50 -0
- data/lib/paperclip/rails_environment.rb +25 -0
- data/lib/paperclip/schema.rb +10 -2
- data/lib/paperclip/storage/filesystem.rb +1 -1
- data/lib/paperclip/storage/fog.rb +18 -7
- data/lib/paperclip/storage/s3.rb +53 -22
- data/lib/paperclip/style.rb +8 -2
- data/lib/paperclip/tempfile_factory.rb +5 -1
- data/lib/paperclip/thumbnail.rb +12 -10
- data/lib/paperclip/url_generator.rb +11 -3
- data/lib/paperclip/validators/attachment_content_type_validator.rb +4 -0
- data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
- data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
- data/lib/paperclip/validators/attachment_presence_validator.rb +4 -0
- data/lib/paperclip/validators/attachment_size_validator.rb +11 -3
- data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
- data/lib/paperclip/validators.rb +10 -3
- data/lib/paperclip/version.rb +1 -1
- data/lib/paperclip.rb +26 -8
- data/lib/tasks/paperclip.rake +17 -2
- data/paperclip.gemspec +16 -14
- data/shoulda_macros/paperclip.rb +0 -1
- data/spec/paperclip/attachment_definitions_spec.rb +13 -0
- data/{test/attachment_processing_test.rb → spec/paperclip/attachment_processing_spec.rb} +20 -21
- data/spec/paperclip/attachment_registry_spec.rb +130 -0
- data/{test/attachment_test.rb → spec/paperclip/attachment_spec.rb} +438 -397
- data/{test/content_type_detector_test.rb → spec/paperclip/content_type_detector_spec.rb} +16 -19
- data/spec/paperclip/deprecations_spec.rb +65 -0
- data/{test/file_command_content_type_detector_test.rb → spec/paperclip/file_command_content_type_detector_spec.rb} +5 -6
- data/spec/paperclip/filename_cleaner_spec.rb +14 -0
- data/spec/paperclip/geometry_detector_spec.rb +39 -0
- data/{test/geometry_parser_test.rb → spec/paperclip/geometry_parser_spec.rb} +27 -27
- data/{test/geometry_test.rb → spec/paperclip/geometry_spec.rb} +50 -52
- data/spec/paperclip/glue_spec.rb +44 -0
- data/{test/has_attached_file_test.rb → spec/paperclip/has_attached_file_spec.rb} +45 -28
- data/{test/integration_test.rb → spec/paperclip/integration_spec.rb} +134 -126
- data/{test/interpolations_test.rb → spec/paperclip/interpolations_spec.rb} +70 -46
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +78 -0
- data/{test/io_adapters/attachment_adapter_test.rb → spec/paperclip/io_adapters/attachment_adapter_spec.rb} +27 -29
- data/{test/io_adapters/data_uri_adapter_test.rb → spec/paperclip/io_adapters/data_uri_adapter_spec.rb} +26 -17
- data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
- data/{test/io_adapters/file_adapter_test.rb → spec/paperclip/io_adapters/file_adapter_spec.rb} +36 -40
- data/{test/io_adapters/http_url_proxy_adapter_test.rb → spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb} +31 -29
- data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
- data/{test/io_adapters/nil_adapter_test.rb → spec/paperclip/io_adapters/nil_adapter_spec.rb} +7 -7
- data/{test/io_adapters/registry_test.rb → spec/paperclip/io_adapters/registry_spec.rb} +10 -7
- data/{test/io_adapters/stringio_adapter_test.rb → spec/paperclip/io_adapters/stringio_adapter_spec.rb} +20 -17
- data/{test/io_adapters/uploaded_file_adapter_test.rb → spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb} +41 -41
- data/{test/io_adapters/uri_adapter_test.rb → spec/paperclip/io_adapters/uri_adapter_spec.rb} +53 -28
- data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
- data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +99 -0
- data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
- data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
- data/spec/paperclip/media_type_spoof_detector_spec.rb +79 -0
- data/spec/paperclip/meta_class_spec.rb +30 -0
- data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
- data/{test/paperclip_test.rb → spec/paperclip/paperclip_spec.rb} +53 -48
- data/spec/paperclip/plural_cache_spec.rb +37 -0
- data/spec/paperclip/processor_helpers_spec.rb +57 -0
- data/{test/processor_test.rb → spec/paperclip/processor_spec.rb} +5 -5
- data/spec/paperclip/rails_environment_spec.rb +33 -0
- data/{test/rake_test.rb → spec/paperclip/rake_spec.rb} +15 -15
- data/spec/paperclip/schema_spec.rb +248 -0
- data/{test/storage/filesystem_test.rb → spec/paperclip/storage/filesystem_spec.rb} +18 -18
- data/spec/paperclip/storage/fog_spec.rb +535 -0
- data/spec/paperclip/storage/s3_live_spec.rb +182 -0
- data/spec/paperclip/storage/s3_spec.rb +1526 -0
- data/spec/paperclip/style_spec.rb +255 -0
- data/spec/paperclip/tempfile_factory_spec.rb +33 -0
- data/{test/thumbnail_test.rb → spec/paperclip/thumbnail_spec.rb} +123 -107
- data/spec/paperclip/url_generator_spec.rb +211 -0
- data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
- data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
- data/{test/validators/attachment_presence_validator_test.rb → spec/paperclip/validators/attachment_presence_validator_spec.rb} +20 -20
- data/{test/validators/attachment_size_validator_test.rb → spec/paperclip/validators/attachment_size_validator_spec.rb} +65 -58
- data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
- data/spec/paperclip/validators_spec.rb +164 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/assertions.rb +71 -0
- data/spec/support/deprecations.rb +9 -0
- data/spec/support/fake_model.rb +25 -0
- data/spec/support/fake_rails.rb +12 -0
- data/spec/support/fixtures/empty.html +1 -0
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/fixtures/spaced file.jpg +0 -0
- data/spec/support/matchers/accept.rb +5 -0
- data/spec/support/matchers/exist.rb +5 -0
- data/spec/support/matchers/have_column.rb +23 -0
- data/spec/support/model_reconstruction.rb +60 -0
- data/spec/support/rails_helpers.rb +7 -0
- data/spec/support/test_data.rb +13 -0
- data/spec/support/version_helper.rb +9 -0
- metadata +334 -219
- data/RUNNING_TESTS.md +0 -4
- data/gemfiles/3.0.gemfile +0 -11
- data/gemfiles/3.1.gemfile +0 -11
- data/gemfiles/4.0.gemfile +0 -11
- data/test/attachment_definitions_test.rb +0 -12
- data/test/attachment_registry_test.rb +0 -88
- data/test/filename_cleaner_test.rb +0 -14
- data/test/generator_test.rb +0 -84
- data/test/geometry_detector_test.rb +0 -24
- data/test/helper.rb +0 -232
- data/test/io_adapters/abstract_adapter_test.rb +0 -58
- data/test/io_adapters/empty_string_adapter_test.rb +0 -18
- data/test/io_adapters/identity_adapter_test.rb +0 -8
- data/test/matchers/have_attached_file_matcher_test.rb +0 -24
- data/test/matchers/validate_attachment_content_type_matcher_test.rb +0 -110
- data/test/matchers/validate_attachment_presence_matcher_test.rb +0 -69
- data/test/matchers/validate_attachment_size_matcher_test.rb +0 -86
- data/test/meta_class_test.rb +0 -32
- data/test/paperclip_missing_attachment_styles_test.rb +0 -90
- data/test/plural_cache_test.rb +0 -36
- data/test/schema_test.rb +0 -200
- data/test/storage/fog_test.rb +0 -473
- data/test/storage/s3_live_test.rb +0 -179
- data/test/storage/s3_test.rb +0 -1356
- data/test/style_test.rb +0 -213
- data/test/support/mock_model.rb +0 -2
- data/test/tempfile_factory_test.rb +0 -17
- data/test/url_generator_test.rb +0 -187
- data/test/validators/attachment_content_type_validator_test.rb +0 -324
- data/test/validators_test.rb +0 -61
- /data/{test → spec}/database.yml +0 -0
- /data/{test → spec/support}/fixtures/12k.png +0 -0
- /data/{test → spec/support}/fixtures/50x50.png +0 -0
- /data/{test → spec/support}/fixtures/5k.png +0 -0
- /data/{test → spec/support}/fixtures/animated +0 -0
- /data/{test → spec/support}/fixtures/animated.gif +0 -0
- /data/{test → spec/support}/fixtures/animated.unknown +0 -0
- /data/{test → spec/support}/fixtures/bad.png +0 -0
- /data/{test → spec/support}/fixtures/fog.yml +0 -0
- /data/{test → spec/support}/fixtures/rotated.jpg +0 -0
- /data/{test → spec/support}/fixtures/s3.yml +0 -0
- /data/{test → spec/support}/fixtures/spaced file.png +0 -0
- /data/{test → spec/support}/fixtures/text.txt +0 -0
- /data/{test → spec/support}/fixtures/twopage.pdf +0 -0
- /data/{test → spec/support}/fixtures/uppercase.PNG +0 -0
- /data/{test → spec}/support/mock_attachment.rb +0 -0
- /data/{test → spec}/support/mock_interpolator.rb +0 -0
- /data/{test → spec}/support/mock_url_generator_builder.rb +0 -0
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', '~> 1.6'
|
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
|
@@ -26,7 +26,7 @@ module Paperclip
|
|
26
26
|
# put your bucket name in this file, instead of adding it to the code directly.
|
27
27
|
# This is useful when you want the same account but a different bucket for
|
28
28
|
# development versus production.
|
29
|
-
# When using a Proc it provides a single parameter which is the attachment itself. A
|
29
|
+
# When using a Proc it provides a single parameter which is the attachment itself. A
|
30
30
|
# method #instance is available on the attachment which will take you back to your
|
31
31
|
# code. eg.
|
32
32
|
# class User
|
@@ -51,7 +51,7 @@ module Paperclip
|
|
51
51
|
# :s3_permissions => :private
|
52
52
|
#
|
53
53
|
# * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
|
54
|
-
# 'http', 'https', or an empty string to generate
|
54
|
+
# 'http', 'https', or an empty string to generate protocol-relative URLs. Defaults to 'http'
|
55
55
|
# when your :s3_permissions are :public_read (the default), and 'https' when your
|
56
56
|
# :s3_permissions are anything else.
|
57
57
|
# * +s3_headers+: A hash of headers or a Proc. You may specify a hash such as
|
@@ -102,6 +102,14 @@ module Paperclip
|
|
102
102
|
# Redundancy Storage. RRS enables customers to reduce their
|
103
103
|
# costs by storing non-critical, reproducible data at lower
|
104
104
|
# levels of redundancy than Amazon S3's standard storage.
|
105
|
+
#
|
106
|
+
# You can set storage class on a per style bases by doing the following:
|
107
|
+
# :s3_storage_class => {
|
108
|
+
# :thumb => :reduced_reduncancy
|
109
|
+
# }
|
110
|
+
# Or globally:
|
111
|
+
# :s3_storage_class => :reduced_redundancy
|
112
|
+
|
105
113
|
module S3
|
106
114
|
def self.extended base
|
107
115
|
begin
|
@@ -133,25 +141,25 @@ module Paperclip
|
|
133
141
|
Proc.new do |style, attachment|
|
134
142
|
permission = (@s3_permissions[style.to_s.to_sym] || @s3_permissions[:default])
|
135
143
|
permission = permission.call(attachment, style) if permission.respond_to?(:call)
|
136
|
-
(permission == :public_read) ? 'http' : 'https'
|
144
|
+
(permission == :public_read) ? 'http'.freeze : 'https'.freeze
|
137
145
|
end
|
138
146
|
@s3_metadata = @options[:s3_metadata] || {}
|
139
147
|
@s3_headers = {}
|
140
148
|
merge_s3_headers(@options[:s3_headers], @s3_headers, @s3_metadata)
|
141
149
|
|
142
|
-
@
|
150
|
+
@s3_storage_class = set_storage_class(@options[:s3_storage_class])
|
143
151
|
|
144
152
|
@s3_server_side_encryption = :aes256
|
145
153
|
if @options[:s3_server_side_encryption].blank?
|
146
154
|
@s3_server_side_encryption = false
|
147
155
|
end
|
148
156
|
if @s3_server_side_encryption
|
149
|
-
@s3_server_side_encryption = @options[:s3_server_side_encryption]
|
157
|
+
@s3_server_side_encryption = @options[:s3_server_side_encryption]
|
150
158
|
end
|
151
159
|
|
152
|
-
unless @options[:url].to_s.match(/\A:s3.*url\Z/) || @options[:url] == ":asset_host"
|
153
|
-
@options[:path] =
|
154
|
-
@options[:url] = ":s3_path_url"
|
160
|
+
unless @options[:url].to_s.match(/\A:s3.*url\Z/) || @options[:url] == ":asset_host".freeze
|
161
|
+
@options[:path] = path_option.gsub(/:url/, @options[:url]).sub(/\A:rails_root\/public\/system/, "".freeze)
|
162
|
+
@options[:url] = ":s3_path_url".freeze
|
155
163
|
end
|
156
164
|
@options[:url] = @options[:url].inspect if @options[:url].is_a?(Symbol)
|
157
165
|
|
@@ -159,16 +167,16 @@ module Paperclip
|
|
159
167
|
end
|
160
168
|
|
161
169
|
Paperclip.interpolates(:s3_alias_url) do |attachment, style|
|
162
|
-
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_alias}/#{attachment.path(style).
|
170
|
+
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_alias}/#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
|
163
171
|
end unless Paperclip::Interpolations.respond_to? :s3_alias_url
|
164
172
|
Paperclip.interpolates(:s3_path_url) do |attachment, style|
|
165
|
-
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_name}/#{attachment.bucket_name}/#{attachment.path(style).
|
173
|
+
"#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_name}/#{attachment.bucket_name}/#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
|
166
174
|
end unless Paperclip::Interpolations.respond_to? :s3_path_url
|
167
175
|
Paperclip.interpolates(:s3_domain_url) do |attachment, style|
|
168
|
-
"#{attachment.s3_protocol(style, true)}//#{attachment.bucket_name}.#{attachment.s3_host_name}/#{attachment.path(style).
|
176
|
+
"#{attachment.s3_protocol(style, true)}//#{attachment.bucket_name}.#{attachment.s3_host_name}/#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
|
169
177
|
end unless Paperclip::Interpolations.respond_to? :s3_domain_url
|
170
178
|
Paperclip.interpolates(:asset_host) do |attachment, style|
|
171
|
-
"#{attachment.path(style).
|
179
|
+
"#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
|
172
180
|
end unless Paperclip::Interpolations.respond_to? :asset_host
|
173
181
|
end
|
174
182
|
|
@@ -189,7 +197,7 @@ module Paperclip
|
|
189
197
|
host_name = @options[:s3_host_name]
|
190
198
|
host_name = host_name.call(self) if host_name.is_a?(Proc)
|
191
199
|
|
192
|
-
host_name || s3_credentials[:s3_host_name] || "s3.amazonaws.com"
|
200
|
+
host_name || s3_credentials[:s3_host_name] || "s3.amazonaws.com".freeze
|
193
201
|
end
|
194
202
|
|
195
203
|
def s3_host_alias
|
@@ -272,11 +280,15 @@ module Paperclip
|
|
272
280
|
permissions.merge :default => (permissions[:default] || :public_read)
|
273
281
|
end
|
274
282
|
|
283
|
+
def set_storage_class(storage_class)
|
284
|
+
storage_class = {:default => storage_class} unless storage_class.respond_to?(:merge)
|
285
|
+
storage_class
|
286
|
+
end
|
287
|
+
|
275
288
|
def parse_credentials creds
|
276
|
-
creds = creds.respond_to?(
|
289
|
+
creds = creds.respond_to?(:call) ? creds.call(self) : creds
|
277
290
|
creds = find_credentials(creds).stringify_keys
|
278
|
-
|
279
|
-
(creds[env] || creds).symbolize_keys
|
291
|
+
(creds[RailsEnvironment.get] || creds).symbolize_keys
|
280
292
|
end
|
281
293
|
|
282
294
|
def exists?(style = default_style)
|
@@ -295,6 +307,10 @@ module Paperclip
|
|
295
307
|
s3_permissions
|
296
308
|
end
|
297
309
|
|
310
|
+
def s3_storage_class(style = default_style)
|
311
|
+
@s3_storage_class[style] || @s3_storage_class[:default]
|
312
|
+
end
|
313
|
+
|
298
314
|
def s3_protocol(style = default_style, with_colon = false)
|
299
315
|
protocol = @s3_protocol
|
300
316
|
protocol = protocol.call(style, self) if protocol.respond_to?(:call)
|
@@ -312,6 +328,7 @@ module Paperclip
|
|
312
328
|
|
313
329
|
def flush_writes #:nodoc:
|
314
330
|
@queued_for_write.each do |style, file|
|
331
|
+
retries = 0
|
315
332
|
begin
|
316
333
|
log("saving #{path(style)}")
|
317
334
|
acl = @s3_permissions[style] || @s3_permissions[:default]
|
@@ -320,6 +337,11 @@ module Paperclip
|
|
320
337
|
:content_type => file.content_type,
|
321
338
|
:acl => acl
|
322
339
|
}
|
340
|
+
|
341
|
+
# add storage class for this style if defined
|
342
|
+
storage_class = s3_storage_class(style)
|
343
|
+
write_options.merge!(:storage_class => storage_class) if storage_class
|
344
|
+
|
323
345
|
if @s3_server_side_encryption
|
324
346
|
write_options[:server_side_encryption] = @s3_server_side_encryption
|
325
347
|
end
|
@@ -335,9 +357,17 @@ module Paperclip
|
|
335
357
|
write_options.merge!(@s3_headers)
|
336
358
|
|
337
359
|
s3_object(style).write(file, write_options)
|
338
|
-
rescue AWS::S3::Errors::NoSuchBucket
|
360
|
+
rescue AWS::S3::Errors::NoSuchBucket
|
339
361
|
create_bucket
|
340
362
|
retry
|
363
|
+
rescue AWS::S3::Errors::SlowDown
|
364
|
+
retries += 1
|
365
|
+
if retries <= 5
|
366
|
+
sleep((2 ** retries) * 0.5)
|
367
|
+
retry
|
368
|
+
else
|
369
|
+
raise
|
370
|
+
end
|
341
371
|
ensure
|
342
372
|
file.rewind
|
343
373
|
end
|
@@ -362,10 +392,11 @@ module Paperclip
|
|
362
392
|
|
363
393
|
def copy_to_local_file(style, local_dest_path)
|
364
394
|
log("copying #{path(style)} to local file #{local_dest_path}")
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
395
|
+
::File.open(local_dest_path, 'wb') do |local_file|
|
396
|
+
s3_object(style).read do |chunk|
|
397
|
+
local_file.write(chunk)
|
398
|
+
end
|
399
|
+
end
|
369
400
|
rescue AWS::Errors::Base => e
|
370
401
|
warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
|
371
402
|
false
|
data/lib/paperclip/style.rb
CHANGED
@@ -29,7 +29,7 @@ module Paperclip
|
|
29
29
|
@geometry, @format = [definition, nil].flatten[0..1]
|
30
30
|
@other_args = {}
|
31
31
|
end
|
32
|
-
@format
|
32
|
+
@format = default_format if @format.blank?
|
33
33
|
end
|
34
34
|
|
35
35
|
# retrieves from the attachment the processors defined in the has_attached_file call
|
@@ -71,7 +71,7 @@ module Paperclip
|
|
71
71
|
# Arguments other than the standard geometry, format etc are just passed through from
|
72
72
|
# initialization and any procs are called here, just before post-processing.
|
73
73
|
def processor_options
|
74
|
-
args = {}
|
74
|
+
args = {:style => name}
|
75
75
|
@other_args.each do |k,v|
|
76
76
|
args[k] = v.respond_to?(:call) ? v.call(attachment) : v
|
77
77
|
end
|
@@ -99,5 +99,11 @@ module Paperclip
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
# defaults to default format (nil by default)
|
103
|
+
def default_format
|
104
|
+
base = attachment.options[:default_format]
|
105
|
+
base.respond_to?(:call) ? base.call(attachment, name) : base
|
106
|
+
end
|
107
|
+
|
102
108
|
end
|
103
109
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class TempfileFactory
|
3
3
|
|
4
|
-
def generate(name)
|
4
|
+
def generate(name = random_name)
|
5
5
|
@name = name
|
6
6
|
file = Tempfile.new([basename, extension])
|
7
7
|
file.binmode
|
@@ -15,5 +15,9 @@ module Paperclip
|
|
15
15
|
def basename
|
16
16
|
Digest::MD5.hexdigest(File.basename(@name, extension))
|
17
17
|
end
|
18
|
+
|
19
|
+
def random_name
|
20
|
+
SecureRandom.uuid
|
21
|
+
end
|
18
22
|
end
|
19
23
|
end
|
data/lib/paperclip/thumbnail.rb
CHANGED
@@ -28,17 +28,16 @@ module Paperclip
|
|
28
28
|
def initialize(file, options = {}, attachment = nil)
|
29
29
|
super
|
30
30
|
|
31
|
-
geometry = options[:geometry]
|
32
|
-
@file = file
|
31
|
+
geometry = options[:geometry].to_s
|
33
32
|
@crop = geometry[-1,1] == '#'
|
34
|
-
@target_geometry = (
|
35
|
-
@current_geometry = (
|
33
|
+
@target_geometry = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
|
34
|
+
@current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
|
36
35
|
@source_file_options = options[:source_file_options]
|
37
36
|
@convert_options = options[:convert_options]
|
38
|
-
@whiny = options
|
37
|
+
@whiny = options.fetch(:whiny, true)
|
39
38
|
@format = options[:format]
|
40
|
-
@animated = options
|
41
|
-
@auto_orient = options
|
39
|
+
@animated = options.fetch(:animated, true)
|
40
|
+
@auto_orient = options.fetch(:auto_orient, true)
|
42
41
|
if @auto_orient && @current_geometry.respond_to?(:auto_orient)
|
43
42
|
@current_geometry.auto_orient
|
44
43
|
end
|
@@ -64,8 +63,8 @@ module Paperclip
|
|
64
63
|
# that contains the new image.
|
65
64
|
def make
|
66
65
|
src = @file
|
67
|
-
|
68
|
-
dst.
|
66
|
+
filename = [@basename, @format ? ".#{@format}" : ""].join
|
67
|
+
dst = TempfileFactory.new.generate(filename)
|
69
68
|
|
70
69
|
begin
|
71
70
|
parameters = []
|
@@ -109,7 +108,10 @@ module Paperclip
|
|
109
108
|
|
110
109
|
# Return true if ImageMagick's +identify+ returns an animated format
|
111
110
|
def identified_as_animated?
|
112
|
-
|
111
|
+
if @identified_as_animated.nil?
|
112
|
+
@identified_as_animated = ANIMATED_FORMATS.include? identify("-format %m :file", :file => "#{@file.path}[0]").to_s.downcase.strip
|
113
|
+
end
|
114
|
+
@identified_as_animated
|
113
115
|
rescue Cocaine::ExitStatusError => e
|
114
116
|
raise Paperclip::Error, "There was an error running `identify` for #{@basename}" if @whiny
|
115
117
|
rescue Cocaine::CommandNotFoundError => e
|
@@ -8,8 +8,8 @@ module Paperclip
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def for(style_name, options)
|
11
|
-
|
12
|
-
|
11
|
+
timestamp_as_needed(
|
12
|
+
escape_url_as_needed(
|
13
13
|
@attachment_options[:interpolator].interpolate(most_appropriate_url, @attachment, style_name),
|
14
14
|
options
|
15
15
|
), options)
|
@@ -58,7 +58,15 @@ module Paperclip
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def escape_url(url)
|
61
|
-
|
61
|
+
if url.respond_to?(:escape)
|
62
|
+
url.escape
|
63
|
+
else
|
64
|
+
URI.escape(url).gsub(escape_regex){|m| "%#{m.ord.to_s(16).upcase}" }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def escape_regex
|
69
|
+
/[\?\(\)\[\]\+]/
|
62
70
|
end
|
63
71
|
end
|
64
72
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Paperclip
|
2
|
+
module Validators
|
3
|
+
class AttachmentFileNameValidator < ActiveModel::EachValidator
|
4
|
+
def initialize(options)
|
5
|
+
options[:allow_nil] = true unless options.has_key?(:allow_nil)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.helper_method_name
|
10
|
+
:validates_attachment_file_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate_each(record, attribute, value)
|
14
|
+
base_attribute = attribute.to_sym
|
15
|
+
attribute = "#{attribute}_file_name".to_sym
|
16
|
+
value = record.send :read_attribute_for_validation, attribute
|
17
|
+
|
18
|
+
return if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
|
19
|
+
|
20
|
+
validate_whitelist(record, attribute, value)
|
21
|
+
validate_blacklist(record, attribute, value)
|
22
|
+
|
23
|
+
if record.errors.include? attribute
|
24
|
+
record.errors[attribute].each do |error|
|
25
|
+
record.errors.add base_attribute, error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_whitelist(record, attribute, value)
|
31
|
+
if allowed.present? && allowed.none? { |type| type === value }
|
32
|
+
mark_invalid record, attribute, allowed
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate_blacklist(record, attribute, value)
|
37
|
+
if forbidden.present? && forbidden.any? { |type| type === value }
|
38
|
+
mark_invalid record, attribute, forbidden
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def mark_invalid(record, attribute, patterns)
|
43
|
+
record.errors.add attribute, :invalid, options.merge(:names => patterns.join(', '))
|
44
|
+
end
|
45
|
+
|
46
|
+
def allowed
|
47
|
+
[options[:matches]].flatten.compact
|
48
|
+
end
|
49
|
+
|
50
|
+
def forbidden
|
51
|
+
[options[:not]].flatten.compact
|
52
|
+
end
|
53
|
+
|
54
|
+
def check_validity!
|
55
|
+
unless options.has_key?(:matches) || options.has_key?(:not)
|
56
|
+
raise ArgumentError, "You must pass in either :matches or :not to the validator"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module HelperMethods
|
62
|
+
# Places ActiveModel validations on the name of the file
|
63
|
+
# assigned. The possible options are:
|
64
|
+
# * +matches+: Allowed filename patterns as Regexps. Can be a single one
|
65
|
+
# or an array.
|
66
|
+
# * +not+: Forbidden file name patterns, specified the same was as +matches+.
|
67
|
+
# * +message+: The message to display when the uploaded file has an invalid
|
68
|
+
# name.
|
69
|
+
# * +if+: A lambda or name of an instance method. Validation will only
|
70
|
+
# be run is this lambda or method returns true.
|
71
|
+
# * +unless+: Same as +if+ but validates if lambda or method returns false.
|
72
|
+
def validates_attachment_file_name(*attr_names)
|
73
|
+
options = _merge_attributes(attr_names)
|
74
|
+
validates_with AttachmentFileNameValidator, options.dup
|
75
|
+
validate_before_processing AttachmentFileNameValidator, options.dup
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'active_model/validations/presence'
|
2
|
+
|
3
|
+
module Paperclip
|
4
|
+
module Validators
|
5
|
+
class AttachmentFileTypeIgnoranceValidator < ActiveModel::EachValidator
|
6
|
+
def validate_each(record, attribute, value)
|
7
|
+
# This doesn't do anything. It's just to mark that you don't care about
|
8
|
+
# the file_names or content_types of your incoming attachments.
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.helper_method_name
|
12
|
+
:do_not_validate_attachment_file_type
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module HelperMethods
|
17
|
+
# Places ActiveModel validations on the presence of a file.
|
18
|
+
# Options:
|
19
|
+
# * +if+: A lambda or name of an instance method. Validation will only
|
20
|
+
# be run if this lambda or method returns true.
|
21
|
+
# * +unless+: Same as +if+ but validates if lambda or method returns false.
|
22
|
+
def do_not_validate_attachment_file_type(*attr_names)
|
23
|
+
options = _merge_attributes(attr_names)
|
24
|
+
validates_with AttachmentFileTypeIgnoranceValidator, options.dup
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -10,6 +10,10 @@ module Paperclip
|
|
10
10
|
super
|
11
11
|
end
|
12
12
|
|
13
|
+
def self.helper_method_name
|
14
|
+
:validates_attachment_size
|
15
|
+
end
|
16
|
+
|
13
17
|
def validate_each(record, attr_name, value)
|
14
18
|
base_attr_name = attr_name
|
15
19
|
attr_name = "#{attr_name}_file_size".to_sym
|
@@ -67,9 +71,13 @@ module Paperclip
|
|
67
71
|
end
|
68
72
|
|
69
73
|
def human_size(size)
|
70
|
-
|
71
|
-
|
72
|
-
|
74
|
+
if defined?(ActiveSupport::NumberHelper) # Rails 4.0+
|
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
|
73
81
|
end
|
74
82
|
|
75
83
|
def min_value_in_human_size(record)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'active_model/validations/presence'
|
2
|
+
|
3
|
+
module Paperclip
|
4
|
+
module Validators
|
5
|
+
class MediaTypeSpoofDetectionValidator < ActiveModel::EachValidator
|
6
|
+
def validate_each(record, attribute, value)
|
7
|
+
adapter = Paperclip.io_adapters.for(value)
|
8
|
+
if Paperclip::MediaTypeSpoofDetector.using(adapter, value.original_filename, value.content_type).spoofed?
|
9
|
+
record.errors.add(attribute, :spoofed_media_type)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module HelperMethods
|
15
|
+
# Places ActiveModel validations on the presence of a file.
|
16
|
+
# Options:
|
17
|
+
# * +if+: A lambda or name of an instance method. Validation will only
|
18
|
+
# be run if this lambda or method returns true.
|
19
|
+
# * +unless+: Same as +if+ but validates if lambda or method returns false.
|
20
|
+
def validates_media_type_spoof_detection(*attr_names)
|
21
|
+
options = _merge_attributes(attr_names)
|
22
|
+
validates_with MediaTypeSpoofDetectionValidator, options.dup
|
23
|
+
validate_before_processing MediaTypeSpoofDetectionValidator, options.dup
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/paperclip/validators.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
require 'active_model'
|
2
2
|
require 'active_support/concern'
|
3
|
+
require 'active_support/core_ext/array/wrap'
|
3
4
|
require 'paperclip/validators/attachment_content_type_validator'
|
5
|
+
require 'paperclip/validators/attachment_file_name_validator'
|
4
6
|
require 'paperclip/validators/attachment_presence_validator'
|
5
7
|
require 'paperclip/validators/attachment_size_validator'
|
8
|
+
require 'paperclip/validators/media_type_spoof_detection_validator'
|
9
|
+
require 'paperclip/validators/attachment_file_type_ignorance_validator'
|
6
10
|
|
7
11
|
module Paperclip
|
8
12
|
module Validators
|
@@ -13,6 +17,8 @@ module Paperclip
|
|
13
17
|
include HelperMethods
|
14
18
|
end
|
15
19
|
|
20
|
+
::Paperclip::REQUIRED_VALIDATORS = [AttachmentFileNameValidator, AttachmentContentTypeValidator, AttachmentFileTypeIgnoranceValidator]
|
21
|
+
|
16
22
|
module ClassMethods
|
17
23
|
# This method is a shortcut to validator classes that is in
|
18
24
|
# "Attachment...Validator" format. It is almost the same thing as the
|
@@ -36,10 +42,11 @@ module Paperclip
|
|
36
42
|
if options.has_key?(validator_kind)
|
37
43
|
validator_options = options.delete(validator_kind)
|
38
44
|
validator_options = {} if validator_options == true
|
39
|
-
local_options = attributes + [validator_options]
|
40
45
|
conditional_options = options.slice(:if, :unless)
|
41
|
-
|
42
|
-
|
46
|
+
Array.wrap(validator_options).each do |local_options|
|
47
|
+
method_name = Paperclip::Validators.const_get(constant.to_s).helper_method_name
|
48
|
+
send(method_name, attributes, local_options.merge(conditional_options))
|
49
|
+
end
|
43
50
|
end
|
44
51
|
end
|
45
52
|
end
|
data/lib/paperclip/version.rb
CHANGED
data/lib/paperclip.rb
CHANGED
@@ -33,6 +33,7 @@ require 'paperclip/geometry_parser_factory'
|
|
33
33
|
require 'paperclip/geometry_detector_factory'
|
34
34
|
require 'paperclip/geometry'
|
35
35
|
require 'paperclip/processor'
|
36
|
+
require 'paperclip/processor_helpers'
|
36
37
|
require 'paperclip/tempfile'
|
37
38
|
require 'paperclip/thumbnail'
|
38
39
|
require 'paperclip/interpolations/plural_cache'
|
@@ -43,6 +44,7 @@ require 'paperclip/attachment'
|
|
43
44
|
require 'paperclip/storage'
|
44
45
|
require 'paperclip/callbacks'
|
45
46
|
require 'paperclip/file_command_content_type_detector'
|
47
|
+
require 'paperclip/media_type_spoof_detector'
|
46
48
|
require 'paperclip/content_type_detector'
|
47
49
|
require 'paperclip/glue'
|
48
50
|
require 'paperclip/errors'
|
@@ -53,11 +55,22 @@ require 'paperclip/helpers'
|
|
53
55
|
require 'paperclip/has_attached_file'
|
54
56
|
require 'paperclip/attachment_registry'
|
55
57
|
require 'paperclip/filename_cleaner'
|
56
|
-
require '
|
58
|
+
require 'paperclip/rails_environment'
|
59
|
+
require "paperclip/deprecations"
|
60
|
+
|
61
|
+
begin
|
62
|
+
# Use mime/types/columnar if available, for reduced memory usage
|
63
|
+
require "mime/types/columnar"
|
64
|
+
rescue LoadError
|
65
|
+
require "mime/types"
|
66
|
+
end
|
67
|
+
|
68
|
+
require 'mimemagic'
|
69
|
+
require 'mimemagic/overlay'
|
57
70
|
require 'logger'
|
58
71
|
require 'cocaine'
|
59
72
|
|
60
|
-
require 'paperclip/railtie' if defined?(Rails)
|
73
|
+
require 'paperclip/railtie' if defined?(Rails::Railtie)
|
61
74
|
|
62
75
|
# The base module that gets included in ActiveRecord::Base. See the
|
63
76
|
# documentation for Paperclip::ClassMethods for more useful information.
|
@@ -74,14 +87,18 @@ module Paperclip
|
|
74
87
|
# * command_path: Defines the path at which to find the command line
|
75
88
|
# programs if they are not visible to Rails the system's search path. Defaults to
|
76
89
|
# nil, which uses the first executable found in the user's search path.
|
90
|
+
# * use_exif_orientation: Whether to inspect EXIF data to determine an
|
91
|
+
# image's orientation. Defaults to true.
|
77
92
|
def self.options
|
78
93
|
@options ||= {
|
79
|
-
:whiny
|
94
|
+
:whiny => true,
|
80
95
|
:image_magick_path => nil,
|
81
|
-
:command_path
|
82
|
-
:log
|
83
|
-
:log_command
|
84
|
-
:swallow_stderr
|
96
|
+
:command_path => nil,
|
97
|
+
:log => true,
|
98
|
+
:log_command => true,
|
99
|
+
:swallow_stderr => true,
|
100
|
+
:content_type_mappings => {},
|
101
|
+
:use_exif_orientation => true
|
85
102
|
}
|
86
103
|
end
|
87
104
|
|
@@ -130,7 +147,7 @@ module Paperclip
|
|
130
147
|
# user.avatar.url # => "/avatars/23/normal_me.png"
|
131
148
|
# * +keep_old_files+: Keep the existing attachment files (original + resized) from
|
132
149
|
# being automatically deleted when an attachment is cleared or updated. Defaults to +false+.
|
133
|
-
# * +preserve_files+: Keep the existing attachment files in all cases, even if the parent
|
150
|
+
# * +preserve_files+: Keep the existing attachment files in all cases, even if the parent
|
134
151
|
# record is destroyed. Defaults to +false+.
|
135
152
|
# * +whiny+: Will raise an error if Paperclip cannot post_process an uploaded file due
|
136
153
|
# to a command line error. This will override the global setting for this attachment.
|
@@ -175,6 +192,7 @@ module Paperclip
|
|
175
192
|
# end
|
176
193
|
# end
|
177
194
|
def has_attached_file(name, options = {})
|
195
|
+
Paperclip::Deprecations.check
|
178
196
|
HasAttachedFile.define_on(self, name, options)
|
179
197
|
end
|
180
198
|
end
|
data/lib/tasks/paperclip.rake
CHANGED
@@ -78,8 +78,7 @@ namespace :paperclip do
|
|
78
78
|
|
79
79
|
desc "Regenerates missing thumbnail styles for all classes using Paperclip."
|
80
80
|
task :missing_styles => :environment do
|
81
|
-
|
82
|
-
Dir[Rails.root + 'app/models/**/*.rb'].each { |path| load path }
|
81
|
+
Rails.application.eager_load!
|
83
82
|
Paperclip.missing_attachments_styles.each do |klass, attachment_definitions|
|
84
83
|
attachment_definitions.each do |attachment_name, missing_styles|
|
85
84
|
puts "Regenerating #{klass} -> #{attachment_name} -> #{missing_styles.inspect}"
|
@@ -109,4 +108,20 @@ namespace :paperclip do
|
|
109
108
|
end
|
110
109
|
end
|
111
110
|
end
|
111
|
+
|
112
|
+
desc "find missing attachments. Useful to know which attachments are broken"
|
113
|
+
task :find_broken_attachments => :environment do
|
114
|
+
klass = Paperclip::Task.obtain_class
|
115
|
+
names = Paperclip::Task.obtain_attachments(klass)
|
116
|
+
names.each do |name|
|
117
|
+
Paperclip.each_instance_with_attachment(klass, name) do |instance|
|
118
|
+
attachment = instance.send(name)
|
119
|
+
if attachment.exists?
|
120
|
+
print "."
|
121
|
+
else
|
122
|
+
Paperclip::Task.log_error("#{instance.class}##{attachment.name}, #{instance.id}, #{attachment.url}")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
112
127
|
end
|