dm-paperclip 2.1.4 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,40 +1,35 @@
1
1
  module Paperclip
2
2
  # Handles thumbnailing images that are uploaded.
3
- class Thumbnail
3
+ class Thumbnail < Processor
4
4
 
5
- attr_accessor :file, :current_geometry, :target_geometry, :format, :whiny_thumbnails, :convert_options
5
+ attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options
6
6
 
7
7
  # Creates a Thumbnail object set to work on the +file+ given. It
8
8
  # will attempt to transform the image into one defined by +target_geometry+
9
9
  # which is a "WxH"-style string. +format+ will be inferred from the +file+
10
10
  # unless specified. Thumbnail creation will raise no errors unless
11
- # +whiny_thumbnails+ is true (which it is, by default. If +convert_options+ is
12
- # set, the options will be appended to the convert command upon image conversion
13
- def initialize file, target_geometry, format = nil, convert_options = nil, whiny_thumbnails = true
11
+ # +whiny+ is true (which it is, by default. If +convert_options+ is
12
+ # set, the options will be appended to the convert command upon image conversion
13
+ def initialize file, options = {}, attachment = nil
14
+ super
15
+ geometry = options[:geometry]
14
16
  @file = file
15
- @crop = target_geometry[-1,1] == '#'
16
- @target_geometry = Geometry.parse target_geometry
17
- @current_geometry = Geometry.from_file file
18
- @convert_options = convert_options
19
- @whiny_thumbnails = whiny_thumbnails
17
+ @crop = geometry[-1,1] == '#'
18
+ @target_geometry = Geometry.parse geometry
19
+ @current_geometry = Geometry.from_file @file
20
+ @convert_options = options[:convert_options]
21
+ @whiny = options[:whiny].nil? ? true : options[:whiny]
22
+ @format = options[:format]
20
23
 
21
24
  @current_format = File.extname(@file.path)
22
25
  @basename = File.basename(@file.path, @current_format)
23
-
24
- @format = format
25
- end
26
-
27
- # Creates a thumbnail, as specified in +initialize+, +make+s it, and returns the
28
- # resulting Tempfile.
29
- def self.make file, dimensions, format = nil, convert_options = nil, whiny_thumbnails = true
30
- new(file, dimensions, format, convert_options, whiny_thumbnails).make
31
26
  end
32
27
 
33
28
  # Returns true if the +target_geometry+ is meant to crop.
34
29
  def crop?
35
30
  @crop
36
31
  end
37
-
32
+
38
33
  # Returns true if the image is meant to make use of additional convert options.
39
34
  def convert_options?
40
35
  not @convert_options.blank?
@@ -48,15 +43,15 @@ module Paperclip
48
43
  dst.binmode
49
44
 
50
45
  command = <<-end_command
51
- #{ Paperclip.path_for_command('convert') }
52
46
  "#{ File.expand_path(src.path) }[0]"
53
47
  #{ transformation_command }
54
48
  "#{ File.expand_path(dst.path) }"
55
49
  end_command
56
- success = system(command.gsub(/\s+/, " "))
57
50
 
58
- if !success && $?.exitstatus != 0 && @whiny_thumbnails
59
- raise PaperclipError, "There was an error processing this thumbnail"
51
+ begin
52
+ success = Paperclip.run("convert", command.gsub(/\s+/, " "))
53
+ rescue PaperclipCommandLineError
54
+ raise PaperclipError, "There was an error processing the thumbnail for #{@basename}" if @whiny
60
55
  end
61
56
 
62
57
  dst
@@ -72,17 +67,4 @@ module Paperclip
72
67
  trans
73
68
  end
74
69
  end
75
-
76
- # Due to how ImageMagick handles its image format conversion and how Tempfile
77
- # handles its naming scheme, it is necessary to override how Tempfile makes
78
- # its names so as to allow for file extensions. Idea taken from the comments
79
- # on this blog post:
80
- # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
81
- class Tempfile < ::Tempfile
82
- # Replaces Tempfile's +make_tmpname+ with one that honors file extensions.
83
- def make_tmpname(basename, n)
84
- extension = File.extname(basename)
85
- sprintf("%s,%d,%d%s", File.basename(basename, extension), $$, n, extension)
86
- end
87
- end
88
70
  end
