bulldog 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG +7 -0
  2. data/lib/bulldog/attachment/base.rb +3 -6
  3. data/lib/bulldog/attachment/has_dimensions.rb +43 -34
  4. data/lib/bulldog/attachment/image.rb +5 -26
  5. data/lib/bulldog/attachment/pdf.rb +3 -28
  6. data/lib/bulldog/attachment/video.rb +48 -45
  7. data/lib/bulldog/style.rb +28 -1
  8. data/lib/bulldog/version.rb +1 -1
  9. data/spec/data/3-bytes.txt +1 -0
  10. data/spec/data/4-bytes.txt +1 -0
  11. data/spec/data/5-bytes.txt +1 -0
  12. data/spec/data/6-bytes.txt +1 -0
  13. data/spec/data/test-20x10.jpg +0 -0
  14. data/spec/data/test-20x10.pdf +0 -0
  15. data/spec/data/test-20x10x1.mov +0 -0
  16. data/spec/data/test-40x30.jpg +0 -0
  17. data/spec/data/test-40x30.pdf +0 -0
  18. data/spec/data/test-40x30x1.mov +0 -0
  19. data/spec/helpers/files.rb +123 -0
  20. data/spec/integration/lifecycle_hooks_spec.rb +1 -1
  21. data/spec/integration/processing_image_attachments.rb +1 -13
  22. data/spec/macros/attachment/has_dimensions_spec.rb +313 -0
  23. data/spec/spec_helper.rb +3 -4
  24. data/spec/unit/attachment/base_spec.rb +25 -45
  25. data/spec/unit/attachment/image_spec.rb +48 -171
  26. data/spec/unit/attachment/maybe_spec.rb +4 -12
  27. data/spec/unit/attachment/pdf_spec.rb +18 -136
  28. data/spec/unit/attachment/video_spec.rb +98 -170
  29. data/spec/unit/attachment_spec.rb +1 -1
  30. data/spec/unit/has_attachment_spec.rb +29 -26
  31. data/spec/unit/interpolation_spec.rb +2 -2
  32. data/spec/unit/processor/ffmpeg_spec.rb +3 -3
  33. data/spec/unit/processor/image_magick_spec.rb +1 -1
  34. data/spec/unit/processor/one_shot_spec.rb +1 -1
  35. data/spec/unit/stream_spec.rb +3 -3
  36. data/spec/unit/style_spec.rb +40 -0
  37. data/spec/unit/validations_spec.rb +33 -33
  38. metadata +28 -8
  39. data/spec/helpers/temporary_directory.rb +0 -25
  40. data/spec/helpers/test_upload_files.rb +0 -108
@@ -16,10 +16,10 @@ ROOT = File.dirname( File.dirname(__FILE__) )
16
16
  require 'helpers/time_travel'
17
17
  require 'helpers/temporary_models'
18
18
  require 'helpers/temporary_values'
19
- require 'helpers/temporary_directory'
20
- require 'helpers/test_upload_files'
19
+ require 'helpers/files'
21
20
  require 'helpers/image_creation'
22
21
  require 'matchers/file_operations'
22
+ require 'macros/attachment/has_dimensions_spec'
23
23
 
24
24
  class Time
25
25
  #
@@ -68,8 +68,7 @@ Spec::Runner.configure do |config|
68
68
  config.include TimeTravel
69
69
  config.include TemporaryModels
70
70
  config.include TemporaryValues
71
- config.include TemporaryDirectory
72
- config.include TestUploadFiles
71
+ config.include Files
73
72
  config.include ImageCreation
74
73
  config.include Matchers
75
74
  config.include SpecHelper
@@ -33,7 +33,7 @@ describe Attachment::Base do
33
33
  end
34
34
 
35
35
  it "should return the path of the given style, interpolated from the path template" do
36
- @thing.photo = test_image_file
36
+ @thing.photo = uploaded_file('test.jpg')
37
37
  @thing.stubs(:id).returns(5)
38
38
  @thing.photo.path(:original).should == original_path
39
39
  @thing.photo.path(:small).should == small_path
@@ -45,7 +45,7 @@ describe Attachment::Base do
45
45
  Thing.attachment_reflections[:photo].configure do
46
46
  path "#{spec.temporary_directory}/:style.:extension"
47
47
  end
48
- @thing.photo = test_image_file
48
+ @thing.photo = uploaded_file('test.jpg')
49
49
  end
50
50
 
51
51
  it "should use the extension of the original file for the original style" do
@@ -62,7 +62,7 @@ describe Attachment::Base do
62
62
  Thing.attachment_reflections[:photo].configure do
