paperclip 5.1.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -0
  3. data/.hound.yml +5 -16
  4. data/.travis.yml +14 -15
  5. data/Appraisals +3 -23
  6. data/Gemfile +1 -0
  7. data/NEWS +36 -1
  8. data/README.md +58 -10
  9. data/Rakefile +1 -1
  10. data/UPGRADING +1 -1
  11. data/features/step_definitions/attachment_steps.rb +6 -6
  12. data/features/step_definitions/rails_steps.rb +29 -22
  13. data/features/step_definitions/s3_steps.rb +1 -1
  14. data/features/support/env.rb +1 -0
  15. data/features/support/paths.rb +1 -1
  16. data/features/support/rails.rb +0 -24
  17. data/gemfiles/{4.2.awsv2.0.gemfile → 4.2.gemfile} +1 -1
  18. data/gemfiles/{5.0.awsv2.1.gemfile → 5.0.gemfile} +2 -2
  19. data/lib/paperclip.rb +12 -10
  20. data/lib/paperclip/attachment.rb +10 -4
  21. data/lib/paperclip/io_adapters/abstract_adapter.rb +24 -2
  22. data/lib/paperclip/io_adapters/attachment_adapter.rb +10 -5
  23. data/lib/paperclip/io_adapters/data_uri_adapter.rb +8 -8
  24. data/lib/paperclip/io_adapters/empty_string_adapter.rb +5 -4
  25. data/lib/paperclip/io_adapters/file_adapter.rb +12 -6
  26. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +7 -7
  27. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -6
  28. data/lib/paperclip/io_adapters/nil_adapter.rb +8 -5
  29. data/lib/paperclip/io_adapters/registry.rb +6 -2
  30. data/lib/paperclip/io_adapters/stringio_adapter.rb +9 -6
  31. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +9 -5
  32. data/lib/paperclip/io_adapters/uri_adapter.rb +13 -11
  33. data/lib/paperclip/storage/filesystem.rb +13 -2
  34. data/lib/paperclip/storage/fog.rb +7 -4
  35. data/lib/paperclip/storage/s3.rb +31 -3
  36. data/lib/paperclip/thumbnail.rb +14 -4
  37. data/lib/paperclip/version.rb +1 -1
  38. data/lib/tasks/paperclip.rake +17 -3
  39. data/paperclip.gemspec +3 -3
  40. data/spec/paperclip/attachment_spec.rb +39 -8
  41. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +44 -21
  42. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +6 -3
  43. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +7 -1
  44. data/spec/paperclip/io_adapters/file_adapter_spec.rb +2 -2
  45. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +6 -1
  46. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +1 -1
  47. data/spec/paperclip/io_adapters/registry_spec.rb +2 -2
  48. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +1 -1
  49. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +3 -3
  50. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +6 -1
  51. data/spec/paperclip/storage/fog_spec.rb +16 -0
  52. data/spec/paperclip/storage/s3_live_spec.rb +12 -10
  53. data/spec/paperclip/storage/s3_spec.rb +85 -4
  54. data/spec/paperclip/tempfile_spec.rb +35 -0
  55. data/spec/paperclip/thumbnail_spec.rb +35 -32
  56. data/spec/spec_helper.rb +3 -1
  57. data/spec/support/assertions.rb +5 -1
  58. data/spec/support/conditional_filter_helper.rb +5 -0
  59. metadata +31 -135
  60. data/gemfiles/4.2.awsv2.1.gemfile +0 -17
  61. data/gemfiles/4.2.awsv2.gemfile +0 -20
  62. data/gemfiles/5.0.awsv2.0.gemfile +0 -17
  63. data/gemfiles/5.0.awsv2.gemfile +0 -20
@@ -1,7 +1,13 @@
1
1
  module Paperclip
2
2
  class UploadedFileAdapter < AbstractAdapter
