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
@@ -129,20 +129,12 @@ describe Attachment::Maybe do
129
129
 
130
130
  describe "#reload" do
131
131
  before do
132
- thing = Thing.create(:photo => test_file('test.png'))
133
- @thing = Thing.find(thing.id)
134
- end
135
-
136
- it "should update the file size stored attribute from the file" do
137
- FileUtils.cp(test_path('test2.jpg'), @thing.photo.path(:original))
132
+ @thing = Thing.create!(:photo => uploaded_file('test.png'))
133
+ FileUtils.cp("#{ROOT}/spec/data/test.jpg", @thing.photo.path(:original))
138
134
  @thing.photo.reload
139
- @thing.photo_file_size.should == File.size(test_path('test2.jpg'))
140
135
  end
141
136
 
142
- it "should reload the content type stored attribute from the file" do
143
- FileUtils.cp(test_path('test.png'), @thing.photo.path(:original))
144
- @thing.photo.reload
145
- @thing.photo_content_type.should =~ %r'image/png'
146
- end
137
+ it "should update the file size stored attribute from the file"
138
+ it "should reload the content type stored attribute from the file"
147
139
  end
148
140
  end
@@ -1,150 +1,32 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Attachment::Pdf do
4
- def test_file
5
- path = "#{temporary_directory}/test.pdf"
6
- FileUtils.cp("#{ROOT}/spec/data/test.pdf", path)
7
- autoclose open(path)
8
- end
9
-
10
- def configure(&block)
11
- Thing.attachment_reflections[:attachment].configure(&block)
12
- end
13
-
14
- describe "when file attributes are not stored" do
4
+ it_should_behave_like_an_attachment_with_dimensions(
5
+ :type => :pdf,
6
+ :missing_dimensions => [1, 1],
7
+ :file_40x30 => 'test-40x30.pdf',
8
+ :file_20x10 => 'test-20x10.pdf'
9
+ )
10
+
11
+ describe "#process" do
15
12
  use_model_class(:Thing, :attachment_file_name => :string)
16
13
 
17
- describe "#dimensions" do
18
- it "should return 1x1 if the file is missing" do
19
- Thing.has_attachment :attachment do
20
- type :pdf
21
- style :double, :size => '1224x1584'
22
- style :filled, :size => '500x500', :filled => true
23
- style :unfilled, :size => '1000x1000'
24
- default_style :double
25
- end
26
- @thing = Thing.new(:attachment => test_file)
27
- @thing.save.should be_true
28
- File.unlink(@thing.attachment.path(:original))
29
- @thing = Thing.find(@thing.id)
30
- @thing.attachment.is_a?(Attachment::Pdf) # sanity check
31
- @thing.attachment.stream.missing? # sanity check
32
- @thing.attachment.dimensions(:original).should == [1, 1]
33
- end
34
- end
35
- end
36
-
37
- describe "when file attributes are stored" do
38
- use_model_class(:Thing,
39
- :attachment_file_name => :string,
40
- :attachment_width => :integer,
41
- :attachment_height => :integer,
42
- :attachment_aspect_ratio => :float,
43
- :attachment_dimensions => :string)
44
-
45
14
  before do
46
- Thing.has_attachment :attachment do
47
- style :double, :size => '1224x1584'
48
- style :filled, :size => '500x500', :filled => true
49
- style :unfilled, :size => '1000x1000'
50
- default_style :double
51
- end
52
- @thing = Thing.new(:attachment => test_file)
15
+ Thing.has_attachment :attachment
16
+ @thing = Thing.new(:attachment => uploaded_file('test.pdf'))
53
17
  end
54
18
 
55
- describe "#process" do
56
- it "should be processed with ImageMagick by default" do
57
- context = nil
58
- configure do
59
- style :output
60
- process :on => :event do
61
- context = self
62
- 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
63
25
  end
