bulldog 0.1.1 → 0.2.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.
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