3
- def initialize(target)
4
- @target = target
3
+ def self.register
4
+ Paperclip.io_adapters.register self do |target|
5
+ target.class.name.include?("UploadedFile")
6
+ end
7
+ end
8
+
9
+ def initialize(target, options = {})
10
+ super
5
11
  cache_current_values
6
12
 
7
13
  if @target.respond_to?(:tempfile)
@@ -37,6 +43,4 @@ module Paperclip
37
43
  end
38
44
  end
39
45
 
40
- Paperclip.io_adapters.register Paperclip::UploadedFileAdapter do |target|
41
- target.class.name.include?("UploadedFile")
42
- end
46
+ Paperclip::UploadedFileAdapter.register
@@ -1,11 +1,17 @@
1
- require 'open-uri'
1
+ require "open-uri"
2
2
 
3
3
  module Paperclip
4
4
  class UriAdapter < AbstractAdapter
5
5
  attr_writer :content_type
6
6
 
7
- def initialize(target)
8
- @target = target
7
+ def self.register
8
+ Paperclip.io_adapters.register self do |target|
9
+ target.is_a?(URI)
10
+ end
11
+ end
12
+
13
+ def initialize(target, options = {})
14
+ super
9
15
  @content = download_content
10
16
  cache_current_values
11
17
  @tempfile = copy_to_tempfile(@content)
@@ -28,9 +34,9 @@ module Paperclip
28
34
  end
29
35
 
30
36
  def filename_from_content_disposition
31
- if @content.meta.has_key?("content-disposition")
32
- @content.meta["content-disposition"].
33
- match(/filename="([^"]*)"/)[1]
37
+ if @content.meta.key?("content-disposition")
38
+ matches = @content.meta["content-disposition"].match(/filename="([^"]*)"/)
39
+ matches[1] if matches
34
40
  end
35
41
  end
36
42
 
@@ -49,7 +55,7 @@ module Paperclip
49
55
  end
50
56
 
51
57
  def copy_to_tempfile(src)
52
- while data = src.read(16*1024)
58
+ while data = src.read(16 * 1024)
53
59
  destination.write(data)
54
60
  end
55
61
  src.close
@@ -58,7 +64,3 @@ module Paperclip
58
64
  end
59
65
  end
60
66
  end
61
-
62
- Paperclip.io_adapters.register Paperclip::UriAdapter do |target|
63
- target.kind_of?(URI)
64
- end
@@ -37,7 +37,7 @@ module Paperclip
37
37
  @queued_for_write.each do |style_name, file|
38
38
  FileUtils.mkdir_p(File.dirname(path(style_name)))
39
39
  begin
40
- FileUtils.mv(file.path, path(style_name))
40
+ move_file(file.path, path(style_name))
41
41
  rescue SystemCallError
42
42
  File.open(path(style_name), "wb") do |new_file|
43
43
  while chunk = file.read(16 * 1024)
@@ -46,7 +46,7 @@ module Paperclip
46
46
  end
47
47
  end
48
48
  unless @options[:override_file_permissions] == false
49
- resolved_chmod = (@options[:override_file_permissions] &~ 0111) || (0666 &~ File.umask)
49
+ resolved_chmod = (@options[:override_file_permissions] & ~0111) || (0666 & ~File.umask)
50
50
  FileUtils.chmod( resolved_chmod, path(style_name) )
51
51
  end
52
52
  file.rewind
@@ -84,6 +84,17 @@ module Paperclip
84
84
  def copy_to_local_file(style, local_dest_path)
85
85
  FileUtils.cp(path(style), local_dest_path)
86
86
  end
87
+
88
+ private
89
+
90
+ def move_file(src, dest)
91
+ # Support hardlinked files
92
+ if File.identical?(src, dest)
93
+ File.unlink(src)
94
+ else
95
+ FileUtils.mv(src, dest)
96
+ end
97
+ end
87
98
  end
88
99
 
89
100
  end
@@ -86,11 +86,14 @@ module Paperclip
86
86
  end
87
87
 
88
88
  def fog_public(style = default_style)