64
-
65
- @thing.attachment.process(:event)
66
- context.should be_a(Processor::ImageMagick)
67
- end
68
- end
69
-
70
- describe "#dimensions" do
71
- it "should return the width and height of the default style if no style name is given" do
72
- @thing.attachment.dimensions.should == [1224, 1584]
73
- end
74
-
75
- it "should return the width and height of the given style" do
76
- @thing.attachment.dimensions(:original).should == [612, 792]
77
- @thing.attachment.dimensions(:double).should == [1224, 1584]
78
- end
79
-
80
- it "should return the calculated width according to style filledness" do
81
- @thing.attachment.dimensions(:filled).should == [500, 500]
82
- @thing.attachment.dimensions(:unfilled).should == [773, 1000]
83
- end
84
-
85
- it "should only invoke identify once"
86
- it "should log the result"
87
- end
88
-
89
- describe "#width" do
90
- it "should return the width of the default style if no style name is given" do
91
- @thing.attachment.width.should == 1224
92
- end
93
-
94
- it "should return the width of the given style" do
95
- @thing.attachment.width(:original).should == 612
96
- @thing.attachment.width(:double).should == 1224
97
- end
98
- end
99
-
100
- describe "#height" do
101
- it "should return the height of the default style if no style name is given" do
102
- @thing.attachment.height.should == 1584
103
- end
104
-
105
- it "should return the height of the given style" do
106
- @thing.attachment.height(:original).should == 792
107
- @thing.attachment.height(:double).should == 1584
108
- end
109
- end
110
-
111
- describe "#aspect_ratio" do
112
- it "should return the aspect ratio of the default style if no style name is given" do
113
- @thing.attachment.aspect_ratio.should be_close(612.0/792, 1e-5)
114
26
  end
115
27
 
116
- it "should return the aspect ratio of the given style" do
117
- @thing.attachment.aspect_ratio(:original).should be_close(612.0/792, 1e-5)
118
- @thing.attachment.aspect_ratio(:filled).should be_close(1, 1e-5)
119
- end
120
- end
121
-
122
- describe "storable attributes" do
123
- it "should set the stored attributes on assignment" do
124
- @thing.attachment_width.should == 612
125
- @thing.attachment_height.should == 792
126
- @thing.attachment_aspect_ratio.should be_close(612.0/792, 1e-5)
127
- @thing.attachment_dimensions.should == '612x792'
128
- end
129
-
130
- describe "after roundtripping through the database" do
131
- before do
132
- @thing.save
133
- @thing = Thing.find(@thing.id)
134
- end
135
-
136
- it "should restore the stored attributes" do
137
- @thing.attachment_width.should == 612
138
- @thing.attachment_height.should == 792
139
- @thing.attachment_aspect_ratio.should be_close(612.0/792, 1e-5)
140
- @thing.attachment_dimensions.should == '612x792'
141
- end
142
-
143
- it "should recalculate the dimensions correctly" do
144
- @thing.attachment.dimensions(:filled).should == [500, 500]
145
- @thing.attachment.dimensions(:unfilled).should == [773, 1000]
146
- end
147
- end
28
+ @thing.attachment.process(:event)
29
+ context.should be_a(Processor::ImageMagick)
148
30
  end
149
31
  end
150
32
  end
