davidray-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 (56) hide show
  1. data/LICENSE +26 -0
  2. data/README.rdoc +179 -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 +393 -0
  12. data/lib/paperclip/attachment.rb +326 -0
  13. data/lib/paperclip/callback_compatability.rb +61 -0
  14. data/lib/paperclip/geometry.rb +115 -0
  15. data/lib/paperclip/interpolations.rb +108 -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 +74 -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 +24 -0
  24. data/lib/paperclip/storage.rb +247 -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 +79 -0
  30. data/rails/init.rb +2 -0
  31. data/shoulda_macros/paperclip.rb +119 -0
  32. data/test/attachment_test.rb +758 -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/s3.yml +8 -0
  39. data/test/fixtures/text.txt +0 -0
  40. data/test/fixtures/twopage.pdf +0 -0
  41. data/test/geometry_test.rb +177 -0
  42. data/test/helper.rb +148 -0
  43. data/test/integration_test.rb +483 -0
  44. data/test/interpolations_test.rb +124 -0
  45. data/test/iostream_test.rb +78 -0
  46. data/test/matchers/have_attached_file_matcher_test.rb +24 -0
  47. data/test/matchers/validate_attachment_content_type_matcher_test.rb +37 -0
  48. data/test/matchers/validate_attachment_presence_matcher_test.rb +26 -0
  49. data/test/matchers/validate_attachment_size_matcher_test.rb +51 -0
  50. data/test/paperclip_test.rb +317 -0
  51. data/test/processor_test.rb +10 -0
  52. data/test/storage_test.rb +343 -0
  53. data/test/style_test.rb +141 -0
  54. data/test/thumbnail_test.rb +227 -0
  55. data/test/upfile_test.rb +36 -0
  56. metadata +205 -0