89
- if @options.has_key?(:fog_public)
90
- if @options[:fog_public].respond_to?(:has_key?) && @options[:fog_public].has_key?(style)
91
- @options[:fog_public][style]
89
+ if @options.key?(:fog_public)
90
+ value = @options[:fog_public]
91
+ if value.respond_to?(:key?) && value.key?(style)
92
+ value[style]
93
+ elsif value.respond_to?(:call)
94
+ value.call(self)
92
95
  else
93
- @options[:fog_public]
96
+ value
94
97
  end
95
98
  else
96
99
  true
@@ -45,10 +45,10 @@ module Paperclip
45
45
  #
46
46
  # You can set permission on a per style bases by doing the following:
47
47
  # :s3_permissions => {
48
- # :original => :private
48
+ # :original => "private"
49
49
  # }
50
50
  # Or globally:
51
- # :s3_permissions => :private
51
+ # :s3_permissions => "private"
52
52
  #
53
53
  # * +s3_protocol+: The protocol for the URLs generated to your S3 assets.
54
54
  # Can be either 'http', 'https', or an empty string to generate
@@ -65,6 +65,9 @@ module Paperclip
65
65
  # * +s3_host_alias+: The fully-qualified domain name (FQDN) that is the alias to the
66
66
  # S3 domain of your bucket. Used with the :s3_alias_url url interpolation. See the
67
67
  # link in the +url+ entry for more information about S3 domains and buckets.
68
+ # * +s3_prefixes_in_alias+: The number of prefixes that is prepended by
69
+ # s3_host_alias. This will remove the prefixes from the path in
70
+ # :s3_alias_url url interpolation
68
71
  # * +url+: There are four options for the S3 url. You can choose to have the bucket's name
69
72
  # placed domain-style (bucket.s3.amazonaws.com) or path-style (s3.amazonaws.com/bucket).
70
73
  # You can also specify a CNAME (which requires the CNAME to be specified as
@@ -102,6 +105,8 @@ module Paperclip
102
105
  # Redundancy Storage. RRS enables customers to reduce their
103
106
  # costs by storing non-critical, reproducible data at lower
104
107
  # levels of redundancy than Amazon S3's standard storage.
108
+ # * +use_accelerate_endpoint+: Use accelerate endpoint
109
+ # http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
105
110
  #
106
111
  # You can set storage class on a per style bases by doing the following:
107
112
  # :s3_storage_class => {
@@ -152,10 +157,23 @@ module Paperclip
152
157
  @options[:url] = @options[:url].inspect if @options[:url].is_a?(Symbol)
153
158
 
154
159
  @http_proxy = @options[:http_proxy] || nil
160
+
161
+ if @options.has_key?(:use_accelerate_endpoint) &&
162
+ Gem::Version.new(Aws::VERSION) < Gem::Version.new("2.3.0")
163
+ raise LoadError, ":use_accelerate_endpoint is only available from aws-sdk version 2.3.0. Please upgrade aws-sdk to a newer version."
164
+ end
165
+
166
+ @use_accelerate_endpoint = @options[:use_accelerate_endpoint]
155
167
  end
156
168
 
157
169
  Paperclip.interpolates(:s3_alias_url) do |attachment, style|
158
- "#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_alias}/#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
170
+ protocol = attachment.s3_protocol(style, true)
171
+ host = attachment.s3_host_alias
172
+ path = attachment.path(style).
173
+ split("/")[attachment.s3_prefixes_in_alias..-1].
174
+ join("/").
175
+ sub(%r{\A/}, "".freeze)
176
+ "#{protocol}//#{host}/#{path}"
159
177
  end unless Paperclip::Interpolations.respond_to? :s3_alias_url
160
178
  Paperclip.interpolates(:s3_path_url) do |attachment, style|
161
179
  "#{attachment.s3_protocol(style, true)}//#{attachment.s3_host_name}/#{attachment.bucket_name}/#{attachment.path(style).sub(%r{\A/}, "".freeze)}"