@@ -28,9 +28,20 @@ module Paperclip
28
28
  File.size(self)
29
29
  end
30
30
  end
31
+ end
31
32
 
33
+ if defined? StringIO
34
+ class StringIO
35
+ attr_accessor :original_filename, :content_type
36
+ def original_filename
37
+ @original_filename ||= "stringio.txt"
38
+ end
39
+ def content_type
40
+ @content_type ||= "text/plain"
41
+ end
42
+ end
32
43
  end
33
44
 
34
45
  class File #:nodoc:
35
46
  include Paperclip::Upfile
36
- end
47
+ end
@@ -1,6 +1,41 @@
1
1
  module Paperclip
2
2
  module Validate
3
3
 
4
+ module ClassMethods
5
+
6
+ # Places ActiveRecord-style validations on the size of the file assigned. The
7
+ # possible options are:
8
+ # * +in+: a Range of bytes (i.e. +1..1.megabyte+),
9
+ # * +less_than+: equivalent to :in => 0..options[:less_than]
10
+ # * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
11
+ # * +message+: error message to display, use :min and :max as replacements
12
+ def validates_attachment_size(*fields)
13
+ opts = opts_from_validator_args(fields)
14
+ add_validator_to_context(opts, fields, Paperclip::Validate::SizeValidator)
15
+ end
16
+
17
+ # Adds errors if thumbnail creation fails. The same as specifying :whiny_thumbnails => true.
18
+ def validates_attachment_thumbnails name, options = {}
19
+ self.attachment_definitions[name][:whiny_thumbnails] = true
20
+ end
21
+
22
+ # Places ActiveRecord-style validations on the presence of a file.
23
+ def validates_attachment_presence(*fields)
24
+ opts = opts_from_validator_args(fields)
25
+ add_validator_to_context(opts, fields, Paperclip::Validate::RequiredFieldValidator)
26
+ end
27
+
28
+ # Places ActiveRecord-style validations on the content type of the file assigned. The
29
+ # possible options are:
30
+ # * +content_type+: Allowed content types. Can be a single content type or an array. Allows all by default.
31
+ # * +message+: The message to display when the uploaded file has an invalid content type.
32
+ def validates_attachment_content_type(*fields)
33
+ opts = opts_from_validator_args(fields)
34
+ add_validator_to_context(opts, fields, Paperclip::Validate::ContentTypeValidator)
35
+ end
36
+
37
+ end
38
+
4
39
  class SizeValidator < DataMapper::Validate::GenericValidator #:nodoc:
5
40
  def initialize(field_name, options={})
6
41
  super
@@ -138,11 +138,9 @@ class AttachmentTest < Test::Unit::TestCase
138
138
  end
139
139
  end
140
140
 
141
- [:thumb, :large].each do |style|
142
- should "call extra_options_for(#{style})" do
143
- @dummy.avatar.expects(:extra_options_for).with(style)
144
- @dummy.avatar = @file
145
- end
141
+ should "call post_process" do
142
+ @dummy.avatar.expects(:post_process).once
143
+ @dummy.avatar = @file
146
144
  end
147
145
  end
148
146
  end
@@ -150,20 +148,20 @@ class AttachmentTest < Test::Unit::TestCase
150
148
  context "Assigning an attachment" do
151
149
  setup do
152
150
  rebuild_model
153
-
151
+
154
152
  @not_file = mock
155
153
  @not_file.stubs(:nil?).returns(false)
156
154
  @not_file.expects(:to_tempfile).returns(self)
157
155
  @not_file.expects(:original_filename).returns("filename.png\r\n")
158
156
  @not_file.expects(:content_type).returns("image/png\r\n")
159
157
  @not_file.expects(:size).returns(10)
160
-
158
+
161
159
  @dummy = Dummy.new
162
160
  @attachment = @dummy.avatar
163
161
  @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
164
162
  @attachment.expects(:queue_existing_for_delete)
165
163
  @attachment.expects(:post_process)
166
- @attachment.expects(:validate)
164
+ @attachment.expects(:validate).twice
167
165
  @dummy.avatar = @not_file
