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