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
@@ -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