168
166
  end
169
167
 
@@ -174,33 +172,31 @@ class AttachmentTest < Test::Unit::TestCase
174
172
  should "strip whitespace from content_type field" do
175
173
  assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
176
174
  end
177
-
178
175
  end
179
176
 
180
177
  context "Attachment with strange letters" do
181
178
  setup do
182
179
  rebuild_model
183
-
180
+
184
181
  @not_file = mock
185
182
  @not_file.stubs(:nil?).returns(false)
186
183
  @not_file.expects(:to_tempfile).returns(self)
187
- @not_file.expects(:original_filename).returns("sheep_say_b�.png\r\n")
184
+ @not_file.expects(:original_filename).returns("sheep_say_b_.png\r\n")
188
185
  @not_file.expects(:content_type).returns("image/png\r\n")
189
186
  @not_file.expects(:size).returns(10)
190
-
187
+
191
188
  @dummy = Dummy.new
192
189
  @attachment = @dummy.avatar
193
190
  @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
194
191
  @attachment.expects(:queue_existing_for_delete)
195
192
  @attachment.expects(:post_process)
196
- @attachment.expects(:validate)
193
+ @attachment.expects(:validate).twice
197
194
  @dummy.avatar = @not_file
198
195
  end
199
-
196
+
200
197
  should "remove strange letters and replace with underscore (_)" do
201
198
  assert_equal "sheep_say_b_.png", @dummy.avatar.original_filename
202
199
  end
203
-
204
200
  end
205
201
 
206
202
  context "An attachment" do
@@ -230,25 +226,24 @@ class AttachmentTest < Test::Unit::TestCase
230
226
  assert_equal "/avatars/original/missing.png", @attachment.url
231
227
  assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
232
228
  end
233
-
229
+
234
230
  context "with a file assigned in the database" do
235
231
  setup do
236
- @instance.stubs(:attribute_get).with(:avatar_file_name).returns('5k.png')
237
- @instance.stubs(:attribute_get).with(:avatar_content_type).returns("image/png")
238
- @instance.stubs(:attribute_get).with(:avatar_file_size).returns(12345)
239
- now = Time.now
240
- Time.stubs(:now).returns(now)
241
- @instance.stubs(:attribute_get).with(:avatar_updated_at).returns(Time.now)
232
+ @instance.stubs(:avatar_file_name).returns('5k.png')
233
+ @instance.stubs(:avatar_content_type).returns("image/png")
234
+ @instance.stubs(:avatar_file_size).returns(12345)
235
+ @now = Time.now
236
+ Time.stubs(:now).returns(@now)
237
+ @instance.stubs(:avatar_updated_at).returns(Time.now)
242
238
  end
243
239
 
244
240
  should "return a correct url even if the file does not exist" do
245
241
  assert_nil @attachment.to_file