@@ -1,212 +1,140 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Attachment::Video do
4
- use_model_class(:Thing,
5
- :video_file_name => :string,
6
- :video_width => :integer,
7
- :video_height => :integer,
8
- :video_aspect_ratio => :float,
9
- :video_dimensions => :string,
10
- :video_duration => :string)
11
-
12
- before do
13
- Thing.has_attachment :video do
14
- # original video is 640x480.
15
- style :half, :size => '320x240'
16
- style :filled, :size => '60x60', :filled => true
17
- style :unfilled, :size => '120x120'
18
- default_style :half
19
- end
20
- @thing = Thing.new(:video => test_file)
21
- end
22
-
23
- def test_file
24
- path = "#{temporary_directory}/test.mov"
25
- FileUtils.cp("#{ROOT}/spec/data/test.mov", path)
26
- autoclose open(path)
27
- end
28
-
29
- def run(command)
30
- `#{command}`
31
- $?.success? or
32
- raise "command failed: #{command}"
33
- end
34
-
35
- describe "#dimensions" do
36
- it "should return 2x2 if the style is missing" do
37
- Thing.attachment_reflections[:video].configure do
38
- detect_type_by{:video}
39
- end
40
- @thing.save.should be_true
41
- File.unlink(@thing.video.path(:original))
42
- @thing = Thing.find(@thing.id)
43
- @thing.video.is_a?(Attachment::Video) # sanity check
44
- @thing.video.stream.missing? # sanity check
45
- @thing.video.dimensions(:original).should == [2, 2]
46
- end
47
-
48
- it "should return the width and height of the default style if no style name is given" do
49
- @thing.video.dimensions.should == [320, 240]
50
- end
4
+ it_should_behave_like_an_attachment_with_dimensions(
5
+ :type => :video,
6
+ :missing_dimensions => [2, 2],
7
+ :file_40x30 => 'test-40x30x1.mov',
8
+ :file_20x10 => 'test-20x10x1.mov'
9
+ )
51
10
 
52
- it "should return the width and height of the given style" do
53
- @thing.video.dimensions(:original).should == [640, 480]
54
- @thing.video.dimensions(:half).should == [320, 240]
55
- end
11
+ describe "when instantiated" do
12
+ use_model_class(:Thing, :attachment_file_name => :string)
56
13
 
57
- it "should return the calculated width according to style filledness" do
58
- @thing.video.dimensions(:filled).should == [60, 60]
59
- @thing.video.dimensions(:unfilled).should == [120, 90]
14
+ before do
15
+ Thing.has_attachment :attachment do
16
+ style :double, :size => '80x60'
17
+ style :filled, :size => '60x60', :filled => true
18
+ style :unfilled, :size => '120x120'
19
+ default_style :double
20
+ end
21
+ @thing = Thing.new(:attachment => uploaded_file('test-40x30x1.mov'))
60
22
  end
61
23
 
62
- it "should round calculated dimensions down to the nearest multiple of 2" do
63
- # TODO: ick!
64
- Thing.attachment_reflections[:video].styles[:filled][:size] = '59x59'
65
- @thing.video.dimensions(:filled).should == [58, 58]
24
+ describe "#dimensions" do
25
+ it "should round calculated dimensions down to the nearest multiple of 2" do
26
+ Thing.has_attachment :attachment do
27
+ style :odd, :size => '59x59', :filled => true
28
+ end
29
+ @thing.attachment.dimensions(:odd).should == [58, 58]
30
+ end
66
31
  end
67
32
 
68
- it "should only invoke ffmpeg once"
69
- it "should log the result"
70
- end
33
+ describe "#duration" do
34
+ it "should return the duration of the given style" do
35
+ @thing.attachment.duration(:original).should == 1.second
36
+ # TODO: Add video slicing, and make duration return the correct duration.
37
+ @thing.attachment.duration(:double).should == 1.second
38
+ end
71
39
 
72
- describe "#width" do
73
- it "should return the width of the default style if no style name is given" do
74
- @thing.video.width.should == 320
40
+ it "should use the default style if no style is given"
75
41
  end
76
42
 
77
- it "should return the width of the given style" do
78
- @thing.video.width(:original).should == 640
79
- @thing.video.width(:half).should == 320
80
- end
81
- end
43
+ describe "#video_tracks" do
44
+ it "should return the video tracks of the given style" do
45
+ @thing.attachment.video_tracks(:original).should have(1).video_track
46
+ @thing.attachment.video_tracks(:original).first.dimensions.should == [40, 30]
47
+ end
82
48
 
83
- describe "#height" do
84
- it "should return the height of the default style if no style name is given" do
85
- @thing.video.height.should == 240
86
- end
49
+ it "should take into account filledness of the style" do
50
+ @thing.attachment.video_tracks(:original).should have(1).video_track
51
+ @thing.attachment.video_tracks(:original).first.dimensions.should == [40, 30]
52
+ end
87
53
 
