dm-paperclip 2.4.1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/Gemfile +29 -0
  2. data/Gemfile.lock +100 -0
  3. data/README.md +145 -0
  4. data/Rakefile +37 -71
  5. data/VERSION +1 -0
  6. data/dm-paperclip.gemspec +103 -0
  7. data/lib/dm-paperclip.rb +88 -74
  8. data/lib/dm-paperclip/attachment.rb +139 -102
  9. data/lib/dm-paperclip/callbacks.rb +55 -0
  10. data/lib/dm-paperclip/command_line.rb +86 -0
  11. data/lib/dm-paperclip/ext/blank.rb +24 -0
  12. data/lib/dm-paperclip/ext/class.rb +50 -0
  13. data/lib/dm-paperclip/ext/compatibility.rb +11 -0
  14. data/lib/dm-paperclip/ext/try_dup.rb +12 -0
  15. data/lib/dm-paperclip/geometry.rb +3 -5
  16. data/lib/dm-paperclip/interpolations.rb +57 -32
  17. data/lib/dm-paperclip/iostream.rb +12 -26
  18. data/lib/dm-paperclip/processor.rb +14 -4
  19. data/lib/dm-paperclip/storage.rb +2 -257
  20. data/lib/dm-paperclip/storage/filesystem.rb +73 -0
  21. data/lib/dm-paperclip/storage/s3.rb +209 -0
  22. data/lib/dm-paperclip/storage/s3/aws_library.rb +41 -0
  23. data/lib/dm-paperclip/storage/s3/aws_s3_library.rb +60 -0
  24. data/lib/dm-paperclip/style.rb +90 -0
  25. data/lib/dm-paperclip/thumbnail.rb +33 -24
  26. data/lib/dm-paperclip/upfile.rb +13 -5
  27. data/lib/dm-paperclip/validations.rb +40 -37
  28. data/lib/dm-paperclip/version.rb +4 -0
  29. data/test/attachment_test.rb +510 -67
  30. data/test/command_line_test.rb +138 -0
  31. data/test/fixtures/s3.yml +8 -0
  32. data/test/fixtures/twopage.pdf +0 -0
  33. data/test/fixtures/uppercase.PNG +0 -0
  34. data/test/geometry_test.rb +54 -19
  35. data/test/helper.rb +91 -28
  36. data/test/integration_test.rb +252 -79
  37. data/test/interpolations_test.rb +150 -0
  38. data/test/iostream_test.rb +8 -15
  39. data/test/paperclip_test.rb +222 -69
  40. data/test/processor_test.rb +10 -0
  41. data/test/storage_test.rb +102 -23
  42. data/test/style_test.rb +141 -0
  43. data/test/thumbnail_test.rb +106 -18
  44. data/test/upfile_test.rb +36 -0
  45. metadata +136 -121
  46. data/README.rdoc +0 -116
  47. data/init.rb +0 -1
  48. data/lib/dm-paperclip/callback_compatability.rb +0 -33
