path-paperclip 2.3.3

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 (57) hide show
  1. data/LICENSE +26 -0
  2. data/README.rdoc +198 -0
  3. data/Rakefile +76 -0
  4. data/generators/paperclip/USAGE +5 -0
  5. data/generators/paperclip/paperclip_generator.rb +27 -0
  6. data/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  7. data/init.rb +1 -0
  8. data/lib/generators/paperclip/USAGE +8 -0
  9. data/lib/generators/paperclip/paperclip_generator.rb +31 -0
  10. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
  11. data/lib/paperclip.rb +440 -0
  12. data/lib/paperclip/attachment.rb +401 -0
  13. data/lib/paperclip/callback_compatability.rb +61 -0
  14. data/lib/paperclip/geometry.rb +150 -0
  15. data/lib/paperclip/interpolations.rb +113 -0
  16. data/lib/paperclip/iostream.rb +59 -0
  17. data/lib/paperclip/matchers.rb +33 -0
  18. data/lib/paperclip/matchers/have_attached_file_matcher.rb +57 -0
  19. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +75 -0
  20. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +54 -0
  21. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +95 -0
  22. data/lib/paperclip/processor.rb +49 -0
  23. data/lib/paperclip/railtie.rb +20 -0
  24. data/lib/paperclip/storage.rb +258 -0
  25. data/lib/paperclip/style.rb +90 -0
  26. data/lib/paperclip/thumbnail.rb +78 -0
  27. data/lib/paperclip/upfile.rb +52 -0
  28. data/lib/paperclip/version.rb +3 -0
  29. data/lib/tasks/paperclip.rake +95 -0
  30. data/rails/init.rb +2 -0
  31. data/shoulda_macros/paperclip.rb +119 -0
  32. data/test/attachment_test.rb +796 -0
  33. data/test/database.yml +4 -0
  34. data/test/fixtures/12k.png +0 -0
  35. data/test/fixtures/50x50.png +0 -0
  36. data/test/fixtures/5k.png +0 -0
  37. data/test/fixtures/bad.png +1 -0
  38. data/test/fixtures/ceedub.gif +0 -0
  39. data/test/fixtures/s3.yml +8 -0
  40. data/test/fixtures/text.txt +1 -0
  41. data/test/fixtures/twopage.pdf +0 -0
  42. data/test/geometry_test.rb +177 -0
  43. data/test/helper.rb +152 -0
  44. data/test/integration_test.rb +610 -0
  45. data/test/interpolations_test.rb +135 -0
  46. data/test/iostream_test.rb +78 -0
  47. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  48. data/test/matchers/validate_attachment_content_type_matcher_test.rb +54 -0
  49. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  50. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  51. data/test/paperclip_test.rb +389 -0
  52. data/test/processor_test.rb +10 -0
  53. data/test/storage_test.rb +407 -0
  54. data/test/style_test.rb +141 -0
  55. data/test/thumbnail_test.rb +227 -0
  56. data/test/upfile_test.rb +36 -0
  57. metadata +221 -0