63
63
  path "#{spec.temporary_directory}/:style.xyz"
64
64
  end
65
- @thing.photo = test_image_file
65
+ @thing.photo = uploaded_file('test.jpg')
66
66
  end
67
67
 
68
68
  it "should use the extension of the path template for the original style" do
@@ -82,7 +82,7 @@ describe Attachment::Base do
82
82
  end
83
83
 
84
84
  it "should default to the attachment's default style" do
85
- @thing.photo = test_image_file
85
+ @thing.photo = uploaded_file('test.jpg')
86
86
  @thing.photo.path.should == "/tmp/small.jpg"
87
87
  end
88
88
  end
@@ -99,7 +99,7 @@ describe Attachment::Base do
99
99
  end
100
100
 
101
101
  it "should return the url of the given style, interpolated from the url template" do
102
- @thing.photo = test_image_file
102
+ @thing.photo = uploaded_file('test.jpg')
103
103
  @thing.photo.url(:original).should == "/assets/original.jpg"
104
104
  @thing.photo.url(:small).should == "/assets/small.jpg"
105
105
  end
@@ -111,7 +111,7 @@ describe Attachment::Base do
111
111
  path "/tmp/:style.:extension"
112
112
  url "/assets/:style.:extension"
113
113
  end
114
- @thing.photo = test_image_file
114
+ @thing.photo = uploaded_file('test.jpg')
115
115
  end
116
116
 
117
117
  it "should use the extension of the original file for the original style" do
@@ -130,7 +130,7 @@ describe Attachment::Base do
130
130
  path "/tmp/:style.xyz"
131
131
  url "/assets/:style.xyz"
132
132
  end
133
- @thing.photo = test_image_file
133
+ @thing.photo = uploaded_file('test.jpg')
134
134
  end
135
135
 
136
136
  it "should use the extension of the url template for the original style" do
@@ -150,7 +150,7 @@ describe Attachment::Base do
150
150
  end
151
151
 
152
152
  it "should default to the attachment's default style" do
153
- @thing.photo = test_image_file
153
+ @thing.photo = uploaded_file('test.jpg')
154
154
  @thing.photo.url.should == "/assets/small.jpg"
155
155
  end
156
156
  end
@@ -168,22 +168,12 @@ describe Attachment::Base do
168
168
  "#{temporary_directory}/#{@thing.id}.original.jpg"
169
169
  end
170
170
 
171
- def with_temporary_file(path, content)
172
- FileUtils.mkdir_p File.dirname(path)
173
- open(path, 'w'){|f| f.print '...'}
174
- begin
175
- yield path
176
- ensure
177
- File.delete(path)
178
- end
179
- end
180
-
181
171
  before do
182
- @thing = Thing.new(:photo => test_image_file)
172
+ @thing = Thing.new(:photo => uploaded_file('test.jpg'))
183
173
  end
184
174
 
185
175
  it "should return the size of the file" do
186
- @thing.photo.file_size.should == File.size(test_image_path)
176
+ @thing.photo.file_size.should == File.size("#{ROOT}/spec/data/test.jpg")
187
177
  end
188
178
  end
189
179
 
@@ -200,22 +190,12 @@ describe Attachment::Base do
200
190
  "#{temporary_directory}/#{@thing.id}.original.jpg"
201
191
  end
202
192
 
203
- def with_temporary_file(path, content)
204
- FileUtils.mkdir_p File.dirname(path)
205
- open(path, 'w'){|f| f.print '...'}
206
- begin
207
- yield path
208
- ensure
209
- File.delete(path)
210
- end
211
- end
212
-
213
193
  before do
214
- @thing = Thing.new(:photo => test_image_file)
194
+ @thing = Thing.new(:photo => uploaded_file('test.jpg'))
215
195
  end
216
196
 
217
197
  it "should return the original base name of the file" do
218
- @thing.photo.file_name.should == File.basename(test_image_path)
198
+ @thing.photo.file_name.should == File.basename("#{ROOT}/spec/data/test.jpg")
219
199
  end
220
200
  end
221
201
 
@@ -245,7 +225,7 @@ describe Attachment::Base do
245
225
  context = self
246
226
  end
247
227
  end
248
- thing = Thing.new(:photo => test_image_file)
228
+ thing = Thing.new(:photo => uploaded_file('test.jpg'))
249
229
  thing.photo.stubs(:default_processor_type).returns(:test)
250
230
  thing.photo.process(:test_event)
251
231
  context.should be_a(Processor::Test)
@@ -259,7 +239,7 @@ describe Attachment::Base do
259
239
  context = self
260
240
  end
