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
@@ -1,5 +1,5 @@
1
1
  module Bulldog
2
- VERSION = [0, 1, 1]
2
+ VERSION = [0, 2, 0]
3
3
 
4
4
  class << VERSION
5
5
  include Comparable
@@ -0,0 +1 @@
1
+ ...
@@ -0,0 +1 @@
1
+ ....
@@ -0,0 +1 @@
1
+ .....
@@ -0,0 +1 @@
1
+ ......
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,123 @@
1
+ #
2
+ # Temporary file and directory support.
3
+ #
4
+ module Files
5
+ def self.included(base)
6
+ base.before(:all) { init_temporary_directory }
7
+ base.after(:all) { remove_temporary_directory }
8
+ base.after(:each) { close_open_files }
9
+ end
10
+
11
+ #
12
+ # Path of the temporary directory.
13
+ #
14
+ # This is automatically created before any specs are run, and
15
+ # destroyed after the last spec has finished.
16
+ #
17
+ def temporary_directory
18
+ "#{ROOT}/spec/tmp"
19
+ end
20
+
21
+ #
22
+ # Return the path to a temporary copy of the file at +path+
23
+ # (relative to spec/data).
24
+ #
25
+ # The copy will be deleted automatically after the last spec has
26
+ # finished.
27
+ #
28
+ # If you want an io object, use #file instead - it will be
29
+ # automatically closed at the end of the spec too.
30
+ #
31
+ def temporary_path(path)
32
+ src = source_path(path)
33
+ dst = temporary_unique_path(path)
34
+ FileUtils.mkdir_p File.dirname(dst)
35
+ FileUtils.cp src, dst
36
+ dst
37
+ end
38
+
39
+ #
40
+ # Return an open file for a copy of the file at +path+ (relative to
41
+ # spec/data).
42
+ #
43
+ # The copy will be deleted automatically after the last spec has
44
+ # finished. The file handle will be automatically closed at the end
45
+ # of the spec.
46
+ #
47
+ def temporary_file(path)
48
+ tmp_path = temporary_path(path)
49
+ file = open(tmp_path)
50
+ files_to_close << file
51
+ file
52
+ end
53
+
54
+ #
55
+ # Like #file, but return an ActionController::UploadedFile.
56
+ #
57
+ # This is the object that Rails provides to a controller when a file
58
+ # is uploaded through a multipart form.
59
+ #
60
+ def uploaded_file(path, content_type=nil)
61
+ io = temporary_file(path)
62
+ io.extend(ActionController::UploadedFile)
63
+ io.original_path = File.basename(path)
64
+ io.content_type = content_type || guess_content_type(path)
65
+ io
66
+ end
67
+
68
+ private
69
+
70
+ def init_temporary_directory
71
+ remove_temporary_directory
72
+ FileUtils.mkdir_p(temporary_directory)
73
+
74
+ # When an attachment is deleted, it deletes empty ancestral
75
+ # directories. Don't delete past the temporary directory.
76
+ FileUtils.touch "#{temporary_directory}/.do_not_delete"
77
+ end
78
+
79
+ def remove_temporary_directory
80
+ FileUtils.rm_rf(temporary_directory)
81
+ end
82
+
83
+ def close_open_files
84
+ files_to_close.each{|f| f.close unless f.closed?}
85
+ files_to_close.clear
86
+ end
87
+
88
+ def files_to_close
89
+ @files_to_close ||= []
90
+ end
91
+
92
+ def source_path(path)
93
+ "#{ROOT}/spec/data/#{path}"
94
+ end
95
+
96
+ def temporary_unique_path(path)
97
+ # Find a unique directory name for this process and
98
+ # thread. Maintain the same base name.
99
+ pid_tid = "#{Process.pid}.#{Thread.current.__id__}"
100
+ n = 0
101
+ begin
102
+ dir = "#{temporary_directory}/#{pid_tid}.#{n += 1}"
103
+ end while File.exist?(dir)
104
+ "#{dir}/#{File.basename(path)}"
105
+ end
106
+
107
+ def guess_content_type(path)
108
+ case path
109
+ when /\.jpe?g\z/
110
+ 'image/jpeg'
111
+ when /\.png\z/
112
+ 'image/png'
113
+ when /\.mov\z/
114
+ 'video/quicktime'
115
+ when /\.pdf\z/
116
+ 'application/pdf'
117
+ when /\.txt\z/
118
+ 'text/plain'
119
+ else
120
+ raise ArgumentError, "can't deduce content type for #{path}"
121
+ end
122
+ end
123
+ end
@@ -194,7 +194,7 @@ describe "Lifecycle hooks" do
194
194
  end