@@ -204,6 +222,10 @@ module Paperclip
204
222
  @s3_host_alias
205
223
  end
206
224
 
225
+ def s3_prefixes_in_alias
226
+ @s3_prefixes_in_alias ||= @options[:s3_prefixes_in_alias].to_i
227
+ end
228
+
207
229
  def s3_url_options
208
230
  s3_url_options = @options[:s3_url_options] || {}
209
231
  s3_url_options = s3_url_options.call(instance) if s3_url_options.respond_to?(:call)
@@ -232,6 +254,8 @@ module Paperclip
232
254
  config[:proxy_uri] = URI::HTTP.build(proxy_opts)
233
255
  end
234
256
 
257
+ config[:use_accelerate_endpoint] = use_accelerate_endpoint?
258
+
235
259
  [:access_key_id, :secret_access_key, :credential_provider, :credentials].each do |opt|
236
260
  config[opt] = s3_credentials[opt] if s3_credentials[opt]
237
261
  end
@@ -257,6 +281,10 @@ module Paperclip
257
281
  s3_bucket.object style_name_as_path(style_name)
258
282
  end
259
283
 
284
+ def use_accelerate_endpoint?
285
+ !!@use_accelerate_endpoint
286
+ end
287
+
260
288
  def using_http_proxy?
261
289
  !!@http_proxy
262
290
  end
@@ -3,10 +3,11 @@ module Paperclip
3
3
  class Thumbnail < Processor
4
4
 
5
5
  attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options,
6
- :source_file_options, :animated, :auto_orient
6
+ :source_file_options, :animated, :auto_orient, :frame_index
7
7
 
8
8
  # List of formats that we need to preserve animation
9
9
  ANIMATED_FORMATS = %w(gif)
10
+ MULTI_FRAME_FORMATS = %w(.mkv .avi .mp4 .mov .mpg .mpeg .gif)
10
11
 
11
12
  # Creates a Thumbnail object set to work on the +file+ given. It
12
13
  # will attempt to transform the image into one defined by +target_geometry+
@@ -25,6 +26,7 @@ module Paperclip
25
26
  # +whiny+ - whether to raise an error when processing fails. Defaults to true
26
27
  # +format+ - the desired filename extension
27
28
  # +animated+ - whether to merge all the layers in the image. Defaults to true
29
+ # +frame_index+ - the frame index of the source file to render as the thumbnail
28
30
  def initialize(file, options = {}, attachment = nil)
29
31
  super
30
32
 
@@ -41,12 +43,12 @@ module Paperclip
41
43
  if @auto_orient && @current_geometry.respond_to?(:auto_orient)
42
44
  @current_geometry.auto_orient
43
45
  end
44
-
45
46
  @source_file_options = @source_file_options.split(/\s+/) if @source_file_options.respond_to?(:split)
46
47
  @convert_options = @convert_options.split(/\s+/) if @convert_options.respond_to?(:split)
47
48
 
48
49
  @current_format = File.extname(@file.path)
49
50
  @basename = File.basename(@file.path, @current_format)
51
+ @frame_index = multi_frame_format? ? options.fetch(:frame_index, 0) : 0
50
52
  end
51
53
 
52
54
  # Returns true if the +target_geometry+ is meant to crop.
@@ -76,7 +78,12 @@ module Paperclip
76
78
 
77
79
  parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
78
80
 
79
- success = convert(parameters, :source => "#{File.expand_path(src.path)}#{'[0]' unless animated?}", :dest => File.expand_path(dst.path))
81
+ frame = animated? ? "" : "[#{@frame_index}]"
82
+ convert(
83
+ parameters,
84
+ source: "#{File.expand_path(src.path)}#{frame}",
85
+ dest: File.expand_path(dst.path),
86
+ )
80
87
  rescue Cocaine::ExitStatusError => e
81
88
  raise Paperclip::Error, "There was an error processing the thumbnail for #{@basename}" if @whiny