261
241
  end
262
- thing = Thing.new(:photo => test_image_file)
242
+ thing = Thing.new(:photo => uploaded_file('test.jpg'))
263
243
  thing.photo.process(:test_event)
264
244
  context.should be_a(Processor::Test)
265
245
  end
@@ -286,7 +266,7 @@ describe Attachment::Base do
286
266
  styles = self.styles
287
267
  end
288
268
  end
289
- thing = Thing.new(:photo => test_image_file)
269
+ thing = Thing.new(:photo => uploaded_file('test.jpg'))
290
270
  thing.photo.process(:test_event)
291
271
  styles.should be_a(StyleSet)
292
272
  styles.map(&:name).should == [:small]
@@ -298,7 +278,7 @@ describe Attachment::Base do
298
278
  style :one
299
279
  process(:on => :event, :with => :test){}
300
280
  end
301
- thing = Thing.new(:attachment => test_empty_file)
281
+ thing = Thing.new(:attachment => uploaded_file('empty.txt'))
302
282
  thing.attachment.process(:event).should be_true
303
283
  end
304
284
  end
@@ -309,7 +289,7 @@ describe Attachment::Base do
309
289
  style :one
310
290
  process(:on => :event, :with => :test){}
311
291
  end
312
- thing = Thing.new(:attachment => test_empty_file)
292
+ thing = Thing.new(:attachment => uploaded_file('empty.txt'))
313
293
  thing.attachment.process(:event).should be_false
314
294
  end
315
295
  end
@@ -324,7 +304,7 @@ describe Attachment::Base do
324
304
  style :one
325
305
  process :on => :event, :with => :test
326
306
  end
327
- thing = Thing.new(:attachment => test_empty_file)
307
+ thing = Thing.new(:attachment => uploaded_file('empty.txt'))
328
308
  lambda{thing.attachment.process!(:event)}.should raise_error(ActiveRecord::RecordInvalid)
329
309
  end
330
310
  end
@@ -335,7 +315,7 @@ describe Attachment::Base do
335
315
  style :one
336
316
  process :on => :event, :with => :test
337
317
  end
338
- thing = Thing.new(:attachment => test_empty_file)
318
+ thing = Thing.new(:attachment => uploaded_file('empty.txt'))
339
319
  lambda{thing.attachment.process!(:event)}.should_not raise_error(ActiveRecord::RecordInvalid)
340
320
  end
341
321
  end
@@ -350,7 +330,7 @@ describe Attachment::Base do
350
330
  styles = self.styles.map(&:name)
351
331
  end
352
332
  end
353
- thing = Thing.new(:attachment => test_empty_file)
333
+ thing = Thing.new(:attachment => uploaded_file('empty.txt'))
354
334
  thing.attachment.process!(:event, :styles => [:one])
355
335
  styles.should == [:one]
356
336
  end
@@ -365,13 +345,13 @@ describe Attachment::Base do
365
345
 
366
346
  before do
367
347
  Thing.has_attachment :photo
368
- @thing = Thing.new(:photo => uploaded_file_with_content('test.jpg', "\xff\xd8"))
348
+ @thing = Thing.new(:photo => uploaded_file('test.jpg'))
369
349
  end
370
350
 
371
351
  it "should set the stored attributes on assignment" do
372
352
  @thing.photo_file_name.should == 'test.jpg'
373
- @thing.photo_file_size.should == 2
374
- @thing.photo_content_type.should =~ /image\/jpeg/
353
+ @thing.photo_file_size.should == File.size('spec/data/test.jpg')
354
+ @thing.photo_content_type.should include('image/jpeg')
375
355
  end
376
356
 
377
357
  it "should successfully roundtrip the stored attributes" do
@@ -379,8 +359,8 @@ describe Attachment::Base do
379
359
  @thing.save
380
360
  @thing = Thing.find(@thing.id)
381
361
  @thing.photo_file_name.should == 'test.jpg'
382
- @thing.photo_file_size.should == 2
383
- @thing.photo_content_type.should =~ /image\/jpeg/
362
+ @thing.photo_file_size.should == File.size('spec/data/test.jpg')
363
+ @thing.photo_content_type.should include('image/jpeg')
384
364
  end
385
365
  end
386
366
  end
