bulldog 0.0.1
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/.gitignore +2 -0
- data/DESCRIPTION.txt +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +64 -0
- data/VERSION +1 -0
- data/bulldog.gemspec +157 -0
- data/lib/bulldog.rb +95 -0
- data/lib/bulldog/attachment.rb +49 -0
- data/lib/bulldog/attachment/base.rb +167 -0
- data/lib/bulldog/attachment/has_dimensions.rb +94 -0
- data/lib/bulldog/attachment/image.rb +63 -0
- data/lib/bulldog/attachment/maybe.rb +229 -0
- data/lib/bulldog/attachment/none.rb +37 -0
- data/lib/bulldog/attachment/pdf.rb +63 -0
- data/lib/bulldog/attachment/unknown.rb +11 -0
- data/lib/bulldog/attachment/video.rb +143 -0
- data/lib/bulldog/error.rb +5 -0
- data/lib/bulldog/has_attachment.rb +214 -0
- data/lib/bulldog/interpolation.rb +73 -0
- data/lib/bulldog/missing_file.rb +12 -0
- data/lib/bulldog/processor.rb +5 -0
- data/lib/bulldog/processor/argument_tree.rb +116 -0
- data/lib/bulldog/processor/base.rb +124 -0
- data/lib/bulldog/processor/ffmpeg.rb +172 -0
- data/lib/bulldog/processor/image_magick.rb +134 -0
- data/lib/bulldog/processor/one_shot.rb +19 -0
- data/lib/bulldog/reflection.rb +234 -0
- data/lib/bulldog/saved_file.rb +19 -0
- data/lib/bulldog/stream.rb +186 -0
- data/lib/bulldog/style.rb +38 -0
- data/lib/bulldog/style_set.rb +101 -0
- data/lib/bulldog/tempfile.rb +28 -0
- data/lib/bulldog/util.rb +92 -0
- data/lib/bulldog/validations.rb +68 -0
- data/lib/bulldog/vector2.rb +18 -0
- data/rails/init.rb +9 -0
- data/script/console +8 -0
- data/spec/data/empty.txt +0 -0
- data/spec/data/test.jpg +0 -0
- data/spec/data/test.mov +0 -0
- data/spec/data/test.pdf +0 -0
- data/spec/data/test.png +0 -0
- data/spec/data/test2.jpg +0 -0
- data/spec/helpers/image_creation.rb +8 -0
- data/spec/helpers/temporary_directory.rb +25 -0
- data/spec/helpers/temporary_models.rb +76 -0
- data/spec/helpers/temporary_values.rb +102 -0
- data/spec/helpers/test_upload_files.rb +108 -0
- data/spec/helpers/time_travel.rb +20 -0
- data/spec/integration/data/test.jpg +0 -0
- data/spec/integration/lifecycle_hooks_spec.rb +213 -0
- data/spec/integration/processing_image_attachments.rb +72 -0
- data/spec/integration/processing_video_attachments_spec.rb +82 -0
- data/spec/integration/saving_an_attachment_spec.rb +31 -0
- data/spec/matchers/file_operations.rb +159 -0
- data/spec/spec_helper.rb +76 -0
- data/spec/unit/attachment/base_spec.rb +311 -0
- data/spec/unit/attachment/image_spec.rb +128 -0
- data/spec/unit/attachment/maybe_spec.rb +126 -0
- data/spec/unit/attachment/pdf_spec.rb +137 -0
- data/spec/unit/attachment/video_spec.rb +176 -0
- data/spec/unit/attachment_spec.rb +61 -0
- data/spec/unit/has_attachment_spec.rb +700 -0
- data/spec/unit/interpolation_spec.rb +108 -0
- data/spec/unit/processor/argument_tree_spec.rb +159 -0
- data/spec/unit/processor/ffmpeg_spec.rb +467 -0
- data/spec/unit/processor/image_magick_spec.rb +260 -0
- data/spec/unit/processor/one_shot_spec.rb +70 -0
- data/spec/unit/reflection_spec.rb +338 -0
- data/spec/unit/stream_spec.rb +234 -0
- data/spec/unit/style_set_spec.rb +44 -0
- data/spec/unit/style_spec.rb +51 -0
- data/spec/unit/validations_spec.rb +491 -0
- data/spec/unit/vector2_spec.rb +27 -0
- data/tasks/bulldog_tasks.rake +4 -0
- metadata +193 -0
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Processor::ImageMagick do
|
4
|
+
use_model_class(:Thing, :attachment_file_name => :string)
|
5
|
+
|
6
|
+
before do
|
7
|
+
spec = self
|
8
|
+
Thing.has_attachment :attachment do
|
9
|
+
path "#{spec.temporary_directory}/attachment.:style.:extension"
|
10
|
+
end
|
11
|
+
thing = Thing.create(:attachment => test_image_file('test.jpg'))
|
12
|
+
@thing = Thing.find(thing.id)
|
13
|
+
end
|
14
|
+
|
15
|
+
def convert
|
16
|
+
Processor::ImageMagick.convert_command
|
17
|
+
end
|
18
|
+
|
19
|
+
def original_path
|
20
|
+
"#{temporary_directory}/attachment.original.jpg"
|
21
|
+
end
|
22
|
+
|
23
|
+
def output_path(style_name=:output)
|
24
|
+
"#{temporary_directory}/attachment.#{style_name}.jpg"
|
25
|
+
end
|
26
|
+
|
27
|
+
def configure(&block)
|
28
|
+
Thing.attachment_reflections[:attachment].configure(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def style(name, attributes={})
|
32
|
+
configure do
|
33
|
+
style name, attributes
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def process(&block)
|
38
|
+
configure do
|
39
|
+
process(:on => :event, :with => :image_magick, &block)
|
40
|
+
end
|
41
|
+
@thing.attachment.process(:event)
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#dimensions" do
|
45
|
+
it "should yield the dimensions of the input file at that point in the processing pipeline if a block is given" do
|
46
|
+
style :output
|
47
|
+
values = []
|
48
|
+
process do
|
49
|
+
dimensions do |*args|
|
50
|
+
values << args
|
51
|
+
end
|
52
|
+
end
|
53
|
+
styles = Thing.attachment_reflections[:attachment].styles.to_a
|
54
|
+
values.should == [[styles, 40, 30]]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should yield the dimensions once per branch if called with a block after a branch in the pipeline" do
|
58
|
+
style :small, :size => '4x3'
|
59
|
+
style :large, :size => '400x300'
|
60
|
+
values = []
|
61
|
+
process do
|
62
|
+
resize
|
63
|
+
dimensions do |*args|
|
64
|
+
values << args
|
65
|
+
end
|
66
|
+
end
|
67
|
+
styles = Thing.attachment_reflections[:attachment].styles
|
68
|
+
values.should have(2).sets_of_arguments
|
69
|
+
values[0].should == [[styles[:small]], 4, 3]
|
70
|
+
values[1].should == [[styles[:large]], 400, 300]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#process" do
|
75
|
+
it "should run convert if no block is given" do
|
76
|
+
style :output
|
77
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", output_path).returns('')
|
78
|
+
process
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should use the given :quality style attribute" do
|
82
|
+
style :output, :quality => 50
|
83
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-quality', '50', output_path).returns('')
|
84
|
+
process
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should use the :colorspace style attribute" do
|
88
|
+
style :output, :colorspace => 'rgb'
|
89
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-colorspace', 'rgb', output_path).returns('')
|
90
|
+
process
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should use the :format style attribute to set the file extension if it's specified by the :extension interpolation key" do
|
94
|
+
style :output, :format => 'png'
|
95
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", output_path.sub(/jpg\z/, 'png')).returns('')
|
96
|
+
process
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should log the command run if a logger is set" do
|
100
|
+
style :output
|
101
|
+
log_path = "#{temporary_directory}/log"
|
102
|
+
open(log_path, 'w') do |file|
|
103
|
+
Bulldog.logger = Logger.new(file)
|
104
|
+
process
|
105
|
+
end
|
106
|
+
File.read(log_path).should include("[Bulldog] Running: #{convert}")
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should not blow up if the logger is set to nil" do
|
110
|
+
style :output
|
111
|
+
Bulldog.logger = nil
|
112
|
+
lambda{process}.should_not raise_error
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "#resize" do
|
117
|
+
it "should resize the images to the style's size" do
|
118
|
+
style :output, :size => '10x10'
|
119
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-resize', '10x10', output_path).returns('')
|
120
|
+
process{resize}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "#auto_orient" do
|
125
|
+
it "should auto-orient the images" do
|
126
|
+
style :output
|
127
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-auto-orient', output_path).returns('')
|
128
|
+
process{auto_orient}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "#strip" do
|
133
|
+
it "should strip the images" do
|
134
|
+
style :output, :size => '10x10'
|
135
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-strip', output_path).returns('')
|
136
|
+
process{strip}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#flip" do
|
141
|
+
it "should flip the image vertically" do
|
142
|
+
style :output
|
143
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-flip', output_path).returns('')
|
144
|
+
process{flip}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "#flop" do
|
149
|
+
it "should flip the image horizontally" do
|
150
|
+
style :output
|
151
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-flop', output_path).returns('')
|
152
|
+
process{flop}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "#thumbnail" do
|
157
|
+
describe "for filled styles" do
|
158
|
+
it "should resize the image to fill the rectangle of the specified size and crop off the edges" do
|
159
|
+
style :output, :size => '10x10', :filled => true
|
160
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-resize', '10x10^',
|
161
|
+
'-gravity', 'Center', '-crop', '10x10+0+0', '+repage', output_path).returns('')
|
162
|
+
process{thumbnail}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "for unfilled styles" do
|
167
|
+
it "should resize the image to fit inside the specified box size" do
|
168
|
+
style :output, :size => '10x10'
|
169
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-resize', '10x10', output_path).returns('')
|
170
|
+
process{thumbnail}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "#rotate" do
|
176
|
+
it "should rotate the image by the given angle" do
|
177
|
+
style :output
|
178
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-rotate', '90', output_path).returns('')
|
179
|
+
process{rotate 90}
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should allow the angle to be given by a string" do
|
183
|
+
style :output
|
184
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-rotate', '90', output_path).returns('')
|
185
|
+
process{rotate '90'}
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should not perform any rotation if the given angle is zero" do
|
189
|
+
style :output
|
190
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", output_path).returns('')
|
191
|
+
process{rotate 0}
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should not perform any rotation if the given angle is blank" do
|
195
|
+
style :output
|
196
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", output_path).returns('')
|
197
|
+
process{rotate ''}
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe "#crop" do
|
202
|
+
it "should crop the image by the given size and origin, and repage" do
|
203
|
+
style :output
|
204
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-crop', '10x20+30-40', '+repage', output_path).returns('')
|
205
|
+
process do
|
206
|
+
crop(:size => '10x20', :origin => '30,-40')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should extract a common prefix if there are multiple styles which start with the same operations" do
|
212
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-auto-orient',
|
213
|
+
'(', '+clone', '-resize', '100x100', '-write', output_path(:big), '+delete', ')', '-resize', '40x40', output_path(:small)).returns('')
|
214
|
+
style :big, :size => '100x100'
|
215
|
+
style :small, :size => '40x40'
|
216
|
+
process do
|
217
|
+
auto_orient
|
218
|
+
resize
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should handle a complex tree of arguments optimally" do
|
223
|
+
# The tree:
|
224
|
+
# auto-orient
|
225
|
+
# resize 10x20
|
226
|
+
# flip [:a]
|
227
|
+
# flop [:b]
|
228
|
+
# flip
|
229
|
+
# strip [:c]
|
230
|
+
# quality 75 [:d]
|
231
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-auto-orient',
|
232
|
+
'(', '+clone', '-resize', '10x20', '(', '+clone', '-flip', '-write', output_path(:a), '+delete', ')',
|
233
|
+
'-flop', '-write', output_path(:b), '+delete', ')',
|
234
|
+
'-flip', '(', '+clone', '-flop', '-write', output_path(:c), '+delete', ')',
|
235
|
+
'-quality', '75', output_path(:d)).returns('')
|
236
|
+
style :a, :size => '10x20'
|
237
|
+
style :b, :size => '10x20'
|
238
|
+
style :c, :size => '30x40'
|
239
|
+
style :d, :size => '30x40', :quality => 75
|
240
|
+
process do
|
241
|
+
auto_orient
|
242
|
+
resize if [:a, :b].include?(style.name)
|
243
|
+
flip unless style.name == :b
|
244
|
+
flop if [:b, :c].include?(style.name)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should allow specifying operations for some styles only by checking #style" do
|
249
|
+
Bulldog.expects(:run).once.with(convert, "#{original_path}[0]", '-auto-orient',
|
250
|
+
'(', '+clone', '-flip', '-write', output_path(:flipped), '+delete', ')',
|
251
|
+
'-flop', output_path(:flopped)).returns('')
|
252
|
+
style :flipped, :path => '/tmp/flipped.jpg'
|
253
|
+
style :flopped, :path => '/tmp/flopped.jpg'
|
254
|
+
process do
|
255
|
+
auto_orient
|
256
|
+
flip if style.name == :flipped
|
257
|
+
flop if style.name == :flopped
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Processor::OneShot do
|
4
|
+
use_model_class(:Thing)
|
5
|
+
|
6
|
+
before do
|
7
|
+
Thing.has_attachment :attachment
|
8
|
+
@thing = Thing.create(:attachment => test_empty_file)
|
9
|
+
end
|
10
|
+
|
11
|
+
def configure(&block)
|
12
|
+
Thing.attachment_reflections[:attachment].configure(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def process(&block)
|
16
|
+
configure do
|
17
|
+
process(:on => :event, :with => :one_shot, &block)
|
18
|
+
end
|
19
|
+
@thing.attachment.process(:event)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "any one shot processor", :shared => true do
|
23
|
+
it "should run the process block exactly once" do
|
24
|
+
num_runs = 0
|
25
|
+
process do
|
26
|
+
num_runs += 1
|
27
|
+
end
|
28
|
+
num_runs.should == 1
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should provide no styles to the process block" do
|
32
|
+
styles = nil
|
33
|
+
process do
|
34
|
+
styles = self.styles
|
35
|
+
end
|
36
|
+
styles.should be_empty
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should provide no style to the process block" do
|
40
|
+
style = nil
|
41
|
+
process do
|
42
|
+
style = self.style
|
43
|
+
end
|
44
|
+
style.should be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should provide no output files to the process block" do
|
48
|
+
output_file = nil
|
49
|
+
process do
|
50
|
+
output_file = self.output_file(:one)
|
51
|
+
end
|
52
|
+
output_file.should be_nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "when there are no styles defined" do
|
57
|
+
it_should_behave_like "any one shot processor"
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "when there are multiple styles defined" do
|
61
|
+
before do
|
62
|
+
configure do
|
63
|
+
style :one
|
64
|
+
style :two
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it_should_behave_like "any one shot processor"
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,338 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Reflection do
|
4
|
+
use_model_class(:Thing,
|
5
|
+
:photo_file_name => :string,
|
6
|
+
:photo_content_type => :string,
|
7
|
+
:custom_file_name => :string,
|
8
|
+
:custom_content_type => :string)
|
9
|
+
|
10
|
+
def reflection
|
11
|
+
Thing.attachment_reflections[:photo]
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#configure" do
|
15
|
+
it "should append the configuration to any existing configuration" do
|
16
|
+
Thing.has_attachment :photo do
|
17
|
+
path "/custom/path"
|
18
|
+
end
|
19
|
+
Thing.attachment_reflections[:photo].configure do
|
20
|
+
url "/custom/url"
|
21
|
+
end
|
22
|
+
reflection.path_template.should == "/custom/path"
|
23
|
+
reflection.url_template.should == "/custom/url"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should overwrite any configuration items specified in later blocks" do
|
27
|
+
Thing.has_attachment :photo do
|
28
|
+
path "/custom/path"
|
29
|
+
end
|
30
|
+
Thing.has_attachment :photo do
|
31
|
+
path "/new/custom/path"
|
32
|
+
end
|
33
|
+
reflection.path_template.should == "/new/custom/path"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "configuration" do
|
38
|
+
# TODO: Restructure the tests so we test by configuration method,
|
39
|
+
# not reflection method.
|
40
|
+
describe "#process_once" do
|
41
|
+
it "should add a process event with a one_shot processor" do
|
42
|
+
Thing.has_attachment :photo do
|
43
|
+
process_once(:on => :test_event){}
|
44
|
+
end
|
45
|
+
events = reflection.events[:test_event]
|
46
|
+
events.map(&:processor_type).should == [:one_shot]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should raise an ArgumentError if a processor is specified" do
|
50
|
+
block_run = false
|
51
|
+
spec = self
|
52
|
+
Thing.has_attachment :photo do
|
53
|
+
block_run = true
|
54
|
+
lambda{process_once(:with => :image_magick){}}.should spec.raise_error(ArgumentError)
|
55
|
+
end
|
56
|
+
block_run.should be_true
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should raise an ArgumentError if styles are specified" do
|
60
|
+
block_run = false
|
61
|
+
spec = self
|
62
|
+
Thing.has_attachment :photo do
|
63
|
+
block_run = true
|
64
|
+
style :one
|
65
|
+
lambda{process_once(:styles => [:one]){}}.should spec.raise_error(ArgumentError)
|
66
|
+
end
|
67
|
+
block_run.should be_true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#path_template" do
|
73
|
+
describe "when a path has been confired for the attachment" do
|
74
|
+
before do
|
75
|
+
Thing.has_attachment :photo do
|
76
|
+
path "/configured/path"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should return the configured path" do
|
81
|
+
reflection.path_template.should == "/configured/path"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "when no path has been configured for the attachment, and there is a default path template" do
|
86
|
+
use_temporary_attribute_value Bulldog, :default_path_template, "/default/path/template"
|
87
|
+
|
88
|
+
before do
|
89
|
+
Thing.has_attachment :photo
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should return the default path template" do
|
93
|
+
reflection.path_template.should == "/default/path/template"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "when no path has been configured, and there is no default path template" do
|
98
|
+
use_temporary_attribute_value Bulldog, :default_path_template, nil
|
99
|
+
|
100
|
+
before do
|
101
|
+
Thing.has_attachment :photo do
|
102
|
+
url "/configured/url"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should return the URL prefixed with the public path" do
|
107
|
+
reflection.path_template.should == ":public_path/configured/url"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#url_template" do
|
113
|
+
describe "when an URL has been configured for the attachment" do
|
114
|
+
before do
|
115
|
+
Thing.has_attachment :photo do
|
116
|
+
url "/path/to/somewhere"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should return the configured URL template" do
|
121
|
+
reflection.url_template.should == "/path/to/somewhere"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "when no URL has been configured for the attachment" do
|
126
|
+
use_temporary_attribute_value Bulldog, :default_url_template, "/default/url/template"
|
127
|
+
|
128
|
+
before do
|
129
|
+
Thing.has_attachment :photo
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should return the default URL template" do
|
133
|
+
reflection.url_template.should == "/default/url/template"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "#style" do
|
139
|
+
it "should return the set of styles" do
|
140
|
+
Thing.has_attachment :photo do
|
141
|
+
style :small, :size => '32x32'
|
142
|
+
style :large, :size => '512x512'
|
143
|
+
end
|
144
|
+
reflection.styles.should == StyleSet[
|
145
|
+
Style.new(:small, {:size => '32x32'}),
|
146
|
+
Style.new(:large, {:size => '512x512'}),
|
147
|
+
]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "#default_style" do
|
152
|
+
it "should return the configured default_style" do
|
153
|
+
Thing.has_attachment :photo do
|
154
|
+
style :small, :size => '32x32'
|
155
|
+
default_style :small
|
156
|
+
end
|
157
|
+
reflection.default_style.should == :small
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should default to :original" do
|
161
|
+
Thing.has_attachment :photo
|
162
|
+
reflection.default_style.should == :original
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should raise an error if the configured default_style is invalid" do
|
166
|
+
Thing.has_attachment :photo do
|
167
|
+
default_style :bad
|
168
|
+
end
|
169
|
+
lambda{reflection.default_style}.should raise_error(Error)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "#events" do
|
174
|
+
it "should return the map of configured events" do
|
175
|
+
Thing.has_attachment :photo do
|
176
|
+
process(:on => :test_event){}
|
177
|
+
end
|
178
|
+
events = reflection.events[:test_event]
|
179
|
+
events.should have(1).event
|
180
|
+
events.first.should be_a(Reflection::Event)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should provide access to the processor type of each event" do
|
184
|
+
Thing.has_attachment :photo do
|
185
|
+
process(:on => :test_event, :with => :test){}
|
186
|
+
end
|
187
|
+
event = reflection.events[:test_event].first
|
188
|
+
event.processor_type.should == :test
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should have nil as the processor type if the default processor type is to be used" do
|
192
|
+
Thing.has_attachment :photo do
|
193
|
+
process(:on => :test_event){}
|
194
|
+
end
|
195
|
+
event = reflection.events[:test_event].first
|
196
|
+
event.processor_type.should be_nil
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should have the configured styles" do
|
200
|
+
Thing.has_attachment :photo do
|
201
|
+
style :small, :size => '10x10'
|
202
|
+
style :large, :size => '1000x1000'
|
203
|
+
process(:on => :test_event, :styles => [:small]){}
|
204
|
+
end
|
205
|
+
event = reflection.events[:test_event].first
|
206
|
+
event.styles.should == [:small]
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should provide access to the callback of each event" do
|
210
|
+
Thing.has_attachment :photo do
|
211
|
+
process(:on => :test_event){}
|
212
|
+
end
|
213
|
+
event = reflection.events[:test_event].first
|
214
|
+
event.callback.should be_a(Proc)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "#detect_type_by" do
|
219
|
+
describe "when configured with a symbol" do
|
220
|
+
it "should use the named registered type detection proc" do
|
221
|
+
args = nil
|
222
|
+
Bulldog::Reflection.to_detect_type_by :test_detector do |*args|
|
223
|
+
:type
|
224
|
+
end
|
225
|
+
Thing.has_attachment :photo do
|
226
|
+
detect_type_by :test_detector
|
227
|
+
end
|
228
|
+
|
229
|
+
thing = Thing.new
|
230
|
+
stream = mock
|
231
|
+
reflection.detect_attachment_type(thing, stream).should == :type
|
232
|
+
args.should == [thing, :photo, stream]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe "when configured with a block" do
|
237
|
+
it "should call the given block" do
|
238
|
+
Thing.has_attachment :photo do
|
239
|
+
detect_type_by{:type}
|
240
|
+
end
|
241
|
+
reflection.detect_attachment_type(Thing.new, mock).should == :type
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "when configured with a BoundMethod" do
|
246
|
+
it "should call the given method" do
|
247
|
+
Thing.stubs(:detect_type).returns(:type)
|
248
|
+
Thing.has_attachment :photo do
|
249
|
+
detect_type_by Thing.method(:detect_type)
|
250
|
+
end
|
251
|
+
reflection.detect_attachment_type(Thing.new, mock).should == :type
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe "when configured with a proc" do
|
256
|
+
it "should call the given proc" do
|
257
|
+
Thing.has_attachment :photo do
|
258
|
+
detect_type_by lambda{:type}
|
259
|
+
end
|
260
|
+
reflection.detect_attachment_type(Thing.new, mock).should == :type
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "when configured with both an argument and a block" do
|
265
|
+
it "should raise an ArgumentError" do
|
266
|
+
block_run = false
|
267
|
+
spec = self
|
268
|
+
Thing.has_attachment :photo do
|
269
|
+
block_run = true
|
270
|
+
lambda do
|
271
|
+
detect_type_by(lambda{:type}){:type}
|
272
|
+
end.should spec.raise_error(ArgumentError)
|
273
|
+
end
|
274
|
+
block_run.should be_true
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe "when configured with #type" do
|
279
|
+
it "should return the given type" do
|
280
|
+
Thing.has_attachment :photo do
|
281
|
+
type :type
|
282
|
+
end
|
283
|
+
reflection.detect_attachment_type(Thing.new, mock).should == :type
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
describe "#stored_attributes" do
|
289
|
+
it "should return the configured stored attributes" do
|
290
|
+
Thing.has_attachment :photo do
|
291
|
+
store_attributes(
|
292
|
+
:file_name => :custom_file_name,
|
293
|
+
:content_type => :custom_content_type
|
294
|
+
)
|
295
|
+
end
|
296
|
+
reflection.stored_attributes.should == {
|
297
|
+
:file_name => :custom_file_name,
|
298
|
+
:content_type => :custom_content_type
|
299
|
+
}
|
300
|
+
end
|
301
|
+
|
302
|
+
it "should allow a shortcut if the field names follow convention" do
|
303
|
+
Thing.has_attachment :photo do
|
304
|
+
store_attributes :file_name, :content_type
|
305
|
+
end
|
306
|
+
reflection.stored_attributes.should == {
|
307
|
+
:file_name => :photo_file_name,
|
308
|
+
:content_type => :photo_content_type,
|
309
|
+
}
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe "#column_name_for_stored_attribute" do
|
314
|
+
it "should return nil if the stored attribute has been explicitly mapped to nil" do
|
315
|
+
Thing.has_attachment :photo do
|
316
|
+
store_attributes :file_name => nil
|
317
|
+
end
|
318
|
+
reflection.column_name_for_stored_attribute(:file_name).should be_nil
|
319
|
+
end
|
320
|
+
|
321
|
+
it "should return the column specified if has been mapped to a column name" do
|
322
|
+
Thing.has_attachment :photo do
|
323
|
+
store_attributes :file_name => :photo_content_type
|
324
|
+
end
|
325
|
+
reflection.column_name_for_stored_attribute(:file_name).should == :photo_content_type
|
326
|
+
end
|
327
|
+
|
328
|
+
it "should return the default column name if the stored attribute is unspecified, and the column exists" do
|
329
|
+
Thing.has_attachment :photo
|
330
|
+
reflection.column_name_for_stored_attribute(:file_name).should == :photo_file_name
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should return nil if the stored attribute is unspecified, and the column does not exist" do
|
334
|
+
Thing.has_attachment :photo
|
335
|
+
reflection.column_name_for_stored_attribute(:file_size).should be_nil
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|