uploadcolumn 0.3.0

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.
@@ -0,0 +1,514 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ gem 'activerecord'
4
+ require 'active_record'
5
+
6
+ require File.join(File.dirname(__FILE__), '../lib/upload_column')
7
+
8
+ class Entry < ActiveRecord::Base; end # setup a basic AR class for testing
9
+
10
+ describe "an ActiveRecord class" do
11
+
12
+ include UploadColumnSpecHelper
13
+
14
+ it "should respond to upload_column" do
15
+ Entry.should respond_to(:upload_column)
16
+ end
17
+
18
+ it "should reflect on upload_columns" do
19
+ Entry.send(:reset_upload_columns)
20
+
21
+ Entry.upload_column(:avatar)
22
+
23
+ Entry.reflect_on_upload_columns[:avatar].should be_an_instance_of(UploadColumn::Column)
24
+ Entry.reflect_on_upload_columns[:monkey].should == nil
25
+
26
+ Entry.upload_column(:monkey)
27
+
28
+ Entry.reflect_on_upload_columns[:avatar].should be_an_instance_of(UploadColumn::Column)
29
+ Entry.reflect_on_upload_columns[:monkey].should be_an_instance_of(UploadColumn::Column)
30
+ end
31
+
32
+ it "should reset upload columns" do
33
+ Entry.upload_column(:avatar)
34
+
35
+ Entry.reflect_on_upload_columns[:avatar].should be_an_instance_of(UploadColumn::Column)
36
+
37
+ Entry.send(:reset_upload_columns)
38
+
39
+ Entry.reflect_on_upload_columns[:avatar].should == nil
40
+ end
41
+
42
+ end
43
+
44
+ describe "an Active Record class with an upload_column" do
45
+
46
+ include UploadColumnSpecHelper
47
+
48
+ it "should add accessor methods" do
49
+ # use a name that hasn't been used before!
50
+ entry = disconnected_model(Entry)
51
+ entry.should_not respond_to(:llama)
52
+ entry.should_not respond_to(:llama_temp)
53
+ entry.should_not respond_to(:llama=)
54
+ entry.should_not respond_to(:llama_temp=)
55
+
56
+ Entry.upload_column(:llama)
57
+
58
+ entry = disconnected_model(Entry)
59
+
60
+ entry.should respond_to(:llama)
61
+ entry.should respond_to(:llama_temp)
62
+ entry.should respond_to(:llama=)
63
+ entry.should respond_to(:llama_temp=)
64
+ end
65
+
66
+ it "should save the name of the column to be reflected upon" do
67
+ Entry.upload_column(:walruss)
68
+ Entry.reflect_on_upload_columns[:walruss].name.should == :walruss
69
+ end
70
+
71
+ it "should save the options to be reflected upon" do
72
+ options = { :donkey => true }
73
+
74
+ Entry.upload_column(:walruss, options)
75
+
76
+ Entry.reflect_on_upload_columns[:walruss].options.should == options
77
+ end
78
+ end
79
+
80
+ describe "an Active Record with no upload_column" do
81
+
82
+ before(:all) do
83
+ class Monkey < ActiveRecord::Base; end
84
+ end
85
+
86
+ it "should have no uploads_column" do
87
+ Monkey.reflect_on_upload_columns.should == {}
88
+ end
89
+
90
+ it "should be instantiable" do
91
+ Monkey.stub!(:columns).and_return([])
92
+ Monkey.new
93
+ end
94
+
95
+ end
96
+
97
+ describe "uploading a file" do
98
+
99
+ include UploadColumnSpecHelper
100
+
101
+ before do
102
+ setup_standard_mocking
103
+ UploadColumn::UploadedFile.should_receive(:upload).with(@file, @entry, :avatar, @options).and_return(@uploaded_file)
104
+ end
105
+
106
+ it "should pass it to UploadedFile and remember it" do
107
+ @entry.avatar.should == nil
108
+ @entry.avatar = @file
109
+ @entry.avatar.should == @uploaded_file
110
+ end
111
+
112
+ it "should set the attribute on the ActiveRecord" do
113
+ @entry.should_receive(:[]=).with(:avatar, 'monkey.png')
114
+ @entry.avatar = @file
115
+ end
116
+
117
+ end
118
+
119
+ describe "uploading an empty String" do
120
+
121
+ include UploadColumnSpecHelper
122
+
123
+ before do
124
+ setup_standard_mocking
125
+ end
126
+
127
+ it "should do nothing" do
128
+ UploadColumn::UploadedFile.should_receive(:upload).with("", @entry, :avatar, @options).and_return(nil)
129
+ @entry.avatar.should == nil
130
+ @entry.avatar = ""
131
+ @entry.avatar.should == nil
132
+ end
133
+
134
+ it "shouldn't affect an already uploaded file" do
135
+ UploadColumn::UploadedFile.should_receive(:upload).with(@file, @entry, :avatar, @options).and_return(@uploaded_file)
136
+ @entry.avatar = @file
137
+ @entry.avatar.should == @uploaded_file
138
+
139
+ UploadColumn::UploadedFile.should_receive(:upload).with("", @entry, :avatar, @options).and_return(nil)
140
+ @entry.avatar = ""
141
+ @entry.avatar.should == @uploaded_file
142
+ end
143
+
144
+ end
145
+
146
+ describe "setting nil explicitly" do
147
+
148
+ include UploadColumnSpecHelper
149
+
150
+ before do
151
+ setup_standard_mocking
152
+ end
153
+
154
+ it "should reset the column" do
155
+ UploadColumn::UploadedFile.should_receive(:upload).with(@file, @entry, :avatar, @options).and_return(@uploaded_file)
156
+ @entry.avatar = @file
157
+ @entry.avatar.should == @uploaded_file
158
+
159
+ @entry.avatar = nil
160
+ @entry.avatar.should == nil
161
+ end
162
+ end
163
+
164
+ describe "an upload_column with a value stored in the database and no uploaded_file" do
165
+
166
+ include UploadColumnSpecHelper
167
+
168
+ before do
169
+ @options = mock('options', :null_object => true)
170
+ Entry.upload_column(:avatar, @options)
171
+
172
+ @entry = disconnected_model(Entry)
173
+ @entry.stub!(:inspect).and_return('<#Entry>')
174
+ @string = mock('some string')
175
+ @entry.should_receive(:[]).with(:avatar).at_least(:once).and_return(@string)
176
+ end
177
+
178
+ it "should retrieve the file from the database" do
179
+ uploaded_file = mock('uploaded file')
180
+
181
+ UploadColumn::UploadedFile.should_receive(:retrieve).with(@string, @entry, :avatar, @options).and_return(uploaded_file)
182
+
183
+ @entry.avatar.should == uploaded_file
184
+ end
185
+ end
186
+
187
+ describe "saving uploaded files" do
188
+
189
+ include UploadColumnSpecHelper
190
+
191
+ before do
192
+ setup_standard_mocking
193
+ end
194
+
195
+ it "should call save on the uploaded file if they are temporary files" do
196
+ UploadColumn::UploadedFile.should_receive(:upload).with(@file, @entry, :avatar, @options).and_return(@uploaded_file)
197
+
198
+ @uploaded_file.should_receive(:tempfile?).and_return(true)
199
+ @uploaded_file.should_receive(:save)
200
+ @entry.avatar = @file
201
+
202
+ @entry.send(:save_uploaded_files)
203
+ end
204
+
205
+ it "should not call save on the uploaded file if they are not temporary files" do
206
+ UploadColumn::UploadedFile.should_receive(:upload).with(@file, @entry, :avatar, @options).and_return(@uploaded_file)
207
+
208
+ @uploaded_file.should_receive(:tempfile?).and_return(false)
209
+ @uploaded_file.should_not_receive(:save)
210
+ @entry.avatar = @file
211
+
212
+ @entry.send(:save_uploaded_files)
213
+ end
214
+
215
+ it "should happen automatically" do
216
+ # TODO: hmmm, how to test this? do we have to rely on an integration test?
217
+ #@entry.should_receive(:save_uploaded_files)
218
+ #@entry.save
219
+ end
220
+
221
+ end
222
+
223
+ describe "fetching a temp value" do
224
+
225
+ include UploadColumnSpecHelper
226
+
227
+ setup do
228
+ setup_standard_mocking
229
+
230
+ UploadColumn::UploadedFile.should_receive(:upload).with(@file, @entry, :avatar, @options).and_return(@uploaded_file)
231
+
232
+ @temp_value = '12345.1234.12345/somewhere.png'
233
+
234
+ @uploaded_file.should_receive(:temp_value).and_return(@temp_value)
235
+ @entry.avatar = @file
236
+ end
237
+
238
+ it "should fetch the value from the uploaded file" do
239
+ @entry.avatar_temp.should == @temp_value
240
+ end
241
+
242
+ end
243
+
244
+ describe "assigning a tempfile" do
245
+
246
+ include UploadColumnSpecHelper
247
+
248
+ setup do
249
+ setup_standard_mocking
250
+ end
251
+
252
+ it "should not override a new file" do
253
+ UploadColumn::UploadedFile.should_receive(:upload).with(@file, @entry, :avatar, @options).and_return(@uploaded_file)
254
+ @uploaded_file.stub!(:new_file?).and_return(true)
255
+ @entry.avatar = @file
256
+
257
+ temp_value = '12345.1234.12345/somewhere.png'
258
+
259
+ UploadColumn::UploadedFile.should_not_receive(:retrieve_temp)
260
+ @entry.avatar_temp = temp_value
261
+
262
+ @entry.avatar.should == @uploaded_file
263
+ end
264
+
265
+ it "should override a file that is not new" do
266
+ UploadColumn::UploadedFile.should_receive(:upload).with(@file, @entry, :avatar, @options).and_return(@uploaded_file)
267
+ @uploaded_file.stub!(:new_file?).and_return(false)
268
+ @entry.avatar = @file
269
+
270
+ temp_value = '12345.1234.12345/somewhere.png'
271
+
272
+ retrieved_file = mock('a retrieved file')
273
+ retrieved_file.should_receive(:actual_filename).and_return('walruss.png')
274
+ UploadColumn::UploadedFile.should_receive(:retrieve_temp).with(temp_value, @entry, :avatar, @options).and_return(retrieved_file)
275
+ @entry.should_receive(:[]=).with(:avatar, 'walruss.png')
276
+
277
+ @entry.avatar_temp = temp_value
278
+
279
+ @entry.avatar.should == retrieved_file
280
+ end
281
+
282
+ it "should set the file if there is none" do
283
+
284
+ temp_value = '12345.1234.12345/somewhere.png'
285
+
286
+ retrieved_file = mock('a retrieved file')
287
+ retrieved_file.should_receive(:actual_filename).and_return('walruss.png')
288
+ UploadColumn::UploadedFile.should_receive(:retrieve_temp).with(temp_value, @entry, :avatar, @options).and_return(retrieved_file)
289
+ @entry.should_receive(:[]=).with(:avatar, 'walruss.png')
290
+
291
+ @entry.avatar_temp = temp_value
292
+
293
+ @entry.avatar.should == retrieved_file
294
+ end
295
+
296
+ end
297
+
298
+ describe "assigning nil to temp" do
299
+
300
+ include UploadColumnSpecHelper
301
+
302
+ before(:each) do
303
+ setup_standard_mocking
304
+ end
305
+
306
+ it "should do nothing" do
307
+ UploadColumn::UploadedFile.stub!(:upload).and_return(@uploaded_file)
308
+ @uploaded_file.stub!(:new_file?).and_return(false)
309
+ @entry.avatar = @file
310
+
311
+ UploadColumn::UploadedFile.should_not_receive(:retrieve_temp)
312
+ @entry.should_not_receive(:[]=)
313
+
314
+ lambda {
315
+ @entry.avatar_temp = nil
316
+ }.should_not change(@entry, :avatar)
317
+ end
318
+ end
319
+
320
+ describe "assigning a blank string to temp" do
321
+
322
+ include UploadColumnSpecHelper
323
+
324
+ before(:each) do
325
+ setup_standard_mocking
326
+ end
327
+
328
+ it "should do nothing" do
329
+ UploadColumn::UploadedFile.should_receive(:upload).with(@file, @entry, :avatar, @options).and_return(@uploaded_file)
330
+ @uploaded_file.stub!(:new_file?).and_return(false)
331
+ @entry.avatar = @file
332
+
333
+ UploadColumn::UploadedFile.should_not_receive(:retrieve_temp)
334
+ @entry.should_not_receive(:[]=)
335
+
336
+ lambda {
337
+ @entry.avatar_temp = ""
338
+ }.should_not change(@entry, :avatar)
339
+ end
340
+ end
341
+
342
+ describe "an upload column with no file" do
343
+
344
+ include UploadColumnSpecHelper
345
+
346
+ before(:each) do
347
+ setup_standard_mocking
348
+ end
349
+
350
+ it "should return no value" do
351
+ @entry.avatar.should be_nil
352
+ end
353
+
354
+ it "should return no temp_value" do
355
+ @entry.avatar_temp.should be_nil
356
+ end
357
+
358
+ it "should return nothing in the _public_path method" do
359
+ @entry.avatar_public_path.should == nil
360
+ end
361
+
362
+ it "should return nothing in the _path method" do
363
+ @entry.avatar_path.should == nil
364
+ end
365
+ end
366
+
367
+ describe "an upload column with an uploaded file" do
368
+
369
+ include UploadColumnSpecHelper
370
+
371
+ before(:each) do
372
+ setup_standard_mocking
373
+ UploadColumn::UploadedFile.stub!(:upload).and_return(@uploaded_file)
374
+ @entry.avatar = @file
375
+ end
376
+
377
+ it "should delegate the _public_path method to the column" do
378
+ @uploaded_file.should_receive(:public_path).and_return('/url/to/file.exe')
379
+ @entry.avatar_public_path.should == '/url/to/file.exe'
380
+ end
381
+
382
+ it "should delegate the _path method to the column" do
383
+ @uploaded_file.should_receive(:path).and_return('/path/to/file.exe')
384
+ @entry.avatar_path.should == '/path/to/file.exe'
385
+ end
386
+
387
+ end
388
+
389
+ describe "an upload column with different versions and no uploaded file" do
390
+
391
+ include UploadColumnSpecHelper
392
+
393
+ before(:each) do
394
+ setup_version_mocking # sets up a column with thumb and large versions
395
+ end
396
+
397
+ it "should return nil for the _thumb method" do
398
+ @entry.avatar_thumb.should == nil
399
+ end
400
+
401
+ it "should return nil for the _large method" do
402
+ @entry.avatar_large.should == nil
403
+ end
404
+
405
+ it "should return nil for the _thumb_url method" do
406
+ @entry.avatar_thumb_public_path.should == nil
407
+ end
408
+
409
+ it "should return nil for the _large_path method" do
410
+ @entry.avatar_large_path.should == nil
411
+ end
412
+
413
+ end
414
+
415
+ describe "an upload column with different versions and an uploaded file" do
416
+
417
+ include UploadColumnSpecHelper
418
+
419
+ before(:each) do
420
+ setup_version_mocking # sets up a column with thumb and large versions
421
+ UploadColumn::UploadedFile.stub!(:upload).and_return(@uploaded_file)
422
+ @entry.avatar = @file
423
+ end
424
+
425
+ it "should delegate the _thumb method to the column" do
426
+ thumb = mock('thumb')
427
+ @uploaded_file.should_receive(:thumb).and_return(thumb)
428
+ @entry.avatar_thumb.should == thumb
429
+ end
430
+
431
+ it "should delegate the _large method to the column" do
432
+ large = mock('large')
433
+ @uploaded_file.should_receive(:large).and_return(large)
434
+ @entry.avatar_large.should == large
435
+ end
436
+
437
+ it "should delegate the _thumb_url method to the column" do
438
+ thumb = mock('thumb')
439
+ thumb.should_receive(:public_path).and_return('/url/to/file.exe')
440
+ @uploaded_file.should_receive(:thumb).and_return(thumb)
441
+
442
+ @entry.avatar_thumb_public_path.should == '/url/to/file.exe'
443
+ end
444
+
445
+ it "should delegate the _large_path method to the column" do
446
+ large = mock('large')
447
+ large.should_receive(:path).and_return('/path/to/file.exe')
448
+ @uploaded_file.should_receive(:large).and_return(large)
449
+
450
+ @entry.avatar_large_path.should == '/path/to/file.exe'
451
+ end
452
+
453
+ end
454
+
455
+ describe "uploading a file that fails an integrity check" do
456
+
457
+ include UploadColumnSpecHelper
458
+
459
+ before(:all) do
460
+ Entry.validates_integrity_of :avatar
461
+ end
462
+
463
+ before(:each) do
464
+ setup_standard_mocking
465
+ end
466
+
467
+ it "should set the column to nil" do
468
+ UploadColumn::UploadedFile.should_receive(:upload).and_raise(UploadColumn::IntegrityError.new('something'))
469
+ @entry.avatar = @file
470
+
471
+ @entry.avatar.should be_nil
472
+ end
473
+
474
+ it "should fail an integrity validation" do
475
+ UploadColumn::UploadedFile.should_receive(:upload).and_raise(UploadColumn::IntegrityError.new('something'))
476
+ @entry.avatar = @file
477
+
478
+ @entry.should_not be_valid
479
+ @entry.errors.on(:avatar).should == 'something'
480
+ end
481
+ end
482
+
483
+ describe UploadColumn::ActiveRecordExtension::ClassMethods, ".image_column" do
484
+
485
+ include UploadColumnSpecHelper
486
+
487
+ before(:each) do
488
+ @class = Class.new(ActiveRecord::Base)
489
+ @class.send(:include, UploadColumn)
490
+ end
491
+
492
+ it "should call an upload column with some specialized options" do
493
+ @class.should_receive(:upload_column).with(:sicada,
494
+ :manipulator => UploadColumn::Manipulators::RMagick,
495
+ :root_dir => File.join(RAILS_ROOT, 'public', 'images'),
496
+ :web_root => '/images',
497
+ :monkey => 'blah',
498
+ :extensions => UploadColumn.image_extensions
499
+ )
500
+ @class.image_column(:sicada, :monkey => 'blah')
501
+ end
502
+ end
503
+
504
+ describe UploadColumn::ActiveRecordExtension::ClassMethods, ".validate_integrity" do
505
+
506
+ include UploadColumnSpecHelper
507
+
508
+ it "should change the options for this upload_column" do
509
+ Entry.upload_column :avatar
510
+ Entry.reflect_on_upload_columns[:avatar].options[:validate_integrity].should be_nil
511
+ Entry.validates_integrity_of :avatar
512
+ Entry.reflect_on_upload_columns[:avatar].options[:validate_integrity].should == true
513
+ end
514
+ end
@@ -0,0 +1,148 @@
1
+ class BeIdenticalWith
2
+ def initialize(expected)
3
+ @expected = expected
4
+ end
5
+ def matches?(actual)
6
+ @actual = actual
7
+ FileUtils.identical?(@actual, @expected)
8
+ end
9
+ def failure_message
10
+ "expected #{@actual.inspect} to be identical with #{@expected.inspect}"
11
+ end
12
+ def negative_failure_message
13
+ "expected #{@actual.inspect} to not be identical with #{@expected.inspect}"
14
+ end
15
+ end
16
+
17
+ def be_identical_with(expected)
18
+ BeIdenticalWith.new(expected)
19
+ end
20
+
21
+ class ExistsPredicate
22
+
23
+ def matches?(actual)
24
+ actual.exists?
25
+ end
26
+ def failure_message
27
+ "expected #{@actual.inspect} to exist, it doesn't."
28
+ end
29
+ def negative_failure_message
30
+ "expected #{@actual.inspect} to not exist, yet it does."
31
+ end
32
+ end
33
+
34
+ def be_in_existence
35
+ ExistsPredicate.new
36
+ end
37
+
38
+ class MatchPath
39
+ def initialize(*expected)
40
+ if(expected.size < 2)
41
+ @expected = File.expand_path(expected.first)
42
+ else
43
+ @expected = expected.map {|e| e.is_a?(Regexp) ? e.to_s : Regexp.escape(e)}
44
+ @expected = File.expand_path(File.join(*@expected), RAILS_ROOT)
45
+ @expected = %r(^#{@expected}$)
46
+ end
47
+ end
48
+ def matches?(actual)
49
+ @actual = actual
50
+ if @expected.is_a?(Regexp)
51
+ File.expand_path(actual) =~ @expected
52
+ else
53
+ File.expand_path(actual) == @expected
54
+ end
55
+ end
56
+ def failure_message
57
+ "expected #{@actual.inspect} to match #{@expected}."
58
+ end
59
+ def negative_failure_message
60
+ "expected #{@actual.inspect} to not match #{@expected}, yet it does."
61
+ end
62
+ end
63
+
64
+ # Match a path without bothering whether they are formatted the same way.
65
+ # can also take several parameters, any number of which may be regexes
66
+ def match_path(*expected)
67
+ MatchPath.new(*expected)
68
+ end
69
+
70
+ class HavePermissions
71
+ def initialize(expected)
72
+ @expected = expected
73
+ end
74
+
75
+ def matches?(actual)
76
+ @actual = actual
77
+ # Satisfy expectation here. Return false or raise an error if it's not met.
78
+ (File.stat(@actual.path).mode & 0777) == @expected
79
+ end
80
+
81
+ def failure_message
82
+ "expected #{@actual.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0777).to_s(8)}"
83
+ end
84
+
85
+ def negative_failure_message
86
+ "expected #{@actual.inspect} not to have permissions #{@expected.to_s(8)}, but it did"
87
+ end
88
+ end
89
+
90
+ def have_permissions(expected)
91
+ HavePermissions.new(expected)
92
+ end
93
+
94
+ class BeNoLargerThan
95
+ def initialize(width, height)
96
+ @width, @height = width, height
97
+ end
98
+
99
+ def matches?(actual)
100
+ @actual = actual
101
+ # Satisfy expectation here. Return false or raise an error if it's not met.
102
+ require 'RMagick'
103
+ img = ::Magick::Image.read(@actual.path).first
104
+ @actual_width = img.columns
105
+ @actual_height = img.rows
106
+ @actual_width <= @width && @actual_height <= @height
107
+ end
108
+
109
+ def failure_message
110
+ "expected #{@actual.inspect} to be no larger than #{@width} by #{@height}, but it was #{@actual_height} by #{@actual_width}."
111
+ end
112
+
113
+ def negative_failure_message
114
+ "expected #{@actual.inspect} to be larger than #{@width} by #{@height}, but it wasn't."
115
+ end
116
+ end
117
+
118
+ def be_no_larger_than(width, height)
119
+ BeNoLargerThan.new(width, height)
120
+ end
121
+
122
+ class HaveTheExactDimensionsOf
123
+ def initialize(width, height)
124
+ @width, @height = width, height
125
+ end
126
+
127
+ def matches?(actual)
128
+ @actual = actual
129
+ # Satisfy expectation here. Return false or raise an error if it's not met.
130
+ require 'RMagick'
131
+ img = ::Magick::Image.read(@actual.path).first
132
+ @actual_width = img.columns
133
+ @actual_height = img.rows
134
+ @actual_width == @width && @actual_height == @height
135
+ end
136
+
137
+ def failure_message
138
+ "expected #{@actual.inspect} to have an exact size of #{@width} by #{@height}, but it was #{@actual_height} by #{@actual_width}."
139
+ end
140
+
141
+ def negative_failure_message
142
+ "expected #{@actual.inspect} not to have an exact size of #{@width} by #{@height}, but it did."
143
+ end
144
+ end
145
+
146
+ def have_the_exact_dimensions_of(width, height)
147
+ HaveTheExactDimensionsOf.new(width, height)
148
+ end
Binary file
@@ -0,0 +1 @@
1
+ this is certainly not a JPEG image
Binary file
Binary file
Binary file
Binary file