@@ -0,0 +1,124 @@
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" do
40
+ attachment = mock
41
+ attachment.expects(:original_filename).returns("one.jpg")
42
+ attachment.expects(:styles).returns({})
43
+ assert_equal "jpg", Paperclip::Interpolations.extension(attachment, :style)
44
+ end
45
+
46
+ should "return the extension of the file as the format if defined in the style" do
47
+ attachment = mock
48
+ attachment.expects(:original_filename).never
49
+ attachment.expects(:styles).returns({:style => {:format => "png"}})
50
+ assert_equal "png", Paperclip::Interpolations.extension(attachment, :style)
51
+ end
52
+
53
+ should "return the id of the attachment" do
54
+ attachment = mock
55
+ attachment.expects(:id).returns(23)
56
+ attachment.expects(:instance).returns(attachment)
57
+ assert_equal 23, Paperclip::Interpolations.id(attachment, :style)
58
+ end
59
+
60
+ should "return the partitioned id of the attachment" do
61
+ attachment = mock
62
+ attachment.expects(:id).returns(23)
63
+ attachment.expects(:instance).returns(attachment)
64
+ assert_equal "000/000/023", Paperclip::Interpolations.id_partition(attachment, :style)
65
+ end
66
+
67
+ should "return the name of the attachment" do
68
+ attachment = mock
69
+ attachment.expects(:name).returns("file")
70
+ assert_equal "files", Paperclip::Interpolations.attachment(attachment, :style)
71
+ end
72
+
73
+ should "return the style" do
74
+ assert_equal :style, Paperclip::Interpolations.style(:attachment, :style)
75
+ end
76
+
77
+ should "return the default style" do
78
+ attachment = mock
79
+ attachment.expects(:default_style).returns(:default_style)
80
+ assert_equal :default_style, Paperclip::Interpolations.style(attachment, nil)
81
+ end
82
+
83
+ should "reinterpolate :url" do
84
+ attachment = mock
85
+ attachment.expects(:options).returns({:url => ":id"})
86
+ attachment.expects(:url).with(:style, false).returns("1234")
87
+ assert_equal "1234", Paperclip::Interpolations.url(attachment, :style)
88
+ end
89
+
90
+ should "raise if infinite loop detcted reinterpolating :url" do
91
+ attachment = mock
92
+ attachment.expects(:options).returns({:url => ":url"})
93
+ assert_raises(Paperclip::InfiniteInterpolationError){ Paperclip::Interpolations.url(attachment, :style) }
94
+ end
95
+
96
+ should "return the filename as basename.extension" do
97
+ attachment = mock
98
+ attachment.expects(:styles).returns({})
99
+ attachment.expects(:original_filename).returns("one.jpg").times(3)
100
+ assert_equal "one.jpg", Paperclip::Interpolations.filename(attachment, :style)
101
+ end
102
+
103
+ should "return the filename as basename.extension when format supplied" do
104
+ attachment = mock
105
+ attachment.expects(:styles).returns({:style => {:format => :png}})
106
+ attachment.expects(:original_filename).returns("one.jpg").times(2)
107
+ assert_equal "one.png", Paperclip::Interpolations.filename(attachment, :style)
108
+ end
109
+
110
+ should "return the timestamp" do
111
+ now = Time.now
112
+ attachment = mock
113
+ attachment.expects(:instance_read).with(:updated_at).returns(now)
114
+ assert_equal now.to_s, Paperclip::Interpolations.timestamp(attachment, :style)
115
+ end
116
+
117
+ should "call all expected interpolations with the given arguments" do
118
+ Paperclip::Interpolations.expects(:id).with(:attachment, :style).returns(1234)
119
+ Paperclip::Interpolations.expects(:attachment).with(:attachment, :style).returns("attachments")
120
+ Paperclip::Interpolations.expects(:notreal).never
121
+ value = Paperclip::Interpolations.interpolate(":notreal/:id/:attachment", :attachment, :style)
122
+ assert_equal ":notreal/1234/attachments", value
123
+ end
124
+ 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,37 @@
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 :avatar_file_name
8
+ d.string :avatar_content_type
9
+ end
10
+ @dummy_class = reset_class "Dummy"
11
+ @dummy_class.has_attached_file :avatar
12
+ @matcher = self.class.validate_attachment_content_type(:avatar).
13
+ allowing(%w(image/png image/jpeg)).
14
+ rejecting(%w(audio/mp3 application/octet-stream))
15
+ end
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 doesn't match" do
22
+ setup do
23
+ @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{audio/.*}
24
+ end
25
+
26
+ should_reject_dummy_class
27
+ end
28
+
29
+ context "given a class with a matching validation" do
30
+ setup do
31
+ @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
32
+ end
33
+
34
+ should_accept_dummy_class
35
+ end
36
+ end
37
+ 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,317 @@
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
+ should "prevent dangerous characters in the command via quoting" do
86
+ Paperclip.options[:image_magick_path] = nil
87
+ Paperclip.options[:command_path] = nil
88
+ Paperclip.options[:log_command] = false
89
+ Paperclip.options[:swallow_stderr] = false
90
+ Paperclip.expects(:"`").with(%q[this 'is' 'jack'\''s' '`command`' 'line!'])
91
+ Paperclip.run("this", "is", "jack's", "`command`", "line!")
92
+ end
93
+
94
+ context "Paperclip.bit_bucket" do
95
+ context "on systems without /dev/null" do
96
+ setup do
97
+ File.expects(:exists?).with("/dev/null").returns(false)
98
+ end
99
+
100
+ should "return 'NUL'" do
101
+ assert_equal "NUL", Paperclip.bit_bucket
102
+ end
103
+ end
104
+
105
+ context "on systems with /dev/null" do
106
+ setup do
107
+ File.expects(:exists?).with("/dev/null").returns(true)
108
+ end
109
+
110
+ should "return '/dev/null'" do
111
+ assert_equal "/dev/null", Paperclip.bit_bucket
112
+ end
113
+ end
114
+ end
115
+
116
+ should "raise when sent #processor and the name of a class that exists but isn't a subclass of Processor" do
117
+ assert_raises(Paperclip::PaperclipError){ Paperclip.processor(:attachment) }
118
+ end
119
+
120
+ should "raise when sent #processor and the name of a class that doesn't exist" do
121
+ assert_raises(NameError){ Paperclip.processor(:boogey_man) }
122
+ end
123
+
124
+ should "return a class when sent #processor and the name of a class under Paperclip" do
125
+ assert_equal ::Paperclip::Thumbnail, Paperclip.processor(:thumbnail)
126
+ end
127
+
128
+ context "An ActiveRecord model with an 'avatar' attachment" do
129
+ setup do
130
+ rebuild_model :path => "tmp/:class/omg/:style.:extension"
131
+ @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
132
+ end
133
+
134
+ teardown { @file.close }
135
+
136
+ should "not error when trying to also create a 'blah' attachment" do
137
+ assert_nothing_raised do
138
+ Dummy.class_eval do
139
+ has_attached_file :blah
140
+ end
141
+ end
142
+ end
143
+
144
+ context "that is attr_protected" do
145
+ setup do
146
+ Dummy.class_eval do
147
+ attr_protected :avatar
148
+ end
149
+ @dummy = Dummy.new
150
+ end
151
+
152
+ should "not assign the avatar on mass-set" do
153
+ @dummy.attributes = { :other => "I'm set!",
154
+ :avatar => @file }
155
+
156
+ assert_equal "I'm set!", @dummy.other
157
+ assert ! @dummy.avatar?
158
+ end
159
+
160
+ should "still allow assigment on normal set" do
161
+ @dummy.other = "I'm set!"
162
+ @dummy.avatar = @file
163
+
164
+ assert_equal "I'm set!", @dummy.other
165
+ assert @dummy.avatar?
166
+ end
167
+ end
168
+
169
+ context "with a subclass" do
170
+ setup do
171
+ class ::SubDummy < Dummy; end
172
+ end
173
+
174
+ should "be able to use the attachment from the subclass" do
175
+ assert_nothing_raised do
176
+ @subdummy = SubDummy.create(:avatar => @file)
177
+ end
178
+ end
179
+
180
+ should "be able to see the attachment definition from the subclass's class" do
181
+ assert_equal "tmp/:class/omg/:style.:extension",
182
+ SubDummy.attachment_definitions[:avatar][:path]
183
+ end
184
+
185
+ teardown do
186
+ Object.send(:remove_const, "SubDummy") rescue nil
187
+ end
188
+ end
189
+
190
+ should "have an #avatar method" do
191
+ assert Dummy.new.respond_to?(:avatar)
192
+ end
193
+
194
+ should "have an #avatar= method" do
195
+ assert Dummy.new.respond_to?(:avatar=)
196
+ end
197
+
198
+ context "that is valid" do
199
+ setup do
200
+ @dummy = Dummy.new
201
+ @dummy.avatar = @file
202
+ end
203
+
204
+ should "be valid" do
205
+ assert @dummy.valid?
206
+ end
207
+ end
208
+
209
+ context "a validation with an if guard clause" do
210
+ setup do
211
+ Dummy.send(:"validates_attachment_presence", :avatar, :if => lambda{|i| i.foo })
212
+ @dummy = Dummy.new
213
+ @dummy.stubs(:avatar_file_name).returns(nil)
214
+ end
215
+
216
+ should "attempt validation if the guard returns true" do
217
+ @dummy.expects(:foo).returns(true)
218
+ assert ! @dummy.valid?
219
+ end
220
+
221
+ should "not attempt validation if the guard returns false" do
222
+ @dummy.expects(:foo).returns(false)
223
+ assert @dummy.valid?
224
+ end
225
+ end
226
+
227
+ context "a validation with an unless guard clause" do
228
+ setup do
229
+ Dummy.send(:"validates_attachment_presence", :avatar, :unless => lambda{|i| i.foo })
230
+ @dummy = Dummy.new
231
+ @dummy.stubs(:avatar_file_name).returns(nil)
232
+ end
233
+
234
+ should "attempt validation if the guard returns true" do
235
+ @dummy.expects(:foo).returns(false)
236
+ assert ! @dummy.valid?
237
+ end
238
+
239
+ should "not attempt validation if the guard returns false" do
240
+ @dummy.expects(:foo).returns(true)
241
+ assert @dummy.valid?
242
+ end
243
+ end
244
+
245
+ def self.should_validate validation, options, valid_file, invalid_file
246
+ context "with #{validation} validation and #{options.inspect} options" do
247
+ setup do
248
+ Dummy.send(:"validates_attachment_#{validation}", :avatar, options)
249
+ @dummy = Dummy.new
250
+ end
251
+ context "and assigning nil" do
252
+ setup do
253
+ @dummy.avatar = nil
254
+ @dummy.valid?
255
+ end
256
+ if validation == :presence
257
+ should "have an error on the attachment" do
258
+ assert @dummy.errors[:avatar_file_name]
259
+ end
260
+ else
261
+ should "not have an error on the attachment" do
262
+ assert @dummy.errors[:avatar_file_name].blank?, @dummy.errors.full_messages.join(", ")
263
+ end
264
+ end
265
+ end
266
+ context "and assigned a valid file" do
267
+ setup do
268
+ @dummy.avatar = valid_file
269
+ @dummy.valid?
270
+ end
271
+ should "not have an error when assigned a valid file" do
272
+ assert_equal 0, @dummy.errors.length, @dummy.errors.full_messages.join(", ")
273
+ end
274
+ end
275
+ context "and assigned an invalid file" do
276
+ setup do
277
+ @dummy.avatar = invalid_file
278
+ @dummy.valid?
279
+ end
280
+ should "have an error when assigned a valid file" do
281
+ assert @dummy.errors.length > 0
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ [[:presence, {}, "5k.png", nil],
288
+ [:size, {:in => 1..10240}, "5k.png", "12k.png"],
289
+ [:size, {:less_than => 10240}, "5k.png", "12k.png"],
290
+ [:size, {:greater_than => 8096}, "12k.png", "5k.png"],
291
+ [:content_type, {:content_type => "image/png"}, "5k.png", "text.txt"],
292
+ [:content_type, {:content_type => "text/plain"}, "text.txt", "5k.png"],
293
+ [:content_type, {:content_type => %r{image/.*}}, "5k.png", "text.txt"]].each do |args|
294
+ validation, options, valid_file, invalid_file = args
295
+ valid_file &&= File.open(File.join(FIXTURES_DIR, valid_file), "rb")
296
+ invalid_file &&= File.open(File.join(FIXTURES_DIR, invalid_file), "rb")
297
+
298
+ should_validate validation, options, valid_file, invalid_file
299
+ end
300
+
301
+ context "with size validation and less_than 10240 option" do
302
+ context "and assigned an invalid file" do
303
+ setup do
304
+ Dummy.send(:"validates_attachment_size", :avatar, :less_than => 10240)
305
+ @dummy = Dummy.new
306
+ @dummy.avatar &&= File.open(File.join(FIXTURES_DIR, "12k.png"), "rb")
307
+ @dummy.valid?
308
+ end
309
+
310
+ should "have a file size min/max error message" do
311
+ assert [@dummy.errors[:avatar_file_size]].flatten.any?{|error| error =~ %r/between 0 and 10240 bytes/ }
312
+ end
313
+ end
314
+ end
315
+
316
+ end
317
+ end