paperclip 4.2.2 → 5.0.0
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/.hound.yml +1066 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +18 -15
- data/Appraisals +20 -12
- data/CONTRIBUTING.md +19 -8
- data/Gemfile +4 -9
- data/LICENSE +1 -1
- data/NEWS +101 -31
- data/README.md +243 -159
- data/RELEASING.md +17 -0
- data/Rakefile +1 -1
- data/UPGRADING +12 -9
- data/features/basic_integration.feature +8 -4
- data/features/migration.feature +0 -24
- data/features/step_definitions/attachment_steps.rb +27 -21
- 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.1.gemfile → 4.2.awsv2.gemfile} +4 -3
- data/gemfiles/5.0.awsv2.0.gemfile +17 -0
- data/gemfiles/5.0.awsv2.1.gemfile +17 -0
- data/gemfiles/{4.2.gemfile → 5.0.awsv2.gemfile} +4 -3
- data/lib/paperclip/attachment.rb +19 -16
- data/lib/paperclip/attachment_registry.rb +3 -2
- data/lib/paperclip/callbacks.rb +8 -6
- data/lib/paperclip/content_type_detector.rb +27 -11
- data/lib/paperclip/errors.rb +3 -1
- data/lib/paperclip/file_command_content_type_detector.rb +6 -8
- data/lib/paperclip/geometry_parser_factory.rb +1 -1
- data/lib/paperclip/glue.rb +1 -1
- data/lib/paperclip/has_attached_file.rb +9 -2
- 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/abstract_adapter.rb +1 -0
- 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 +2 -2
- data/lib/paperclip/rails_environment.rb +25 -0
- data/lib/paperclip/schema.rb +3 -9
- data/lib/paperclip/storage/fog.rb +21 -12
- data/lib/paperclip/storage/s3.rb +51 -50
- data/lib/paperclip/thumbnail.rb +2 -3
- data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
- data/lib/paperclip/version.rb +3 -1
- data/lib/paperclip.rb +15 -4
- data/lib/tasks/paperclip.rake +17 -1
- data/paperclip.gemspec +18 -15
- data/spec/paperclip/attachment_definitions_spec.rb +1 -1
- data/spec/paperclip/attachment_processing_spec.rb +2 -4
- data/spec/paperclip/attachment_registry_spec.rb +84 -13
- data/spec/paperclip/attachment_spec.rb +91 -31
- data/spec/paperclip/content_type_detector_spec.rb +8 -1
- data/spec/paperclip/file_command_content_type_detector_spec.rb +0 -1
- 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 +16 -13
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +2 -1
- data/spec/paperclip/io_adapters/file_adapter_spec.rb +4 -1
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +12 -0
- data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +4 -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 +34 -11
- data/spec/paperclip/paperclip_spec.rb +4 -29
- data/spec/paperclip/plural_cache_spec.rb +17 -16
- data/spec/paperclip/rails_environment_spec.rb +33 -0
- data/spec/paperclip/storage/fog_spec.rb +42 -3
- data/spec/paperclip/storage/s3_live_spec.rb +8 -4
- data/spec/paperclip/storage/s3_spec.rb +255 -180
- data/spec/paperclip/tempfile_factory_spec.rb +4 -0
- data/spec/paperclip/thumbnail_spec.rb +16 -0
- data/spec/paperclip/url_generator_spec.rb +1 -1
- 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/fake_model.rb +4 -0
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/matchers/have_column.rb +11 -2
- data/spec/support/model_reconstruction.rb +9 -1
- data/spec/support/reporting.rb +11 -0
- metadata +105 -54
- data/RUNNING_TESTS.md +0 -4
- data/cucumber/paperclip_steps.rb +0 -6
- data/gemfiles/3.2.gemfile +0 -19
- data/gemfiles/4.0.gemfile +0 -19
- 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/mock_model.rb +0 -2
- data/spec/support/rails_helpers.rb +0 -7
|
@@ -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
|
|
@@ -141,8 +147,9 @@ module Paperclip
|
|
|
141
147
|
|
|
142
148
|
def expiring_url(time = (Time.now + 3600), style_name = default_style)
|
|
143
149
|
time = convert_time(time)
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
http_url_method = "get_#{scheme}_url"
|
|
151
|
+
if path(style_name) && directory.files.respond_to?(http_url_method)
|
|
152
|
+
expiring_url = directory.files.public_send(http_url_method, path(style_name), time)
|
|
146
153
|
|
|
147
154
|
if @options[:fog_host]
|
|
148
155
|
expiring_url.gsub!(/#{host_name_for_directory}/, dynamic_fog_host_for_style(style_name))
|
|
@@ -156,14 +163,14 @@ module Paperclip
|
|
|
156
163
|
|
|
157
164
|
def parse_credentials(creds)
|
|
158
165
|
creds = find_credentials(creds).stringify_keys
|
|
159
|
-
|
|
160
|
-
(creds[env] || creds).symbolize_keys
|
|
166
|
+
(creds[RailsEnvironment.get] || creds).symbolize_keys
|
|
161
167
|
end
|
|
162
168
|
|
|
163
169
|
def copy_to_local_file(style, local_dest_path)
|
|
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
|
@@ -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,10 +290,9 @@ 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
|
-
|
|
292
|
-
(creds[env] || creds).symbolize_keys
|
|
295
|
+
(creds[RailsEnvironment.get] || creds).symbolize_keys
|
|
293
296
|
end
|
|
294
297
|
|
|
295
298
|
def exists?(style = default_style)
|
|
@@ -298,7 +301,7 @@ module Paperclip
|
|
|
298
301
|
else
|
|
299
302
|
false
|
|
300
303
|
end
|
|
301
|
-
rescue
|
|
304
|
+
rescue Aws::Errors::ServiceError => e
|
|
302
305
|
false
|
|
303
306
|
end
|
|
304
307
|
|
|
@@ -324,7 +327,7 @@ module Paperclip
|
|
|
324
327
|
end
|
|
325
328
|
|
|
326
329
|
def create_bucket
|
|
327
|
-
s3_interface.
|
|
330
|
+
s3_interface.bucket(bucket_name).create
|
|
328
331
|
end
|
|
329
332
|
|
|
330
333
|
def flush_writes #:nodoc:
|
|
@@ -332,11 +335,9 @@ module Paperclip
|
|
|
332
335
|
retries = 0
|
|
333
336
|
begin
|
|
334
337
|
log("saving #{path(style)}")
|
|
335
|
-
acl = @s3_permissions[style] || @s3_permissions[:default]
|
|
336
|
-
acl = acl.call(self, style) if acl.respond_to?(:call)
|
|
337
338
|
write_options = {
|
|
338
339
|
:content_type => file.content_type,
|
|
339
|
-
:acl =>
|
|
340
|
+
:acl => s3_permissions(style)
|
|
340
341
|
}
|
|
341
342
|
|
|
342
343
|
# add storage class for this style if defined
|
|
@@ -357,11 +358,11 @@ module Paperclip
|
|
|
357
358
|
write_options[:metadata] = @s3_metadata unless @s3_metadata.empty?
|
|
358
359
|
write_options.merge!(@s3_headers)
|
|
359
360
|
|
|
360
|
-
s3_object(style).
|
|
361
|
-
rescue
|
|
361
|
+
s3_object(style).upload_file(file.path, write_options)
|
|
362
|
+
rescue ::Aws::S3::Errors::NoSuchBucket
|
|
362
363
|
create_bucket
|
|
363
364
|
retry
|
|
364
|
-
rescue
|
|
365
|
+
rescue ::Aws::S3::Errors::SlowDown
|
|
365
366
|
retries += 1
|
|
366
367
|
if retries <= 5
|
|
367
368
|
sleep((2 ** retries) * 0.5)
|
|
@@ -383,8 +384,8 @@ module Paperclip
|
|
|
383
384
|
@queued_for_delete.each do |path|
|
|
384
385
|
begin
|
|
385
386
|
log("deleting #{path}")
|
|
386
|
-
s3_bucket.
|
|
387
|
-
rescue
|
|
387
|
+
s3_bucket.object(path.sub(%r{\A/}, "")).delete
|
|
388
|
+
rescue Aws::Errors::ServiceError => e
|
|
388
389
|
# Ignore this.
|
|
389
390
|
end
|
|
390
391
|
end
|
|
@@ -394,11 +395,11 @@ module Paperclip
|
|
|
394
395
|
def copy_to_local_file(style, local_dest_path)
|
|
395
396
|
log("copying #{path(style)} to local file #{local_dest_path}")
|
|
396
397
|
::File.open(local_dest_path, 'wb') do |local_file|
|
|
397
|
-
s3_object(style).
|
|
398
|
+
s3_object(style).get do |chunk|
|
|
398
399
|
local_file.write(chunk)
|
|
399
400
|
end
|
|
400
401
|
end
|
|
401
|
-
rescue
|
|
402
|
+
rescue Aws::Errors::ServiceError => e
|
|
402
403
|
warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
|
|
403
404
|
false
|
|
404
405
|
end
|
data/lib/paperclip/thumbnail.rb
CHANGED
|
@@ -29,7 +29,6 @@ module Paperclip
|
|
|
29
29
|
super
|
|
30
30
|
|
|
31
31
|
geometry = options[:geometry].to_s
|
|
32
|
-
@file = file
|
|
33
32
|
@crop = geometry[-1,1] == '#'
|
|
34
33
|
@target_geometry = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
|
|
35
34
|
@current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
|
|
@@ -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 = []
|
|
@@ -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)
|
data/lib/paperclip/version.rb
CHANGED
data/lib/paperclip.rb
CHANGED
|
@@ -55,11 +55,21 @@ require 'paperclip/helpers'
|
|
|
55
55
|
require 'paperclip/has_attached_file'
|
|
56
56
|
require 'paperclip/attachment_registry'
|
|
57
57
|
require 'paperclip/filename_cleaner'
|
|
58
|
-
require '
|
|
58
|
+
require 'paperclip/rails_environment'
|
|
59
|
+
|
|
60
|
+
begin
|
|
61
|
+
# Use mime/types/columnar if available, for reduced memory usage
|
|
62
|
+
require "mime/types/columnar"
|
|
63
|
+
rescue LoadError
|
|
64
|
+
require "mime/types"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
require 'mimemagic'
|
|
68
|
+
require 'mimemagic/overlay'
|
|
59
69
|
require 'logger'
|
|
60
70
|
require 'cocaine'
|
|
61
71
|
|
|
62
|
-
require 'paperclip/railtie' if defined?(Rails)
|
|
72
|
+
require 'paperclip/railtie' if defined?(Rails::Railtie)
|
|
63
73
|
|
|
64
74
|
# The base module that gets included in ActiveRecord::Base. See the
|
|
65
75
|
# documentation for Paperclip::ClassMethods for more useful information.
|
|
@@ -87,7 +97,8 @@ module Paperclip
|
|
|
87
97
|
:log_command => true,
|
|
88
98
|
:swallow_stderr => true,
|
|
89
99
|
:content_type_mappings => {},
|
|
90
|
-
:use_exif_orientation => true
|
|
100
|
+
:use_exif_orientation => true,
|
|
101
|
+
:read_timeout => nil
|
|
91
102
|
}
|
|
92
103
|
end
|
|
93
104
|
|
|
@@ -136,7 +147,7 @@ module Paperclip
|
|
|
136
147
|
# user.avatar.url # => "/avatars/23/normal_me.png"
|
|
137
148
|
# * +keep_old_files+: Keep the existing attachment files (original + resized) from
|
|
138
149
|
# being automatically deleted when an attachment is cleared or updated. Defaults to +false+.
|
|
139
|
-
# * +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
|
|
140
151
|
# record is destroyed. Defaults to +false+.
|
|
141
152
|
# * +whiny+: Will raise an error if Paperclip cannot post_process an uploaded file due
|
|
142
153
|
# to a command line error. This will override the global setting for this attachment.
|
data/lib/tasks/paperclip.rake
CHANGED
|
@@ -18,7 +18,7 @@ module Paperclip
|
|
|
18
18
|
raise "Class #{klass.name} has no attachments specified"
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
if
|
|
21
|
+
if name.present? && attachment_names.map(&:to_s).include?(name.to_s)
|
|
22
22
|
[ name ]
|
|
23
23
|
else
|
|
24
24
|
attachment_names
|
|
@@ -108,4 +108,20 @@ namespace :paperclip do
|
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
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
|
|
111
127
|
end
|
data/paperclip.gemspec
CHANGED
|
@@ -12,40 +12,43 @@ Gem::Specification.new do |s|
|
|
|
12
12
|
s.description = "Easy upload management for ActiveRecord"
|
|
13
13
|
s.license = "MIT"
|
|
14
14
|
|
|
15
|
-
s.rubyforge_project = "paperclip"
|
|
16
|
-
|
|
17
15
|
s.files = `git ls-files`.split("\n")
|
|
18
16
|
s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
|
|
19
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
20
18
|
s.require_paths = ["lib"]
|
|
21
19
|
|
|
20
|
+
if File.exist?('UPGRADING')
|
|
21
|
+
s.post_install_message = File.read("UPGRADING")
|
|
22
|
+
end
|
|
23
|
+
|
|
22
24
|
s.requirements << "ImageMagick"
|
|
23
|
-
s.required_ruby_version = ">= 1.
|
|
25
|
+
s.required_ruby_version = ">= 2.1.0"
|
|
24
26
|
|
|
25
|
-
s.add_dependency('activemodel', '>=
|
|
26
|
-
s.add_dependency('activesupport', '>=
|
|
27
|
-
s.add_dependency('cocaine', '~> 0.5.
|
|
27
|
+
s.add_dependency('activemodel', '>= 4.2.0')
|
|
28
|
+
s.add_dependency('activesupport', '>= 4.2.0')
|
|
29
|
+
s.add_dependency('cocaine', '~> 0.5.5')
|
|
28
30
|
s.add_dependency('mime-types')
|
|
31
|
+
s.add_dependency('mimemagic', '~> 0.3.0')
|
|
29
32
|
|
|
30
|
-
s.add_development_dependency('activerecord', '>=
|
|
33
|
+
s.add_development_dependency('activerecord', '>= 4.2.0')
|
|
31
34
|
s.add_development_dependency('shoulda')
|
|
32
|
-
s.add_development_dependency('rspec')
|
|
35
|
+
s.add_development_dependency('rspec', '~> 3.0')
|
|
33
36
|
s.add_development_dependency('appraisal')
|
|
34
37
|
s.add_development_dependency('mocha')
|
|
35
|
-
s.add_development_dependency('aws-sdk', '>=
|
|
38
|
+
s.add_development_dependency('aws-sdk', '>= 2.0.34', '< 3.0')
|
|
36
39
|
s.add_development_dependency('bourne')
|
|
37
|
-
s.add_development_dependency('cucumber', '~> 1.3.
|
|
38
|
-
s.add_development_dependency('aruba')
|
|
40
|
+
s.add_development_dependency('cucumber', '~> 1.3.18')
|
|
41
|
+
s.add_development_dependency('aruba', '~> 0.9.0')
|
|
39
42
|
s.add_development_dependency('nokogiri')
|
|
40
|
-
|
|
41
|
-
s.add_development_dependency('capybara', '= 2.0.3')
|
|
43
|
+
s.add_development_dependency('capybara')
|
|
42
44
|
s.add_development_dependency('bundler')
|
|
43
|
-
s.add_development_dependency('fog'
|
|
45
|
+
s.add_development_dependency('fog-aws')
|
|
46
|
+
s.add_development_dependency('fog-local')
|
|
44
47
|
s.add_development_dependency('launchy')
|
|
45
48
|
s.add_development_dependency('rake')
|
|
46
49
|
s.add_development_dependency('fakeweb')
|
|
47
50
|
s.add_development_dependency('railties')
|
|
48
|
-
s.add_development_dependency('actionmailer', '>=
|
|
51
|
+
s.add_development_dependency('actionmailer', '>= 4.2.0')
|
|
49
52
|
s.add_development_dependency('generator_spec')
|
|
50
53
|
s.add_development_dependency('timecop')
|
|
51
54
|
end
|
|
@@ -8,6 +8,6 @@ describe "Attachment Definitions" do
|
|
|
8
8
|
Dummy.do_not_validate_attachment_file_type :avatar
|
|
9
9
|
expected = {avatar: {path: "abc"}, other_attachment: {url: "123"}}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
expect(Dummy.attachment_definitions).to eq expected
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
require 'spec_helper'
|
|
3
3
|
|
|
4
4
|
describe 'Attachment Processing' do
|
|
5
|
-
|
|
6
|
-
before do
|
|
7
|
-
rebuild_class
|
|
8
|
-
end
|
|
5
|
+
before { rebuild_class }
|
|
9
6
|
|
|
7
|
+
context 'using validates_attachment_content_type' do
|
|
10
8
|
it 'processes attachments given a valid assignment' do
|
|
11
9
|
file = File.new(fixture_file("5k.png"))
|
|
12
10
|
Dummy.validates_attachment_content_type :avatar, content_type: "image/png"
|