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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +1066 -0
  3. data/.rubocop.yml +1 -0
  4. data/.travis.yml +18 -15
  5. data/Appraisals +20 -12
  6. data/CONTRIBUTING.md +19 -8
  7. data/Gemfile +4 -9
  8. data/LICENSE +1 -1
  9. data/NEWS +101 -31
  10. data/README.md +243 -159
  11. data/RELEASING.md +17 -0
  12. data/Rakefile +1 -1
  13. data/UPGRADING +12 -9
  14. data/features/basic_integration.feature +8 -4
  15. data/features/migration.feature +0 -24
  16. data/features/step_definitions/attachment_steps.rb +27 -21
  17. data/features/step_definitions/html_steps.rb +2 -2
  18. data/features/step_definitions/rails_steps.rb +11 -17
  19. data/features/step_definitions/s3_steps.rb +2 -2
  20. data/features/step_definitions/web_steps.rb +1 -103
  21. data/features/support/file_helpers.rb +2 -2
  22. data/gemfiles/4.2.awsv2.0.gemfile +17 -0
  23. data/gemfiles/4.2.awsv2.1.gemfile +17 -0
  24. data/gemfiles/{4.1.gemfile → 4.2.awsv2.gemfile} +4 -3
  25. data/gemfiles/5.0.awsv2.0.gemfile +17 -0
  26. data/gemfiles/5.0.awsv2.1.gemfile +17 -0
  27. data/gemfiles/{4.2.gemfile → 5.0.awsv2.gemfile} +4 -3
  28. data/lib/paperclip/attachment.rb +19 -16
  29. data/lib/paperclip/attachment_registry.rb +3 -2
  30. data/lib/paperclip/callbacks.rb +8 -6
  31. data/lib/paperclip/content_type_detector.rb +27 -11
  32. data/lib/paperclip/errors.rb +3 -1
  33. data/lib/paperclip/file_command_content_type_detector.rb +6 -8
  34. data/lib/paperclip/geometry_parser_factory.rb +1 -1
  35. data/lib/paperclip/glue.rb +1 -1
  36. data/lib/paperclip/has_attached_file.rb +9 -2
  37. data/lib/paperclip/helpers.rb +14 -10
  38. data/lib/paperclip/interpolations/plural_cache.rb +6 -5
  39. data/lib/paperclip/interpolations.rb +18 -13
  40. data/lib/paperclip/io_adapters/abstract_adapter.rb +1 -0
  41. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +1 -1
  42. data/lib/paperclip/io_adapters/uri_adapter.rb +3 -1
  43. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
  44. data/lib/paperclip/media_type_spoof_detector.rb +2 -2
  45. data/lib/paperclip/rails_environment.rb +25 -0
  46. data/lib/paperclip/schema.rb +3 -9
  47. data/lib/paperclip/storage/fog.rb +21 -12
  48. data/lib/paperclip/storage/s3.rb +51 -50
  49. data/lib/paperclip/thumbnail.rb +2 -3
  50. data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
  51. data/lib/paperclip/version.rb +3 -1
  52. data/lib/paperclip.rb +15 -4
  53. data/lib/tasks/paperclip.rake +17 -1
  54. data/paperclip.gemspec +18 -15
  55. data/spec/paperclip/attachment_definitions_spec.rb +1 -1
  56. data/spec/paperclip/attachment_processing_spec.rb +2 -4
  57. data/spec/paperclip/attachment_registry_spec.rb +84 -13
  58. data/spec/paperclip/attachment_spec.rb +91 -31
  59. data/spec/paperclip/content_type_detector_spec.rb +8 -1
  60. data/spec/paperclip/file_command_content_type_detector_spec.rb +0 -1
  61. data/spec/paperclip/geometry_spec.rb +1 -1
  62. data/spec/paperclip/glue_spec.rb +44 -0
  63. data/spec/paperclip/has_attached_file_spec.rb +24 -8
  64. data/spec/paperclip/integration_spec.rb +4 -3
  65. data/spec/paperclip/interpolations_spec.rb +16 -13
  66. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +2 -1
  67. data/spec/paperclip/io_adapters/file_adapter_spec.rb +4 -1
  68. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +12 -0
  69. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +4 -0
  70. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +27 -0
  71. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +10 -0
  72. data/spec/paperclip/media_type_spoof_detector_spec.rb +34 -11
  73. data/spec/paperclip/paperclip_spec.rb +4 -29
  74. data/spec/paperclip/plural_cache_spec.rb +17 -16
  75. data/spec/paperclip/rails_environment_spec.rb +33 -0
  76. data/spec/paperclip/storage/fog_spec.rb +42 -3
  77. data/spec/paperclip/storage/s3_live_spec.rb +8 -4
  78. data/spec/paperclip/storage/s3_spec.rb +255 -180
  79. data/spec/paperclip/tempfile_factory_spec.rb +4 -0
  80. data/spec/paperclip/thumbnail_spec.rb +16 -0
  81. data/spec/paperclip/url_generator_spec.rb +1 -1
  82. data/spec/paperclip/validators/attachment_size_validator_spec.rb +26 -20
  83. data/spec/paperclip/validators_spec.rb +3 -3
  84. data/spec/spec_helper.rb +6 -1
  85. data/spec/support/assertions.rb +7 -0
  86. data/spec/support/fake_model.rb +4 -0
  87. data/spec/support/fixtures/empty.xlsx +0 -0
  88. data/spec/support/matchers/have_column.rb +11 -2
  89. data/spec/support/model_reconstruction.rb +9 -1
  90. data/spec/support/reporting.rb +11 -0
  91. metadata +105 -54
  92. data/RUNNING_TESTS.md +0 -4
  93. data/cucumber/paperclip_steps.rb +0 -6
  94. data/gemfiles/3.2.gemfile +0 -19
  95. data/gemfiles/4.0.gemfile +0 -19
  96. data/lib/paperclip/locales/de.yml +0 -18
  97. data/lib/paperclip/locales/es.yml +0 -18
  98. data/lib/paperclip/locales/ja.yml +0 -18
  99. data/lib/paperclip/locales/pt-BR.yml +0 -18
  100. data/lib/paperclip/locales/zh-CN.yml +0 -18
  101. data/lib/paperclip/locales/zh-HK.yml +0 -18
  102. data/lib/paperclip/locales/zh-TW.yml +0 -18
  103. data/spec/support/mock_model.rb +0 -2
  104. 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