@@ -0,0 +1,138 @@
1
+ require './test/helper'
2
+
3
+ class CommandLineTest < Test::Unit::TestCase
4
+ def setup
5
+ Paperclip::CommandLine.path = nil
6
+ File.stubs(:exist?).with("/dev/null").returns(true)
7
+ end
8
+
9
+ should "allow colons in parameters" do
10
+ cmd = Paperclip::CommandLine.new("convert", "'a.jpg' -resize 175x220> -size 175x220 xc:black +swap -gravity center -composite 'b.jpg'", :swallow_stderr => false)
11
+ assert_equal "convert 'a.jpg' -resize 175x220> -size 175x220 xc:black +swap -gravity center -composite 'b.jpg'", cmd.command
12
+ end
13
+
14
+ should "take a command and parameters and produce a shell command for bash" do
15
+ cmd = Paperclip::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
16
+ assert_equal "convert a.jpg b.png", cmd.command
17
+ end
18
+
19
+ should "be able to set a path and produce commands with that path" do
20
+ Paperclip::CommandLine.path = "/opt/bin"
21
+ cmd = Paperclip::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
22
+ assert_equal "/opt/bin/convert a.jpg b.png", cmd.command
23
+ end
24
+
25
+ should "be able to interpolate quoted variables into the parameters" do
26
+ cmd = Paperclip::CommandLine.new("convert",
27
+ ":one :{two}",
28
+ :one => "a.jpg",
29
+ :two => "b.png",
30
+ :swallow_stderr => false)
31
+ assert_equal "convert 'a.jpg' 'b.png'", cmd.command
32
+ end
33
+
34
+ should "quote command line options differently if we're on windows" do
35
+ File.stubs(:exist?).with("/dev/null").returns(false)
36
+ cmd = Paperclip::CommandLine.new("convert",
37
+ ":one :{two}",
38
+ :one => "a.jpg",
39
+ :two => "b.png",
40
+ :swallow_stderr => false)
41
+ assert_equal 'convert "a.jpg" "b.png"', cmd.command
42
+ end
43
+
44
+ should "be able to quote and interpolate dangerous variables" do
45
+ cmd = Paperclip::CommandLine.new("convert",
46
+ ":one :two",
47
+ :one => "`rm -rf`.jpg",
48
+ :two => "ha'ha.png",
49
+ :swallow_stderr => false)
50
+ assert_equal "convert '`rm -rf`.jpg' 'ha'\\''ha.png'", cmd.command
51
+ end
52
+
53
+ should "be able to quote and interpolate dangerous variables even on windows" do
54
+ File.stubs(:exist?).with("/dev/null").returns(false)
55
+ cmd = Paperclip::CommandLine.new("convert",
56
+ ":one :two",
57
+ :one => "`rm -rf`.jpg",
58
+ :two => "ha'ha.png",
59
+ :swallow_stderr => false)
60
+ assert_equal %{convert "`rm -rf`.jpg" "ha'ha.png"}, cmd.command
61
+ end
62
+
63
+ should "add redirection to get rid of stderr in bash" do
64
+ File.stubs(:exist?).with("/dev/null").returns(true)
65
+ cmd = Paperclip::CommandLine.new("convert",
66
+ "a.jpg b.png",
67
+ :swallow_stderr => true)
68
+
69
+ assert_equal "convert a.jpg b.png 2>/dev/null", cmd.command
70
+ end
71
+
72
+ should "add redirection to get rid of stderr in cmd.exe" do
73
+ File.stubs(:exist?).with("/dev/null").returns(false)
74
+ cmd = Paperclip::CommandLine.new("convert",
75
+ "a.jpg b.png",
76
+ :swallow_stderr => true)
77
+
78
+ assert_equal "convert a.jpg b.png 2>NUL", cmd.command
79
+ end
80
+
81
+ should "raise if trying to interpolate :swallow_stderr or :expected_outcodes" do
82
+ cmd = Paperclip::CommandLine.new("convert",
83
+ ":swallow_stderr :expected_outcodes",
84
+ :swallow_stderr => false,
85
+ :expected_outcodes => [0, 1])
86
+ assert_raise(Paperclip::PaperclipCommandLineError) do
87
+ cmd.command
88
+ end
89
+ end
90
+
91
+ should "run the #command it's given and return the output" do
92
+ cmd = Paperclip::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
93
+ cmd.class.stubs(:"`").with("convert a.jpg b.png").returns(:correct_value)
94
+ with_exitstatus_returning(0) do
95
+ assert_equal :correct_value, cmd.run
96
+ end
97
+ end
98
+
99
+ should "raise a PaperclipCommandLineError if the result code isn't expected" do
100
+ cmd = Paperclip::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
101
+ cmd.class.stubs(:"`").with("convert a.jpg b.png").returns(:correct_value)
102
+ with_exitstatus_returning(1) do
103
+ assert_raises(Paperclip::PaperclipCommandLineError) do
104
+ cmd.run
105
+ end
106
+ end
107
+ end
108
+
109
+ should "not raise a PaperclipCommandLineError if the result code is expected" do
110
+ cmd = Paperclip::CommandLine.new("convert",
111
+ "a.jpg b.png",
112
+ :expected_outcodes => [0, 1],
113
+ :swallow_stderr => false)
114
+ cmd.class.stubs(:"`").with("convert a.jpg b.png").returns(:correct_value)
115
+ with_exitstatus_returning(1) do
116
+ assert_nothing_raised do
117
+ cmd.run
118
+ end
119
+ end
120
+ end
121
+
122
+ should "log the command" do
123
+ cmd = Paperclip::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
124
+ cmd.class.stubs(:'`')
125
+ Paperclip.expects(:log).with("convert a.jpg b.png")
126
+ cmd.run
127
+ end
128
+
129
+ should "detect that the system is unix or windows based on presence of /dev/null" do
130
+ File.stubs(:exist?).returns(true)
131
+ assert Paperclip::CommandLine.unix?
132
+ end
133
+
134
+ should "detect that the system is not unix or windows based on absence of /dev/null" do
135
+ File.stubs(:exist?).returns(false)
136
+ assert ! Paperclip::CommandLine.unix?
137
+ end
138
+ end
@@ -0,0 +1,8 @@
1
+ development:
2
+ key: 54321
3
+ production:
4
+ key: 12345
5
+ test:
6
+ bucket: <%= ENV['S3_BUCKET'] %>
7
+ access_key_id: <%= ENV['S3_KEY'] %>
8
+ secret_access_key: <%= ENV['S3_SECRET'] %>
Binary file
@@ -1,8 +1,4 @@
1
- require 'rubygems'
2
- require 'test/unit'
3
- require 'shoulda'
4
-
5
- require File.join(File.dirname(__FILE__), '..', 'lib', 'dm-paperclip', 'geometry.rb')
1
+ require './test/helper'
6
2
 
7
3
  class GeometryTest < Test::Unit::TestCase
8
4
  context "Paperclip::Geometry" do
@@ -12,15 +8,15 @@ class GeometryTest < Test::Unit::TestCase
12
8
  assert_equal 768, @geo.height
13
9
  end
14
10
 
15
- should "correctly create a square if the height dimension is missing" do
11
+ should "set height to 0 if height dimension is missing" do
16
12
  assert @geo = Paperclip::Geometry.new(1024)
17
13
  assert_equal 1024, @geo.width
18
- assert_equal 1024, @geo.height
14
+ assert_equal 0, @geo.height
19
15
  end
20
16
 
21
- should "correctly create a square if the width dimension is missing" do
17
+ should "set width to 0 if width dimension is missing" do
22
18
  assert @geo = Paperclip::Geometry.new(nil, 768)
23
- assert_equal 768, @geo.width
19
+ assert_equal 0, @geo.width
24
20
  assert_equal 768, @geo.height
25
21
  end
26
22
 
@@ -32,19 +28,20 @@ class GeometryTest < Test::Unit::TestCase
32
28
 
33
29
  should "be generated from a xH-formatted string" do
34
30
  assert @geo = Paperclip::Geometry.parse("x600")