195
195
  thing = Thing.new(:attachment => @file)
196
196
  thing.process_attachment(:attachment, :test_event)
197
- thing.attachment = uploaded_file_with_content('test.avi', "RIFF AVI ")
197
+ thing.attachment = uploaded_file('test.mov')
198
198
  thing.process_attachment(:attachment, :test_event)
199
199
  runs.should == 2
200
200
  end
@@ -28,19 +28,7 @@ describe "Processing image attachments" do
28
28
  end
29
29
  @thing = Thing.new
30
30
  @thing.stubs(:id).returns(5)
31
- @file = small_uploaded_file(:size => "40x30")
32
- end
33
-
34
- def small_uploaded_file(options={})
35
- tmp_path = "#{temporary_directory}/small_uploaded_file.tmp.png"
36
- create_image(tmp_path, options)
37
- content = File.read(tmp_path)
38
- File.unlink(tmp_path)
39
-
40
- io = ActionController::UploadedStringIO.new(content)
41
- io.original_path = "tmp.png"
42
- io.content_type = 'image/png'
43
- io
31
+ @file = uploaded_file('test-40x30.jpg')
44
32
  end
45
33
 
46
34
  after do
@@ -0,0 +1,313 @@
1
+ require 'spec_helper'
2
+
3
+ module HasDimensionsSpec
4
+ def it_should_behave_like_an_attachment_with_dimensions(params)
5
+ # For some reason, params gets clobbered by subsequent invocations
6
+ # if this is a shared example group.
7
+ def it_should_behave_like_whether_or_not_the_width_and_height_are_stored(params)
8
+ describe 'for a new record with an attachment' do
9
+ before do
10
+ @thing = Thing.new(:attachment => uploaded_file(params[:file_40x30]))
11
+ end
12
+
13
+ describe "#width" do
14
+ it "should return the width of the given style" do
15
+ @thing.attachment.width(:original).should == 40
16
+ @thing.attachment.width(:double).should == 80
17
+ end
18
+
19
+ it "should take into account the filledness of the style" do
20
+ @thing.attachment.width(:filled).should == 60
21
+ @thing.attachment.width(:unfilled).should == 120
22
+ end
23
+
24
+ it "should use the default style if no style is given" do
25
+ @thing.attachment.width.should == 80
26
+ end
27
+ end
28
+
29
+ describe "#height" do
30
+ it "should return the height of the given style" do
31
+ @thing.attachment.height(:original).should == 30
32
+ @thing.attachment.height(:double).should == 60
33
+ end
34
+
35
+ it "should take into account the filledness of the style" do
36
+ @thing.attachment.height(:filled).should == 60
37
+ @thing.attachment.height(:unfilled).should == 90
38
+ end
39
+
40
+ it "should use the default style if no style is given" do
41
+ @thing.attachment.height.should == 60
42
+ end
43
+ end
44
+
45
+ describe "#dimensions" do
46
+ it "should return the width and height of the given style" do
47
+ @thing.attachment.dimensions(:original).should == [40, 30]
48
+ @thing.attachment.dimensions(:double).should == [80, 60]
49
+ end
50
+
51
+ it "should take into account the filledness of the style" do
52
+ @thing.attachment.dimensions(:filled).should == [60, 60]
53
+ @thing.attachment.dimensions(:unfilled).should == [120, 90]
54
+ end
55
+
56
+ it "should use the default style if no style is given" do
57
+ @thing.attachment.dimensions.should == [80, 60]
58
+ end
59
+ end
60
+
61
+ describe "#aspect_ratio" do
62
+ it "should return the aspect ratio of the given style" do
63
+ @thing.attachment.aspect_ratio(:original).should be_close(4.0/3, 1e-5)
64
+ @thing.attachment.aspect_ratio(:double).should be_close(4.0/3, 1e-5)
65
+ end
66
+
67
+ it "should take into account the filledness of the style" do
68
+ @thing.attachment.aspect_ratio(:filled).should be_close(1, 1e-5)
69
+ @thing.attachment.aspect_ratio(:unfilled).should be_close(4.0/3, 1e-5)
70
+ end
71
+
72
+ it "should use the default style if no style is given" do
73
+ @thing.attachment.aspect_ratio.should be_close(4.0/3, 1e-5)
74
+ end
75
+ end
76
+
77
+ describe "when the attachment is updated and the record reloaded" do
78
+ before do
79
+ @thing.update_attributes(:attachment => uploaded_file(params[:file_20x10]))
80
+ @thing = Thing.find(@thing.id)
81
+ end
82
+
83
+ describe "#width" do
84
+ it "should return the stored width for the original style" do
85
+ @thing.attachment.width(:original).should == 20
86
+ end
87
+ end
88
+
89
+ describe "#height" do
90
+ it "should return the stored height for the original style" do
91
+ @thing.attachment.height(:original).should == 10
92
+ end
93
+ end
94
+
95
+ describe "#dimensions" do
96
+ it "should return the stored width and height for the original style" do
97
+ @thing.attachment.dimensions(:original) == [20, 10]
98
+ end
99
+ end
100
+
101
+ describe "#aspect_ratio" do
102
+ it "should use the stored width and height for the original" do
103
+ @thing.attachment.aspect_ratio(:original).should be_close(2, 1e-5)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "when the width and height are stored" do
111
+ use_model_class(:Thing,
112
+ :attachment_file_name => :string,
113
+ :attachment_width => :integer,
114
+ :attachment_height => :integer)
115
+
116
+ before do
117
+ Thing.has_attachment :attachment do
118
+ type params[:type]
119
+ style :double, :size => '80x60'
120
+ style :filled, :size => '60x60', :filled => true
121
+ style :unfilled, :size => '120x120'
122
+ default_style :double
123
+ end
124
+ end
125
+
126
+ it_should_behave_like_whether_or_not_the_width_and_height_are_stored(params)
127
+
128
+ describe "when a record is instantiated" do
129
+ before do
130
+ @thing = Thing.new
131
+ end
132
+
133
+ describe "when the attachment is assigned a file" do
134
+ before do
135
+ @thing.attachment = uploaded_file(params[:file_40x30])
136
+ end
137
+
138
+ it "should set the stored attributes" do
139
+ @thing.attachment_width.should == 40
140
+ @thing.attachment_height.should == 30
141
+ end
142
+
143
+ describe "when the record is saved" do
144
+ before do
145
+ @thing.save!
146
+ end
147
+
148
+ describe "when the record is reinstantiated" do
149
+ it "should not make any system calls to find the dimensions"
150
+ end
151
+
152
+ describe "when the stored values are hacked, and the record is reinstantiated" do
153
+ before do
154
+ Thing.update_all(
155
+ {:attachment_width => 100, :attachment_height => 10},
156
+ {:id => @thing.id}
157
+ )
158
+ @thing = Thing.find(@thing.id)
159
+ end
160
+
161
+ describe "#width" do
162
+ it "should use the stored width for the original" do
163
+ @thing.attachment.width(:original).should == 100
164
+ end
165
+
166
+ it "should calculate the width of other styles from that of the original" do
167
+ @thing.attachment.width(:double).should == 80
168
+ end
169
+ end
170
+
171
+ describe "#height" do
172
+ it "should use the stored height for the original" do
173
+ @thing.attachment.height(:original).should == 10
174
+ end
175
+
176
+ it "should calculate the height of other styles from that of the original" do
177
+ @thing.attachment.height(:double).should == 8
178
+ end
179
+ end
180
+
181
+ describe "#dimensions" do
182
+ it "should use the stored width and height for the original" do
183
+ @thing.attachment.dimensions(:original).should == [100, 10]
184
+ end
185
+
186
+ it "should calculate the width and height of other styles from those of the original" do
187
+ @thing.attachment.dimensions(:double).should == [80, 8]
188
+ end
189
+ end
190
+
191
+ describe "#aspect_ratio" do
192
+ it "should use the stored width and height for the original" do
193
+ @thing.attachment.aspect_ratio(:original).should be_close(10, 1e-5)
194
+ end
195
+
196
+ it "should calculate the width and height of other styles from those of the original" do
197
+ @thing.attachment.aspect_ratio(:double).should be_close(10, 1e-5)
198
+ end
199
+
200
+ it "should take into account the filledness of the style" do
201
+ @thing.attachment.aspect_ratio(:filled).should be_close(1, 1e-5)
202
+ end
203
+ end
204
+ end
205
+
206
+ describe "when the file is removed and the record reloaded (file is missing)" do
207
+ before do
208
+ File.unlink(@thing.attachment.path(:original))
209
+ @thing = Thing.find(@thing.id)
210
+ end
211
+
212
+ describe "#width" do
213
+ it "should return the stored width for the original" do
214
+ @thing.attachment.width(:original).should == 40
215
+ end
216
+ end
217
+
218
+ describe "#height" do
219
+ it "should return the stored height for the original" do
220
+ @thing.attachment.height(:original).should == 30
221
+ end
222
+ end
223
+
224
+ describe "#dimensions" do
225
+ it "should return the stored width and height for the original" do
226
+ @thing.attachment.dimensions(:original).should == [40, 30]
227
+ end
228
+ end
229
+
230
+ describe "#aspect_ratio" do
231
+ it "should return the stored aspect ratio for the original" do
232
+ @thing.attachment.aspect_ratio(:original).should be_close(4.0/3, 1e-5)
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ describe "when the attachment is assigned nil" do
240
+ before do
241
+ @thing.attachment = nil
242
+ end
243
+
244
+ it "should clear the stored attributes" do
245
+ @thing.attachment_width.should be_nil
246
+ @thing.attachment_height.should be_nil
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ describe "when the width and height are not stored" do
253
+ use_model_class(:Thing, :attachment_file_name => :string)
254
+
255
+ it_should_behave_like_whether_or_not_the_width_and_height_are_stored(params)
256
+
257
+ before do
258
+ Thing.has_attachment :attachment do
259
+ type params[:type]
260
+ style :double, :size => '80x60'
261
+ style :filled, :size => '60x60', :filled => true
262
+ style :unfilled, :size => '120x120'
263
+ default_style :double
264
+ end
265
+ end
266
+
267
+ describe "when a record is created with an attachment" do
268
+ before do
269
+ @thing = Thing.create!(:attachment => uploaded_file(params[:file_40x30]))
270
+ end
271
+
272
+ describe "when the record is reinstantiated" do
273
+ it "should log the system calls to find the dimensions"
274
+ end
275
+
276
+ describe "when the record is reinstantiated but the file is missing" do
277
+ w, h = *params[:missing_dimensions]
278
+
279
+ before do
280
+ File.unlink(@thing.attachment.path(:original))
281
+ @thing = Thing.find(@thing.id)
282
+ end
283
+
284
+ describe "#width" do
285
+ it "should return #{w} for the original" do
286
+ @thing.attachment.width(:original).should == w
287
+ end
288
+ end
289
+
290
+ describe "#height" do
291
+ it "should return #{h} for the original" do
292
+ @thing.attachment.height(:original).should == h
293
+ end
294
+ end
295
+
296
+ describe "#dimensions" do
297
+ it "should return [#{w}, #{h}] for the original" do
298
+ @thing.attachment.dimensions(:original).should == [w, h]
299
+ end
300
+ end
301
+
302
+ describe "#aspect_ratio" do
303
+ it "should return #{w}/#{h} for the original" do
304
+ @thing.attachment.aspect_ratio(:original).should be_close(w.to_f/h, 1e-5)
305
+ end
306
+ end
307
+ end
308
+ end
309
+ end
310
+ end
311
+
312
+ Spec::Runner.configure{|c| c.extend self}
313
+ end