paperclip_with_versions 2.3.1.1
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.
- data/LICENSE +26 -0
- data/README.rdoc +174 -0
- data/Rakefile +103 -0
- data/generators/paperclip/USAGE +5 -0
- data/generators/paperclip/paperclip_generator.rb +27 -0
- data/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
- data/init.rb +1 -0
- data/lib/paperclip.rb +356 -0
- data/lib/paperclip/attachment.rb +414 -0
- data/lib/paperclip/callback_compatability.rb +33 -0
- data/lib/paperclip/geometry.rb +115 -0
- data/lib/paperclip/interpolations.rb +108 -0
- data/lib/paperclip/iostream.rb +58 -0
- data/lib/paperclip/matchers.rb +4 -0
- data/lib/paperclip/matchers/have_attached_file_matcher.rb +49 -0
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +66 -0
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +48 -0
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +83 -0
- data/lib/paperclip/processor.rb +49 -0
- data/lib/paperclip/storage.rb +243 -0
- data/lib/paperclip/thumbnail.rb +73 -0
- data/lib/paperclip/upfile.rb +49 -0
- data/shoulda_macros/paperclip.rb +117 -0
- data/tasks/paperclip_tasks.rake +79 -0
- data/test/attachment_test.rb +815 -0
- data/test/database.yml +4 -0
- data/test/fixtures/12k.png +0 -0
- data/test/fixtures/50x50.png +0 -0
- data/test/fixtures/5k.png +0 -0
- data/test/fixtures/bad.png +1 -0
- data/test/fixtures/s3.yml +8 -0
- data/test/fixtures/text.txt +0 -0
- data/test/fixtures/twopage.pdf +0 -0
- data/test/geometry_test.rb +177 -0
- data/test/helper.rb +108 -0
- data/test/integration_test.rb +483 -0
- data/test/interpolations_test.rb +124 -0
- data/test/iostream_test.rb +71 -0
- data/test/matchers/have_attached_file_matcher_test.rb +21 -0
- data/test/matchers/validate_attachment_content_type_matcher_test.rb +30 -0
- data/test/matchers/validate_attachment_presence_matcher_test.rb +21 -0
- data/test/matchers/validate_attachment_size_matcher_test.rb +50 -0
- data/test/paperclip_test.rb +327 -0
- data/test/processor_test.rb +10 -0
- data/test/storage_test.rb +303 -0
- data/test/thumbnail_test.rb +227 -0
- data/test/upfile_test.rb +28 -0
- metadata +161 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
def obtain_class
|
2
|
+
class_name = ENV['CLASS'] || ENV['class']
|
3
|
+
raise "Must specify CLASS" unless class_name
|
4
|
+
@klass = Object.const_get(class_name)
|
5
|
+
end
|
6
|
+
|
7
|
+
def obtain_attachments
|
8
|
+
name = ENV['ATTACHMENT'] || ENV['attachment']
|
9
|
+
raise "Class #{@klass.name} has no attachments specified" unless @klass.respond_to?(:attachment_definitions)
|
10
|
+
if !name.blank? && @klass.attachment_definitions.keys.include?(name)
|
11
|
+
[ name ]
|
12
|
+
else
|
13
|
+
@klass.attachment_definitions.keys
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def for_all_attachments
|
18
|
+
klass = obtain_class
|
19
|
+
names = obtain_attachments
|
20
|
+
ids = klass.connection.select_values(klass.send(:construct_finder_sql, :select => 'id'))
|
21
|
+
|
22
|
+
ids.each do |id|
|
23
|
+
instance = klass.find(id)
|
24
|
+
names.each do |name|
|
25
|
+
result = if instance.send("#{ name }?")
|
26
|
+
yield(instance, name)
|
27
|
+
else
|
28
|
+
true
|
29
|
+
end
|
30
|
+
print result ? "." : "x"; $stdout.flush
|
31
|
+
end
|
32
|
+
end
|
33
|
+
puts " Done."
|
34
|
+
end
|
35
|
+
|
36
|
+
namespace :paperclip do
|
37
|
+
desc "Refreshes both metadata and thumbnails."
|
38
|
+
task :refresh => ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]
|
39
|
+
|
40
|
+
namespace :refresh do
|
41
|
+
desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT)."
|
42
|
+
task :thumbnails => :environment do
|
43
|
+
errors = []
|
44
|
+
for_all_attachments do |instance, name|
|
45
|
+
result = instance.send(name).reprocess!
|
46
|
+
errors << [instance.id, instance.errors] unless instance.errors.blank?
|
47
|
+
result
|
48
|
+
end
|
49
|
+
errors.each{|e| puts "#{e.first}: #{e.last.full_messages.inspect}" }
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
|
53
|
+
task :metadata => :environment do
|
54
|
+
for_all_attachments do |instance, name|
|
55
|
+
if file = instance.send(name).to_file
|
56
|
+
instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
|
57
|
+
instance.send("#{name}_content_type=", file.content_type.strip)
|
58
|
+
instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
|
59
|
+
instance.save(false)
|
60
|
+
else
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Cleans out invalid attachments. Useful after you've added new validations."
|
68
|
+
task :clean => :environment do
|
69
|
+
for_all_attachments do |instance, name|
|
70
|
+
instance.send(name).send(:validate)
|
71
|
+
if instance.send(name).valid?
|
72
|
+
true
|
73
|
+
else
|
74
|
+
instance.send("#{name}=", nil)
|
75
|
+
instance.save
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,815 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'test/helper'
|
3
|
+
|
4
|
+
class Dummy
|
5
|
+
# This is a dummy class
|
6
|
+
end
|
7
|
+
|
8
|
+
class AttachmentTest < Test::Unit::TestCase
|
9
|
+
should "return the path based on the url by default" do
|
10
|
+
@attachment = attachment :url => "/:class/:id/:basename"
|
11
|
+
@model = @attachment.instance
|
12
|
+
@model.id = 1234
|
13
|
+
@model.avatar_file_name = "fake.jpg"
|
14
|
+
assert_equal "#{RAILS_ROOT}/public/fake_models/1234/fake", @attachment.path
|
15
|
+
end
|
16
|
+
|
17
|
+
should "call a proc sent to check_guard" do
|
18
|
+
@dummy = Dummy.new
|
19
|
+
@dummy.expects(:one).returns(:one)
|
20
|
+
assert_equal :one, @dummy.avatar.send(:check_guard, lambda{|x| x.one })
|
21
|
+
end
|
22
|
+
|
23
|
+
should "call a method name sent to check_guard" do
|
24
|
+
@dummy = Dummy.new
|
25
|
+
@dummy.expects(:one).returns(:one)
|
26
|
+
assert_equal :one, @dummy.avatar.send(:check_guard, :one)
|
27
|
+
end
|
28
|
+
|
29
|
+
context "Attachment default_options" do
|
30
|
+
setup do
|
31
|
+
rebuild_model
|
32
|
+
@old_default_options = Paperclip::Attachment.default_options.dup
|
33
|
+
@new_default_options = @old_default_options.merge({
|
34
|
+
:path => "argle/bargle",
|
35
|
+
:url => "fooferon",
|
36
|
+
:default_url => "not here.png"
|
37
|
+
})
|
38
|
+
end
|
39
|
+
|
40
|
+
teardown do
|
41
|
+
Paperclip::Attachment.default_options.merge! @old_default_options
|
42
|
+
end
|
43
|
+
|
44
|
+
should "be overrideable" do
|
45
|
+
Paperclip::Attachment.default_options.merge!(@new_default_options)
|
46
|
+
@new_default_options.keys.each do |key|
|
47
|
+
assert_equal @new_default_options[key],
|
48
|
+
Paperclip::Attachment.default_options[key]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "without an Attachment" do
|
53
|
+
setup do
|
54
|
+
@dummy = Dummy.new
|
55
|
+
end
|
56
|
+
|
57
|
+
should "return false when asked exists?" do
|
58
|
+
assert !@dummy.avatar.exists?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "on an Attachment" do
|
63
|
+
setup do
|
64
|
+
@dummy = Dummy.new
|
65
|
+
@attachment = @dummy.avatar
|
66
|
+
end
|
67
|
+
|
68
|
+
Paperclip::Attachment.default_options.keys.each do |key|
|
69
|
+
should "be the default_options for #{key}" do
|
70
|
+
assert_equal @old_default_options[key],
|
71
|
+
@attachment.instance_variable_get("@#{key}"),
|
72
|
+
key
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when redefined" do
|
77
|
+
setup do
|
78
|
+
Paperclip::Attachment.default_options.merge!(@new_default_options)
|
79
|
+
@dummy = Dummy.new
|
80
|
+
@attachment = @dummy.avatar
|
81
|
+
end
|
82
|
+
|
83
|
+
Paperclip::Attachment.default_options.keys.each do |key|
|
84
|
+
should "be the new default_options for #{key}" do
|
85
|
+
assert_equal @new_default_options[key],
|
86
|
+
@attachment.instance_variable_get("@#{key}"),
|
87
|
+
key
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "An attachment with similarly named interpolations" do
|
95
|
+
setup do
|
96
|
+
rebuild_model :path => ":id.omg/:id-bbq/:idwhat/:id_partition.wtf"
|
97
|
+
@dummy = Dummy.new
|
98
|
+
@dummy.stubs(:id).returns(1024)
|
99
|
+
@file = File.new(File.join(File.dirname(__FILE__),
|
100
|
+
"fixtures",
|
101
|
+
"5k.png"), 'rb')
|
102
|
+
@dummy.avatar = @file
|
103
|
+
end
|
104
|
+
|
105
|
+
teardown { @file.close }
|
106
|
+
|
107
|
+
should "make sure that they are interpolated correctly" do
|
108
|
+
assert_equal "1024.omg/1024-bbq/1024what/000/001/024.wtf", @dummy.avatar.path
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "An attachment with a :rails_env interpolation" do
|
113
|
+
setup do
|
114
|
+
@rails_env = "blah"
|
115
|
+
@id = 1024
|
116
|
+
rebuild_model :path => ":rails_env/:id.png"
|
117
|
+
@dummy = Dummy.new
|
118
|
+
@dummy.stubs(:id).returns(@id)
|
119
|
+
@file = StringIO.new(".")
|
120
|
+
@dummy.avatar = @file
|
121
|
+
end
|
122
|
+
|
123
|
+
should "return the proper path" do
|
124
|
+
temporary_rails_env(@rails_env) {
|
125
|
+
assert_equal "#{@rails_env}/#{@id}.png", @dummy.avatar.path
|
126
|
+
}
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "An attachment with a default style and an extension interpolation" do
|
131
|
+
setup do
|
132
|
+
@attachment = attachment :path => ":basename.:extension",
|
133
|
+
:styles => { :default => ["100x100", :png] },
|
134
|
+
:default_style => :default
|
135
|
+
@file = StringIO.new("...")
|
136
|
+
@file.expects(:original_filename).returns("file.jpg")
|
137
|
+
end
|
138
|
+
should "return the right extension for the path" do
|
139
|
+
@attachment.assign(@file)
|
140
|
+
assert_equal "file.png", @attachment.path
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "An attachment with :convert_options" do
|
145
|
+
setup do
|
146
|
+
rebuild_model :styles => {
|
147
|
+
:thumb => "100x100",
|
148
|
+
:large => "400x400"
|
149
|
+
},
|
150
|
+
:convert_options => {
|
151
|
+
:all => "-do_stuff",
|
152
|
+
:thumb => "-thumbnailize"
|
153
|
+
}
|
154
|
+
@dummy = Dummy.new
|
155
|
+
@dummy.avatar
|
156
|
+
end
|
157
|
+
|
158
|
+
should "report the correct options when sent #extra_options_for(:thumb)" do
|
159
|
+
assert_equal "-thumbnailize -do_stuff", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
|
160
|
+
end
|
161
|
+
|
162
|
+
should "report the correct options when sent #extra_options_for(:large)" do
|
163
|
+
assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large)
|
164
|
+
end
|
165
|
+
|
166
|
+
before_should "call extra_options_for(:thumb/:large)" do
|
167
|
+
Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
|
168
|
+
Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "An attachment with :convert_options that is a proc" do
|
173
|
+
setup do
|
174
|
+
rebuild_model :styles => {
|
175
|
+
:thumb => "100x100",
|
176
|
+
:large => "400x400"
|
177
|
+
},
|
178
|
+
:convert_options => {
|
179
|
+
:all => lambda{|i| i.all },
|
180
|
+
:thumb => lambda{|i| i.thumb }
|
181
|
+
}
|
182
|
+
Dummy.class_eval do
|
183
|
+
def all; "-all"; end
|
184
|
+
def thumb; "-thumb"; end
|
185
|
+
end
|
186
|
+
@dummy = Dummy.new
|
187
|
+
@dummy.avatar
|
188
|
+
end
|
189
|
+
|
190
|
+
should "report the correct options when sent #extra_options_for(:thumb)" do
|
191
|
+
assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
|
192
|
+
end
|
193
|
+
|
194
|
+
should "report the correct options when sent #extra_options_for(:large)" do
|
195
|
+
assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
|
196
|
+
end
|
197
|
+
|
198
|
+
before_should "call extra_options_for(:thumb/:large)" do
|
199
|
+
Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
|
200
|
+
Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "An attachment with :path that is a proc" do
|
205
|
+
setup do
|
206
|
+
rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
|
207
|
+
|
208
|
+
@file = File.new(File.join(File.dirname(__FILE__),
|
209
|
+
"fixtures",
|
210
|
+
"5k.png"), 'rb')
|
211
|
+
@dummyA = Dummy.new(:other => 'a')
|
212
|
+
@dummyA.avatar = @file
|
213
|
+
@dummyB = Dummy.new(:other => 'b')
|
214
|
+
@dummyB.avatar = @file
|
215
|
+
end
|
216
|
+
|
217
|
+
teardown { @file.close }
|
218
|
+
|
219
|
+
should "return correct path" do
|
220
|
+
assert_equal "path/a.png", @dummyA.avatar.path
|
221
|
+
assert_equal "path/b.png", @dummyB.avatar.path
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context "An attachment with :styles that is a proc" do
|
226
|
+
setup do
|
227
|
+
rebuild_model :styles => lambda{ |attachment| {:thumb => "50x50#", :large => "400x400"} }
|
228
|
+
|
229
|
+
@attachment = Dummy.new.avatar
|
230
|
+
end
|
231
|
+
|
232
|
+
should "have the correct geometry" do
|
233
|
+
assert_equal "50x50#", @attachment.styles[:thumb][:geometry]
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "An attachment with :url that is a proc" do
|
238
|
+
setup do
|
239
|
+
rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
|
240
|
+
|
241
|
+
@file = File.new(File.join(File.dirname(__FILE__),
|
242
|
+
"fixtures",
|
243
|
+
"5k.png"), 'rb')
|
244
|
+
@dummyA = Dummy.new(:other => 'a')
|
245
|
+
@dummyA.avatar = @file
|
246
|
+
@dummyB = Dummy.new(:other => 'b')
|
247
|
+
@dummyB.avatar = @file
|
248
|
+
end
|
249
|
+
|
250
|
+
teardown { @file.close }
|
251
|
+
|
252
|
+
should "return correct url" do
|
253
|
+
assert_equal "path/a.png", @dummyA.avatar.url(:original, false)
|
254
|
+
assert_equal "path/b.png", @dummyB.avatar.url(:original, false)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
geometry_specs = [
|
259
|
+
[ lambda{|z| "50x50#" }, :png ],
|
260
|
+
lambda{|z| "50x50#" },
|
261
|
+
{ :geometry => lambda{|z| "50x50#" } }
|
262
|
+
]
|
263
|
+
geometry_specs.each do |geometry_spec|
|
264
|
+
context "An attachment geometry like #{geometry_spec}" do
|
265
|
+
setup do
|
266
|
+
rebuild_model :styles => { :normal => geometry_spec }
|
267
|
+
@attachment = Dummy.new.avatar
|
268
|
+
end
|
269
|
+
|
270
|
+
should "not run the procs immediately" do
|
271
|
+
assert_kind_of Proc, @attachment.styles[:normal][:geometry]
|
272
|
+
end
|
273
|
+
|
274
|
+
context "when assigned" do
|
275
|
+
setup do
|
276
|
+
@file = StringIO.new(".")
|
277
|
+
@attachment.assign(@file)
|
278
|
+
end
|
279
|
+
|
280
|
+
should "have the correct geometry" do
|
281
|
+
assert_equal "50x50#", @attachment.styles[:normal][:geometry]
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context "An attachment with both 'normal' and hash-style styles" do
|
288
|
+
setup do
|
289
|
+
rebuild_model :styles => {
|
290
|
+
:normal => ["50x50#", :png],
|
291
|
+
:hash => { :geometry => "50x50#", :format => :png }
|
292
|
+
}
|
293
|
+
@dummy = Dummy.new
|
294
|
+
@attachment = @dummy.avatar
|
295
|
+
end
|
296
|
+
|
297
|
+
[:processors, :whiny, :convert_options, :geometry, :format].each do |field|
|
298
|
+
should "have the same #{field} field" do
|
299
|
+
assert_equal @attachment.styles[:normal][field], @attachment.styles[:hash][field]
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
context "An attachment with :processors that is a proc" do
|
305
|
+
setup do
|
306
|
+
rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] }
|
307
|
+
@attachment = Dummy.new.avatar
|
308
|
+
end
|
309
|
+
|
310
|
+
should "not run the proc immediately" do
|
311
|
+
assert_kind_of Proc, @attachment.styles[:normal][:processors]
|
312
|
+
end
|
313
|
+
|
314
|
+
context "when assigned" do
|
315
|
+
setup do
|
316
|
+
@attachment.assign(StringIO.new("."))
|
317
|
+
end
|
318
|
+
|
319
|
+
should "have the correct processors" do
|
320
|
+
assert_equal [ :test ], @attachment.styles[:normal][:processors]
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context "An attachment with erroring processor" do
|
326
|
+
setup do
|
327
|
+
rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
|
328
|
+
@dummy = Dummy.new
|
329
|
+
Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.")
|
330
|
+
@file = StringIO.new("...")
|
331
|
+
@file.stubs(:to_tempfile).returns(@file)
|
332
|
+
@dummy.avatar = @file
|
333
|
+
end
|
334
|
+
|
335
|
+
should "correctly forward processing error message to the instance" do
|
336
|
+
@dummy.valid?
|
337
|
+
assert_contains @dummy.errors.full_messages, "Avatar cannot be processed."
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
context "An attachment with multiple processors" do
|
342
|
+
setup do
|
343
|
+
class Paperclip::Test < Paperclip::Processor; end
|
344
|
+
@style_params = { :once => {:one => 1, :two => 2} }
|
345
|
+
rebuild_model :processors => [:thumbnail, :test], :styles => @style_params
|
346
|
+
@dummy = Dummy.new
|
347
|
+
@file = StringIO.new("...")
|
348
|
+
@file.stubs(:to_tempfile).returns(@file)
|
349
|
+
Paperclip::Test.stubs(:make).returns(@file)
|
350
|
+
Paperclip::Thumbnail.stubs(:make).returns(@file)
|
351
|
+
end
|
352
|
+
|
353
|
+
context "when assigned" do
|
354
|
+
setup { @dummy.avatar = @file }
|
355
|
+
|
356
|
+
before_should "call #make on all specified processors" do
|
357
|
+
expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
|
358
|
+
Paperclip::Thumbnail.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
|
359
|
+
Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
|
360
|
+
end
|
361
|
+
|
362
|
+
before_should "call #make with attachment passed as third argument" do
|
363
|
+
expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
|
364
|
+
Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
context "An attachment with no processors defined" do
|
370
|
+
setup do
|
371
|
+
rebuild_model :processors => [], :styles => {:something => 1}
|
372
|
+
@dummy = Dummy.new
|
373
|
+
@file = StringIO.new("...")
|
374
|
+
end
|
375
|
+
should "raise when assigned to" do
|
376
|
+
assert_raises(RuntimeError){ @dummy.avatar = @file }
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
context "Assigning an attachment with post_process hooks" do
|
381
|
+
setup do
|
382
|
+
rebuild_model :styles => { :something => "100x100#" }
|
383
|
+
Dummy.class_eval do
|
384
|
+
before_avatar_post_process :do_before_avatar
|
385
|
+
after_avatar_post_process :do_after_avatar
|
386
|
+
before_post_process :do_before_all
|
387
|
+
after_post_process :do_after_all
|
388
|
+
def do_before_avatar; end
|
389
|
+
def do_after_avatar; end
|
390
|
+
def do_before_all; end
|
391
|
+
def do_after_all; end
|
392
|
+
end
|
393
|
+
@file = StringIO.new(".")
|
394
|
+
@file.stubs(:to_tempfile).returns(@file)
|
395
|
+
@dummy = Dummy.new
|
396
|
+
Paperclip::Thumbnail.stubs(:make).returns(@file)
|
397
|
+
@attachment = @dummy.avatar
|
398
|
+
end
|
399
|
+
|
400
|
+
should "call the defined callbacks when assigned" do
|
401
|
+
@dummy.expects(:do_before_avatar).with()
|
402
|
+
@dummy.expects(:do_after_avatar).with()
|
403
|
+
@dummy.expects(:do_before_all).with()
|
404
|
+
@dummy.expects(:do_after_all).with()
|
405
|
+
Paperclip::Thumbnail.expects(:make).returns(@file)
|
406
|
+
@dummy.avatar = @file
|
407
|
+
end
|
408
|
+
|
409
|
+
should "not cancel the processing if a before_post_process returns nil" do
|
410
|
+
@dummy.expects(:do_before_avatar).with().returns(nil)
|
411
|
+
@dummy.expects(:do_after_avatar).with()
|
412
|
+
@dummy.expects(:do_before_all).with().returns(nil)
|
413
|
+
@dummy.expects(:do_after_all).with()
|
414
|
+
Paperclip::Thumbnail.expects(:make).returns(@file)
|
415
|
+
@dummy.avatar = @file
|
416
|
+
end
|
417
|
+
|
418
|
+
should "cancel the processing if a before_post_process returns false" do
|
419
|
+
@dummy.expects(:do_before_avatar).never
|
420
|
+
@dummy.expects(:do_after_avatar).never
|
421
|
+
@dummy.expects(:do_before_all).with().returns(false)
|
422
|
+
@dummy.expects(:do_after_all).never
|
423
|
+
Paperclip::Thumbnail.expects(:make).never
|
424
|
+
@dummy.avatar = @file
|
425
|
+
end
|
426
|
+
|
427
|
+
should "cancel the processing if a before_avatar_post_process returns false" do
|
428
|
+
@dummy.expects(:do_before_avatar).with().returns(false)
|
429
|
+
@dummy.expects(:do_after_avatar).never
|
430
|
+
@dummy.expects(:do_before_all).with().returns(true)
|
431
|
+
@dummy.expects(:do_after_all).never
|
432
|
+
Paperclip::Thumbnail.expects(:make).never
|
433
|
+
@dummy.avatar = @file
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
context "Assigning an attachment" do
|
438
|
+
setup do
|
439
|
+
rebuild_model :styles => { :something => "100x100#" }
|
440
|
+
@file = StringIO.new(".")
|
441
|
+
@file.expects(:original_filename).returns("5k.png\n\n")
|
442
|
+
@file.expects(:content_type).returns("image/png\n\n")
|
443
|
+
@file.stubs(:to_tempfile).returns(@file)
|
444
|
+
@dummy = Dummy.new
|
445
|
+
Paperclip::Thumbnail.expects(:make).returns(@file)
|
446
|
+
@dummy.expects(:run_callbacks).with(:before_avatar_post_process, {:original => @file})
|
447
|
+
@dummy.expects(:run_callbacks).with(:before_post_process, {:original => @file})
|
448
|
+
@dummy.expects(:run_callbacks).with(:after_avatar_post_process, {:original => @file, :something => @file})
|
449
|
+
@dummy.expects(:run_callbacks).with(:after_post_process, {:original => @file, :something => @file})
|
450
|
+
@attachment = @dummy.avatar
|
451
|
+
@dummy.avatar = @file
|
452
|
+
end
|
453
|
+
|
454
|
+
should "strip whitespace from original_filename field" do
|
455
|
+
assert_equal "5k.png", @dummy.avatar.original_filename
|
456
|
+
end
|
457
|
+
|
458
|
+
should "strip whitespace from content_type field" do
|
459
|
+
assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
context "Attachment with strange letters" do
|
464
|
+
setup do
|
465
|
+
rebuild_model
|
466
|
+
|
467
|
+
@not_file = mock
|
468
|
+
@tempfile = mock
|
469
|
+
@not_file.stubs(:nil?).returns(false)
|
470
|
+
@not_file.expects(:size).returns(10)
|
471
|
+
@tempfile.expects(:size).returns(10)
|
472
|
+
@not_file.expects(:to_tempfile).returns(@tempfile)
|
473
|
+
@not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
|
474
|
+
@not_file.expects(:content_type).returns("image/png\r\n")
|
475
|
+
|
476
|
+
@dummy = Dummy.new
|
477
|
+
@attachment = @dummy.avatar
|
478
|
+
@attachment.expects(:valid_assignment?).with(@not_file).returns(true)
|
479
|
+
@attachment.expects(:queue_existing_for_delete)
|
480
|
+
@attachment.expects(:post_process)
|
481
|
+
@attachment.expects(:valid?).returns(true)
|
482
|
+
@attachment.expects(:validate)
|
483
|
+
@dummy.avatar = @not_file
|
484
|
+
end
|
485
|
+
|
486
|
+
should "remove strange letters and replace with underscore (_)" do
|
487
|
+
assert_equal "sheep_say_b_.png", @dummy.avatar.original_filename
|
488
|
+
end
|
489
|
+
|
490
|
+
end
|
491
|
+
|
492
|
+
context "An attachment" do
|
493
|
+
setup do
|
494
|
+
@old_defaults = Paperclip::Attachment.default_options.dup
|
495
|
+
Paperclip::Attachment.default_options.merge!({
|
496
|
+
:path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
|
497
|
+
})
|
498
|
+
FileUtils.rm_rf("tmp")
|
499
|
+
rebuild_model
|
500
|
+
@instance = Dummy.new
|
501
|
+
@attachment = Paperclip::Attachment.new(:avatar, @instance)
|
502
|
+
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
|
503
|
+
end
|
504
|
+
|
505
|
+
teardown do
|
506
|
+
@file.close
|
507
|
+
Paperclip::Attachment.default_options.merge!(@old_defaults)
|
508
|
+
end
|
509
|
+
|
510
|
+
should "raise if there are not the correct columns when you try to assign" do
|
511
|
+
@other_attachment = Paperclip::Attachment.new(:not_here, @instance)
|
512
|
+
assert_raises(Paperclip::PaperclipError) do
|
513
|
+
@other_attachment.assign(@file)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
should "return its default_url when no file assigned" do
|
518
|
+
assert @attachment.to_file.nil?
|
519
|
+
assert_equal "/avatars/original/missing.png", @attachment.url
|
520
|
+
assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
|
521
|
+
end
|
522
|
+
|
523
|
+
should "return nil as path when no file assigned" do
|
524
|
+
assert @attachment.to_file.nil?
|
525
|
+
assert_equal nil, @attachment.path
|
526
|
+
assert_equal nil, @attachment.path(:blah)
|
527
|
+
end
|
528
|
+
|
529
|
+
context "with a file assigned in the database" do
|
530
|
+
setup do
|
531
|
+
@attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
|
532
|
+
@attachment.stubs(:instance_read).with(:content_type).returns("image/png")
|
533
|
+
@attachment.stubs(:instance_read).with(:file_size).returns(12345)
|
534
|
+
dtnow = DateTime.now
|
535
|
+
@now = Time.now
|
536
|
+
Time.stubs(:now).returns(@now)
|
537
|
+
@attachment.stubs(:instance_read).with(:updated_at).returns(dtnow)
|
538
|
+
end
|
539
|
+
|
540
|
+
should "return a correct url even if the file does not exist" do
|
541
|
+
assert_nil @attachment.to_file
|
542
|
+
assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
|
543
|
+
end
|
544
|
+
|
545
|
+
should "make sure the updated_at mtime is in the url if it is defined" do
|
546
|
+
assert_match %r{#{@now.to_i}$}, @attachment.url(:blah)
|
547
|
+
end
|
548
|
+
|
549
|
+
should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
|
550
|
+
assert_no_match %r{#{@now.to_i}$}, @attachment.url(:blah, false)
|
551
|
+
end
|
552
|
+
|
553
|
+
context "with the updated_at field removed" do
|
554
|
+
setup do
|
555
|
+
@attachment.stubs(:instance_read).with(:updated_at).returns(nil)
|
556
|
+
end
|
557
|
+
|
558
|
+
should "only return the url without the updated_at when sent #url" do
|
559
|
+
assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
should "return the proper path when filename has a single .'s" do
|
564
|
+
assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
|
565
|
+
end
|
566
|
+
|
567
|
+
should "return the proper path when filename has multiple .'s" do
|
568
|
+
@attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
|
569
|
+
assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
|
570
|
+
end
|
571
|
+
|
572
|
+
context "when expecting three styles" do
|
573
|
+
setup do
|
574
|
+
styles = {:styles => { :large => ["400x400", :png],
|
575
|
+
:medium => ["100x100", :gif],
|
576
|
+
:small => ["32x32#", :jpg]}}
|
577
|
+
@attachment = Paperclip::Attachment.new(:avatar,
|
578
|
+
@instance,
|
579
|
+
styles)
|
580
|
+
end
|
581
|
+
|
582
|
+
context "and assigned a file" do
|
583
|
+
setup do
|
584
|
+
now = Time.now
|
585
|
+
Time.stubs(:now).returns(now)
|
586
|
+
@attachment.assign(@file)
|
587
|
+
end
|
588
|
+
|
589
|
+
should "be dirty" do
|
590
|
+
assert @attachment.dirty?
|
591
|
+
end
|
592
|
+
|
593
|
+
context "and saved" do
|
594
|
+
setup do
|
595
|
+
@attachment.save
|
596
|
+
end
|
597
|
+
|
598
|
+
should "return the real url" do
|
599
|
+
file = @attachment.to_file
|
600
|
+
assert file
|
601
|
+
assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
|
602
|
+
assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
|
603
|
+
file.close
|
604
|
+
end
|
605
|
+
|
606
|
+
should "commit the files to disk" do
|
607
|
+
[:large, :medium, :small].each do |style|
|
608
|
+
io = @attachment.to_file(style)
|
609
|
+
assert File.exists?(io)
|
610
|
+
assert ! io.is_a?(::Tempfile)
|
611
|
+
io.close
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
should "save the files as the right formats and sizes" do
|
616
|
+
[[:large, 400, 61, "PNG"],
|
617
|
+
[:medium, 100, 15, "GIF"],
|
618
|
+
[:small, 32, 32, "JPEG"]].each do |style|
|
619
|
+
cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
|
620
|
+
out = `#{cmd}`
|
621
|
+
width, height, size, format = out.split(" ")
|
622
|
+
assert_equal style[1].to_s, width.to_s
|
623
|
+
assert_equal style[2].to_s, height.to_s
|
624
|
+
assert_equal style[3].to_s, format.to_s
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
should "still have its #file attribute not be nil" do
|
629
|
+
assert ! (file = @attachment.to_file).nil?
|
630
|
+
file.close
|
631
|
+
end
|
632
|
+
|
633
|
+
context "and trying to delete" do
|
634
|
+
setup do
|
635
|
+
@existing_names = @attachment.styles.keys.collect do |style|
|
636
|
+
@attachment.path(style)
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
should "delete the files after assigning nil" do
|
641
|
+
@attachment.expects(:instance_write).with(:file_name, nil)
|
642
|
+
@attachment.expects(:instance_write).with(:content_type, nil)
|
643
|
+
@attachment.expects(:instance_write).with(:file_size, nil)
|
644
|
+
@attachment.expects(:instance_write).with(:updated_at, nil)
|
645
|
+
@attachment.assign nil
|
646
|
+
@attachment.save
|
647
|
+
@existing_names.each{|f| assert ! File.exists?(f) }
|
648
|
+
end
|
649
|
+
|
650
|
+
should "delete the files when you call #clear and #save" do
|
651
|
+
@attachment.expects(:instance_write).with(:file_name, nil)
|
652
|
+
@attachment.expects(:instance_write).with(:content_type, nil)
|
653
|
+
@attachment.expects(:instance_write).with(:file_size, nil)
|
654
|
+
@attachment.expects(:instance_write).with(:updated_at, nil)
|
655
|
+
@attachment.clear
|
656
|
+
@attachment.save
|
657
|
+
@existing_names.each{|f| assert ! File.exists?(f) }
|
658
|
+
end
|
659
|
+
|
660
|
+
should "delete the files when you call #delete" do
|
661
|
+
@attachment.expects(:instance_write).with(:file_name, nil)
|
662
|
+
@attachment.expects(:instance_write).with(:content_type, nil)
|
663
|
+
@attachment.expects(:instance_write).with(:file_size, nil)
|
664
|
+
@attachment.expects(:instance_write).with(:updated_at, nil)
|
665
|
+
@attachment.destroy
|
666
|
+
@existing_names.each{|f| assert ! File.exists?(f) }
|
667
|
+
end
|
668
|
+
|
669
|
+
context "when keeping old files" do
|
670
|
+
setup do
|
671
|
+
@attachment.options[:keep_old_files] = true
|
672
|
+
end
|
673
|
+
|
674
|
+
should "keep the files after assigning nil" do
|
675
|
+
@attachment.expects(:instance_write).with(:file_name, nil)
|
676
|
+
@attachment.expects(:instance_write).with(:content_type, nil)
|
677
|
+
@attachment.expects(:instance_write).with(:file_size, nil)
|
678
|
+
@attachment.expects(:instance_write).with(:updated_at, nil)
|
679
|
+
@attachment.assign nil
|
680
|
+
@attachment.save
|
681
|
+
@existing_names.each{|f| assert File.exists?(f) }
|
682
|
+
end
|
683
|
+
|
684
|
+
should "keep the files when you call #clear and #save" do
|
685
|
+
@attachment.expects(:instance_write).with(:file_name, nil)
|
686
|
+
@attachment.expects(:instance_write).with(:content_type, nil)
|
687
|
+
@attachment.expects(:instance_write).with(:file_size, nil)
|
688
|
+
@attachment.expects(:instance_write).with(:updated_at, nil)
|
689
|
+
@attachment.clear
|
690
|
+
@attachment.save
|
691
|
+
@existing_names.each{|f| assert File.exists?(f) }
|
692
|
+
end
|
693
|
+
|
694
|
+
should "keep the files when you call #delete" do
|
695
|
+
@attachment.expects(:instance_write).with(:file_name, nil)
|
696
|
+
@attachment.expects(:instance_write).with(:content_type, nil)
|
697
|
+
@attachment.expects(:instance_write).with(:file_size, nil)
|
698
|
+
@attachment.expects(:instance_write).with(:updated_at, nil)
|
699
|
+
@attachment.destroy
|
700
|
+
@existing_names.each{|f| assert File.exists?(f) }
|
701
|
+
end
|
702
|
+
end
|
703
|
+
end
|
704
|
+
end
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
end
|
709
|
+
|
710
|
+
context "when trying a nonexistant storage type" do
|
711
|
+
setup do
|
712
|
+
rebuild_model :storage => :not_here
|
713
|
+
end
|
714
|
+
|
715
|
+
should "not be able to find the module" do
|
716
|
+
assert_raise(NameError){ Dummy.new.avatar }
|
717
|
+
end
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
context "An attachment with only a avatar_file_name column" do
|
722
|
+
setup do
|
723
|
+
ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
|
724
|
+
table.column :avatar_file_name, :string
|
725
|
+
end
|
726
|
+
rebuild_class
|
727
|
+
@dummy = Dummy.new
|
728
|
+
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
|
729
|
+
end
|
730
|
+
|
731
|
+
teardown { @file.close }
|
732
|
+
|
733
|
+
should "not error when assigned an attachment" do
|
734
|
+
assert_nothing_raised { @dummy.avatar = @file }
|
735
|
+
end
|
736
|
+
|
737
|
+
should "return the time when sent #avatar_updated_at" do
|
738
|
+
now = Time.now
|
739
|
+
Time.stubs(:now).returns(now)
|
740
|
+
@dummy.avatar = @file
|
741
|
+
assert now, @dummy.avatar.updated_at
|
742
|
+
end
|
743
|
+
|
744
|
+
should "return nil when reloaded and sent #avatar_updated_at" do
|
745
|
+
@dummy.save
|
746
|
+
@dummy.reload
|
747
|
+
assert_nil @dummy.avatar.updated_at
|
748
|
+
end
|
749
|
+
|
750
|
+
should "return the right value when sent #avatar_file_size" do
|
751
|
+
@dummy.avatar = @file
|
752
|
+
assert_equal @file.size, @dummy.avatar.size
|
753
|
+
end
|
754
|
+
|
755
|
+
context "and avatar_updated_at column" do
|
756
|
+
setup do
|
757
|
+
ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
|
758
|
+
rebuild_class
|
759
|
+
@dummy = Dummy.new
|
760
|
+
end
|
761
|
+
|
762
|
+
should "not error when assigned an attachment" do
|
763
|
+
assert_nothing_raised { @dummy.avatar = @file }
|
764
|
+
end
|
765
|
+
|
766
|
+
should "return the right value when sent #avatar_updated_at" do
|
767
|
+
now = Time.now
|
768
|
+
Time.stubs(:now).returns(now)
|
769
|
+
@dummy.avatar = @file
|
770
|
+
assert_equal now.to_i, @dummy.avatar.updated_at
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
context "and avatar_content_type column" do
|
775
|
+
setup do
|
776
|
+
ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
|
777
|
+
rebuild_class
|
778
|
+
@dummy = Dummy.new
|
779
|
+
end
|
780
|
+
|
781
|
+
should "not error when assigned an attachment" do
|
782
|
+
assert_nothing_raised { @dummy.avatar = @file }
|
783
|
+
end
|
784
|
+
|
785
|
+
should "return the right value when sent #avatar_content_type" do
|
786
|
+
@dummy.avatar = @file
|
787
|
+
assert_equal "image/png", @dummy.avatar.content_type
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
context "and avatar_file_size column" do
|
792
|
+
setup do
|
793
|
+
ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
|
794
|
+
rebuild_class
|
795
|
+
@dummy = Dummy.new
|
796
|
+
end
|
797
|
+
|
798
|
+
should "not error when assigned an attachment" do
|
799
|
+
assert_nothing_raised { @dummy.avatar = @file }
|
800
|
+
end
|
801
|
+
|
802
|
+
should "return the right value when sent #avatar_file_size" do
|
803
|
+
@dummy.avatar = @file
|
804
|
+
assert_equal @file.size, @dummy.avatar.size
|
805
|
+
end
|
806
|
+
|
807
|
+
should "return the right value when saved, reloaded, and sent #avatar_file_size" do
|
808
|
+
@dummy.avatar = @file
|
809
|
+
@dummy.save
|
810
|
+
@dummy = Dummy.find(@dummy.id)
|
811
|
+
assert_equal @file.size, @dummy.avatar.size
|
812
|
+
end
|
813
|
+
end
|
814
|
+
end
|
815
|
+
end
|