35
- assert_equal 600, @geo.width
31
+ assert_equal 0, @geo.width
36
32
  assert_equal 600, @geo.height
37
33
  end
38
-
34
+
39
35
  should "be generated from a Wx-formatted string" do
40
36
  assert @geo = Paperclip::Geometry.parse("800x")
41
37
  assert_equal 800, @geo.width
42
- assert_equal 800, @geo.height
38
+ assert_equal 0, @geo.height
43
39
  end
44
40
 
45
- should "ensure the modifier is nil if only one dimension present" do
46
- assert @geo = Paperclip::Geometry.parse("123x")
47
- assert_nil @geo.modifier
41
+ should "be generated from a W-formatted string" do
42
+ assert @geo = Paperclip::Geometry.parse("800")
43
+ assert_equal 800, @geo.width
44
+ assert_equal 0, @geo.height
48
45
  end
49
46
 
50
47
  should "ensure the modifier is nil if not present" do
@@ -52,22 +49,60 @@ class GeometryTest < Test::Unit::TestCase
52
49
  assert_nil @geo.modifier
53
50
  end
54
51
 
55
- ['>', '<', '#', '@', '%', '^', '!'].each do |mod|
56
- should "ensure the modifier #{mod} is preserved" do
52
+ should "treat x and X the same in geometries" do
53
+ @lower = Paperclip::Geometry.parse("123x456")
54
+ @upper = Paperclip::Geometry.parse("123X456")
55
+ assert_equal 123, @lower.width
56
+ assert_equal 123, @upper.width
57
+ assert_equal 456, @lower.height
58
+ assert_equal 456, @upper.height
59
+ end
60
+
61
+ ['>', '<', '#', '@', '%', '^', '!', nil].each do |mod|
62
+ should "ensure the modifier #{mod.inspect} is preserved" do
57
63
  assert @geo = Paperclip::Geometry.parse("123x456#{mod}")
58
64
  assert_equal mod, @geo.modifier
65
+ assert_equal "123x456#{mod}", @geo.to_s
66
+ end
67
+ end
68
+
69
+ ['>', '<', '#', '@', '%', '^', '!', nil].each do |mod|
70
+ should "ensure the modifier #{mod.inspect} is preserved with no height" do
71
+ assert @geo = Paperclip::Geometry.parse("123x#{mod}")
72
+ assert_equal mod, @geo.modifier
73
+ assert_equal "123#{mod}", @geo.to_s
59
74
  end
60
75
  end
61
76
 
62
77
  should "make sure the modifier gets passed during transformation_to" do
63
78
  assert @src = Paperclip::Geometry.parse("123x456")
64
79
  assert @dst = Paperclip::Geometry.parse("123x456>")
65
- assert_equal "123x456>", @src.transformation_to(@dst).join
80
+ assert_equal ["123x456>", nil], @src.transformation_to(@dst)
81
+ end
82
+
83
+ should "generate correct ImageMagick formatting string for W-formatted string" do
84
+ assert @geo = Paperclip::Geometry.parse("800")
85
+ assert_equal "800", @geo.to_s
86
+ end
87
+
88
+ should "generate correct ImageMagick formatting string for Wx-formatted string" do
89
+ assert @geo = Paperclip::Geometry.parse("800x")
90
+ assert_equal "800", @geo.to_s
91
+ end
92
+
93
+ should "generate correct ImageMagick formatting string for xH-formatted string" do
94
+ assert @geo = Paperclip::Geometry.parse("x600")
95
+ assert_equal "x600", @geo.to_s
96
+ end
97
+
98
+ should "generate correct ImageMagick formatting string for WxH-formatted string" do
99
+ assert @geo = Paperclip::Geometry.parse("800x600")
100
+ assert_equal "800x600", @geo.to_s
66
101
  end
67
102
 
68
103
  should "be generated from a file" do
69
104
  file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
70
- file = File.new(file)
105
+ file = File.new(file, 'rb')
71
106
  assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
72
107
  assert @geo.height > 0
73
108
  assert @geo.width > 0
@@ -1,31 +1,39 @@
1
- require 'rubygems'
1
+ require "rubygems"
2
+
3
+ begin
4
+ require 'simplecov'
5
+ SimpleCov.start do
6
+ add_filter "/test/"
7
+ end
8
+ rescue LoadError
9
+ end
10
+
11
+ require 'tempfile'
12
+ require 'fileutils'
2
13
  require 'test/unit'
14
+
3
15
  require 'shoulda'
4
16
  require 'mocha'
5
- require 'tempfile'
6
17
 
7
- require 'extlib'
8
18
  require 'dm-core'
9
19
  require 'dm-validations'
10
20
  require 'dm-migrations'
21
+
22
+ `ruby -e 'exit 0'` # Prime $? with a value.
23
+
11
24
  begin
12
25
  require 'ruby-debug'
13
- rescue LoadError
14
- puts "ruby-debug not loaded"
26
+ rescue LoadError => e
15
27
  end
16
28
 
17
- ROOT = File.join(File.dirname(__FILE__), '..')
18
- RAILS_ROOT = ROOT
19
- RAILS_ENV = ENV['RAILS_ENV']
29
+ ROOT = File.join(File.dirname(__FILE__), '..')
20
30
 
21
- Object.const_set("Merb", Class.new())
22
- Merb.class_eval do
23
- def self.root
24
- "#{ROOT}"
25
- end
26
- def self.env(str=nil)
27
- ENV['RAILS_ENV'] = str if str
28
- ENV['RAILS_ENV']
31
+ class Test::Unit::TestCase
32
+ def setup
33
+ silence_warnings do
34
+ Object.const_set(:Rails, stub('Rails', :root => ROOT, :env => 'test'))
35
+ Object.const_set(:Merb, stub('Merb', :root => ROOT, :env => 'test'))
36
+ end
29
37
  end