88
- it "should return the height of the given style" do
89
- @thing.video.height(:original).should == 480
90
- @thing.video.height(:half).should == 240
54
+ it "should use the default style if no style is given" do
55
+ @thing.attachment.video_tracks.should have(1).video_track
56
+ @thing.attachment.video_tracks.first.dimensions.should == [80, 60]
57
+ end
91
58
  end
92
- end
93
59
 
94
- describe "#aspect_ratio" do
95
- it "should return the aspect ratio of the default style if no style name is given" do
96
- @thing.video.aspect_ratio.should be_close(4.0/3, 1e-5)
97
- end
60
+ describe "#audio_tracks" do
61
+ it "should return the audio tracks of the given style" do
62
+ @thing.attachment.audio_tracks(:original).should have(1).audio_track
63
+ @thing.attachment.audio_tracks(:original).first.duration.should == 1
64
+ end
98
65
 
99
- it "should return the aspect ratio of the given style" do
100
- @thing.video.aspect_ratio(:original).should be_close(4.0/3, 1e-5)
101
- @thing.video.aspect_ratio(:filled).should be_close(1, 1e-5)
66
+ it "should use the default style if no style is given" do
67
+ @thing.attachment.audio_tracks.should have(1).audio_track
68
+ @thing.attachment.audio_tracks.first.duration.should == 1
69
+ end
102
70
  end
103
71
  end
104
72
 
105
- describe "#duration" do
106
- it "should return the duration of the original style if no style name is given" do
107
- @thing.video.duration.should == 1.second
108
- end
73
+ describe "when the duration is stored" do
74
+ use_model_class(:Thing, :attachment_file_name => :string, :attachment_duration => :integer)
109
75
 
110
- it "should return the duration of the original style if a style name is given" do
111
- @thing.video.duration(:filled).should == 1.second
76
+ before do
77
+ Thing.has_attachment :attachment do
78
+ type :video
79
+ style :double, :size => '80x60'
80
+ end
112
81
  end
113
82
 
114
- # TODO: make these work instead of the above
115
- it "should return the duration of the default style if no style name is given"
116
- it "should return the duration of the given style"
117
- end
118
-
119
- describe "#video_tracks" do
120
- it "should return the video tracks of the original style if no style name is given" do
121
- @thing.video.video_tracks.should have(1).video_track
122
- @thing.video.video_tracks.first.dimensions.should == [320, 240]
123
- end
83
+ describe "when the stored values are hacked, and the record reinstantiated" do
84
+ before do
85
+ @thing = Thing.create!(:attachment => uploaded_file('test-40x30x1.mov'))
86
+ Thing.update_all({:attachment_duration => 2}, {:id => @thing.id})
87
+ @thing = Thing.find(@thing.id)
88
+ end
124
89
 
125
- it "should return the video tracks of the target style if a style name is given" do
126
- @thing.video.video_tracks(:original).should have(1).video_track
127
- @thing.video.video_tracks(:original).first.dimensions.should == [640, 480]
90
+ it "should use the stored duration for the original" do
91
+ @thing.attachment.duration(:original).should == 2
92
+ end
128
93
 
129
- @thing.video.video_tracks(:filled).should have(1).video_track
130
- @thing.video.video_tracks(:filled).first.dimensions.should == [60, 60]
94
+ it "should calculate the duration of other styles from that of the original" do
95
+ @thing.attachment.duration(:double).should == 2
96
+ end
131
97
  end
132
98
  end
133
99
 
134
- describe "#audio_tracks" do
135
- it "should return the audio tracks of the original style if no style name is given" do
136
- @thing.video.video_tracks.should have(1).video_track
137
- @thing.video.video_tracks.first.dimensions.should == [320, 240]
138
- end
139
-
140
- it "should return the audio tracks of the target style if a style name is given" do
141
- @thing.video.video_tracks(:original).should have(1).video_track
142
- @thing.video.video_tracks(:original).first.dimensions.should == [640, 480]
143
-
144
- @thing.video.video_tracks(:filled).should have(1).video_track
145
- @thing.video.video_tracks(:filled).first.dimensions.should == [60, 60]
146
- end
147
- end
100
+ describe "when the duration is not stored" do
101
+ use_model_class(:Thing, :attachment_file_name => :string)
148
102
 
