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,234 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Stream do
|
4
|
+
before do
|
5
|
+
@streams = []
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
@streams.each(&:close)
|
10
|
+
end
|
11
|
+
|
12
|
+
def autoclose_stream(stream)
|
13
|
+
@streams << stream
|
14
|
+
stream
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.it_should_behave_like_all_streams(options={})
|
18
|
+
class_eval do
|
19
|
+
def object(content)
|
20
|
+
raise 'example group must define #object'
|
21
|
+
end
|
22
|
+
|
23
|
+
def stream(content)
|
24
|
+
Stream.new( object(content) )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#size" do
|
29
|
+
it "should return the number of bytes in the object" do
|
30
|
+
stream = stream('content')
|
31
|
+
stream.size.should == 'content'.size
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#path" do
|
36
|
+
it "should return the path of a file which contains the contents of the object" do
|
37
|
+
stream = stream('content')
|
38
|
+
File.read(stream.path).should == 'content'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#content_type" do
|
43
|
+
it "should return the MIME type of the object" do
|
44
|
+
stream = stream("\xff\xd8")
|
45
|
+
stream.content_type.split(/;/).first.should == 'image/jpeg'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#write_to" do
|
50
|
+
it "should write the contents of the file to the given path" do
|
51
|
+
stream = stream('content')
|
52
|
+
path = "#{temporary_directory}/written"
|
53
|
+
stream.write_to(path)
|
54
|
+
File.read(path).should == 'content'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#file_name" do
|
59
|
+
case options[:file_name]
|
60
|
+
when :original_path
|
61
|
+
it "should return the original path" do
|
62
|
+
stream = stream('content')
|
63
|
+
stream.target.original_path = 'test.jpg'
|
64
|
+
stream.file_name.should == 'test.jpg'
|
65
|
+
end
|
66
|
+
when :file_name
|
67
|
+
it "should return the file name" do
|
68
|
+
stream = stream('content')
|
69
|
+
stream.target.stubs(:file_name).returns('test.jpg')
|
70
|
+
stream.file_name.should == 'test.jpg'
|
71
|
+
end
|
72
|
+
when :basename
|
73
|
+
it "should return the basename of the path" do
|
74
|
+
stream = stream('content')
|
75
|
+
basename = File.basename(stream.path)
|
76
|
+
stream.file_name.should == basename
|
77
|
+
end
|
78
|
+
else
|
79
|
+
it "should return nil" do
|
80
|
+
stream = stream('content')
|
81
|
+
stream.file_name.should be_nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'for a small uploaded file' do
|
88
|
+
it_should_behave_like_all_streams :file_name => :original_path
|
89
|
+
|
90
|
+
def object(content)
|
91
|
+
stringio = StringIO.new(content)
|
92
|
+
class << stringio
|
93
|
+
attr_accessor :original_path
|
94
|
+
end
|
95
|
+
stringio
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'for a large uploaded file' do
|
100
|
+
it_should_behave_like_all_streams :file_name => :original_path
|
101
|
+
|
102
|
+
def object(content)
|
103
|
+
stringio = StringIO.new(content)
|
104
|
+
class << stringio
|
105
|
+
attr_accessor :original_path
|
106
|
+
end
|
107
|
+
stringio
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'for a StringIO' do
|
112
|
+
it_should_behave_like_all_streams
|
113
|
+
|
114
|
+
def object(content)
|
115
|
+
tempfile = Tempfile.new('bulldog-spec')
|
116
|
+
tempfile.print(content)
|
117
|
+
class << tempfile
|
118
|
+
attr_accessor :original_path
|
119
|
+
end
|
120
|
+
tempfile
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe 'for a Tempfile' do
|
125
|
+
it_should_behave_like_all_streams
|
126
|
+
|
127
|
+
def object(content)
|
128
|
+
file = Tempfile.new('bulldog-spec')
|
129
|
+
file.print(content)
|
130
|
+
file
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'for a File opened for reading' do
|
135
|
+
it_should_behave_like_all_streams :file_name => :basename
|
136
|
+
|
137
|
+
def object(content)
|
138
|
+
path = "#{temporary_directory}/file"
|
139
|
+
File.open(path, 'w'){|f| f.print content}
|
140
|
+
file = File.open(path)
|
141
|
+
autoclose_stream(file)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should not do anything if we try to write to the original file" do
|
145
|
+
path = "#{temporary_directory}/file"
|
146
|
+
open(path, 'w'){|f| f.print 'content'}
|
147
|
+
open(path) do |file|
|
148
|
+
stream = Stream.new(file)
|
149
|
+
lambda{stream.write_to(path)}.should_not raise_error
|
150
|
+
File.read(path).should == 'content'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe 'for a File opened for writing' do
|
156
|
+
it_should_behave_like_all_streams :file_name => :basename
|
157
|
+
|
158
|
+
def object(content)
|
159
|
+
file = File.open("#{temporary_directory}/file", 'w')
|
160
|
+
file.print content
|
161
|
+
autoclose_stream(file)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'for an SavedFile' do
|
166
|
+
it_should_behave_like_all_streams :file_name => :file_name
|
167
|
+
|
168
|
+
def object(content)
|
169
|
+
path = "#{temporary_directory}/file"
|
170
|
+
open(path, 'w'){|f| f.print content}
|
171
|
+
SavedFile.new(path)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe 'for a MissingFile' do
|
176
|
+
it_should_behave_like_all_streams :file_name => :file_name
|
177
|
+
|
178
|
+
def object(content)
|
179
|
+
path = "#{temporary_directory}/missing-file"
|
180
|
+
open(path, 'w'){|f| f.print content}
|
181
|
+
MissingFile.new(:path => path)
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "for a default MissingFile" do
|
185
|
+
before do
|
186
|
+
@stream = Stream.new( MissingFile.new )
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should have a size of 0" do
|
190
|
+
@stream.size.should == 0
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should return a string for the path" do
|
194
|
+
@stream.path.should be_a(String)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should return a string for the content type" do
|
198
|
+
@stream.content_type.should be_a(String)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should default to a file_name that indicates it's a missing file" do
|
202
|
+
@stream.file_name.should == 'missing-file'
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should write an empty file for #write_to" do
|
206
|
+
path = "#{temporary_directory}/missing_file"
|
207
|
+
@stream.write_to(path)
|
208
|
+
File.read(path).should == ''
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe 'for an IO' do
|
214
|
+
it_should_behave_like_all_streams
|
215
|
+
|
216
|
+
def object(content)
|
217
|
+
io = IO.popen("echo -n #{content}")
|
218
|
+
autoclose_stream(io)
|
219
|
+
end
|
220
|
+
|
221
|
+
describe "#path" do
|
222
|
+
it "should preserve the file extension if an #original_path is available" do
|
223
|
+
io = object('content')
|
224
|
+
class << io
|
225
|
+
def original_path
|
226
|
+
'test.xyz'
|
227
|
+
end
|
228
|
+
end
|
229
|
+
stream = Stream.new(io)
|
230
|
+
File.extname(stream.path).should == '.xyz'
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StyleSet do
|
4
|
+
before do
|
5
|
+
@one = Style.new(:one, {})
|
6
|
+
@two = Style.new(:two, {})
|
7
|
+
@original = Style.new(:original, {})
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#[]" do
|
11
|
+
it "should allow lookup by style name" do
|
12
|
+
style_set = StyleSet[@one, @two]
|
13
|
+
style_set[:one].should equal(@one)
|
14
|
+
style_set[:two].should equal(@two)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should still allow lookup by index" do
|
18
|
+
style_set = StyleSet[@one, @two]
|
19
|
+
style_set[0].should equal(@one)
|
20
|
+
style_set[1].should equal(@two)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return a special, empty style for :original" do
|
24
|
+
style_set = StyleSet[]
|
25
|
+
style_set[:original].should == @original
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#clear" do
|
30
|
+
before do
|
31
|
+
@style_set = StyleSet[@one, @two]
|
32
|
+
@style_set.clear
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should remove all non-original styles" do
|
36
|
+
@style_set[:one].should be_nil
|
37
|
+
@style_set[:two].should be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should leave the original style" do
|
41
|
+
@style_set[:original].should == @original
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Style do
|
4
|
+
describe "#initialize" do
|
5
|
+
it "should create a Style with the given name and attributes" do
|
6
|
+
style = Style.new(:big, :size => '100x100')
|
7
|
+
style.name.should == :big
|
8
|
+
style.attributes.should == {:size => '100x100'}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#[]" do
|
13
|
+
it "should return the value of the named attribute" do
|
14
|
+
style = Style.new(:big, :size => '100x100')
|
15
|
+
style[:size].should == '100x100'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#==" do
|
20
|
+
it "should return true if the names and attributes are both equal" do
|
21
|
+
a = Style.new(:big, :size => '100x100')
|
22
|
+
b = Style.new(:big, :size => '100x100')
|
23
|
+
a.should == b
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return false if the names are not equal" do
|
27
|
+
a = Style.new(:big, :size => '100x100')
|
28
|
+
b = Style.new(:bad, :size => '100x100')
|
29
|
+
a.should_not == b
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return true if the attributes are not equal" do
|
33
|
+
a = Style.new(:big, :size => '100x100')
|
34
|
+
b = Style.new(:big, :size => '1x1')
|
35
|
+
a.should_not == b
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not blow up if the argument is not a Style" do
|
39
|
+
a = Style.new(:big, :size => '100x100')
|
40
|
+
b = Object.new
|
41
|
+
a.should_not == b
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#inspect" do
|
46
|
+
it "should show the name and attributes" do
|
47
|
+
style = Style.new(:big, :size => '100x100')
|
48
|
+
style.inspect.should == "#<Style :big {:size=>\"100x100\"}>"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,491 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Validations do
|
4
|
+
use_model_class(:Thing, :photo_file_name => :string)
|
5
|
+
|
6
|
+
before do
|
7
|
+
Thing.has_attachment :photo
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "an ActiveRecord validation", :shared => true do
|
11
|
+
# Includers must define:
|
12
|
+
# - validation
|
13
|
+
# - make_thing_pass
|
14
|
+
# - make_thing_fail
|
15
|
+
describe "when :on => :create is given" do
|
16
|
+
before do
|
17
|
+
Thing.send validation, :photo, validation_options.merge(:on => :create)
|
18
|
+
@thing = Thing.new
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should run the validation when creating the record" do
|
22
|
+
make_thing_fail
|
23
|
+
@thing.should_not be_valid
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not run the validation when updating the record" do
|
27
|
+
make_thing_pass
|
28
|
+
@thing.save.should be_true # sanity check
|
29
|
+
|
30
|
+
make_thing_fail
|
31
|
+
@thing.should be_valid
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "when :on => :update is given" do
|
36
|
+
before do
|
37
|
+
Thing.send validation, :photo, validation_options.merge(:on => :update)
|
38
|
+
@thing = Thing.new
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should not run the validation when creating the record" do
|
42
|
+
make_thing_fail
|
43
|
+
@thing.should be_valid
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should run the validation when updating the record" do
|
47
|
+
make_thing_pass
|
48
|
+
@thing.save.should be_true # sanity check
|
49
|
+
|
50
|
+
make_thing_fail
|
51
|
+
@thing.should_not be_valid
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "when :on => :save is given" do
|
56
|
+
before do
|
57
|
+
Thing.send validation, :photo, validation_options.merge(:on => :save)
|
58
|
+
@thing = Thing.new
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should run the validation when creating the record" do
|
62
|
+
make_thing_fail
|
63
|
+
@thing.should_not be_valid
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should run the validation when updating the record" do
|
67
|
+
make_thing_pass
|
68
|
+
@thing.save.should be_true # sanity check
|
69
|
+
|
70
|
+
make_thing_fail
|
71
|
+
@thing.should_not be_valid
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "when an :if option is given" do
|
76
|
+
before do
|
77
|
+
this = self
|
78
|
+
Thing.class_eval do
|
79
|
+
attr_accessor :flag
|
80
|
+
send this.validation, :photo, this.validation_options.merge(:if => :flag)
|
81
|
+
end
|
82
|
+
@thing = Thing.new
|
83
|
+
make_thing_fail
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should run the validation if the condition is true" do
|
87
|
+
@thing.flag = true
|
88
|
+
@thing.should_not be_valid
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should not run the validation if the condition is false" do
|
92
|
+
@thing.flag = false
|
93
|
+
@thing.should be_valid
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "when an :unless option is given" do
|
98
|
+
before do
|
99
|
+
this = self
|
100
|
+
Thing.class_eval do
|
101
|
+
attr_accessor :flag
|
102
|
+
send this.validation, :photo, this.validation_options.merge(:unless => :flag)
|
103
|
+
end
|
104
|
+
@thing = Thing.new
|
105
|
+
make_thing_fail
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should not run the validation if the condition is true" do
|
109
|
+
@thing.flag = true
|
110
|
+
@thing.should be_valid
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should run the validation if the condition is false" do
|
114
|
+
@thing.flag = false
|
115
|
+
@thing.should_not be_valid
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "when a :message option is given with a string" do
|
120
|
+
before do
|
121
|
+
Thing.send validation, :photo, validation_options.merge(:message => 'Bugger.')
|
122
|
+
@thing = Thing.new
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should use a string given as a :message option as the error message" do
|
126
|
+
make_thing_fail
|
127
|
+
@thing.valid?.should be_false
|
128
|
+
@thing.errors[:photo].should == 'Bugger.'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.it_should_use_i18n_key(key, validation_options={}, &block)
|
133
|
+
describe "when the value is #{key.to_s.humanize.downcase}" do
|
134
|
+
use_model_class(:Subthing => :Thing)
|
135
|
+
|
136
|
+
before do
|
137
|
+
I18n.default_locale = :en
|
138
|
+
Thing.send validation, :photo, validation_options
|
139
|
+
@thing = Subthing.new
|
140
|
+
instance_eval(&block)
|
141
|
+
end
|
142
|
+
|
143
|
+
def define_translation(path, value)
|
144
|
+
hash = path.split(/\./).reverse.inject(value){|hash, key| {key => hash}}
|
145
|
+
path = "#{temporary_directory}/translations.yml"
|
146
|
+
open(path, 'w'){|f| f.write(hash.to_yaml)}
|
147
|
+
begin
|
148
|
+
I18n.backend.load_translations(path)
|
149
|
+
ensure
|
150
|
+
File.unlink(path)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should use the activerecord.errors.models.CLASS.attributes.ATTRIBUTE.#{key} translation if available" do
|
155
|
+
define_translation "en.activerecord.errors.models.subthing.attributes.photo.#{key}", "ERROR"
|
156
|
+
@thing.valid?.should be_false
|
157
|
+
@thing.errors[:photo].should == 'ERROR'
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should fallback to activerecord.errors.models.CLASS.#{key} translation if available" do
|
161
|
+
define_translation "en.activerecord.errors.models.subthing.#{key}", "ERROR"
|
162
|
+
@thing.valid?.should be_false
|
163
|
+
@thing.errors[:photo].should == 'ERROR'
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should fallback to the activerecord.errors.models.SUPERCLASS.attributes.ATTRIBUTE.#{key} if available" do
|
167
|
+
define_translation "en.activerecord.errors.models.thing.attributes.photo.#{key}", "ERROR"
|
168
|
+
@thing.valid?.should be_false
|
169
|
+
@thing.errors[:photo].should == 'ERROR'
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should fallback to the activerecord.errors.models.SUPERCLASS.#{key} translation if available" do
|
173
|
+
define_translation "en.activerecord.errors.models.thing.#{key}", "ERROR"
|
174
|
+
@thing.valid?.should be_false
|
175
|
+
@thing.errors[:photo].should == 'ERROR'
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should fallback to activerecord.errors.messages.#{key} translation if available" do
|
179
|
+
define_translation "en.activerecord.errors.messages.#{key}", "ERROR"
|
180
|
+
@thing.valid?.should be_false
|
181
|
+
@thing.errors[:photo].should == 'ERROR'
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe ".validates_attachment_presence_of" do
|
188
|
+
def validation
|
189
|
+
:validates_attachment_presence_of
|
190
|
+
end
|
191
|
+
|
192
|
+
def validation_options
|
193
|
+
{}
|
194
|
+
end
|
195
|
+
|
196
|
+
def make_thing_pass
|
197
|
+
@thing.photo = test_image_file
|
198
|
+
end
|
199
|
+
|
200
|
+
def make_thing_fail
|
201
|
+
@thing.photo = test_empty_file
|
202
|
+
end
|
203
|
+
|
204
|
+
it_should_behave_like "an ActiveRecord validation"
|
205
|
+
|
206
|
+
describe "validation" do
|
207
|
+
it "should fail if the value is blank" do
|
208
|
+
Thing.validates_attachment_presence_of :photo
|
209
|
+
@thing = Thing.new(:photo => nil)
|
210
|
+
@thing.should_not be_valid
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should fail if the file is empty" do
|
214
|
+
Thing.validates_attachment_presence_of :photo
|
215
|
+
@thing = Thing.new(:photo => test_empty_file)
|
216
|
+
@thing.should_not be_valid
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should pass if the file is not empty" do
|
220
|
+
Thing.validates_attachment_presence_of :photo
|
221
|
+
@thing = Thing.new(:photo => test_image_file)
|
222
|
+
@thing.should be_valid
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
it_should_use_i18n_key(:attachment_blank){@thing.photo = test_empty_file}
|
227
|
+
end
|
228
|
+
|
229
|
+
describe ".validates_attachment_file_size_of" do
|
230
|
+
def validation
|
231
|
+
:validates_attachment_file_size_of
|
232
|
+
end
|
233
|
+
|
234
|
+
def validation_options
|
235
|
+
{:in => 3..5}
|
236
|
+
end
|
237
|
+
|
238
|
+
def make_thing_pass
|
239
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '....')
|
240
|
+
end
|
241
|
+
|
242
|
+
def make_thing_fail
|
243
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '..')
|
244
|
+
end
|
245
|
+
|
246
|
+
it_should_behave_like "an ActiveRecord validation"
|
247
|
+
|
248
|
+
describe "validation" do
|
249
|
+
it "should not fail if the attachment is blank" do
|
250
|
+
Thing.validates_attachment_file_size_of :photo, :greater_than => 5
|
251
|
+
thing = Thing.new
|
252
|
+
thing.should be_valid
|
253
|
+
end
|
254
|
+
|
255
|
+
describe "when :greater_than is given" do
|
256
|
+
before do
|
257
|
+
Thing.validates_attachment_file_size_of :photo, :greater_than => 5
|
258
|
+
@thing = Thing.new
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should fail if the file size is less than the limit" do
|
262
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*4)
|
263
|
+
@thing.should_not be_valid
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should fail if the file size is equal to the limit" do
|
267
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*5)
|
268
|
+
@thing.should_not be_valid
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should pass if the file size is greater than the limit" do
|
272
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*6)
|
273
|
+
@thing.should be_valid
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe "when :less_than is given" do
|
278
|
+
before do
|
279
|
+
Thing.validates_attachment_file_size_of :photo, :less_than => 5
|
280
|
+
@thing = Thing.new
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should fail if the file size is greater than the limit" do
|
284
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*6)
|
285
|
+
@thing.should_not be_valid
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should fail if the file size is equal to the limit" do
|
289
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*5)
|
290
|
+
@thing.should_not be_valid
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should pass if the file size is less than the limit" do
|
294
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*4)
|
295
|
+
@thing.should be_valid
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
describe "when :in is given" do
|
300
|
+
before do
|
301
|
+
Thing.validates_attachment_file_size_of :photo, :in => 3..5
|
302
|
+
@thing = Thing.new
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should fail if the file size is less than the lower bound" do
|
306
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*2)
|
307
|
+
@thing.should_not be_valid
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should pass if the file size is equal to the lower bound" do
|
311
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*3)
|
312
|
+
@thing.should be_valid
|
313
|
+
end
|
314
|
+
|
315
|
+
it "should fail if the file size is greater than the upper bound" do
|
316
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*6)
|
317
|
+
@thing.should_not be_valid
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "when :in is given with an inclusive range" do
|
322
|
+
before do
|
323
|
+
Thing.validates_attachment_file_size_of :photo, :in => 3..5
|
324
|
+
@thing = Thing.new
|
325
|
+
end
|
326
|
+
|
327
|
+
it "should pass if the file size is equal to the upper bound" do
|
328
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*5)
|
329
|
+
@thing.should be_valid
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
describe "when :in is given with an exclusive range" do
|
334
|
+
before do
|
335
|
+
Thing.validates_attachment_file_size_of :photo, :in => 3...5
|
336
|
+
@thing = Thing.new
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should fail if the file size is equal to the upper bound" do
|
340
|
+
@thing.photo = uploaded_file_with_content('test.jpg', '.'*5)
|
341
|
+
@thing.should_not be_valid
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
it_should_use_i18n_key(:attachment_too_large, :in => 3..5){@thing.photo = uploaded_file_with_content('test.jpg', '......')}
|
347
|
+
it_should_use_i18n_key(:attachment_too_large, :less_than => 5){@thing.photo = uploaded_file_with_content('test.jpg', '......')}
|
348
|
+
it_should_use_i18n_key(:attachment_too_small, :in => 3..5){@thing.photo = uploaded_file_with_content('test.jpg', '..')}
|
349
|
+
it_should_use_i18n_key(:attachment_too_small, :greater_than => 5){@thing.photo = uploaded_file_with_content('test.jpg', '..')}
|
350
|
+
end
|
351
|
+
|
352
|
+
describe ".validates_attachment_type_of" do
|
353
|
+
def validation
|
354
|
+
:validates_attachment_type_of
|
355
|
+
end
|
356
|
+
|
357
|
+
def validation_options
|
358
|
+
{:matches => /\Aimage/}
|
359
|
+
end
|
360
|
+
|
361
|
+
def make_thing_pass
|
362
|
+
@thing.photo = uploaded_file_with_content('test.jpg', "\xff\xd8")
|
363
|
+
end
|
364
|
+
|
365
|
+
def make_thing_fail
|
366
|
+
@thing.photo = uploaded_file_with_content('test.avi', "RIFF AVI ")
|
367
|
+
end
|
368
|
+
|
369
|
+
it_should_behave_like "an ActiveRecord validation"
|
370
|
+
|
371
|
+
describe "validation" do
|
372
|
+
describe "when :matches is given" do
|
373
|
+
before do
|
374
|
+
Thing.validates_attachment_type_of :photo, :matches => /^image/
|
375
|
+
@thing = Thing.new(:photo => uploaded_file)
|
376
|
+
end
|
377
|
+
|
378
|
+
it "should pass if the attachment is nil" do
|
379
|
+
@thing.photo = nil
|
380
|
+
@thing.should be_valid
|
381
|
+
end
|
382
|
+
|
383
|
+
it "should pass if the content type matches the given pattern" do
|
384
|
+
@thing.photo.stubs(:content_type).returns('image/jpeg')
|
385
|
+
@thing.should be_valid
|
386
|
+
end
|
387
|
+
|
388
|
+
it "should fail if the content type does not match the given pattern" do
|
389
|
+
@thing.photo.stubs(:content_type).returns('video/x-msvideo')
|
390
|
+
@thing.should_not be_valid
|
391
|
+
@thing.errors.on(:photo).should_not be_blank
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
describe "when :is is given" do
|
396
|
+
describe "when the value is a symbol" do
|
397
|
+
before do
|
398
|
+
Thing.validates_attachment_type_of :photo, :is => :image
|
399
|
+
@thing = Thing.new(:photo => uploaded_file)
|
400
|
+
end
|
401
|
+
|
402
|
+
it "should pass if the symbol matches the Attachment type" do
|
403
|
+
@thing.photo = uploaded_file_with_content('test.jpg', "\xff\xd8")
|
404
|
+
@thing.should be_valid
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should fail if the symbol does not match the Attachment type" do
|
408
|
+
@thing.photo = uploaded_file_with_content('test.avi', "RIFF AVI ")
|
409
|
+
@thing.should_not be_valid
|
410
|
+
@thing.errors.on(:photo).should_not be_blank
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
describe "when a mime-type string is given" do
|
415
|
+
before do
|
416
|
+
@thing = Thing.new(:photo => uploaded_file)
|
417
|
+
end
|
418
|
+
|
419
|
+
describe "when the string contains optional parameters" do
|
420
|
+
before do
|
421
|
+
Thing.validates_attachment_type_of :photo, :is => 'image/jpeg; a=1, b=2'
|
422
|
+
end
|
423
|
+
|
424
|
+
it "should fail if the type is different" do
|
425
|
+
@thing.photo.stubs(:content_type).returns('other/jpeg; a=1, b=2')
|
426
|
+
@thing.should_not be_valid
|
427
|
+
@thing.errors.on(:photo).should_not be_blank
|
428
|
+
end
|
429
|
+
|
430
|
+
it "should fail if the subtype is different" do
|
431
|
+
@thing.photo.stubs(:content_type).returns('image/png; a=1, b=2')
|
432
|
+
@thing.should_not be_valid
|
433
|
+
@thing.errors.on(:photo).should_not be_blank
|
434
|
+
end
|
435
|
+
|
436
|
+
it "should fail if a parameter is missing from the attachment" do
|
437
|
+
@thing.photo.stubs(:content_type).returns('image/jpeg; a=1')
|
438
|
+
@thing.should_not be_valid
|
439
|
+
@thing.errors.on(:photo).should_not be_blank
|
440
|
+
end
|
441
|
+
|
442
|
+
it "should pass if the type, subtype, and parameters are the same" do
|
443
|
+
@thing.photo.stubs(:content_type).returns('image/jpeg; a=1, b=2')
|
444
|
+
@thing.should be_valid
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should pass if the attachment has additional parameters" do
|
448
|
+
@thing.photo.stubs(:content_type).returns('image/jpeg; a=1, b=2, c=3')
|
449
|
+
@thing.should be_valid
|
450
|
+
end
|
451
|
+
|
452
|
+
it "should pass if the attachment has the same parameters in a different order" do
|
453
|
+
@thing.photo.stubs(:content_type).returns('image/jpeg; b=2, a=1')
|
454
|
+
@thing.should be_valid
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
describe "when the string does not contain optional parameters" do
|
459
|
+
before do
|
460
|
+
Thing.validates_attachment_type_of :photo, :is => 'image/jpeg'
|
461
|
+
end
|
462
|
+
|
463
|
+
it "should fail if the type is different" do
|
464
|
+
@thing.photo.stubs(:content_type).returns('other/jpeg')
|
465
|
+
@thing.should_not be_valid
|
466
|
+
@thing.errors.on(:photo).should_not be_blank
|
467
|
+
end
|
468
|
+
|
469
|
+
it "should fail if the subtype is different" do
|
470
|
+
@thing.photo.stubs(:content_type).returns('image/png')
|
471
|
+
@thing.should_not be_valid
|
472
|
+
@thing.errors.on(:photo).should_not be_blank
|
473
|
+
end
|
474
|
+
|
475
|
+
it "should pass if the type and subtype are the same" do
|
476
|
+
@thing.photo.stubs(:content_type).returns('image/jpeg')
|
477
|
+
@thing.should be_valid
|
478
|
+
end
|
479
|
+
|
480
|
+
it "should pass if the type and subtype are the same, and the attachment has optional parameters" do
|
481
|
+
@thing.photo.stubs(:content_type).returns('image/jpeg; a=1')
|
482
|
+
@thing.should be_valid
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
it_should_use_i18n_key(:attachment_wrong_type, :matches => /\Aimage/){@thing.photo = uploaded_file_with_content('test.avi', "RIFF AVI ")}
|
490
|
+
end
|
491
|
+
end
|