30
38
  end
31
39
 
@@ -36,7 +44,7 @@ require File.join(ROOT, 'lib', 'dm-paperclip.rb')
36
44
 
37
45
  ENV['RAILS_ENV'] ||= 'test'
38
46
 
39
- FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
47
+ FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
40
48
  DataMapper.setup(:default, 'sqlite3::memory:')
41
49
 
42
50
  unless defined?(Mash)
@@ -45,28 +53,83 @@ unless defined?(Mash)
45
53
  end
46
54
 
47
55
  Paperclip.configure do |config|
48
- config.root = Merb.root # the application root to anchor relative urls (defaults to Dir.pwd)
49
- config.env = Merb.env # server env support, defaults to ENV['RACK_ENV'] or 'development'
50
- config.use_dm_validations = true # validate attachment sizes and such, defaults to false
56
+ config.root = ROOT # the application root to anchor relative urls (defaults to Dir.pwd)
57
+ config.env = 'test' # server env support, defaults to ENV['RACK_ENV'] or 'development'
58
+ config.use_dm_validations = true # validate attachment sizes and such, defaults to false
51
59
  end
52
60
 
53
61
  def rebuild_model options = {}
62
+ rebuild_class options
63
+ Dummy.auto_migrate!
64
+ end
65
+
66
+ def rebuild_class options = {}
54
67
  Object.send(:remove_const, "Dummy") rescue nil
55
- Object.const_set("Dummy", Class.new())
68
+ Object.const_set("Dummy", Class.new)
56
69
  Dummy.class_eval do
57
70
  include DataMapper::Resource
58
71
  include DataMapper::Validate
59
72
  include Paperclip::Resource
60
- property :id, ::DataMapper::Types::Serial
73
+ property :id, ::DataMapper::Property::Serial
61
74
  property :other, String
62
75
  has_attached_file :avatar, options
63
76
  end
64
- Dummy.auto_migrate!
65
77
  end
66
78
 
67
- def temporary_env(new_env)
68
- old_env = Merb.env
69
- Merb.env(new_env)
79
+ class FakeModel
80
+ attr_accessor :avatar_file_name,
81
+ :avatar_file_size,
82
+ :avatar_updated_at,
83
+ :avatar_content_type,
84
+ :avatar_fingerprint,
85
+ :id
86
+
87
+ def errors
88
+ @errors ||= []
89
+ end
90
+
91
+ def run_paperclip_callbacks name, *args
92
+ end
93
+
94
+ end
95
+
96
+ def attachment options
97
+ Paperclip::Attachment.new(:avatar, FakeModel.new, options)
98
+ end
99
+
100
+ def silence_warnings
101
+ old_verbose, $VERBOSE = $VERBOSE, nil
70
102
  yield
71
- Merb.env(old_env)
72
- end
103
+ ensure
104
+ $VERBOSE = old_verbose
105
+ end
106
+
107
+ def should_accept_dummy_class
108
+ should "accept the class" do
109
+ assert_accepts @matcher, @dummy_class
110
+ end
111
+
112
+ should "accept an instance of that class" do
113
+ assert_accepts @matcher, @dummy_class.new
114
+ end
115
+ end
116
+
117
+ def should_reject_dummy_class
118
+ should "reject the class" do
119
+ assert_rejects @matcher, @dummy_class
120
+ end
121
+
122
+ should "reject an instance of that class" do
123
+ assert_rejects @matcher, @dummy_class.new
124
+ end
125
+ end
126
+
127
+ def with_exitstatus_returning(code)
128
+ saved_exitstatus = $?.nil? ? 0 : $?.exitstatus
129
+ begin
130
+ `ruby -e 'exit #{code.to_i}'`
131
+ yield
132
+ ensure
133
+ `ruby -e 'exit #{saved_exitstatus.to_i}'`
134
+ end
135
+ end
@@ -1,15 +1,15 @@
1
- require 'test/helper.rb'
1
+ require File.expand_path("./helper", File.dirname(__FILE__))
2
2
 
3
3
  class IntegrationTest < Test::Unit::TestCase
4
4
  context "Many models at once" do
5
5
  setup do
6
6
  rebuild_model
7
- @file = File.new(File.join(FIXTURES_DIR, "5k.png"))
7
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
8
8
  300.times do |i|
9
9
  Dummy.create! :avatar => @file
10
10
  end
11
11
  end
12
-
12
+
13
13
  should "not exceed the open file limit" do
14
14
  assert_nothing_raised do
15
15
  dummies = Dummy.all
@@ -22,22 +22,40 @@ class IntegrationTest < Test::Unit::TestCase
22
22
  setup do
23
23
  rebuild_model :styles => { :thumb => "50x50#" }
24
24
  @dummy = Dummy.new
25
- @dummy.id = 1
26
25
  @file = File.new(File.join(File.dirname(__FILE__),
27
26
  "fixtures",
28
- "5k.png"))
27
+ "5k.png"), 'rb')
29
28
  @dummy.avatar = @file
30
29
  assert @dummy.save
31
30
  end
32
31
 
32
+ teardown { @file.close }
33
+
33
34
  should "create its thumbnails properly" do