246
- assert_match %r{^/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
242
+ assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
247
243
  end
248
244
 
249
245
  should "make sure the updated_at mtime is in the url if it is defined" do
250
- time = Time.now
251
- assert_match %r{#{time.year}#{time.month}#{time.day}#{time.hour}#{time.min}#{time.sec}$}, @attachment.url(:blah)
246
+ assert_match %r{#{@now.to_i.to_s}$}, @attachment.url(:blah)
252
247
  end
253
248
 
254
249
  context "with the updated_at field removed" do
@@ -266,7 +261,7 @@ class AttachmentTest < Test::Unit::TestCase
266
261
  end
267
262
 
268
263
  should "return the proper path when filename has multiple .'s" do
269
- @instance.stubs(:attribute_get).with(:avatar_file_name).returns("5k.old.png")
264
+ @instance.stubs(:avatar_file_name).returns("5k.old.png")
270
265
  assert_equal "./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png", @attachment.path
271
266
  end
272
267
 
@@ -298,8 +293,8 @@ class AttachmentTest < Test::Unit::TestCase
298
293
 
299
294
  should "return the real url" do
300
295
  assert @attachment.to_file
301
- assert_match %r{^/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
302
- assert_match %r{^/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
296
+ assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
297
+ assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
303
298
  end
304
299
 
305
300
  should "commit the files to disk" do
@@ -333,15 +328,12 @@ class AttachmentTest < Test::Unit::TestCase
333
328
  @existing_names = @attachment.styles.keys.collect do |style|
334
329
  @attachment.path(style)
335
330
  end
336
- @instance.expects(:attributes=).with({ :avatar_file_name => nil,
337
- :avatar_content_type => nil,
338
- :avatar_file_size => nil })
339
331
  @attachment.assign nil
340
332
  @attachment.save
341
333
  end
342
334
 
343
335
  should "delete the files" do
344
- @existing_names.each{|f| assert ! File.exists?(f) }
336
+ @existing_names.each { |f| assert !File.exists?(f) }
345
337
  end
346
338
  end
347
339
  end
@@ -62,7 +62,7 @@ class GeometryTest < Test::Unit::TestCase
62
62
  should "make sure the modifier gets passed during transformation_to" do
63
63
  assert @src = Paperclip::Geometry.parse("123x456")
64
64
  assert @dst = Paperclip::Geometry.parse("123x456>")
65
- assert_equal "123x456>", @src.transformation_to(@dst).to_s
65
+ assert_equal "123x456>", @src.transformation_to(@dst).join
66
66
  end
67
67
 
68
68
  should "be generated from a file" do
@@ -42,6 +42,12 @@ unless defined?(Mash)
42
42
  end
43
43
  end
44
44
 
45
+ Paperclip.configure do |config|
46
+ config.root = Merb.root # the application root to anchor relative urls (defaults to Dir.pwd)
47
+ config.env = Merb.env # server env support, defaults to ENV['RACK_ENV'] or 'development'
48
+ config.use_dm_validations = true # validate attachment sizes and such, defaults to false
49
+ end
50
+
45
51
  def rebuild_model options = {}
46
52
  Object.send(:remove_const, "Dummy") rescue nil
47
53
  Object.const_set("Dummy", Class.new())
@@ -24,21 +24,21 @@ class StorageTest < Test::Unit::TestCase
24
24
 
25
25
  should "get the correct credentials when RAILS_ENV is production" do
26
26
  ENV['RAILS_ENV'] = 'production'
27
- assert_equal({:key => "12345"},
27
+ assert_equal({'key' => "12345"},
28
28
  @avatar.parse_credentials('production' => {:key => '12345'},
29
29
  :development => {:key => "54321"}))
30
30
  end
31
31
 
32
32
  should "get the correct credentials when RAILS_ENV is development" do
33
33
  ENV['RAILS_ENV'] = 'development'
34
- assert_equal({:key => "54321"},
34
+ assert_equal({'key' => "54321"},
35
35
  @avatar.parse_credentials('production' => {:key => '12345'},
36
36
  :development => {:key => "54321"}))
37
37
  end
38
38
 
39
39
  should "return the argument if the key does not exist" do
40
40
  ENV['RAILS_ENV'] = "not really an env"
41
- assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345"))
41
+ assert_equal({'test' => "12345"}, @avatar.parse_credentials(:test => "12345"))
42
42
  end
43
43
  end
44
44
 
@@ -48,11 +48,11 @@ class ThumbnailTest < Test::Unit::TestCase
48
48
  ].each do |args|
49
49
  context "being thumbnailed with a geometry of #{args[0]}" do
50
50
  setup do
51
- @thumb = Paperclip::Thumbnail.new(@file, args[0])
51
+ @thumb = Paperclip::Thumbnail.new(@file, :geometry => args[0])
52
52
  end
53
53
 
54
54
  should "start with dimensions of 434x66" do
55
- cmd = %Q[identify -format "%wx%h" #{@file.path}]
55
+ cmd = %Q[identify -format "%wx%h" #{@file.path}]
56
56
  assert_equal "434x66", `#{cmd}`.chomp
57
57
  end
58
58
 
@@ -66,7 +66,7 @@ class ThumbnailTest < Test::Unit::TestCase
66
66
  end
67
67
 
68
68
  should "be the size we expect it to be" do
69
- cmd = %Q[identify -format "%wx%h" #{@thumb_result.path}]
69
+ cmd = %Q[identify -format "%wx%h" #{@thumb_result.path}]
70
70
  assert_equal args[1], `#{cmd}`.chomp
71
71
  end
72
72
  end
@@ -75,7 +75,7 @@ class ThumbnailTest < Test::Unit::TestCase
75
75
 
76
76
  context "being thumbnailed at 100x50 with cropping" do
77
77
  setup do
78
- @thumb = Paperclip::Thumbnail.new(@file, "100x50#")
78
+ @thumb = Paperclip::Thumbnail.new(@file, :geometry => "100x50#")
79
79
  end
80
80
 
81
81
  should "report its correct current and target geometries" do
@@ -88,16 +88,17 @@ class ThumbnailTest < Test::Unit::TestCase
88
88
  end
89
89
 
90
90
  should "have whiny_thumbnails turned on by default" do
91
- assert @thumb.whiny_thumbnails
91
+ assert @thumb.whiny
92
92
  end
93
-
93
+
94
94
  should "have convert_options set to nil by default" do
95
95
  assert_equal nil, @thumb.convert_options
96
96
  end
97
97
 
98
98
  should "send the right command to convert when sent #make" do
99
- @thumb.expects(:system).with do |arg|
100
- arg.match %r{convert\s+"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+\"x50\"\s+-crop\s+\"100x50\+114\+0\"\s+\+repage\s+".*?"}
99
+ Paperclip.expects(:run).with do |cmd, arg|
100
+ cmd.match 'convert'
101
+ arg.match %r{"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+\"x50\"\s+-crop\s+\"100x50\+114\+0\"\s+\+repage\s+".*?"}
101
102
  end
102
103
  @thumb.make
103
104
  end
@@ -107,10 +108,10 @@ class ThumbnailTest < Test::Unit::TestCase
107
108
  assert_match /100x50/, `identify #{dst.path}`
108
109
  end
109
110
  end
110
-
111
+
111
112
  context "being thumbnailed with convert options set" do
112
113
  setup do
113
- @thumb = Paperclip::Thumbnail.new(@file, "100x50#", format=nil, convert_options="-strip -depth 8", whiny_thumbnails=true)
114
+ @thumb = Paperclip::Thumbnail.new(@file, :geometry => "100x50#", :format => (format = nil), :convert_options => (convert_options="-strip -depth 8"), :whiny => (whiny_thumbnails=true))
114
115
  end
115
116
 
116
117
  should "have convert_options value set" do
@@ -118,8 +119,9 @@ class ThumbnailTest < Test::Unit::TestCase
118
119
  end
119
120
 
120
121
  should "send the right command to convert when sent #make" do
121
- @thumb.expects(:system).with do |arg|
122
- arg.match %r{convert\s+"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+"x50"\s+-crop\s+"100x50\+114\+0"\s+\+repage\s+-strip\s+-depth\s+8\s+".*?"}
122
+ Paperclip.expects(:run).with do |cmd, arg|
123
+ cmd.match 'convert'
124
+ arg.match %r{"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+"x50"\s+-crop\s+"100x50\+114\+0"\s+\+repage\s+-strip\s+-depth\s+8\s+".*?"}
123
125
  end
124
126
  @thumb.make
125
127
  end
@@ -128,10 +130,10 @@ class ThumbnailTest < Test::Unit::TestCase
128
130
  dst = @thumb.make
129
131
  assert_match /100x50/, `identify #{dst.path}`
130
132
  end
131
-
133
+
132
134
  context "redefined to have bad convert_options setting" do
133
135
  setup do
134
- @thumb = Paperclip::Thumbnail.new(@file, "100x50#", format=nil, convert_options="-this-aint-no-option", whiny_thumbnails=true)
136
+ @thumb = Paperclip::Thumbnail.new(@file, :geometry => "100x50#", :format => nil, :convert_options => "-this-aint-no-option", :whiny => true)
135
137
  end
136
138
 
137
139
  should "error when trying to create the thumbnail" do
@@ -139,7 +141,7 @@ class ThumbnailTest < Test::Unit::TestCase
139
141
  @thumb.make
140
142
  end
141
143
  end
142
- end
144
+ end
143
145
  end
144
146
  end
145
147
  end