82
89
  rescue Cocaine::CommandNotFoundError => e
@@ -101,7 +108,10 @@ module Paperclip
101
108
 
102
109
  protected
103
110
 
104
- # Return true if the format is animated
111
+ def multi_frame_format?
112
+ MULTI_FRAME_FORMATS.include? @current_format
113
+ end
114
+
105
115
  def animated?
106
116
  @animated && (ANIMATED_FORMATS.include?(@format.to_s) || @format.blank?) && identified_as_animated?
107
117
  end
@@ -1,5 +1,5 @@
1
1
  module Paperclip
2
2
  unless defined?(Paperclip::VERSION)
3
- VERSION = "5.1.0".freeze
3
+ VERSION = "5.2.0".freeze
4
4
  end
5
5
  end
@@ -46,7 +46,7 @@ namespace :paperclip do
46
46
  attachment = instance.send(name)
47
47
  begin
48
48
  attachment.reprocess!(*styles)
49
- rescue Exception => e
49
+ rescue StandardError => e
50
50
  Paperclip::Task.log_error("exception while processing #{klass} ID #{instance.id}:")
51
51
  Paperclip::Task.log_error(" " + e.message + "\n")
52
52
  end
@@ -64,7 +64,8 @@ namespace :paperclip do
64
64
  names = Paperclip::Task.obtain_attachments(klass)
65
65
  names.each do |name|
66
66
  Paperclip.each_instance_with_attachment(klass, name) do |instance|
67
- if file = Paperclip.io_adapters.for(instance.send(name))
67
+ attachment = instance.send(name)
68
+ if file = Paperclip.io_adapters.for(attachment, attachment.options[:adapter_options])
68
69
  instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
69
70
  instance.send("#{name}_content_type=", file.content_type.to_s.strip)
70
71
  instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
@@ -90,6 +91,19 @@ namespace :paperclip do
90
91
  end
91
92
  Paperclip.save_current_attachments_styles!
92
93
  end
94
+
95
+ desc "Regenerates fingerprints for a given CLASS (and optional ATTACHMENT). Useful when changing digest."
96
+ task :fingerprints => :environment do
97
+ klass = Paperclip::Task.obtain_class
98
+ names = Paperclip::Task.obtain_attachments(klass)
99
+ names.each do |name|
100
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
101
+ attachment = instance.send(name)
102
+ attachment.assign(attachment)
103
+ instance.save(:validate => false)
104
+ end
105
+ end
106
+ end
93
107
  end
94
108
 
95
109
  desc "Cleans out invalid attachments. Useful after you've added new validations."
@@ -109,7 +123,7 @@ namespace :paperclip do
109
123
  end
110
124
  end
111
125
 
112
- desc "find missing attachments. Useful to know which attachments are broken"
126
+ desc "find missing attachments. Useful to know which attachments are broken"
113
127
  task :find_broken_attachments => :environment do
114
128
  klass = Paperclip::Task.obtain_class
115
129
  names = Paperclip::Task.obtain_attachments(klass)
@@ -35,9 +35,10 @@ Gem::Specification.new do |s|
35
35
  s.add_development_dependency('rspec', '~> 3.0')
36
36
  s.add_development_dependency('appraisal')
37
37
  s.add_development_dependency('mocha')
38
- s.add_development_dependency('aws-sdk', '>= 2.0.34', '< 3.0')
38
+ s.add_development_dependency('aws-sdk', '>= 2.3.0', '< 3.0')
39
39
  s.add_development_dependency('bourne')
40
- s.add_development_dependency('cucumber', '~> 1.3.18')
40
+ s.add_development_dependency('cucumber-rails')
41
+ s.add_development_dependency('cucumber-expressions', '4.0.3') # TODO: investigate failures on 4.0.4
41
42
  s.add_development_dependency('aruba', '~> 0.9.0')
42
43
  s.add_development_dependency('nokogiri')