34
- assert_match /\b50x50\b/, `identify '#{@dummy.avatar.path(:thumb)}'`
35
+ assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
36
+ end
37
+
38
+ context 'reprocessing with unreadable original' do
39
+ setup { File.chmod(0000, @dummy.avatar.path) }
40
+
41
+ should "not raise an error" do
42
+ assert_nothing_raised do
43
+ @dummy.avatar.reprocess!
44
+ end
45
+ end
46
+
47
+ should "return false" do
48
+ assert ! @dummy.avatar.reprocess!
49
+ end
50
+
51
+ teardown { File.chmod(0644, @dummy.avatar.path) }
35
52
  end
36
53
 
37
54
  context "redefining its attachment styles" do
38
55
  setup do
39
56
  Dummy.class_eval do
40
57
  has_attached_file :avatar, :styles => { :thumb => "150x25#" }
58
+ has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } }
41
59
  end
42
60
  @d2 = Dummy.get(@dummy.id)
43
61
  @d2.avatar.reprocess!
@@ -45,7 +63,143 @@ class IntegrationTest < Test::Unit::TestCase
45
63
  end
46
64
 
47
65
  should "create its thumbnails properly" do
48
- assert_match /\b150x25\b/, `identify '#{@dummy.avatar.path(:thumb)}'`
66
+ assert_match /\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
67
+ assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`
68
+ end
69
+ end
70
+ end
71
+
72
+ context "Attachment" do
73
+ setup do
74
+ @thumb_path = "./test/../public/system/avatars/1/thumb/5k.png"
75
+ File.delete(@thumb_path) if File.exists?(@thumb_path)
76
+ rebuild_model :styles => { :thumb => "50x50#" }
77
+ @dummy = Dummy.new
78
+ @file = File.new(File.join(File.dirname(__FILE__),
79
+ "fixtures",
80
+ "5k.png"), 'rb')
81
+
82
+ end
83
+
84
+ teardown { @file.close }
85
+
86
+ should "not create the thumbnails upon saving when post-processing is disabled" do
87
+ @dummy.avatar.post_processing = false
88
+ @dummy.avatar = @file
89
+ assert @dummy.save
90
+ assert !File.exists?(@thumb_path)
91
+ end
92
+
93
+ should "create the thumbnails upon saving when post_processing is enabled" do
94
+ @dummy.avatar.post_processing = true
95
+ @dummy.avatar = @file
96
+ assert @dummy.save
97
+ assert File.exists?(@thumb_path)
98
+ end
99
+ end
100
+
101
+ context "Attachment with no generated thumbnails" do
102
+ setup do
103
+ @thumb_small_path = "./test/../public/system/avatars/1/thumb_small/5k.png"
104
+ @thumb_large_path = "./test/../public/system/avatars/1/thumb_large/5k.png"
105
+ File.delete(@thumb_small_path) if File.exists?(@thumb_small_path)
106
+ File.delete(@thumb_large_path) if File.exists?(@thumb_large_path)
107
+ rebuild_model :styles => { :thumb_small => "50x50#", :thumb_large => "60x60#" }
108
+ @dummy = Dummy.new
109
+ @file = File.new(File.join(File.dirname(__FILE__),
110
+ "fixtures",
111
+ "5k.png"), 'rb')
112
+
113
+ @dummy.avatar.post_processing = false
114
+ @dummy.avatar = @file
115
+ assert @dummy.save
116
+ @dummy.avatar.post_processing = true
117
+ end
118
+
119
+ teardown { @file.close }
120
+
121
+ should "allow us to create all thumbnails in one go" do
122
+ assert !File.exists?(@thumb_small_path)
123
+ assert !File.exists?(@thumb_large_path)
124
+
125
+ @dummy.avatar.reprocess!
126
+
127
+ assert File.exists?(@thumb_small_path)
128
+ assert File.exists?(@thumb_large_path)
129
+ end
130
+
131
+ should "allow us to selectively create each thumbnail" do
132
+ assert !File.exists?(@thumb_small_path)
133
+ assert !File.exists?(@thumb_large_path)
134
+
135
+ @dummy.avatar.reprocess! :thumb_small
136
+ assert File.exists?(@thumb_small_path)
137
+ assert !File.exists?(@thumb_large_path)
138
+
139
+ @dummy.avatar.reprocess! :thumb_large
140
+ assert File.exists?(@thumb_large_path)
141
+ end
142
+ end
143
+
144
+ context "A model that modifies its original" do
145
+ setup do
146
+ rebuild_model :styles => { :original => "2x2#" }
147
+ @dummy = Dummy.new
148
+ @file = File.new(File.join(File.dirname(__FILE__),
149
+ "fixtures",
150
+ "5k.png"), 'rb')
151
+ @dummy.avatar = @file
152
+ end
153
+
154
+ should "report the file size of the processed file and not the original" do
155
+ assert_not_equal @file.size, @dummy.avatar.size
156
+ end
157
+
158
+ teardown { @file.close }
159
+ end
160
+
161
+ context "A model with attachments scoped under an id" do
162
+ setup do
163
+ rebuild_model :styles => { :large => "100x100",
164
+ :medium => "50x50" },
165
+ :path => ":rails_root/tmp/:id/:attachments/:style.:extension"
166
+ @dummy = Dummy.new
167
+ @file = File.new(File.join(File.dirname(__FILE__),
168
+ "fixtures",
169
+ "5k.png"), 'rb')
170
+ @dummy.avatar = @file
171
+ end
172
+
173
+ teardown { @file.close }
174
+
175
+ context "when saved" do
176
+ setup do
177
+ @dummy.save
178
+ @saved_path = @dummy.avatar.path(:large)
179
+ end
180
+
181
+ should "have a large file in the right place" do
182
+ assert File.exists?(@dummy.avatar.path(:large))
183
+ end
184
+
185
+ context "and deleted" do
186
+ setup do
187
+ @dummy.avatar.clear
188
+ @dummy.save
189
+ end
190
+
191
+ should "not have a large file in the right place anymore" do
192
+ assert ! File.exists?(@saved_path)
193
+ end
194
+
195
+ should "not have its next two parent directories" do
196
+ assert ! File.exists?(File.dirname(@saved_path))
197
+ assert ! File.exists?(File.dirname(File.dirname(@saved_path)))
198
+ end
199
+
200
+ before_should "not die if an unexpected SystemCallError happens" do
201
+ FileUtils.stubs(:rmdir).raises(Errno::EPIPE)
202
+ end
49
203
  end
50
204
  end
51
205
  end
@@ -57,7 +211,7 @@ class IntegrationTest < Test::Unit::TestCase
57
211
  :thumb => ["32x32#", :gif] },
58
212
  :default_style => :medium,
59
213
  :url => "/:attachment/:class/:style/:id/:basename.:extension",
60
- :path => ":merb_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
214
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
61
215
  @dummy = Dummy.new
62
216
  end
63
217
 
@@ -83,7 +237,7 @@ class IntegrationTest < Test::Unit::TestCase
83
237
  :whiny_thumbnails => true,
84
238
  :default_style => :medium,
85
239
  :url => "/:attachment/:class/:style/:id/:basename.:extension",
86
- :path => ":merb_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
240
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
87
241
  end
88
242
 
89
243
  should "have its definition return true when asked about whiny_thumbnails" do
@@ -91,20 +245,20 @@ class IntegrationTest < Test::Unit::TestCase
91
245
  end
92
246
  end
93
247
  end
94
-
95
- context "A model with no thumbnail_convert_options setting" do
248
+
249
+ context "A model with no convert_options setting" do
96
250
  setup do
97
251
  rebuild_model :styles => { :large => "300x300>",
98
252
  :medium => "100x100",
99
253
  :thumb => ["32x32#", :gif] },
100
254
  :default_style => :medium,
101
255
  :url => "/:attachment/:class/:style/:id/:basename.:extension",
102
- :path => ":merb_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
256
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
103
257
  @dummy = Dummy.new
104
258
  end
105
-
259
+
106
260
  should "have its definition return nil when asked about convert_options" do
107
- assert ! Dummy.attachment_definitions[:avatar][:thumbnail_convert_options]
261
+ assert ! Dummy.attachment_definitions[:avatar][:convert_options]
108
262
  end
109
263
 
110
264
  context "redefined to have convert_options setting" do
@@ -112,18 +266,18 @@ class IntegrationTest < Test::Unit::TestCase
112
266
  rebuild_model :styles => { :large => "300x300>",
113
267
  :medium => "100x100",
114
268
  :thumb => ["32x32#", :gif] },
115
- :thumbnail_convert_options => "-strip -depth 8",
269
+ :convert_options => "-strip -depth 8",
116
270
  :default_style => :medium,
117
271
  :url => "/:attachment/:class/:style/:id/:basename.:extension",
118
- :path => ":merb_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
272
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
119
273
  end
120
274
 
121
275
  should "have its definition return convert_options value when asked about convert_options" do
122
- assert_equal "-strip -depth 8", Dummy.attachment_definitions[:avatar][:thumbnail_convert_options]
276
+ assert_equal "-strip -depth 8", Dummy.attachment_definitions[:avatar][:convert_options]
123
277
  end
124
278
  end
125
279
  end
126
-
280
+
127
281
  context "A model with a filesystem attachment" do
128
282
  setup do
129
283
  rebuild_model :styles => { :large => "300x300>",
@@ -132,14 +286,13 @@ class IntegrationTest < Test::Unit::TestCase
132
286
  :whiny_thumbnails => true,
133
287
  :default_style => :medium,
134
288
  :url => "/:attachment/:class/:style/:id/:basename.:extension",
135
- :path => ":merb_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
289
+ :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
136
290
  @dummy = Dummy.new
137
- @file = File.new(File.join(FIXTURES_DIR, "5k.png"))
138
- @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"))
291
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
292
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
139
293
 
140
- @dummy.id = 1
141
294
  assert @dummy.avatar = @file
142
- assert @dummy.valid?
295
+ assert @dummy.valid?, @dummy.errors.full_messages.join(", ")
143
296
  assert @dummy.save
144
297
  end
145
298
 
@@ -148,18 +301,18 @@ class IntegrationTest < Test::Unit::TestCase
148
301
  ["300x46", :large],
149
302
  ["100x15", :medium],
150
303
  ["32x32", :thumb]].each do |geo, style|
151
- cmd = %Q[identify -format "%wx%h" #{@dummy.avatar.to_file(style).path}]
304
+ cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"]
152
305
  assert_equal geo, `#{cmd}`.chomp, cmd