- directory.files.create(fog_file.merge(
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
- if path(style_name) && directory.files.respond_to?(:get_http_url)
145
- expiring_url = directory.files.get_http_url(path(style_name), time)
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
- env = Object.const_defined?(:Rails) ? Rails.env : nil
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 @options[:fog_directory].to_s =~ Fog::AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX
193
- "#{@options[:fog_directory]}.s3.amazonaws.com"
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/#{@options[:fog_directory]}"
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
- dir = if @options[:fog_directory].respond_to?(:call)
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
@@ -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 :public_read.
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 it's name at runtime.
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 unless defined?(AWS::Core)
121
-
122
- # Overriding log formatter to make sure it return a UTF-8 string
123
- if defined?(AWS::Core::LogFormatter)
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 == :public_read) ? 'http' : 'https'
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 = :aes256
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]).gsub(/\A:rails_root\/public\/system/, '')
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).gsub(%r{\A/}, "")}"
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).gsub(%r{\A/}, "")}"
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).gsub(%r{\A/}, "")}"
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).gsub(%r{\A/}, "")}"
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 = { :expires => time, :secure => use_secure_protocol?(style_name) }
186
- s3_object(style_name).url_for(:read, base_options.merge(s3_url_options)).to_s
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 = { :s3_endpoint => s3_host_name }
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] ||= AWS::S3.new(options)
247
+ instances[options] ||= ::Aws::S3::Resource.new(options)
248
248
  end
249
249
 
250
250
  def s3_bucket
251
- @s3_bucket ||= s3_interface.buckets[bucket_name]
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.objects[path(style_name).sub(%r{\A/},'')]
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] || :public_read)
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?('call') ? creds.call(self) : creds
293
+ creds = creds.respond_to?(:call) ? creds.call(self) : creds
290
294
  creds = find_credentials(creds).stringify_keys
291
- env = Object.const_defined?(:Rails) ? Rails.env : nil
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 AWS::Errors::Base => e
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.buckets.create(bucket_name)
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 => 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).write(file, write_options)
361
- rescue AWS::S3::Errors::NoSuchBucket
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 AWS::S3::Errors::SlowDown
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.objects[path.sub(%r{\A/},'')].delete
387
- rescue AWS::Errors::Base => e
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).read do |chunk|
398
+ s3_object(style).get do |chunk|
398
399
  local_file.write(chunk)
399
400
  end
400
401
  end
401
- rescue AWS::Errors::Base => e
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
@@ -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
- dst = Tempfile.new([@basename, @format ? ".#{@format}" : ''])
68
- dst.binmode
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
- 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
74
+ ActiveSupport::NumberHelper.number_to_human_size(size)
81
75
  end
82
76
 
83
77
  def min_value_in_human_size(record)
@@ -1,3 +1,5 @@
1
1
  module Paperclip
2
- VERSION = "4.2.2" unless defined? Paperclip::VERSION
2
+ unless defined?(Paperclip::VERSION)
3
+ VERSION = "5.0.0".freeze
4
+ end
3
5
  end
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 'mime/types'
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.
@@ -18,7 +18,7 @@ module Paperclip
18
18
  raise "Class #{klass.name} has no attachments specified"
19
19
  end
20
20
 
21
- if !name.blank? && attachment_names.map(&:to_s).include?(name.to_s)
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.9.2"
25
+ s.required_ruby_version = ">= 2.1.0"
24
26
 
25
- s.add_dependency('activemodel', '>= 3.0.0')
26
- s.add_dependency('activesupport', '>= 3.0.0')
27
- s.add_dependency('cocaine', '~> 0.5.3')
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', '>= 3.0.0')
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', '>= 1.5.7')
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.11')
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
- # Ruby version < 1.9.3 can't install capybara > 2.0.3.
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', '~> 1.0')
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', '>= 3.0.0')
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
- assert_equal expected, Dummy.attachment_definitions
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
- context 'using validates_attachment_content_type' do
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"