@@ -0,0 +1,135 @@
1
+ require 'test/helper'
2
+
3
+ class InterpolationsTest < Test::Unit::TestCase
4
+ should "return all methods but the infrastructure when sent #all" do
5
+ methods = Paperclip::Interpolations.all
6
+ assert ! methods.include?(:[])
7
+ assert ! methods.include?(:[]=)
8
+ assert ! methods.include?(:all)
9
+ methods.each do |m|
10
+ assert Paperclip::Interpolations.respond_to?(m)
11
+ end
12
+ end
13
+
14
+ should "return the Rails.root" do
15
+ assert_equal Rails.root, Paperclip::Interpolations.rails_root(:attachment, :style)
16
+ end
17
+
18
+ should "return the Rails.env" do
19
+ assert_equal Rails.env, Paperclip::Interpolations.rails_env(:attachment, :style)
20
+ end
21
+
22
+ should "return the class of the Interpolations module when called with no params" do
23
+ assert_equal Module, Paperclip::Interpolations.class
24
+ end
25
+
26
+ should "return the class of the instance" do
27
+ attachment = mock
28
+ attachment.expects(:instance).returns(attachment)
29
+ attachment.expects(:class).returns("Thing")
30
+ assert_equal "things", Paperclip::Interpolations.class(attachment, :style)
31
+ end
32
+
33
+ should "return the basename of the file" do
34
+ attachment = mock
35
+ attachment.expects(:original_filename).returns("one.jpg").times(2)
36
+ assert_equal "one", Paperclip::Interpolations.basename(attachment, :style)
37
+ end
38
+
39
+ should "return the extension of the file as the format if defined in the style" do
40
+ attachment = mock
41
+ attachment.expects(:styles).returns({:style => {:format => "png"}})
42
+ attachment.expects(:original_filename).never
43
+ assert_equal "png", Paperclip::Interpolations.extension(attachment, :style)
44
+ end
45
+
46
+ should "return the extension of the file if content_type is blank" do
47
+ attachment = mock
48
+ attachment.expects(:styles).returns({})
49
+ attachment.expects(:content_type).returns(nil)
50
+ attachment.expects(:original_filename).returns("one.jpg")
51
+ assert_equal "jpg", Paperclip::Interpolations.extension(attachment, :style)
52
+ end
53
+
54
+ should "return the default file extension for the content_type" do
55
+ attachment = mock
56
+ attachment.expects(:styles).returns({})
57
+ attachment.expects(:content_type).returns("image/jpeg")
58
+ attachment.expects(:original_filename).never
59
+ assert_equal "jpeg", Paperclip::Interpolations.extension(attachment, :style)
60
+ end
61
+
62
+ should "return the id of the attachment" do
63
+ attachment = mock
64
+ attachment.expects(:id).returns(23)
65
+ attachment.expects(:instance).returns(attachment)
66
+ assert_equal 23, Paperclip::Interpolations.id(attachment, :style)
67
+ end
68
+
69
+ should "return the partitioned id of the attachment" do
70
+ attachment = mock
71
+ attachment.expects(:id).returns(23)
72
+ attachment.expects(:instance).returns(attachment)
73
+ assert_equal "000/000/023", Paperclip::Interpolations.id_partition(attachment, :style)
74
+ end
75
+
76
+ should "return the name of the attachment" do
77
+ attachment = mock
78
+ attachment.expects(:name).returns("file")
79
+ assert_equal "files", Paperclip::Interpolations.attachment(attachment, :style)
80
+ end
81
+
82
+ should "return the style" do
83
+ assert_equal :style, Paperclip::Interpolations.style(:attachment, :style)
84
+ end
85
+
86
+ should "return the default style" do
87
+ attachment = mock
88
+ attachment.expects(:default_style).returns(:default_style)
89
+ assert_equal :default_style, Paperclip::Interpolations.style(attachment, nil)
90
+ end
91
+
92
+ should "reinterpolate :url" do
93
+ attachment = mock
94
+ attachment.expects(:options).returns({:url => ":id"})
95
+ attachment.expects(:url).with(:style, false).returns("1234")
96
+ assert_equal "1234", Paperclip::Interpolations.url(attachment, :style)
97
+ end
98
+
99
+ should "raise if infinite loop detected reinterpolating :url" do
100
+ attachment = mock
101
+ attachment.expects(:options).returns({:url => ":url"})
102
+ assert_raises(Paperclip::InfiniteInterpolationError){ Paperclip::Interpolations.url(attachment, :style) }
103
+ end
104
+
105
+ should "return the filename as basename.extension" do
106
+ attachment = mock
107
+ attachment.expects(:styles).returns({})
108
+ attachment.expects(:content_type).returns(nil)
109
+ attachment.expects(:original_filename).returns("one.jpg").times(3)
110
+ assert_equal "one.jpg", Paperclip::Interpolations.filename(attachment, :style)
111
+ end
112
+
113
+ should "return the filename as basename.extension when format supplied" do
114
+ attachment = mock
115
+ attachment.expects(:styles).returns({:style => {:format => :png}})
116
+ attachment.expects(:original_filename).returns("one.jpg").times(2)
117
+ assert_equal "one.png", Paperclip::Interpolations.filename(attachment, :style)
118
+ end
119
+
120
+ should "return the timestamp" do
121
+ now = Time.now
122
+ attachment = mock
123
+ attachment.expects(:instance_read).with(:updated_at).returns(now)
124
+ assert_equal now.to_s, Paperclip::Interpolations.timestamp(attachment, :style)
125
+ end
126
+
127
+ should "call all expected interpolations with the given arguments" do
128
+ Paperclip::Interpolations.expects(:id).with(:attachment, :style).returns(1234)
129
+ Paperclip::Interpolations.expects(:attachment).with(:attachment, :style).returns("attachments")
130
+ Paperclip::Interpolations.expects(:notreal).never
131
+ Paperclip::Interpolations.expects(:digest).with(:attachment, :style).returns("DIGEST")
132
+ value = Paperclip::Interpolations.interpolate(":notreal/:id/:attachment/:digest", :attachment, :style)
133
+ assert_equal ":notreal/1234/attachments/DIGEST", value
134
+ end
135
+ end
@@ -0,0 +1,78 @@
1
+ require 'test/helper'
2
+
3
+ class IOStreamTest < Test::Unit::TestCase
4
+ context "IOStream" do
5
+ should "be included in IO, File, Tempfile, and StringIO" do
6
+ [IO, File, Tempfile, StringIO].each do |klass|
7
+ assert klass.included_modules.include?(IOStream), "Not in #{klass}"
8
+ end
9
+ end
10
+ end
11
+
12
+ context "A file" do
13
+ setup do
14
+ @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
15
+ end
16
+
17
+ teardown { @file.close }
18
+
19
+ context "that is sent #stream_to" do
20
+
21
+ context "and given a String" do
22
+ setup do
23
+ FileUtils.mkdir_p(File.join(ROOT, 'tmp'))
24
+ assert @result = @file.stream_to(File.join(ROOT, 'tmp', 'iostream.string.test'))
25
+ end
26
+
27
+ should "return a File" do
28
+ assert @result.is_a?(File)
29
+ end
30
+
31
+ should "contain the same data as the original file" do
32
+ @file.rewind; @result.rewind
33
+ assert_equal @file.read, @result.read
34
+ end
35
+ end
36
+
37
+ context "and given a Tempfile" do
38
+ setup do
39
+ tempfile = Tempfile.new('iostream.test')
40
+ tempfile.binmode
41
+ assert @result = @file.stream_to(tempfile)
42
+ end
43
+
44
+ should "return a Tempfile" do
45
+ assert @result.is_a?(Tempfile)
46
+ end
47
+
48
+ should "contain the same data as the original file" do
49
+ @file.rewind; @result.rewind
50
+ assert_equal @file.read, @result.read
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ context "that is sent #to_tempfile" do
57
+ setup do
58
+ assert @tempfile = @file.to_tempfile
59
+ end
60
+
61
+ should "convert it to a Paperclip Tempfile" do
62
+ assert @tempfile.is_a?(Paperclip::Tempfile)
63
+ end
64
+
65
+ should "have the name be based on the original_filename" do
66
+ name = File.basename(@file.path)
67
+ extension = File.extname(name)
68
+ basename = File.basename(name, extension)
69
+ assert_match %r[^stream.*?#{Regexp.quote(extension)}], File.basename(@tempfile.path)
70
+ end
71
+
72
+ should "have the Tempfile contain the same data as the file" do
73
+ @file.rewind; @tempfile.rewind
74
+ assert_equal @file.read, @tempfile.read
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,24 @@
1
+ require 'test/helper'
2
+
3
+ class HaveAttachedFileMatcherTest < Test::Unit::TestCase
4
+ context "have_attached_file" do
5
+ setup do
6
+ @dummy_class = reset_class "Dummy"
7
+ reset_table "dummies"
8
+ @matcher = self.class.have_attached_file(:avatar)
9
+ end
10
+
11
+ context "given a class with no attachment" do
12
+ should_reject_dummy_class
13
+ end
14
+
15
+ context "given a class with an attachment" do
16
+ setup do
17
+ modify_table("dummies"){|d| d.string :avatar_file_name }
18
+ @dummy_class.has_attached_file :avatar
19
+ end
20
+
21
+ should_accept_dummy_class
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ require 'test/helper'
2
+
3
+ class ValidateAttachmentContentTypeMatcherTest < Test::Unit::TestCase
4
+ context "validate_attachment_content_type" do
5
+ setup do
6
+ reset_table("dummies") do |d|
7
+ d.string :title
8
+ d.string :avatar_file_name
9
+ d.string :avatar_content_type
10
+ end
11
+ @dummy_class = reset_class "Dummy"
12
+ @dummy_class.has_attached_file :avatar
13
+ @matcher = self.class.validate_attachment_content_type(:avatar).
14
+ allowing(%w(image/png image/jpeg)).
15
+ rejecting(%w(audio/mp3 application/octet-stream))
16
+ Paperclip.stubs(:content_type_for_file).returns(nil)
17
+ end
18
+
19
+ context "given a class with no validation" do
20
+ should_reject_dummy_class
21
+ end
22
+
23
+ context "given a class with a validation that doesn't match" do
24
+ setup do
25
+ @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{audio/.*}
26
+ end
27
+
28
+ should_reject_dummy_class
29
+ end
30
+
31
+ context "given a class with a matching validation" do
32
+ setup do
33
+ @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
34
+ end
35
+
36
+ should_accept_dummy_class
37
+ end
38
+
39
+ context "given a class with other validations but matching types" do
40
+ setup do
41
+ @dummy_class.validates_presence_of :title
42
+ @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
43
+ end
44
+
45
+ should_accept_dummy_class
46
+ end
47
+
48
+ should "accept a class with a validation" do
49
+ Paperclip.stubs(:content_type_for_file).returns(nil)
50
+ @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
51
+ assert_accepts @matcher, @dummy_class
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,26 @@
1
+ require 'test/helper'
2
+
3
+ class ValidateAttachmentPresenceMatcherTest < Test::Unit::TestCase
4
+ context "validate_attachment_presence" do
5
+ setup do
6
+ reset_table("dummies") do |d|
7
+ d.string :avatar_file_name
8
+ end
9
+ @dummy_class = reset_class "Dummy"
10
+ @dummy_class.has_attached_file :avatar
11
+ @matcher = self.class.validate_attachment_presence(:avatar)
12
+ end
13
+
14
+ context "given a class with no validation" do
15
+ should_reject_dummy_class
16
+ end
17
+
18
+ context "given a class with a matching validation" do
19
+ setup do
20
+ @dummy_class.validates_attachment_presence :avatar
21
+ end
22
+
23
+ should_accept_dummy_class
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,51 @@
1
+ require 'test/helper'
2
+
3
+ class ValidateAttachmentSizeMatcherTest < Test::Unit::TestCase
4
+ context "validate_attachment_size" do
5
+ setup do
6
+ reset_table("dummies") do |d|
7
+ d.string :avatar_file_name
8
+ d.integer :avatar_file_size
9
+ end
10
+ @dummy_class = reset_class "Dummy"
11
+ @dummy_class.has_attached_file :avatar
12
+ end
13
+
14
+ context "of limited size" do
15
+ setup{ @matcher = self.class.validate_attachment_size(:avatar).in(256..1024) }
16
+
17
+ context "given a class with no validation" do
18
+ should_reject_dummy_class
19
+ end
20
+
21
+ context "given a class with a validation that's too high" do
22
+ setup { @dummy_class.validates_attachment_size :avatar, :in => 256..2048 }
23
+ should_reject_dummy_class
24
+ end
25
+
26
+ context "given a class with a validation that's too low" do
27
+ setup { @dummy_class.validates_attachment_size :avatar, :in => 0..1024 }
28
+ should_reject_dummy_class
29
+ end
30
+
31
+ context "given a class with a validation that matches" do
32
+ setup { @dummy_class.validates_attachment_size :avatar, :in => 256..1024 }
33
+ should_accept_dummy_class
34
+ end
35
+ end
36
+
37
+ context "validates_attachment_size with infinite range" do
38
+ setup{ @matcher = self.class.validate_attachment_size(:avatar) }
39
+
40
+ context "given a class with an upper limit" do
41
+ setup { @dummy_class.validates_attachment_size :avatar, :less_than => 1 }
42
+ should_accept_dummy_class
43
+ end
44
+
45
+ context "given a class with no upper limit" do
46
+ setup { @dummy_class.validates_attachment_size :avatar, :greater_than => 1 }
47
+ should_accept_dummy_class
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,389 @@
1
+ require 'test/helper'
2
+
3
+ class PaperclipTest < Test::Unit::TestCase
4
+ [:image_magick_path, :command_path].each do |path|
5
+ context "Calling Paperclip.run with #{path} specified" do
6
+ setup do
7
+ Paperclip.options[:image_magick_path] = nil
8
+ Paperclip.options[:command_path] = nil
9
+ Paperclip.options[path] = "/usr/bin"
10
+ end
11
+
12
+ should "return the expected path for path_for_command" do
13
+ assert_equal "/usr/bin/convert", Paperclip.path_for_command("convert")
14
+ end
15
+
16
+ should "execute the right command" do
17
+ Paperclip.expects(:path_for_command).with("convert").returns("/usr/bin/convert")
18
+ Paperclip.expects(:bit_bucket).returns("/dev/null")
19
+ Paperclip.expects(:"`").with("/usr/bin/convert 'one.jpg' 'two.jpg' 2>/dev/null")
20
+ Paperclip.run("convert", "one.jpg", "two.jpg")
21
+ end
22
+ end
23
+ end
24
+
25
+ context "Calling Paperclip.run with no path specified" do
26
+ setup do
27
+ Paperclip.options[:image_magick_path] = nil
28
+ Paperclip.options[:command_path] = nil
29
+ end
30
+
31
+ should "return the expected path fro path_for_command" do
32
+ assert_equal "convert", Paperclip.path_for_command("convert")
33
+ end
34
+
35
+ should "execute the right command" do
36
+ Paperclip.expects(:path_for_command).with("convert").returns("convert")
37
+ Paperclip.expects(:bit_bucket).returns("/dev/null")
38
+ Paperclip.expects(:"`").with("convert 'one.jpg' 'two.jpg' 2>/dev/null")
39
+ Paperclip.run("convert", "one.jpg", "two.jpg")
40
+ end
41
+ end
42
+
43
+ context "Calling Paperclip.run and logging" do
44
+ should "log the command when :log_command is true" do
45
+ Paperclip.options[:image_magick_path] = nil
46
+ Paperclip.options[:command_path] = nil
47
+ Paperclip.stubs(:bit_bucket).returns("/dev/null")
48
+ Paperclip.expects(:log).with("this 'is the command' 2>/dev/null")
49
+ Paperclip.expects(:"`").with("this 'is the command' 2>/dev/null")
50
+ Paperclip.options[:log_command] = true
51
+ Paperclip.run("this","is the command")
52
+ end
53
+
54
+ should "not log the command when :log_command is false" do
55
+ Paperclip.options[:image_magick_path] = nil
56
+ Paperclip.options[:command_path] = nil
57
+ Paperclip.stubs(:bit_bucket).returns("/dev/null")
58
+ Paperclip.expects(:log).with("this 'is the command' 2>/dev/null").never
59
+ Paperclip.expects(:"`").with("this 'is the command' 2>/dev/null")
60
+ Paperclip.options[:log_command] = false
61
+ Paperclip.run("this","is the command")
62
+ end
63
+ end
64
+
65
+ context "Calling Paperclip.run when the command is not found" do
66
+ should "tell you the command isn't there if the shell returns 127" do
67
+ begin
68
+ assert_raises(Paperclip::CommandNotFoundError) do
69
+ `ruby -e 'exit 127'` # Stub $?.exitstatus to be 127, i.e. Command Not Found.
70
+ Paperclip.stubs(:"`").returns("")
71
+ Paperclip.run("command")
72
+ end
73
+ ensure
74
+ `ruby -e 'exit 0'` # Unstub $?.exitstatus
75
+ end
76
+ end
77
+ should "tell you the command isn't there if an ENOENT is raised" do
78
+ assert_raises(Paperclip::CommandNotFoundError) do
79
+ Paperclip.stubs(:"`").raises(Errno::ENOENT)
80
+ Paperclip.run("command")
81
+ end
82
+ end
83
+ end
84
+
85
+ context "Paperclip.content_type_for_file" do
86
+ context "with a string argument" do
87
+ should "return image/gif for a GIF image" do
88
+ file = File.join(File.dirname(__FILE__), "fixtures", "ceedub.gif")
89
+ assert_equal "image/gif", Paperclip.content_type_for_file(file)
90
+ end
91
+
92
+ should "return text/plain for a YAML file" do
93
+ file = File.join(File.dirname(__FILE__), "fixtures", "s3.yml")
94
+ assert_equal "text/plain", Paperclip.content_type_for_file(file)
95
+ end
96
+
97
+ should "return application/pdf for a PDF file" do
98
+ file = File.join(File.dirname(__FILE__), "fixtures", "twopage.pdf")
99
+ assert_equal "application/pdf", Paperclip.content_type_for_file(file)
100
+ end
101
+
102
+ should "return application/octet-stream for a bad PNG file" do
103
+ file = File.join(File.dirname(__FILE__), "fixtures", "bad.png")
104
+ assert_equal "application/octet-stream", Paperclip.content_type_for_file(file)
105
+ end
106
+ end
107
+
108
+ context "with a file argument" do
109
+ should "return image/gif for a GIF image" do
110
+ file = File.new(File.join(File.dirname(__FILE__), "fixtures", "ceedub.gif"), "rb")
111
+ assert_equal "image/gif", Paperclip.content_type_for_file(file)
112
+ end
113
+
114
+ should "return text/plain for a YAML file" do
115
+ file = File.new(File.join(File.dirname(__FILE__), "fixtures", "s3.yml"), "rb")
116
+ assert_equal "text/plain", Paperclip.content_type_for_file(file)
117
+ end
118
+
119
+ should "return application/pdf for a PDF file" do
120
+ file = File.new(File.join(File.dirname(__FILE__), "fixtures", "twopage.pdf"), "rb")
121
+ assert_equal "application/pdf", Paperclip.content_type_for_file(file)
122
+ end
123
+
124
+ should "return application/octet-stream for a bad PNG file" do
125
+ file = File.new(File.join(File.dirname(__FILE__), "fixtures", "bad.png"), "rb")
126
+ assert_equal "application/octet-stream", Paperclip.content_type_for_file(file)
127
+ end
128
+ end
129
+ end
130
+
131
+ should "prevent dangerous characters in the command via quoting" do
132
+ Paperclip.options[:image_magick_path] = nil
133
+ Paperclip.options[:command_path] = nil
134
+ Paperclip.options[:log_command] = false
135
+ Paperclip.options[:swallow_stderr] = false
136
+ Paperclip.expects(:"`").with(%q[this 'is' 'jack'\''s' '`command`' 'line!'])
137
+ Paperclip.run("this", "is", "jack's", "`command`", "line!")
138
+ end
139
+
140
+ context "Paperclip.extension_for_content_type" do
141
+ should "return nil for nil" do
142
+ assert_nil Paperclip.extension_for_content_type(nil)
143
+ end
144
+
145
+ Hash[
146
+ nil, nil,
147
+ "text/plain", "txt",
148
+ "text/html", "html",
149
+ "text/csv", "csv",
150
+ "text/xml", "xml",
151
+ "text/css", "css",
152
+ "image/gif", "gif",
153
+ "image/jpeg", "jpeg",
154
+ "image/png", "png",
155
+ "image/tiff", "tiff",
156
+ "application/pdf", "pdf",
157
+ "application/octet-stream", "bin",
158
+ ].each do |content_type, extension|
159
+ should "return #{extension} for #{content_type}" do
160
+ assert_equal extension, Paperclip.extension_for_content_type(content_type)
161
+ end
162
+ end
163
+ end
164
+
165
+ context "Paperclip.bit_bucket" do
166
+ context "on systems without /dev/null" do
167
+ setup do
168
+ File.expects(:exists?).with("/dev/null").returns(false)
169
+ end
170
+
171
+ should "return 'NUL'" do
172
+ assert_equal "NUL", Paperclip.bit_bucket
173
+ end
174
+ end
175
+
176
+ context "on systems with /dev/null" do
177
+ setup do
178
+ File.expects(:exists?).with("/dev/null").returns(true)
179
+ end
180
+
181
+ should "return '/dev/null'" do
182
+ assert_equal "/dev/null", Paperclip.bit_bucket
183
+ end
184
+ end
185
+ end
186
+
187
+ should "raise when sent #processor and the name of a class that exists but isn't a subclass of Processor" do
188
+ assert_raises(Paperclip::PaperclipError){ Paperclip.processor(:attachment) }
189
+ end
190
+
191
+ should "raise when sent #processor and the name of a class that doesn't exist" do
192
+ assert_raises(NameError){ Paperclip.processor(:boogey_man) }
193
+ end
194
+
195
+ should "return a class when sent #processor and the name of a class under Paperclip" do
196
+ assert_equal ::Paperclip::Thumbnail, Paperclip.processor(:thumbnail)
197
+ end
198
+
199
+ context "An ActiveRecord model with an 'avatar' attachment" do
200
+ setup do
201
+ rebuild_model :path => "tmp/:class/omg/:style.:extension"
202
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
203
+ end
204
+
205
+ teardown { @file.close }
206
+
207
+ should "not error when trying to also create a 'blah' attachment" do
208
+ assert_nothing_raised do
209
+ Dummy.class_eval do
210
+ has_attached_file :blah
211
+ end
212
+ end
213
+ end
214
+
215
+ context "that is attr_protected" do
216
+ setup do
217
+ Dummy.class_eval do
218
+ attr_protected :avatar
219
+ end
220
+ @dummy = Dummy.new
221
+ end
222
+
223
+ should "not assign the avatar on mass-set" do
224
+ @dummy.attributes = { :other => "I'm set!",
225
+ :avatar => @file }
226
+
227
+ assert_equal "I'm set!", @dummy.other
228
+ assert ! @dummy.avatar?
229
+ end
230
+
231
+ should "still allow assigment on normal set" do
232
+ @dummy.other = "I'm set!"
233
+ @dummy.avatar = @file
234
+
235
+ assert_equal "I'm set!", @dummy.other
236
+ assert @dummy.avatar?
237
+ end
238
+ end
239
+
240
+ context "with a subclass" do
241
+ setup do
242
+ class ::SubDummy < Dummy; end
243
+ end
244
+
245
+ should "be able to use the attachment from the subclass" do
246
+ assert_nothing_raised do
247
+ @subdummy = SubDummy.create(:avatar => @file)
248
+ end
249
+ end
250
+
251
+ should "be able to see the attachment definition from the subclass's class" do
252
+ assert_equal "tmp/:class/omg/:style.:extension",
253
+ SubDummy.attachment_definitions[:avatar][:path]
254
+ end
255
+
256
+ teardown do
257
+ Object.send(:remove_const, "SubDummy") rescue nil
258
+ end
259
+ end
260
+
261
+ should "have an #avatar method" do
262
+ assert Dummy.new.respond_to?(:avatar)
263
+ end
264
+
265
+ should "have an #avatar= method" do
266
+ assert Dummy.new.respond_to?(:avatar=)
267
+ end
268
+
269
+ context "that is valid" do
270
+ setup do
271
+ @dummy = Dummy.new
272
+ @dummy.avatar = @file
273
+ end
274
+
275
+ should "be valid" do
276
+ assert @dummy.valid?
277
+ end
278
+ end
279
+
280
+ context "a validation with an if guard clause" do
281
+ setup do
282
+ Dummy.send(:"validates_attachment_presence", :avatar, :if => lambda{|i| i.foo })
283
+ @dummy = Dummy.new
284
+ @dummy.stubs(:avatar_file_name).returns(nil)
285
+ end
286
+
287
+ should "attempt validation if the guard returns true" do
288
+ @dummy.expects(:foo).returns(true)
289
+ assert ! @dummy.valid?
290
+ end
291
+
292
+ should "not attempt validation if the guard returns false" do
293
+ @dummy.expects(:foo).returns(false)
294
+ assert @dummy.valid?
295
+ end
296
+ end
297
+
298
+ context "a validation with an unless guard clause" do
299
+ setup do
300
+ Dummy.send(:"validates_attachment_presence", :avatar, :unless => lambda{|i| i.foo })
301
+ @dummy = Dummy.new
302
+ @dummy.stubs(:avatar_file_name).returns(nil)
303
+ end
304
+
305
+ should "attempt validation if the guard returns true" do
306
+ @dummy.expects(:foo).returns(false)
307
+ assert ! @dummy.valid?
308
+ end
309
+
310
+ should "not attempt validation if the guard returns false" do
311
+ @dummy.expects(:foo).returns(true)
312
+ assert @dummy.valid?
313
+ end
314
+ end
315
+
316
+ def self.should_validate validation, options, valid_file, invalid_file
317
+ context "with #{validation} validation and #{options.inspect} options" do
318
+ setup do
319
+ rebuild_class
320
+ Dummy.send(:"validates_attachment_#{validation}", :avatar, options)
321
+ @dummy = Dummy.new
322
+ end
323
+ context "and assigning nil" do
324
+ setup do
325
+ @dummy.avatar = nil
326
+ @dummy.valid?
327
+ end
328
+ if validation == :presence
329
+ should "have an error on the attachment" do
330
+ assert @dummy.errors[:avatar_file_name]
331
+ end
332
+ else
333
+ should "not have an error on the attachment" do
334
+ assert @dummy.errors.blank?, @dummy.errors.full_messages.join(", ")
335
+ end
336
+ end
337
+ end
338
+ context "and assigned a valid file" do
339
+ setup do
340
+ @dummy.avatar = valid_file
341
+ @dummy.valid?
342
+ end
343
+ should "not have an error when assigned a valid file" do
344
+ assert_equal 0, @dummy.errors.length, @dummy.errors.full_messages.join(", ")
345
+ end
346
+ end
347
+ context "and assigned an invalid file" do
348
+ setup do
349
+ @dummy.avatar = invalid_file
350
+ @dummy.valid?
351
+ end
352
+ should "have an error when assigned a valid file" do
353
+ assert @dummy.errors.length > 0
354
+ end
355
+ end
356
+ end
357
+ end
358
+
359
+ [[:presence, {}, "5k.png", nil],
360
+ [:size, {:in => 1..10240}, "5k.png", "12k.png"],
361
+ [:size, {:less_than => 10240}, "5k.png", "12k.png"],
362
+ [:size, {:greater_than => 8096}, "12k.png", "5k.png"],
363
+ [:content_type, {:content_type => "image/png"}, "5k.png", "text.txt"],
364
+ [:content_type, {:content_type => "text/plain"}, "text.txt", "5k.png"],
365
+ [:content_type, {:content_type => %r{image/.*}}, "5k.png", "text.txt"]].each do |args|
366
+ validation, options, valid_file, invalid_file = args
367
+ valid_file &&= File.open(File.join(FIXTURES_DIR, valid_file), "rb")
368
+ invalid_file &&= File.open(File.join(FIXTURES_DIR, invalid_file), "rb")
369
+
370
+ should_validate validation, options, valid_file, invalid_file
371
+ end
372
+
373
+ context "with size validation and less_than 10240 option" do
374
+ context "and assigned an invalid file" do
375
+ setup do
376
+ Dummy.send(:"validates_attachment_size", :avatar, :less_than => 10240)
377
+ @dummy = Dummy.new
378
+ @dummy.avatar &&= File.open(File.join(FIXTURES_DIR, "12k.png"), "rb")
379
+ @dummy.valid?
380
+ end
381
+
382
+ should "have a file size min/max error message" do
383
+ assert [@dummy.errors[:avatar_file_size]].flatten.any?{|error| error =~ %r/between 0 and 10240 bytes/ }
384
+ end
385
+ end
386
+ end
387
+
388
+ end
389
+ end