153
306
  end
154
307
 
155
- saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s).path }
308
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
156
309
 
157
310
  @d2 = Dummy.get(@dummy.id)
158
- assert_equal "100x15", `identify -format "%wx%h" #{@d2.avatar.to_file.path}`.chomp
159
- assert_equal "434x66", `identify -format "%wx%h" #{@d2.avatar.to_file(:original).path}`.chomp
160
- assert_equal "300x46", `identify -format "%wx%h" #{@d2.avatar.to_file(:large).path}`.chomp
161
- assert_equal "100x15", `identify -format "%wx%h" #{@d2.avatar.to_file(:medium).path}`.chomp
162
- assert_equal "32x32", `identify -format "%wx%h" #{@d2.avatar.to_file(:thumb).path}`.chomp
311
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp
312
+ assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp
313
+ assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp
314
+ assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
315
+ assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
163
316
 
164
317
  @dummy.avatar = "not a valid file but not nil"
165
318
  assert_equal File.basename(@file.path), @dummy.avatar_file_name
@@ -171,7 +324,7 @@ class IntegrationTest < Test::Unit::TestCase
171
324
  assert File.exists?(p)
172
325
  end
173
326
 
174
- @dummy.avatar = nil
327
+ @dummy.avatar.clear
175
328
  assert_nil @dummy.avatar_file_name