43
44
  s.add_development_dependency('capybara')
@@ -48,7 +49,6 @@ Gem::Specification.new do |s|
48
49
  s.add_development_dependency('rake')
49
50
  s.add_development_dependency('fakeweb')
50
51
  s.add_development_dependency('railties')
51
- s.add_development_dependency('actionmailer', '>= 4.2.0')
52
52
  s.add_development_dependency('generator_spec')
53
53
  s.add_development_dependency('timecop')
54
54
  end
@@ -500,6 +500,7 @@ describe Paperclip::Attachment do
500
500
  @attachment.expects(:post_process).with(:thumb)
501
501
  @attachment.expects(:post_process).with(:large).never
502
502
  @attachment.assign(@file)
503
+ @attachment.save
503
504
  end
504
505
  end
505
506
 
@@ -1433,16 +1434,46 @@ describe Paperclip::Attachment do
1433
1434
  assert_nothing_raised { @dummy.avatar = @file }
1434
1435
  end
1435
1436
 
1436
- it "returns the right value when sent #avatar_fingerprint" do
1437
- @dummy.avatar = @file
1438
- assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
1437
+ context "with explicitly set digest" do
1438
+ before do
1439
+ rebuild_class adapter_options: { hash_digest: Digest::SHA256 }
1440
+ @dummy = Dummy.new
1441
+ end
1442
+
1443
+ it "returns the right value when sent #avatar_fingerprint" do
1444
+ @dummy.avatar = @file
1445
+ assert_equal "734016d801a497f5579cdd4ef2ae1d020088c1db754dc434482d76dd5486520a",
1446
+ @dummy.avatar_fingerprint
1447
+ end
1448
+
1449
+ it "returns the right value when saved, reloaded, and sent #avatar_fingerprint" do
1450
+ @dummy.avatar = @file
1451
+ @dummy.save
1452
+ @dummy = Dummy.find(@dummy.id)
1453
+ assert_equal "734016d801a497f5579cdd4ef2ae1d020088c1db754dc434482d76dd5486520a",
1454
+ @dummy.avatar_fingerprint
1455
+ end
1439
1456
  end
1440
1457
 
1441
- it "returns the right value when saved, reloaded, and sent #avatar_fingerprint" do
1442
- @dummy.avatar = @file
1443
- @dummy.save
1444
- @dummy = Dummy.find(@dummy.id)
1445
- assert_equal 'aec488126c3b33c08a10c3fa303acf27', @dummy.avatar_fingerprint
1458
+ context "with the default digest" do
1459
+ before do
1460
+ rebuild_class # MD5 is the default
1461
+ @dummy = Dummy.new
1462
+ end
1463
+
1464
+ it "returns the right value when sent #avatar_fingerprint" do
1465
+ @dummy.avatar = @file
1466
+ assert_equal "aec488126c3b33c08a10c3fa303acf27",
1467
+ @dummy.avatar_fingerprint
1468
+ end
1469
+
1470
+ it "returns the right value when saved, reloaded, and sent #avatar_fingerprint" do
1471
+ @dummy.avatar = @file
1472
+ @dummy.save
1473
+ @dummy = Dummy.find(@dummy.id)
1474
+ assert_equal "aec488126c3b33c08a10c3fa303acf27",
1475
+ @dummy.avatar_fingerprint
1476
+ end
1446
1477
  end
1447
1478
  end
1448
1479
  end
@@ -9,70 +9,93 @@ describe Paperclip::AbstractAdapter do
9
9
  end
10
10
  end
11
11
 
12
+ subject { TestAdapter.new(nil) }
13
+
12
14
  context "content type from file contents" do
13
15
  before do
14
- @adapter = TestAdapter.new
15
- @adapter.stubs(:path).returns("image.png")
16
+ subject.stubs(:path).returns("image.png")
16
17
  Paperclip.stubs(:run).returns("image/png\n")
17
18
  Paperclip::ContentTypeDetector.any_instance.stubs(:type_from_mime_magic).returns("image/png")