@@ -1,199 +1,76 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Attachment::Image do
4
- def run(command)
5
- `#{command}`
6
- $?.success? or
7
- raise "command failed: #{command}"
8
- end
9
-
10
- describe "when file attributes are not stored" do
11
- use_model_class(:Thing, :photo_file_name => :string)
12
-
13
- describe "#dimensions" do
14
- it "should return 1x1 if the file is missing" do
15
- Thing.has_attachment :photo do
16
- type :image
17
- style :double, :size => '80x60'
18
- style :filled, :size => '60x60', :filled => true
19
- style :unfilled, :size => '120x120'
20
- default_style :double
21
- end
22
- @thing = Thing.new(:photo => test_image_file)
23
- @thing.save.should be_true
24
- File.unlink(@thing.photo.path(:original))
25
- @thing = Thing.find(@thing.id)
26
- @thing.photo.is_a?(Attachment::Image) # sanity check
27
- @thing.photo.stream.missing? # sanity check
28
- @thing.photo.dimensions(:original).should == [1, 1]
29
- end
30
- end
31
- end
4
+ it_should_behave_like_an_attachment_with_dimensions(
5
+ :type => :image,
6
+ :missing_dimensions => [1, 1],
7
+ :file_40x30 => 'test-40x30.jpg',
8
+ :file_20x10 => 'test-20x10.jpg'
9
+ )
32
10
 
33
- describe "when file attributes are stored" do
34
- use_model_class(:Thing,
35
- :photo_file_name => :string,
36
- :photo_width => :integer,
37
- :photo_height => :integer,
38
- :photo_aspect_ratio => :float,
39
- :photo_dimensions => :string)
11
+ describe "#process" do
12
+ use_model_class(:Thing, :attachment_file_name => :string)
40
13
 
41
14
  before do
42
- Thing.has_attachment :photo do
43
- style :double, :size => '80x60'
44
- style :filled, :size => '60x60', :filled => true
45
- style :unfilled, :size => '120x120'
46
- default_style :double
47
- end
48
- @thing = Thing.new(:photo => test_image_file)
15
+ Thing.has_attachment :attachment
16
+ @thing = Thing.new(:attachment => uploaded_file('test.jpg'))
49
17
  end
50
18
 
51
- describe "#dimensions" do
52
- it "should return the width and height of the default style if no style name is given" do
53
- @thing.photo.dimensions.should == [80, 60]
54
- end
55
-
56
- it "should return the width and height of the given style" do
57
- @thing.photo.dimensions(:original).should == [40, 30]
58
- @thing.photo.dimensions(:double).should == [80, 60]
59
- end
60
-
61
- it "should return the calculated width according to style filledness" do
62
- @thing.photo.dimensions(:filled).should == [60, 60]
63
- @thing.photo.dimensions(:unfilled).should == [120, 90]
64
- end
65
-
66
- describe "when an exif:Orientation header is set" do
67
- before do
68
- @path = create_image("#{temporary_directory}/test.jpg", :size => '40x30')
69
- @rotated_path = "#{temporary_directory}/rotated-test.jpg"
70
- end
71
-
72
- attr_reader :path, :rotated_path
73
-
74
- def set_header(value)
75
- run "exif --create-exif --ifd=EXIF --tag=Orientation --set-value=#{value} --output=#{rotated_path} #{path}"
76
- end
77
-
78
- it "should not swap the dimensions if the value is between 1 and 4" do
79
- (1..4).each do |value|
80
- set_header value
81
- open(rotated_path) do |file|
82
- @thing.photo = file
83
- @thing.photo.dimensions(:original).should == [40, 30]
84
- end
85
- end
86
- end
87
-
88
- it "should swap the dimensions if the value is between 5 and 8" do
89
- (5..8).each do |value|
90
- set_header value
91
- open(rotated_path) do |file|
92
- @thing.photo = file
93
- @thing.photo.dimensions(:original).should == [30, 40]
94
- end
95
- end
19
+ it "should process with ImageMagick by default" do
20
+ context = nil
21
+ Thing.has_attachment :attachment do
22
+ style :output
23
+ process :on => :event do
24
+ context = self
96
25
  end
97
26
  end
98
27
 
99
- it "should only invoke identify once"
100
- it "should log the result"
28
+ @thing.attachment.process(:event)
29
+ context.should be_a(Processor::ImageMagick)
101
30
  end
31
+ end
102
32
 
103
- describe "#width" do
104
- it "should return the width of the default style if no style name is given" do
105
- @thing.photo.width.should == 80
106
- end
33
+ describe "when an exif:Orientation header is set" do
34
+ use_model_class(:Thing, :attachment_file_name => :string)
107
35
 
108
- it "should return the width of the given style" do
109
- @thing.photo.width(:original).should == 40
110
- @thing.photo.width(:double).should == 80
111
- end
36
+ before do
37
+ Thing.has_attachment :attachment
112
38
  end
113
39
 