176
329
  assert @dummy.valid?
177
330
  assert @dummy.save
@@ -189,12 +342,12 @@ class IntegrationTest < Test::Unit::TestCase
189
342
 
190
343
  assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
191
344
  [:thumb, :medium, :large, :original].each do |style|
192
- assert_equal @dummy.avatar.to_file(style).path, @d2.avatar.to_file(style).path
345
+ assert_equal @dummy.avatar.path(style), @d2.avatar.path(style)
193
346
  end
194
347
 
195
- saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s).path }
348
+ saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
196
349
 
197
- @d2.avatar = nil
350
+ @d2.avatar.clear
198
351
  assert @d2.save
199
352
 
200
353
  saved_paths.each do |p|
@@ -202,74 +355,79 @@ class IntegrationTest < Test::Unit::TestCase
202
355
  end
203
356
  end
204
357
 
205
- should "know the difference between good files, bad files, not files, and nil" do
358
+ should "know the difference between good files, bad files, and not files" do
206
359
  expected = @dummy.avatar.to_file
207
360
  @dummy.avatar = "not a file"
208
361
  assert @dummy.valid?
209
- assert_equal expected.path, @dummy.avatar.to_file.path
362
+ assert_equal expected.path, @dummy.avatar.path
363
+ expected.close
210
364
 
211
365
  @dummy.avatar = @bad_file
212
366
  assert ! @dummy.valid?
213
- @dummy.avatar = nil
214
- assert @dummy.valid?
215
367
  end
216
368
 
217
- should "know the difference between good files, bad files, not files, and nil when validating" do
369
+ should "know the difference between good files, bad files, and not files when validating" do
218
370
  Dummy.validates_attachment_presence :avatar
219
371
  @d2 = Dummy.get(@dummy.id)
220
372
  @d2.avatar = @file
221
- assert @d2.valid?
373
+ assert @d2.valid?, @d2.errors.full_messages.inspect
222
374
  @d2.avatar = @bad_file
223
375
  assert ! @d2.valid?
224
- @d2.avatar = nil
225
- assert ! @d2.valid?
226
376
  end
227
377
 
228
- should "be able to reload without saving an not have the file disappear" do
378
+ should "be able to reload without saving and not have the file disappear" do
229
379
  @dummy.avatar = @file
230
- @dummy.save
231
- @dummy.avatar = nil
380
+ assert @dummy.save
381
+ @dummy.avatar.clear
232
382
  assert_nil @dummy.avatar_file_name
233
383
  @dummy.reload
234
384
  assert_equal "5k.png", @dummy.avatar_file_name
235
385
  end
236
-
386
+
237
387
  context "that is assigned its file from another Paperclip attachment" do
238
388
  setup do
239
389
  @dummy2 = Dummy.new
240
- @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"))
390
+ @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"), 'rb')
241
391
  assert @dummy2.avatar = @file2
242
392
  @dummy2.save
243
393
  end
244
-
394
+
245
395
  should "work when assigned a file" do
246
- assert_not_equal `identify -format "%wx%h" #{@dummy.avatar.to_file(:original).path}`,
247
- `identify -format "%wx%h" #{@dummy2.avatar.to_file(:original).path}`
396
+ assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
397
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
248
398
 
249
399
  assert @dummy.avatar = @dummy2.avatar
250
400
  @dummy.save
251
- assert_equal `identify -format "%wx%h" #{@dummy.avatar.to_file(:original).path}`,
252
- `identify -format "%wx%h" #{@dummy2.avatar.to_file(:original).path}`
401
+ assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
402
+ `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
253
403
  end
254
-
255
- should "work when assigned a nil file" do
256
- @dummy2.avatar = nil
257
- @dummy2.save
404
+ end
258
405
 
259
- @dummy.avatar = @dummy2.avatar
260
- @dummy.save
261
-
262
- assert !@dummy.avatar?
406
+ end
407
+
408
+ context "A model with an attachments association and a Paperclip attachment" do
409
+ setup do
410
+ Dummy.class_eval do
411
+ has n, :attachments, :model => 'Dummy'
263
412
  end
264
- end
265
413
 
414
+ @dummy = Dummy.new
415
+ @dummy.avatar = File.new(File.join(File.dirname(__FILE__),
416
+ "fixtures",
417
+ "5k.png"), 'rb')
418
+ end
419
+
420
+ should "should not error when saving" do
421
+ assert @dummy.save
422
+ end
266
423
  end
267
424
 
268
425
  if ENV['S3_TEST_BUCKET']
269
426
  def s3_files_for attachment
270
427
  [:thumb, :medium, :large, :original].inject({}) do |files, style|
271
- data = `curl '#{attachment.url(style)}' 2>/dev/null`.chomp
428
+ data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
272
429
  t = Tempfile.new("paperclip-test")
430
+ t.binmode
273
431
  t.write(data)
274
432
  t.rewind
275
433
  files[style] = t
@@ -277,6 +435,14 @@ class IntegrationTest < Test::Unit::TestCase
277
435
  end
278
436
  end
279
437
 
438
+ def s3_headers_for attachment, style
439
+ `curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head|
440
+ split_head = head.chomp.split(/\s*:\s*/, 2)
441
+ h[split_head.first.downcase] = split_head.last unless split_head.empty?
442
+ h
443
+ end
444
+ end
445
+
280
446
  context "A model with an S3 attachment" do