18
19
  end
19
20
 
20
21
  it "returns the content type without newline" do
21
- assert_equal "image/png", @adapter.content_type
22
+ assert_equal "image/png", subject.content_type
22
23
  end
23
24
  end
24
25
 
25
26
  context "nil?" do
26
27
  it "returns false" do
27
- assert !TestAdapter.new.nil?
28
+ assert !subject.nil?
28
29
  end
29
30
  end
30
31
 
31
32
  context "delegation" do
32
33
  before do
33
- @adapter = TestAdapter.new
34
- @adapter.tempfile = stub("Tempfile")
34
+ subject.tempfile = stub("Tempfile")
35
35
  end
36
36
 
37
37
  [:binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :readbyte, :rewind, :unlink].each do |method|
38
38
  it "delegates #{method} to @tempfile" do
39
- @adapter.tempfile.stubs(method)
40
- @adapter.public_send(method)
41
- assert_received @adapter.tempfile, method
39
+ subject.tempfile.stubs(method)
40
+ subject.public_send(method)
41
+ assert_received subject.tempfile, method
42
42
  end
43
43
  end
44
44
  end
45
45
 
46
46
  it 'gets rid of slashes and colons in filenames' do
47
- @adapter = TestAdapter.new
48
- @adapter.original_filename = "awesome/file:name.png"
47
+ subject.original_filename = "awesome/file:name.png"
49
48
 
50
- assert_equal "awesome_file_name.png", @adapter.original_filename
49
+ assert_equal "awesome_file_name.png", subject.original_filename
51
50
  end
52
51
 
53
52
  it 'is an assignment' do
54
- assert TestAdapter.new.assignment?
53
+ assert subject.assignment?
55
54
  end
56
55
 
57
56
  it 'is not nil' do
58
- assert !TestAdapter.new.nil?
57
+ assert !subject.nil?
59
58
  end
60
59
 
61
60
  it "generates a destination filename with no original filename" do
62
- @adapter = TestAdapter.new
63
- expect(@adapter.send(:destination).path).to_not be_nil
61
+ expect(subject.send(:destination).path).to_not be_nil
64
62
  end
65
63
 
66
64
  it 'uses the original filename to generate the tempfile' do
67
- @adapter = TestAdapter.new
68
- @adapter.original_filename = "file.png"
69
- expect(@adapter.send(:destination).path).to end_with(".png")
65
+ subject.original_filename = "file.png"
66
+ expect(subject.send(:destination).path).to end_with(".png")
67
+ end
68
+
69
+ context "generates a fingerprint" do
70
+ subject { TestAdapter.new(nil, options) }
71
+
72
+ before do
73
+ subject.stubs(:path).returns(fixture_file("50x50.png"))
74
+ end
75
+
76
+ context "MD5" do
77
+ let(:options) { { hash_digest: Digest::MD5 } }
78
+
79
+ it "returns a fingerprint" do
80
+ expect(subject.fingerprint).to be_a String
81
+ expect(subject.fingerprint).to eq "a790b00c9b5d58a8fd17a1ec5a187129"
82
+ end
83
+ end
84
+
85
+ context "SHA256" do
86
+ let(:options) { { hash_digest: Digest::SHA256 } }
87
+
88
+ it "returns a fingerprint" do
89
+ expect(subject.fingerprint).to be_a String
90
+ expect(subject.fingerprint).
91
+ to eq "243d7ce1099719df25f600f1c369c629fb979f88d5a01dbe7d0d48c8e6715bb1"
92
+ end
93
+ end
70
94
  end
71
95
 
72
96
  context "#original_filename=" do
73
97
  it "should not fail with a nil original filename" do
74
- adapter = TestAdapter.new
75
- expect{ adapter.original_filename = nil }.not_to raise_error
98
+ expect { subject.original_filename = nil }.not_to raise_error
76
99
  end
77
100
  end
78
101
  end