149
- describe "storable attributes" do
150
- it "should set the stored attributes on assignment" do
151
- @thing.video_width.should == 640
152
- @thing.video_height.should == 480
153
- @thing.video_aspect_ratio.should be_close(4.0/3, 1e-5)
154
- @thing.video_dimensions.should == '640x480'
103
+ before do
104
+ Thing.has_attachment :attachment do
105
+ type :video
106
+ style :double, :size => '80x60'
107
+ end
155
108
  end
156
109
 
157
- describe "after roundtripping through the database" do
110
+ describe "when the file is missing" do
158
111
  before do
159
- @thing.save
112
+ @thing = Thing.create!(:attachment => uploaded_file('test-40x30x1.mov'))
113
+ File.unlink(@thing.attachment.path(:original))
160
114
  @thing = Thing.find(@thing.id)
161
115
  end
162
116
 
163
- it "should restore the stored attributes" do
164
- @thing.video_width.should == 640
165
- @thing.video_height.should == 480
166
- @thing.video_aspect_ratio.should be_close(4.0/3, 1e-5)
167
- @thing.video_dimensions.should == '640x480'
168
- end
117
+ describe "#duration" do
118
+ it "should return 0 for the original style" do
119
+ @thing.attachment.duration(:original).should == 0
120
+ end
169
121
 
170
- it "should recalculate the dimensions correctly" do
171
- @thing.video.dimensions(:filled).should == [60, 60]
172
- @thing.video.dimensions(:unfilled).should == [120, 90]
122
+ it "should calculate the duration of other styles from that of the original" do
123
+ @thing.attachment.duration(:double).should == 0
124
+ end
173
125
  end
174
- end
175
- end
176
126
 
177
- describe "#reload" do
178
- before do
179
- thing = Thing.create(:video => test_video_file('test.mov'))
180
- @thing = Thing.find(thing.id)
181
- end
182
-
183
- it "should update the stored attributes from the file" do
184
- # Prime the cached values.
185
- @thing.video_width.should == 640
186
- @thing.video_height.should == 480
187
- @thing.video_aspect_ratio.should be_close(4.0/3, 1e-5)
188
- @thing.video_dimensions.should == '640x480'
189
-
190
- FileUtils.cp(test_path('test.ogg'), @thing.video.path(:original))
191
- @thing.video.reload
192
- @thing.video_width.should == 176
193
- @thing.video_height.should == 144
194
- @thing.video_aspect_ratio.should == 176.0/144
195
- @thing.video_dimensions.should == '176x144'
196
- end
197
-
198
- it "should update the original dimensions from the file" do
199
- @thing.video.dimensions(:original).should == [640, 480]
200
- FileUtils.cp(test_path('test.ogg'), @thing.video.path(:original))
201
- @thing.video.reload
202
- @thing.video.dimensions(:original).should == [176, 144]
203
- end
127
+ describe "#video_tracks" do
128
+ it "should return no video tracks" do
129
+ @thing.attachment.video_tracks.should have(0).video_tracks
130
+ end
131
+ end
204
132
 
205
- it "should update the dimensions for each style from the file" do
206
- @thing.video.dimensions(:half).should == [320, 240]
207
- FileUtils.cp(test_path('test.ogg'), @thing.video.path(:original))
208
- @thing.video.reload
209
- @thing.video.dimensions(:half).should == [292, 240]
133
+ describe "#audio_tracks" do
134
+ it "should return no audio tracks" do
135
+ @thing.attachment.audio_tracks.should have(0).audio_tracks
136
+ end
137
+ end
210
138
  end
211
139
  end
212
140
  end