281
447
  setup do
282
448
  rebuild_model :styles => { :large => "300x300>",
@@ -284,14 +450,13 @@ class IntegrationTest < Test::Unit::TestCase
284
450
  :thumb => ["32x32#", :gif] },
285
451
  :storage => :s3,
286
452
  :whiny_thumbnails => true,
287
- # :s3_options => {:logger => Logger.new(StringIO.new)},
288
453
  :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")),
289
454
  :default_style => :medium,
290
455
  :bucket => ENV['S3_TEST_BUCKET'],
291
456
  :path => ":class/:attachment/:id/:style/:basename.:extension"
292
457
  @dummy = Dummy.new
293
- @file = File.new(File.join(FIXTURES_DIR, "5k.png"))
294
- @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"))
458
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
459
+ @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
295
460
 
296
461
  assert @dummy.avatar = @file
297
462
  assert @dummy.valid?
@@ -300,12 +465,17 @@ class IntegrationTest < Test::Unit::TestCase
300
465
  @files_on_s3 = s3_files_for @dummy.avatar
301
466
  end
302
467
 
468
+ should "have the same contents as the original" do
469
+ @file.rewind
470
+ assert_equal @file.read, @files_on_s3[:original].read
471
+ end
472
+
303
473
  should "write and delete its files" do
304
474
  [["434x66", :original],
305
475
  ["300x46", :large],
306
476
  ["100x15", :medium],
307
477
  ["32x32", :thumb]].each do |geo, style|
308
- cmd = %Q[identify -format "%wx%h" #{@files_on_s3[style].path}]
478
+ cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"]
309
479
  assert_equal geo, `#{cmd}`.chomp, cmd
310
480
  end
311
481
 
@@ -315,7 +485,7 @@ class IntegrationTest < Test::Unit::TestCase
315
485
  ["300x46", :large],
316
486
  ["100x15", :medium],
317
487
  ["32x32", :thumb]].each do |geo, style|
318
- cmd = %Q[identify -format "%wx%h" #{@d2_files[style].path}]
488
+ cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"]
319
489
  assert_equal geo, `#{cmd}`.chomp, cmd
320
490
  end
321
491
 
@@ -324,19 +494,17 @@ class IntegrationTest < Test::Unit::TestCase
324
494
  assert @dummy.valid?
325
495
  assert @dummy.save
326
496
 
327
- saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
328
-
329
- saved_keys.each do |key|
330
- assert key.exists?
497
+ [:thumb, :medium, :large, :original].each do |style|
498
+ assert @dummy.avatar.exists?(style)
331
499
  end
332
500
 
333
- @dummy.avatar = nil
501
+ @dummy.avatar.clear
334
502
  assert_nil @dummy.avatar_file_name
335
503
  assert @dummy.valid?
336
504
  assert @dummy.save
337
505
 
338
- saved_keys.each do |key|
339
- assert ! key.exists?
506
+ [:thumb, :medium, :large, :original].each do |style|
507
+ assert ! @dummy.avatar.exists?(style)
340
508
  end
341
509
 
342
510
  @d2 = Dummy.get(@dummy.id)
@@ -348,16 +516,16 @@ class IntegrationTest < Test::Unit::TestCase
348
516
 
349
517
  assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
350
518
  [:thumb, :medium, :large, :original].each do |style|
351
- assert_equal @dummy.avatar.to_file(style).to_s, @d2.avatar.to_file(style).to_s
519
+ assert_equal @dummy.avatar.to_file(style).read, @d2.avatar.to_file(style).read
352
520
  end
353
521
 
354
522
  saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
355
523
 
356
- @d2.avatar = nil
524
+ @d2.avatar.clear
357
525
  assert @d2.save
358
526
 
359
- saved_keys.each do |key|
360
- assert ! key.exists?
527
+ [:thumb, :medium, :large, :original].each do |style|
528
+ assert ! @dummy.avatar.exists?(style)
361
529
  end
362
530
  end
363
531
 
@@ -365,7 +533,7 @@ class IntegrationTest < Test::Unit::TestCase
365
533
  expected = @dummy.avatar.to_file
366
534
  @dummy.avatar = "not a file"
367
535
  assert @dummy.valid?
368
- assert_equal expected.full_name, @dummy.avatar.to_file.full_name
536
+ assert_equal expected.read, @dummy.avatar.to_file.read
369
537
 
370
538
  @dummy.avatar = @bad_file
371
539
  assert ! @dummy.valid?
@@ -390,6 +558,11 @@ class IntegrationTest < Test::Unit::TestCase
390
558
  @dummy.reload
391
559
  assert_equal "5k.png", @dummy.avatar_file_name
392
560
  end
561
+
562
+ should "have the right content type" do
563
+ headers = s3_headers_for(@dummy.avatar, :original)
564
+ assert_equal 'image/png', headers['content-type']
565
+ end
393
566
  end
394
567
  end
395
568
  end