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.
- data/CHANGELOG +7 -0
- data/lib/bulldog/attachment/base.rb +3 -6
- data/lib/bulldog/attachment/has_dimensions.rb +43 -34
- data/lib/bulldog/attachment/image.rb +5 -26
- data/lib/bulldog/attachment/pdf.rb +3 -28
- data/lib/bulldog/attachment/video.rb +48 -45
- data/lib/bulldog/style.rb +28 -1
- data/lib/bulldog/version.rb +1 -1
- data/spec/data/3-bytes.txt +1 -0
- data/spec/data/4-bytes.txt +1 -0
- data/spec/data/5-bytes.txt +1 -0
- data/spec/data/6-bytes.txt +1 -0
- data/spec/data/test-20x10.jpg +0 -0
- data/spec/data/test-20x10.pdf +0 -0
- data/spec/data/test-20x10x1.mov +0 -0
- data/spec/data/test-40x30.jpg +0 -0
- data/spec/data/test-40x30.pdf +0 -0
- data/spec/data/test-40x30x1.mov +0 -0
- data/spec/helpers/files.rb +123 -0
- data/spec/integration/lifecycle_hooks_spec.rb +1 -1
- data/spec/integration/processing_image_attachments.rb +1 -13
- data/spec/macros/attachment/has_dimensions_spec.rb +313 -0
- data/spec/spec_helper.rb +3 -4
- data/spec/unit/attachment/base_spec.rb +25 -45
- data/spec/unit/attachment/image_spec.rb +48 -171
- data/spec/unit/attachment/maybe_spec.rb +4 -12
- data/spec/unit/attachment/pdf_spec.rb +18 -136
- data/spec/unit/attachment/video_spec.rb +98 -170
- data/spec/unit/attachment_spec.rb +1 -1
- data/spec/unit/has_attachment_spec.rb +29 -26
- data/spec/unit/interpolation_spec.rb +2 -2
- data/spec/unit/processor/ffmpeg_spec.rb +3 -3
- data/spec/unit/processor/image_magick_spec.rb +1 -1
- data/spec/unit/processor/one_shot_spec.rb +1 -1
- data/spec/unit/stream_spec.rb +3 -3
- data/spec/unit/style_spec.rb +40 -0
- data/spec/unit/validations_spec.rb +33 -33
- metadata +28 -8
- data/spec/helpers/temporary_directory.rb +0 -25
- data/spec/helpers/test_upload_files.rb +0 -108
data/lib/bulldog/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
...
|
@@ -0,0 +1 @@
|
|
1
|
+
....
|
@@ -0,0 +1 @@
|
|
1
|
+
.....
|
@@ -0,0 +1 @@
|
|
1
|
+
......
|
Binary file
|
Binary file
|
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 =
|
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 =
|
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
|