bulldog 0.0.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/.gitignore +2 -0
- data/DESCRIPTION.txt +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +64 -0
- data/VERSION +1 -0
- data/bulldog.gemspec +157 -0
- data/lib/bulldog.rb +95 -0
- data/lib/bulldog/attachment.rb +49 -0
- data/lib/bulldog/attachment/base.rb +167 -0
- data/lib/bulldog/attachment/has_dimensions.rb +94 -0
- data/lib/bulldog/attachment/image.rb +63 -0
- data/lib/bulldog/attachment/maybe.rb +229 -0
- data/lib/bulldog/attachment/none.rb +37 -0
- data/lib/bulldog/attachment/pdf.rb +63 -0
- data/lib/bulldog/attachment/unknown.rb +11 -0
- data/lib/bulldog/attachment/video.rb +143 -0
- data/lib/bulldog/error.rb +5 -0
- data/lib/bulldog/has_attachment.rb +214 -0
- data/lib/bulldog/interpolation.rb +73 -0
- data/lib/bulldog/missing_file.rb +12 -0
- data/lib/bulldog/processor.rb +5 -0
- data/lib/bulldog/processor/argument_tree.rb +116 -0
- data/lib/bulldog/processor/base.rb +124 -0
- data/lib/bulldog/processor/ffmpeg.rb +172 -0
- data/lib/bulldog/processor/image_magick.rb +134 -0
- data/lib/bulldog/processor/one_shot.rb +19 -0
- data/lib/bulldog/reflection.rb +234 -0
- data/lib/bulldog/saved_file.rb +19 -0
- data/lib/bulldog/stream.rb +186 -0
- data/lib/bulldog/style.rb +38 -0
- data/lib/bulldog/style_set.rb +101 -0
- data/lib/bulldog/tempfile.rb +28 -0
- data/lib/bulldog/util.rb +92 -0
- data/lib/bulldog/validations.rb +68 -0
- data/lib/bulldog/vector2.rb +18 -0
- data/rails/init.rb +9 -0
- data/script/console +8 -0
- data/spec/data/empty.txt +0 -0
- data/spec/data/test.jpg +0 -0
- data/spec/data/test.mov +0 -0
- data/spec/data/test.pdf +0 -0
- data/spec/data/test.png +0 -0
- data/spec/data/test2.jpg +0 -0
- data/spec/helpers/image_creation.rb +8 -0
- data/spec/helpers/temporary_directory.rb +25 -0
- data/spec/helpers/temporary_models.rb +76 -0
- data/spec/helpers/temporary_values.rb +102 -0
- data/spec/helpers/test_upload_files.rb +108 -0
- data/spec/helpers/time_travel.rb +20 -0
- data/spec/integration/data/test.jpg +0 -0
- data/spec/integration/lifecycle_hooks_spec.rb +213 -0
- data/spec/integration/processing_image_attachments.rb +72 -0
- data/spec/integration/processing_video_attachments_spec.rb +82 -0
- data/spec/integration/saving_an_attachment_spec.rb +31 -0
- data/spec/matchers/file_operations.rb +159 -0
- data/spec/spec_helper.rb +76 -0
- data/spec/unit/attachment/base_spec.rb +311 -0
- data/spec/unit/attachment/image_spec.rb +128 -0
- data/spec/unit/attachment/maybe_spec.rb +126 -0
- data/spec/unit/attachment/pdf_spec.rb +137 -0
- data/spec/unit/attachment/video_spec.rb +176 -0
- data/spec/unit/attachment_spec.rb +61 -0
- data/spec/unit/has_attachment_spec.rb +700 -0
- data/spec/unit/interpolation_spec.rb +108 -0
- data/spec/unit/processor/argument_tree_spec.rb +159 -0
- data/spec/unit/processor/ffmpeg_spec.rb +467 -0
- data/spec/unit/processor/image_magick_spec.rb +260 -0
- data/spec/unit/processor/one_shot_spec.rb +70 -0
- data/spec/unit/reflection_spec.rb +338 -0
- data/spec/unit/stream_spec.rb +234 -0
- data/spec/unit/style_set_spec.rb +44 -0
- data/spec/unit/style_spec.rb +51 -0
- data/spec/unit/validations_spec.rb +491 -0
- data/spec/unit/vector2_spec.rb +27 -0
- data/tasks/bulldog_tasks.rake +4 -0
- metadata +193 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Attachment do
|
4
|
+
use_model_class(:Thing)
|
5
|
+
|
6
|
+
before do
|
7
|
+
Thing.has_attachment :attachment
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".of_type" do
|
11
|
+
before do
|
12
|
+
@thing = Thing.new
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "when the type is nil" do
|
16
|
+
before do
|
17
|
+
@type = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "when the stream is nil" do
|
21
|
+
before do
|
22
|
+
@stream = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return a None attachment" do
|
26
|
+
attachment = Attachment.of_type(@type, @thing, :attachment, @stream)
|
27
|
+
attachment.should be_a(Attachment::None)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "when the stream is not nil" do
|
32
|
+
before do
|
33
|
+
@stream = Stream.new(test_image_file)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return a Base attachment" do
|
37
|
+
attachment = Attachment.of_type(@type, @thing, :attachment, @stream)
|
38
|
+
attachment.should be_a(Attachment::Base)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "when the type is not nil" do
|
44
|
+
before do
|
45
|
+
@type = :video
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should return an attachment of the specified type" do
|
49
|
+
attachment = Attachment.of_type(@type, @thing, :attachment, @stream)
|
50
|
+
attachment.should be_a(Attachment::Video)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".none" do
|
56
|
+
it "should return a None attachment" do
|
57
|
+
attachment = Attachment.none(Thing.new, :attachment)
|
58
|
+
attachment.should be_a(Attachment::None)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,700 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HasAttachment do
|
4
|
+
describe ".has_attachment" do
|
5
|
+
use_model_class(:Thing)
|
6
|
+
|
7
|
+
it "should provide accessors for the attachment" do
|
8
|
+
Thing.has_attachment :photo
|
9
|
+
thing = Thing.new
|
10
|
+
thing.photo.should be_a(Attachment::Maybe)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should provide a query method for the attachment" do
|
14
|
+
Thing.has_attachment :photo
|
15
|
+
thing = Thing.new
|
16
|
+
file = uploaded_file
|
17
|
+
thing.photo?.should be_false
|
18
|
+
thing.photo = file
|
19
|
+
thing.photo?.should be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should configure the existing attachment declared if one exists" do
|
23
|
+
Thing.has_attachment :photo do
|
24
|
+
style :one
|
25
|
+
end
|
26
|
+
Thing.has_attachment :photo do
|
27
|
+
style :two
|
28
|
+
end
|
29
|
+
Thing.attachment_reflections[:photo].styles[:one].should_not be_blank
|
30
|
+
Thing.attachment_reflections[:photo].styles[:two].should_not be_blank
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "when an attachment is inherited" do
|
34
|
+
use_model_class(:Parent)
|
35
|
+
|
36
|
+
before do
|
37
|
+
# Create the attachment before subclassing
|
38
|
+
Parent.has_attachment :photo do
|
39
|
+
style :one
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
use_model_class(:Child => :Parent)
|
44
|
+
|
45
|
+
it "should not affect the superclasses' attachment" do
|
46
|
+
Child.has_attachment :photo do
|
47
|
+
style :two
|
48
|
+
end
|
49
|
+
Child.attachment_reflections[:photo].styles[:two].should_not be_blank
|
50
|
+
Parent.attachment_reflections[:photo].styles[:two].should be_blank
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".attachment_reflections" do
|
56
|
+
use_model_class(:Thing)
|
57
|
+
|
58
|
+
it "should allow reflection on the field names" do
|
59
|
+
Thing.has_attachment :photo
|
60
|
+
Thing.attachment_reflections[:photo].name.should == :photo
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#process_attachment" do
|
65
|
+
use_model_class(:Thing)
|
66
|
+
|
67
|
+
describe "when there is an attachment set" do
|
68
|
+
it "should trigger the configured callbacks" do
|
69
|
+
args = nil
|
70
|
+
Thing.has_attachment :photo do
|
71
|
+
style :normal
|
72
|
+
process(:on => :my_event){|*args|}
|
73
|
+
end
|
74
|
+
thing = Thing.new(:photo => uploaded_file)
|
75
|
+
thing.process_attachment(:photo, :my_event, 1, 2)
|
76
|
+
args.should == [1, 2]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "when there is no attachment set" do
|
81
|
+
it "should not trigger any callbacks" do
|
82
|
+
args = nil
|
83
|
+
Thing.has_attachment :photo do
|
84
|
+
style :normal
|
85
|
+
process(:on => :my_event){|*args|}
|
86
|
+
end
|
87
|
+
thing = Thing.new(:photo => nil)
|
88
|
+
thing.process_attachment(:photo, :my_event, 1, 2)
|
89
|
+
args.should be_nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should raise an ArgumentError if the attachment name is invalid" do
|
94
|
+
args = nil
|
95
|
+
Thing.has_attachment :photo do
|
96
|
+
style :normal
|
97
|
+
process(:on => :my_event){|*args|}
|
98
|
+
end
|
99
|
+
thing = Thing.new
|
100
|
+
lambda do
|
101
|
+
thing.process_attachment(:fail, :my_event, 1, 2)
|
102
|
+
end.should raise_error(ArgumentError)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should evaluate the callback in the context of the specified processor" do
|
106
|
+
with_temporary_constant_value Processor, :Test, Class.new(Processor::Base) do
|
107
|
+
context = nil
|
108
|
+
Thing.has_attachment :photo do
|
109
|
+
style :normal
|
110
|
+
process(:on => :my_event, :with => :test){context = self}
|
111
|
+
end
|
112
|
+
thing = Thing.new(:photo => uploaded_file)
|
113
|
+
thing.process_attachment(:photo, :my_event)
|
114
|
+
context.should be_a(Processor::Test)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should default to a base processor instance" do
|
119
|
+
context = nil
|
120
|
+
Thing.has_attachment :photo do
|
121
|
+
style :normal
|
122
|
+
process(:on => :my_event){context = self}
|
123
|
+
end
|
124
|
+
thing = Thing.new(:photo => uploaded_file)
|
125
|
+
thing.process_attachment(:photo, :my_event)
|
126
|
+
context.should be_a(Processor::Base)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "object lifecycle" do
|
131
|
+
outline "building a record" do
|
132
|
+
with_model_class :Thing do
|
133
|
+
spec = self
|
134
|
+
|
135
|
+
Thing.has_attachment :attachment do
|
136
|
+
detect_type_by{spec.detect_as}
|
137
|
+
path "#{spec.temporary_directory}/:style.jpg"
|
138
|
+
end
|
139
|
+
|
140
|
+
original_path = "#{temporary_directory}/original.jpg"
|
141
|
+
|
142
|
+
thing = Thing.new(:attachment => eval(value.to_s))
|
143
|
+
thing.attachment.should be_a(attachment_class)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
fields :detect_as, :value , :attachment_class
|
148
|
+
values nil , nil , Attachment::None
|
149
|
+
values :image , nil , Attachment::None
|
150
|
+
values nil , :test_image_file, Attachment::Unknown
|
151
|
+
values :image , :test_image_file, Attachment::Image
|
152
|
+
|
153
|
+
outline "loading a record" do
|
154
|
+
columns = store_file_name ? {:attachment_file_name => :string} : {}
|
155
|
+
with_model_class :Thing, columns do
|
156
|
+
spec = self
|
157
|
+
|
158
|
+
Thing.has_attachment :attachment do
|
159
|
+
detect_type_by{spec.detect_as}
|
160
|
+
path "#{spec.temporary_directory}/:style.jpg"
|
161
|
+
end
|
162
|
+
|
163
|
+
original_path = "#{temporary_directory}/original.jpg"
|
164
|
+
|
165
|
+
thing = Thing.create(:attachment => eval(value_saved.to_s))
|
166
|
+
if file_exists?
|
167
|
+
FileUtils.touch(original_path)
|
168
|
+
else
|
169
|
+
FileUtils.rm_f(original_path)
|
170
|
+
end
|
171
|
+
|
172
|
+
thing = Thing.find(thing.id)
|
173
|
+
thing.attachment.should be_a(attachment_class)
|
174
|
+
if stream_missing.nil?
|
175
|
+
thing.attachment.stream.should be_nil
|
176
|
+
else
|
177
|
+
thing.attachment.stream.missing?.should == stream_missing
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
fields :store_file_name, :detect_as, :value_saved , :file_exists?, :attachment_class , :stream_missing
|
183
|
+
values false , nil , nil , false , Attachment::None , nil
|
184
|
+
values false , nil , nil , true , Attachment::Unknown, false
|
185
|
+
values false , nil , :test_image_file, false , Attachment::None , nil
|
186
|
+
values false , nil , :test_image_file, true , Attachment::Unknown, false
|
187
|
+
values false , :image , nil , false , Attachment::None , nil
|
188
|
+
values false , :image , nil , true , Attachment::Image , false
|
189
|
+
values false , :image , :test_image_file, false , Attachment::None , nil
|
190
|
+
values false , :image , :test_image_file, true , Attachment::Image , false
|
191
|
+
values true , nil , nil , false , Attachment::None , nil
|
192
|
+
values true , nil , nil , true , Attachment::None , nil
|
193
|
+
values true , nil , :test_image_file, false , Attachment::Unknown, true
|
194
|
+
values true , nil , :test_image_file, true , Attachment::Unknown, false
|
195
|
+
values true , :image , nil , false , Attachment::None , nil
|
196
|
+
values true , :image , nil , true , Attachment::None , nil
|
197
|
+
values true , :image , :test_image_file, false , Attachment::Image , true
|
198
|
+
values true , :image , :test_image_file, true , Attachment::Image , false
|
199
|
+
|
200
|
+
describe "when no attributes are stored" do
|
201
|
+
use_model_class(:Thing)
|
202
|
+
|
203
|
+
before do
|
204
|
+
@file = uploaded_file('test.jpg', 'test.jpg')
|
205
|
+
end
|
206
|
+
|
207
|
+
def configure(&block)
|
208
|
+
Thing.attachment_reflections[:photo].configure(&block)
|
209
|
+
end
|
210
|
+
|
211
|
+
describe "saving the record" do
|
212
|
+
it "should create the original file as long as :basename and :extension are not used" do
|
213
|
+
spec = self
|
214
|
+
Thing.has_attachment :photo do
|
215
|
+
path "#{spec.temporary_directory}/:style.jpg"
|
216
|
+
end
|
217
|
+
thing = Thing.new(:photo => @file)
|
218
|
+
lambda{thing.save}.should create_file("#{temporary_directory}/original.jpg")
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should raise an error if :basename is used" do
|
222
|
+
spec = self
|
223
|
+
Thing.has_attachment :photo do
|
224
|
+
path "#{spec.temporary_directory}/:basename"
|
225
|
+
end
|
226
|
+
thing = Thing.new(:photo => @file)
|
227
|
+
lambda{thing.save}.should raise_error(Interpolation::Error)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should raise an error if :extension is used" do
|
231
|
+
spec = self
|
232
|
+
Thing.has_attachment :photo do
|
233
|
+
path "#{spec.temporary_directory}/photo.:extension"
|
234
|
+
end
|
235
|
+
thing = Thing.new(:photo => @file)
|
236
|
+
lambda{thing.save}.should raise_error(Interpolation::Error)
|
237
|
+
end
|
238
|
+
|
239
|
+
describe "when the record is reloaded" do
|
240
|
+
before do
|
241
|
+
spec = self
|
242
|
+
Thing.has_attachment :photo do
|
243
|
+
path "#{spec.temporary_directory}/:style.jpg"
|
244
|
+
end
|
245
|
+
@thing = Thing.new(:photo => @file)
|
246
|
+
@thing.save
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should load the saved original file" do
|
250
|
+
@thing.photo.should be_present
|
251
|
+
@thing.photo.path.should == "#{temporary_directory}/original.jpg"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "when the file name is stored" do
|
258
|
+
use_model_class(:Thing,
|
259
|
+
:photo_file_name => :string,
|
260
|
+
:photo_content_type => :string,
|
261
|
+
:photo_file_size => :integer)
|
262
|
+
|
263
|
+
before do
|
264
|
+
spec = self
|
265
|
+
Thing.has_attachment :photo do
|
266
|
+
path "#{spec.temporary_directory}/photos/:id-:style.:extension"
|
267
|
+
style :small, :size => '10x10'
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def configure(&block)
|
272
|
+
Thing.attachment_reflections[:photo].configure(&block)
|
273
|
+
end
|
274
|
+
|
275
|
+
def original_path
|
276
|
+
"#{temporary_directory}/photos/#{@thing.id}-original.jpg"
|
277
|
+
end
|
278
|
+
|
279
|
+
def small_path
|
280
|
+
"#{temporary_directory}/photos/#{@thing.id}-small.jpg"
|
281
|
+
end
|
282
|
+
|
283
|
+
describe "instantiating the record" do
|
284
|
+
describe "when the record is new" do
|
285
|
+
before do
|
286
|
+
@thing = Thing.new
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should have no stored attributes set" do
|
290
|
+
@thing.photo_file_name.should be_nil
|
291
|
+
@thing.photo_content_type.should be_nil
|
292
|
+
@thing.photo_file_size.should be_nil
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe "when the record already exists" do
|
297
|
+
describe "when a file name is set, and the original file exists" do
|
298
|
+
def instantiate
|
299
|
+
file = uploaded_file('test.jpg', 'test.jpg')
|
300
|
+
thing = Thing.create(:photo => file)
|
301
|
+
@thing = Thing.find(thing.id)
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should have stored attributes set" do
|
305
|
+
instantiate
|
306
|
+
@thing.photo_file_name.should == 'test.jpg'
|
307
|
+
@thing.photo_content_type.split(/;/).first.should == "image/jpeg"
|
308
|
+
@thing.photo_file_size.should == File.size(test_path('test.jpg'))
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
describe "when the no file name is set, and the original file does not exist" do
|
313
|
+
before do
|
314
|
+
thing = Thing.create
|
315
|
+
@thing = Thing.find(thing.id)
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should have no stored attributes set" do
|
319
|
+
@thing.photo_file_name.should be_nil
|
320
|
+
@thing.photo_content_type.should be_nil
|
321
|
+
@thing.photo_file_size.should be_nil
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "when a file name is set, but the original file is missing" do
|
326
|
+
def instantiate
|
327
|
+
file = uploaded_file('test.jpg', 'test.jpg')
|
328
|
+
@thing = Thing.create(:photo => file)
|
329
|
+
File.unlink(original_path)
|
330
|
+
@thing = Thing.find(@thing.id)
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should have stored attributes set" do
|
334
|
+
instantiate
|
335
|
+
@thing.photo_file_name.should == 'test.jpg'
|
336
|
+
@thing.photo_content_type == "image/jpeg"
|
337
|
+
@thing.photo_file_size.should == File.size(test_path('test.jpg'))
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
describe "when the record exists and there is no attachment" do
|
343
|
+
before do
|
344
|
+
thing = Thing.create
|
345
|
+
@thing = Thing.find(thing.id)
|
346
|
+
end
|
347
|
+
|
348
|
+
describe "when an attachment is assigned" do
|
349
|
+
before do
|
350
|
+
@file = uploaded_file('test.jpg', 'test.jpg')
|
351
|
+
end
|
352
|
+
|
353
|
+
it "should set the stored attributes" do
|
354
|
+
@thing.photo = @file
|
355
|
+
@thing.photo_file_name.should == 'test.jpg'
|
356
|
+
@thing.photo_content_type.split(/;/).first.should == "image/jpeg"
|
357
|
+
@thing.photo_file_size.should == File.size(test_path('test.jpg'))
|
358
|
+
end
|
359
|
+
|
360
|
+
it "should not create the original file" do
|
361
|
+
lambda do
|
362
|
+
@thing.photo = @file
|
363
|
+
end.should_not create_file(original_path)
|
364
|
+
end
|
365
|
+
|
366
|
+
describe "when the record is saved" do
|
367
|
+
before do
|
368
|
+
@thing.photo = @file
|
369
|
+
end
|
370
|
+
|
371
|
+
it "should create the original file" do
|
372
|
+
lambda do
|
373
|
+
@thing.save.should be_true
|
374
|
+
end.should create_file(original_path)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
describe "when the record exists and there is an attachment" do
|
381
|
+
before do
|
382
|
+
@old_file = test_file('test.jpg')
|
383
|
+
thing = Thing.create(:photo => @old_file)
|
384
|
+
@thing = Thing.find(thing.id)
|
385
|
+
end
|
386
|
+
|
387
|
+
def old_original_path
|
388
|
+
original_path
|
389
|
+
end
|
390
|
+
|
391
|
+
def new_original_path
|
392
|
+
new_dirname = File.dirname(original_path)
|
393
|
+
new_basename = File.basename(original_path, 'jpg') + 'png'
|
394
|
+
File.join(new_dirname, new_basename)
|
395
|
+
end
|
396
|
+
|
397
|
+
describe "when a new attachment is assigned" do
|
398
|
+
before do
|
399
|
+
@new_file = test_file('test.png')
|
400
|
+
end
|
401
|
+
|
402
|
+
it "should set the stored attributes" do
|
403
|
+
@thing.photo = @new_file
|
404
|
+
@thing.photo_file_name.should == 'test.png'
|
405
|
+
@thing.photo_content_type.split(/;/).first.should == 'image/png'
|
406
|
+
@thing.photo_file_size.should == File.size(test_path('test.png'))
|
407
|
+
end
|
408
|
+
|
409
|
+
it "should not create the new original file yet" do
|
410
|
+
lambda do
|
411
|
+
@thing.photo = @new_file
|
412
|
+
end.should_not create_file(new_original_path)
|
413
|
+
end
|
414
|
+
|
415
|
+
it "should not delete the old original file yet" do
|
416
|
+
lambda do
|
417
|
+
@thing.photo = @new_file
|
418
|
+
end.should_not delete_file(old_original_path)
|
419
|
+
end
|
420
|
+
|
421
|
+
describe "when the record is saved" do
|
422
|
+
before do
|
423
|
+
@thing.photo = @new_file
|
424
|
+
end
|
425
|
+
|
426
|
+
it "should create the new original file" do
|
427
|
+
lambda do
|
428
|
+
@thing.save.should be_true
|
429
|
+
end.should create_file(new_original_path)
|
430
|
+
end
|
431
|
+
|
432
|
+
it "should delete the old original file" do
|
433
|
+
lambda do
|
434
|
+
@thing.save.should be_true
|
435
|
+
end.should delete_file(old_original_path)
|
436
|
+
end
|
437
|
+
|
438
|
+
it "should remove any old processed files" do
|
439
|
+
FileUtils.touch small_path
|
440
|
+
lambda do
|
441
|
+
@thing.save.should be_true
|
442
|
+
end.should delete_file(small_path)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
describe "when nil is assigned" do
|
448
|
+
it "should not remove the old original file yet" do
|
449
|
+
lambda do
|
450
|
+
@thing.photo = nil
|
451
|
+
end.should_not delete_file(original_path)
|
452
|
+
end
|
453
|
+
|
454
|
+
it "should not remove any old processed files yet" do
|
455
|
+
FileUtils.touch small_path
|
456
|
+
lambda do
|
457
|
+
@thing.photo = nil
|
458
|
+
end.should_not delete_file(small_path)
|
459
|
+
end
|
460
|
+
|
461
|
+
describe "when the record is saved" do
|
462
|
+
before do
|
463
|
+
@thing.photo = nil
|
464
|
+
end
|
465
|
+
|
466
|
+
it "should remove the old original file" do
|
467
|
+
lambda do
|
468
|
+
@thing.save
|
469
|
+
end.should delete_file(original_path)
|
470
|
+
end
|
471
|
+
|
472
|
+
it "should remove any old processed files" do
|
473
|
+
FileUtils.touch small_path
|
474
|
+
lambda do
|
475
|
+
@thing.save
|
476
|
+
end.should delete_file(small_path)
|
477
|
+
end
|
478
|
+
|
479
|
+
it "should remove any empty parent directories" do
|
480
|
+
lambda do
|
481
|
+
@thing.save
|
482
|
+
end.should delete_file("#{temporary_directory}/photos")
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
describe "#destroy" do
|
490
|
+
describe "when the record is new" do
|
491
|
+
before do
|
492
|
+
file = uploaded_file('test.jpg', 'test.jpg')
|
493
|
+
@thing = Thing.new(:photo => file)
|
494
|
+
end
|
495
|
+
|
496
|
+
it "should not raise an error" do
|
497
|
+
lambda{@thing.destroy}.should_not raise_error
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
describe "when the record existed but had no attachment" do
|
502
|
+
before do
|
503
|
+
thing = Thing.create
|
504
|
+
@thing = Thing.find(thing.id)
|
505
|
+
end
|
506
|
+
|
507
|
+
it "should not raise an error" do
|
508
|
+
lambda{@thing.destroy}.should_not raise_error
|
509
|
+
end
|
510
|
+
|
511
|
+
it "should not stop the record being destroyed" do
|
512
|
+
@thing.destroy
|
513
|
+
Thing.exists?(@thing).should be_false
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
describe "when the record existed and had an attachment" do
|
518
|
+
before do
|
519
|
+
file = uploaded_file('test.jpg', 'test.jpg')
|
520
|
+
thing = Thing.create(:photo => file)
|
521
|
+
@thing = Thing.find(thing.id)
|
522
|
+
end
|
523
|
+
|
524
|
+
it "should not stop the record being destroyed" do
|
525
|
+
@thing.destroy
|
526
|
+
Thing.exists?(@thing).should be_false
|
527
|
+
end
|
528
|
+
|
529
|
+
it "should remove the original file" do
|
530
|
+
lambda do
|
531
|
+
@thing.destroy
|
532
|
+
end.should delete_file(original_path)
|
533
|
+
end
|
534
|
+
|
535
|
+
it "should remove any processed files" do
|
536
|
+
FileUtils.touch small_path
|
537
|
+
lambda do
|
538
|
+
@thing.destroy
|
539
|
+
end.should delete_file(small_path)
|
540
|
+
end
|
541
|
+
|
542
|
+
it "should remove any empty parent directories" do
|
543
|
+
lambda do
|
544
|
+
@thing.destroy
|
545
|
+
end.should delete_file("#{temporary_directory}/photos")
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
describe "AR::Dirty" do
|
553
|
+
use_model_class(:Thing, :name => :string)
|
554
|
+
|
555
|
+
before do
|
556
|
+
spec = self
|
557
|
+
Thing.has_attachment :photo do
|
558
|
+
path "#{spec.temporary_directory}/:id.jpg"
|
559
|
+
end
|
560
|
+
thing = Thing.create(:name => 'old', :photo => uploaded_file)
|
561
|
+
@thing = Thing.find(thing.id)
|
562
|
+
end
|
563
|
+
|
564
|
+
def original_path
|
565
|
+
"#{temporary_directory}/#{@thing.id}.jpg"
|
566
|
+
end
|
567
|
+
|
568
|
+
describe "#ATTACHMENT_changed?" do
|
569
|
+
it "should return false if nothing has been assigned to the attachment" do
|
570
|
+
@thing.photo_changed?.should be_false
|
571
|
+
end
|
572
|
+
|
573
|
+
it "should return false if the same value has been assigned to the attachment" do
|
574
|
+
@thing.photo = @thing.photo.value
|
575
|
+
@thing.photo_changed?.should be_false
|
576
|
+
end
|
577
|
+
|
578
|
+
it "should return true if a new value has been assigned to the attachment" do
|
579
|
+
@thing.photo = uploaded_file
|
580
|
+
@thing.photo_changed?.should be_true
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
describe "#ATTACHMENT_was" do
|
585
|
+
it "should return the original value before assignment" do
|
586
|
+
original_photo = @thing.photo
|
587
|
+
@thing.photo_was.should equal(original_photo)
|
588
|
+
end
|
589
|
+
|
590
|
+
it "should return a clone of the original value after assignment" do
|
591
|
+
original_photo = @thing.photo
|
592
|
+
@thing.photo = uploaded_file
|
593
|
+
@thing.photo_was.should_not equal(original_photo)
|
594
|
+
@thing.photo_was.should == original_photo
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
describe "#changes" do
|
599
|
+
it "should return attachment changes along with other attribute changes" do
|
600
|
+
old_photo = @thing.photo
|
601
|
+
@thing.name = 'new'
|
602
|
+
@thing.photo = uploaded_file
|
603
|
+
@thing.changes.should == {
|
604
|
+
'name' => ['old', 'new'],
|
605
|
+
'photo' => [old_photo, @thing.photo],
|
606
|
+
}
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
describe "when the record is saved and only attachments have been modified" do
|
611
|
+
before do
|
612
|
+
@thing.photo = uploaded_file
|
613
|
+
end
|
614
|
+
|
615
|
+
it "should not hit the database"
|
616
|
+
|
617
|
+
it "should still save the attachment files" do
|
618
|
+
lambda do
|
619
|
+
@thing.save
|
620
|
+
end.should modify_file(original_path)
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
describe "#save" do
|
625
|
+
before do
|
626
|
+
@thing.name = 'new'
|
627
|
+
@thing.photo = uploaded_file
|
628
|
+
end
|
629
|
+
|
630
|
+
it "should clear all changes" do
|
631
|
+
@thing.save
|
632
|
+
@thing.changes.should == {}
|
633
|
+
end
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
637
|
+
describe "automatic timestamps" do
|
638
|
+
describe "#save" do
|
639
|
+
use_model_class(:Thing, :photo_updated_at => :datetime)
|
640
|
+
|
641
|
+
before do
|
642
|
+
Thing.has_attachment :photo
|
643
|
+
end
|
644
|
+
|
645
|
+
describe "when the record is new" do
|
646
|
+
before do
|
647
|
+
@thing = Thing.new
|
648
|
+
end
|
649
|
+
|
650
|
+
describe "when the attachment was not assigned to" do
|
651
|
+
it "should not set ATTACHMENT_updated_at" do
|
652
|
+
@thing.save.should be_true
|
653
|
+
@thing.photo_updated_at.should be_nil
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
describe "when nil was assigned to the attachment" do
|
658
|
+
it "should not set ATTACHMENT_updated_at" do
|
659
|
+
@thing.photo = nil
|
660
|
+
@thing.save.should be_true
|
661
|
+
@thing.photo_updated_at.should be_nil
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
describe "when a file was assigned to the attachment" do
|
666
|
+
it "should update ATTACHMENT_updated_at" do
|
667
|
+
@thing.photo = uploaded_file
|
668
|
+
@thing.save.should be_true
|
669
|
+
@thing.photo_updated_at.should == Time.now
|
670
|
+
end
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
describe "when the record already exists" do
|
675
|
+
before do
|
676
|
+
thing = Thing.create(:photo => uploaded_file('test.jpg', 'test.jpg'))
|
677
|
+
@thing = Thing.find(thing.id)
|
678
|
+
end
|
679
|
+
|
680
|
+
describe "when the attachment was not assigned to" do
|
681
|
+
it "should not update ATTACHMENT_updated_at" do
|
682
|
+
warp_ahead 1.minute
|
683
|
+
original_updated_at = @thing.photo_updated_at
|
684
|
+
@thing.save.should be_true
|
685
|
+
@thing.photo_updated_at.should == original_updated_at.drop_subseconds
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
describe "when a new file was assigned to the attachment" do
|
690
|
+
it "should update ATTACHMENT_updated_at" do
|
691
|
+
warp_ahead 1.minute
|
692
|
+
@thing.photo = uploaded_file('test.jpg', 'test.jpg')
|
693
|
+
@thing.save.should be_true
|
694
|
+
@thing.photo_updated_at.should == Time.now
|
695
|
+
end
|
696
|
+
end
|
697
|
+
end
|
698
|
+
end
|
699
|
+
end
|
700
|
+
end
|