114
- describe "#height" do
115
- it "should return the height of the default style if no style name is given" do
116
- @thing.photo.height.should == 60
117
- end
118
-
119
- it "should return the height of the given style" do
120
- @thing.photo.height(:original).should == 30
121
- @thing.photo.height(:double).should == 60
122
- end
40
+ before do
41
+ @path = temporary_path('test-40x30.jpg')
42
+ @thing = Thing.new
123
43
  end
124
44
 
125
- describe "#aspect_ratio" do
126
- it "should return the aspect ratio of the default style if no style name is given" do
127
- @thing.photo.aspect_ratio.should be_close(4.0/3, 1e-5)
128
- end
129
-
130
- it "should return the aspect ratio of the given style" do
131
- @thing.photo.aspect_ratio(:original).should be_close(4.0/3, 1e-5)
132
- @thing.photo.aspect_ratio(:filled).should be_close(1, 1e-5)
133
- end
45
+ def run(command)
46
+ output = `#{command} 2>&1`
47
+ $?.success? or
48
+ raise "command failed: #{command}\noutput: #{output}"
134
49
  end
135
50
 
136
- describe "storable attributes" do
137
- it "should set the stored attributes on assignment" do
138
- @thing.photo_width.should == 40
139
- @thing.photo_height.should == 30
140
- @thing.photo_aspect_ratio.should be_close(4.0/3, 1e-5)
141
- @thing.photo_dimensions.should == '40x30'
142
- end
143
-
144
- describe "after roundtripping through the database" do
145
- before do
146
- @thing.save
147
- @thing = Thing.find(@thing.id)
148
- end
149
-
150
- it "should restore the stored attributes" do
151
- @thing.photo_width.should == 40
152
- @thing.photo_height.should == 30
153
- @thing.photo_aspect_ratio.should be_close(4.0/3, 1e-5)
154
- @thing.photo_dimensions.should == '40x30'
155
- end
51
+ def set_header(path, value)
52
+ tmp = "#{temporary_directory}/exif-tmp.jpg"
53
+ run "exif --create-exif --ifd=EXIF --tag=Orientation --set-value=#{value} --output=#{tmp} #{path}"
54
+ File.rename(tmp, path)
55
+ end
156
56
 
157
- it "should recalculate the dimensions correctly" do
158
- @thing.photo.dimensions(:filled).should == [60, 60]
159
- @thing.photo.dimensions(:unfilled).should == [120, 90]
57
+ it "should not swap the dimensions if the value is between 1 and 4" do
58
+ (1..4).each do |value|
59
+ set_header @path, value
60
+ open(@path) do |file|
61
+ @thing.attachment = file
62
+ @thing.attachment.dimensions(:original).should == [40, 30]
160
63
  end
161
64
  end
162
65
  end
163
66
 
164
- describe "#reload" do
165
- before do
166
- thing = Thing.create(:photo => test_image_file('test.jpg'))
167
- @thing = Thing.find(thing.id)
168
- end
169
-
170
- it "should update the stored attributes from the file" do
171
- # Prime the cached values.
172
- @thing.photo_width.should == 40
173
- @thing.photo_height.should == 30
174
- @thing.photo_aspect_ratio.should be_close(4.0/3, 1e-5)
175
- @thing.photo_dimensions.should == '40x30'
176
-
177
- FileUtils.cp(test_path('test2.jpg'), @thing.photo.path(:original))
178
- @thing.photo.reload
179
- @thing.photo_width.should == 2
180
- @thing.photo_height.should == 2
181
- @thing.photo_aspect_ratio.should == 1
182
- @thing.photo_dimensions.should == '2x2'
183
- end
184
-
185
- it "should update the original dimensions from the file" do
186
- @thing.photo.dimensions(:original).should == [40, 30]
187
- FileUtils.cp(test_path('test2.jpg'), @thing.photo.path(:original))
188
- @thing.photo.reload
189
- @thing.photo.dimensions(:original).should == [2, 2]
190
- end
191
-
192
- it "should update the dimensions for each style from the file" do
193
- @thing.photo.dimensions(:double).should == [80, 60]
194
- FileUtils.cp(test_path('test2.jpg'), @thing.photo.path(:original))
195
- @thing.photo.reload
196
- @thing.photo.dimensions(:double).should == [60, 60]
67
+ it "should swap the dimensions if the value is between 5 and 8" do
68
+ (5..8).each do |value|
69
+ set_header @path, value
70
+ open(@path) do |file|
71
+ @thing.attachment = file
72
+ @thing.attachment.dimensions(:original).should == [30, 40]
73
+ end
197
74
  end
198
75
  end
199
76
  end