bteitelb-paperclip 2.3.1.11 → 2.3.1.12
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -2
- data/lib/paperclip.rb +18 -14
- data/lib/paperclip/attachment.rb +46 -134
- data/lib/paperclip/interpolations.rb +17 -17
- data/lib/paperclip/iostream.rb +2 -1
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +2 -3
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +6 -6
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +5 -3
- data/lib/paperclip/storage.rb +13 -9
- data/lib/paperclip/thumbnail.rb +2 -0
- data/test/attachment_test.rb +22 -38
- data/test/helper.rb +1 -1
- data/test/iostream_test.rb +9 -2
- data/test/matchers/validate_attachment_content_type_matcher_test.rb +1 -0
- data/test/matchers/validate_attachment_presence_matcher_test.rb +3 -1
- data/test/matchers/validate_attachment_size_matcher_test.rb +1 -0
- data/test/paperclip_test.rb +12 -41
- data/test/storage_test.rb +27 -0
- data/test/thumbnail_test.rb +1 -1
- metadata +1 -1
data/Rakefile
CHANGED
@@ -75,8 +75,8 @@ spec = Gem::Specification.new do |s|
|
|
75
75
|
s.extra_rdoc_files = FileList["README*"].to_a
|
76
76
|
s.rdoc_options << '--line-numbers' << '--inline-source'
|
77
77
|
s.requirements << "ImageMagick"
|
78
|
-
s.add_development_dependency '
|
79
|
-
s.add_development_dependency 'jferris-mocha', '
|
78
|
+
s.add_development_dependency 'shoulda'
|
79
|
+
s.add_development_dependency 'jferris-mocha', '>= 0.9.5.0.1241126838'
|
80
80
|
s.add_development_dependency 'aws-s3'
|
81
81
|
s.add_development_dependency 'sqlite3-ruby'
|
82
82
|
s.add_development_dependency 'activerecord'
|
data/lib/paperclip.rb
CHANGED
@@ -34,6 +34,7 @@ require 'paperclip/processor'
|
|
34
34
|
require 'paperclip/thumbnail'
|
35
35
|
require 'paperclip/storage'
|
36
36
|
require 'paperclip/interpolations'
|
37
|
+
require 'paperclip/style'
|
37
38
|
require 'paperclip/attachment'
|
38
39
|
if defined? RAILS_ROOT
|
39
40
|
Dir.glob(File.join(File.expand_path(RAILS_ROOT), "lib", "paperclip_processors", "*.rb")).each do |processor|
|
@@ -239,7 +240,7 @@ module Paperclip
|
|
239
240
|
|
240
241
|
validates_each(name) do |record, attr, value|
|
241
242
|
attachment = record.attachment_for(name)
|
242
|
-
attachment.send(:flush_errors)
|
243
|
+
attachment.send(:flush_errors)
|
243
244
|
end
|
244
245
|
end
|
245
246
|
|
@@ -257,13 +258,13 @@ module Paperclip
|
|
257
258
|
max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
|
258
259
|
range = (min..max)
|
259
260
|
message = options[:message] || "file size must be between :min and :max bytes."
|
261
|
+
message = message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
|
260
262
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
:unless => options[:unless]}]
|
263
|
+
validates_inclusion_of :"#{name}_file_size",
|
264
|
+
:in => range,
|
265
|
+
:message => message,
|
266
|
+
:if => options[:if],
|
267
|
+
:unless => options[:unless]
|
267
268
|
end
|
268
269
|
|
269
270
|
# Adds errors if thumbnail creation fails. The same as specifying :whiny_thumbnails => true.
|
@@ -281,9 +282,10 @@ module Paperclip
|
|
281
282
|
# * +unless+: Same as +if+ but validates if lambda or method returns false.
|
282
283
|
def validates_attachment_presence name, options = {}
|
283
284
|
message = options[:message] || "must be set."
|
284
|
-
|
285
|
-
|
286
|
-
|
285
|
+
validates_presence_of :"#{name}_file_name",
|
286
|
+
:message => message,
|
287
|
+
:if => options[:if],
|
288
|
+
:unless => options[:unless]
|
287
289
|
end
|
288
290
|
|
289
291
|
# Places ActiveRecord-style validations on the content type of the file
|
@@ -303,10 +305,12 @@ module Paperclip
|
|
303
305
|
# model, content_type validation will work _ONLY upon assignment_ and
|
304
306
|
# re-validation after the instance has been reloaded will always succeed.
|
305
307
|
def validates_attachment_content_type name, options = {}
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
308
|
+
types = [options.delete(:content_type)].flatten
|
309
|
+
validates_each(:"#{name}_content_type", options) do |record, attr, value|
|
310
|
+
unless types.any?{|t| t === value }
|
311
|
+
record.errors.add(:"#{name}_content_type", :inclusion, :default => options[:message], :value => value)
|
312
|
+
end
|
313
|
+
end
|
310
314
|
end
|
311
315
|
|
312
316
|
# Returns the attachment definitions defined by each call to
|
data/lib/paperclip/attachment.rb
CHANGED
@@ -9,18 +9,19 @@ module Paperclip
|
|
9
9
|
|
10
10
|
def self.default_options
|
11
11
|
@default_options ||= {
|
12
|
-
:url
|
13
|
-
:path
|
14
|
-
:styles
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
12
|
+
:url => "/system/:attachment/:id/:style/:filename",
|
13
|
+
:path => ":rails_root/public:url",
|
14
|
+
:styles => {},
|
15
|
+
:processors => [:thumbnail],
|
16
|
+
:convert_options => {},
|
17
|
+
:default_url => "/:attachment/:style/missing.png",
|
18
|
+
:default_style => :original,
|
19
|
+
:storage => :filesystem,
|
20
|
+
:whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails]
|
20
21
|
}
|
21
22
|
end
|
22
23
|
|
23
|
-
attr_reader :name, :instance, :
|
24
|
+
attr_reader :name, :instance, :default_style, :convert_options, :queued_for_write, :whiny, :options
|
24
25
|
|
25
26
|
# Creates an Attachment object. +name+ is the name of the attachment,
|
26
27
|
# +instance+ is the ActiveRecord object instance it's attached to, and
|
@@ -36,14 +37,13 @@ module Paperclip
|
|
36
37
|
@path = options[:path]
|
37
38
|
@path = @path.call(self) if @path.is_a?(Proc)
|
38
39
|
@styles = options[:styles]
|
39
|
-
@
|
40
|
+
@normalized_styles = nil
|
40
41
|
@default_url = options[:default_url]
|
41
|
-
@validations = options[:validations]
|
42
42
|
@default_style = options[:default_style]
|
43
43
|
@storage = options[:storage]
|
44
44
|
@whiny = options[:whiny_thumbnails] || options[:whiny]
|
45
|
-
@convert_options = options[:convert_options]
|
46
|
-
@processors = options[:processors]
|
45
|
+
@convert_options = options[:convert_options]
|
46
|
+
@processors = options[:processors]
|
47
47
|
@options = options
|
48
48
|
@queued_for_delete = []
|
49
49
|
@queued_for_write = {}
|
@@ -52,18 +52,29 @@ module Paperclip
|
|
52
52
|
@validation_errors = nil
|
53
53
|
@dirty = false
|
54
54
|
|
55
|
-
normalize_style_definition
|
56
55
|
initialize_storage
|
57
56
|
end
|
57
|
+
|
58
|
+
def styles
|
59
|
+
unless @normalized_styles
|
60
|
+
@normalized_styles = {}
|
61
|
+
(@styles.respond_to?(:call) ? @styles.call(self) : @styles).each do |name, args|
|
62
|
+
@normalized_styles[name] = Paperclip::Style.new(name, args, self)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
@normalized_styles
|
66
|
+
end
|
67
|
+
|
68
|
+
def processors
|
69
|
+
@processors.respond_to?(:call) ? @processors.call(instance) : @processors
|
70
|
+
end
|
58
71
|
|
59
72
|
# What gets called when you call instance.attachment = File. It clears
|
60
|
-
# errors, assigns attributes, processes the file
|
73
|
+
# errors, assigns attributes, and processes the file. It
|
61
74
|
# also queues up the previous file for deletion, to be flushed away on
|
62
75
|
# #save of its host. In addition to form uploads, you can also assign
|
63
76
|
# another Paperclip attachment:
|
64
77
|
# new_user.avatar = old_user.avatar
|
65
|
-
# If the file that is assigned is not valid, the processing (i.e.
|
66
|
-
# thumbnailing, etc) will NOT be run.
|
67
78
|
def assign uploaded_file
|
68
79
|
ensure_required_accessors!
|
69
80
|
|
@@ -93,7 +104,7 @@ module Paperclip
|
|
93
104
|
|
94
105
|
@dirty = true
|
95
106
|
|
96
|
-
post_process
|
107
|
+
post_process
|
97
108
|
|
98
109
|
# Reset the file size if the original file was reprocessed.
|
99
110
|
instance_write(:file_size, @queued_for_write[:original].size.to_i)
|
@@ -115,7 +126,6 @@ module Paperclip
|
|
115
126
|
end
|
116
127
|
ensure
|
117
128
|
uploaded_file.close if close_uploaded_file
|
118
|
-
validate
|
119
129
|
end
|
120
130
|
|
121
131
|
# Returns the public URL of the attachment, with a given style. Note that
|
@@ -125,8 +135,8 @@ module Paperclip
|
|
125
135
|
# security, however, for performance reasons. set
|
126
136
|
# include_updated_timestamp to false if you want to stop the attachment
|
127
137
|
# update time appended to the url
|
128
|
-
def url
|
129
|
-
url = original_filename.nil? ? interpolate(@default_url,
|
138
|
+
def url style_name = default_style, include_updated_timestamp = true
|
139
|
+
url = original_filename.nil? ? interpolate(@default_url, style_name) : interpolate(@url, style_name)
|
130
140
|
include_updated_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
|
131
141
|
end
|
132
142
|
|
@@ -134,19 +144,13 @@ module Paperclip
|
|
134
144
|
# file is stored in the filesystem the path refers to the path of the file
|
135
145
|
# on disk. If the file is stored in S3, the path is the "key" part of the
|
136
146
|
# URL, and the :bucket option refers to the S3 bucket.
|
137
|
-
def path
|
138
|
-
original_filename.nil? ? nil : interpolate(@path,
|
147
|
+
def path style_name = default_style
|
148
|
+
original_filename.nil? ? nil : interpolate(@path, style_name)
|
139
149
|
end
|
140
150
|
|
141
151
|
# Alias to +url+
|
142
|
-
def to_s
|
143
|
-
url(
|
144
|
-
end
|
145
|
-
|
146
|
-
# Returns true if there are no errors on this attachment.
|
147
|
-
def valid?
|
148
|
-
validate
|
149
|
-
errors.empty?
|
152
|
+
def to_s style_name = nil
|
153
|
+
url(style_name)
|
150
154
|
end
|
151
155
|
|
152
156
|
# Returns an array containing the errors on this attachment.
|
@@ -162,15 +166,10 @@ module Paperclip
|
|
162
166
|
# Saves the file, if there are no errors. If there are, it flushes them to
|
163
167
|
# the instance's errors and returns false, cancelling the save.
|
164
168
|
def save
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
true
|
170
|
-
else
|
171
|
-
flush_errors
|
172
|
-
false
|
173
|
-
end
|
169
|
+
flush_deletes
|
170
|
+
flush_writes
|
171
|
+
@dirty = false
|
172
|
+
true
|
174
173
|
end
|
175
174
|
|
176
175
|
# Clears out the attachment. Has the same effect as previously assigning
|
@@ -179,7 +178,6 @@ module Paperclip
|
|
179
178
|
def clear
|
180
179
|
queue_existing_for_delete
|
181
180
|
@errors = {}
|
182
|
-
@validation_errors = nil
|
183
181
|
end
|
184
182
|
|
185
183
|
# Destroys the attachment. Has the same effect as previously assigning
|
@@ -271,15 +269,6 @@ module Paperclip
|
|
271
269
|
# Determines whether or not the attachment is an image based on the content_type
|
272
270
|
def image?
|
273
271
|
!content_type.nil? and !!content_type.match(%r{\Aimage/})
|
274
|
-
|
275
|
-
# Fix content type when it's application/octet-stream
|
276
|
-
if content_type.to_s.strip == 'application/octet-stream'
|
277
|
-
mime_type = MIME::Types.type_for(uploaded_file.original_filename.strip).to_s
|
278
|
-
end
|
279
|
-
instance_write(:content_type, uploaded_file.content_type.to_s.strip)
|
280
|
-
instance_write(:file_size, uploaded_file.size.to_i)
|
281
|
-
instance_write(:updated_at, Time.now)
|
282
|
-
|
283
272
|
end
|
284
273
|
|
285
274
|
# Writes the attachment-specific attribute on the instance. For example,
|
@@ -320,82 +309,6 @@ module Paperclip
|
|
320
309
|
file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
|
321
310
|
end
|
322
311
|
|
323
|
-
def validate #:nodoc:
|
324
|
-
unless @validation_errors
|
325
|
-
@validation_errors = @validations.inject({}) do |errors, validation|
|
326
|
-
name, options = validation
|
327
|
-
errors[name] = send(:"validate_#{name}", options) if allow_validation?(options)
|
328
|
-
errors
|
329
|
-
end
|
330
|
-
@validation_errors.reject!{|k,v| v == nil }
|
331
|
-
@errors.merge!(@validation_errors)
|
332
|
-
end
|
333
|
-
@validation_errors
|
334
|
-
end
|
335
|
-
|
336
|
-
def allow_validation? options #:nodoc:
|
337
|
-
(options[:if].nil? || check_guard(options[:if])) && (options[:unless].nil? || !check_guard(options[:unless]))
|
338
|
-
end
|
339
|
-
|
340
|
-
def check_guard guard #:nodoc:
|
341
|
-
if guard.respond_to? :call
|
342
|
-
guard.call(instance)
|
343
|
-
elsif ! guard.blank?
|
344
|
-
instance.send(guard.to_s)
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
def validate_size options #:nodoc:
|
349
|
-
if file? && !options[:range].include?(size.to_i)
|
350
|
-
options[:message].gsub(/:min/, options[:min].to_s).gsub(/:max/, options[:max].to_s)
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
|
-
def validate_presence options #:nodoc:
|
355
|
-
options[:message] unless file?
|
356
|
-
end
|
357
|
-
|
358
|
-
def validate_content_type options #:nodoc:
|
359
|
-
valid_types = [options[:content_type]].flatten
|
360
|
-
unless original_filename.blank?
|
361
|
-
unless valid_types.blank?
|
362
|
-
content_type = instance_read(:content_type)
|
363
|
-
unless valid_types.any?{|t| content_type.nil? || t === content_type }
|
364
|
-
options[:message] || "is not one of the allowed file types."
|
365
|
-
end
|
366
|
-
end
|
367
|
-
end
|
368
|
-
end
|
369
|
-
|
370
|
-
def normalize_style_definition #:nodoc:
|
371
|
-
@styles.each do |name, args|
|
372
|
-
unless args.is_a? Hash
|
373
|
-
dimensions, format = [args, nil].flatten[0..1]
|
374
|
-
format = nil if format.blank?
|
375
|
-
@styles[name] = {
|
376
|
-
:processors => @processors,
|
377
|
-
:geometry => dimensions,
|
378
|
-
:format => format,
|
379
|
-
:whiny => @whiny,
|
380
|
-
:convert_options => extra_options_for(name)
|
381
|
-
}
|
382
|
-
else
|
383
|
-
@styles[name] = {
|
384
|
-
:processors => @processors,
|
385
|
-
:whiny => @whiny,
|
386
|
-
:convert_options => extra_options_for(name)
|
387
|
-
}.merge(@styles[name])
|
388
|
-
end
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
def solidify_style_definitions #:nodoc:
|
393
|
-
@styles.each do |name, args|
|
394
|
-
@styles[name][:geometry] = @styles[name][:geometry].call(instance) if @styles[name][:geometry].respond_to?(:call)
|
395
|
-
@styles[name][:processors] = @styles[name][:processors].call(instance) if @styles[name][:processors].respond_to?(:call)
|
396
|
-
end
|
397
|
-
end
|
398
|
-
|
399
312
|
def initialize_storage #:nodoc:
|
400
313
|
@storage_module = Paperclip::Storage.const_get(@storage.to_s.capitalize)
|
401
314
|
self.extend(@storage_module)
|
@@ -412,7 +325,6 @@ module Paperclip
|
|
412
325
|
|
413
326
|
def post_process #:nodoc:
|
414
327
|
return if @queued_for_write[:original].nil?
|
415
|
-
solidify_style_definitions
|
416
328
|
return if fire_events(:before)
|
417
329
|
post_process_styles
|
418
330
|
return if fire_events(:after)
|
@@ -428,11 +340,11 @@ module Paperclip
|
|
428
340
|
end
|
429
341
|
|
430
342
|
def post_process_styles #:nodoc:
|
431
|
-
|
343
|
+
styles.each do |name, style|
|
432
344
|
begin
|
433
|
-
raise RuntimeError.new("Style #{name} has no processors defined.") if
|
434
|
-
@queued_for_write[name] =
|
435
|
-
Paperclip.processor(processor).make(file,
|
345
|
+
raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
|
346
|
+
@queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
|
347
|
+
Paperclip.processor(processor).make(file, style.processor_options, self)
|
436
348
|
end
|
437
349
|
rescue PaperclipError => e
|
438
350
|
log("An error was received while processing: #{e.inspect}")
|
@@ -441,8 +353,8 @@ module Paperclip
|
|
441
353
|
end
|
442
354
|
end
|
443
355
|
|
444
|
-
def interpolate pattern,
|
445
|
-
Paperclip::Interpolations.interpolate(pattern, self,
|
356
|
+
def interpolate pattern, style_name = default_style #:nodoc:
|
357
|
+
Paperclip::Interpolations.interpolate(pattern, self, style_name)
|
446
358
|
end
|
447
359
|
|
448
360
|
def mime_type
|
@@ -469,7 +381,7 @@ module Paperclip
|
|
469
381
|
|
470
382
|
def queue_existing_for_delete #:nodoc:
|
471
383
|
return unless file?
|
472
|
-
@queued_for_delete += [:original,
|
384
|
+
@queued_for_delete += [:original, *styles.keys].uniq.map do |style|
|
473
385
|
path(style) if exists?(style)
|
474
386
|
end.compact
|
475
387
|
instance_write(:file_name, nil)
|
@@ -34,30 +34,30 @@ module Paperclip
|
|
34
34
|
end
|
35
35
|
|
36
36
|
# Returns the filename, the same way as ":basename.:extension" would.
|
37
|
-
def filename attachment,
|
38
|
-
"#{basename(attachment,
|
37
|
+
def filename attachment, style_name
|
38
|
+
"#{basename(attachment, style_name)}.#{extension(attachment, style_name)}"
|
39
39
|
end
|
40
40
|
|
41
41
|
# Returns the interpolated URL. Will raise an error if the url itself
|
42
42
|
# contains ":url" to prevent infinite recursion. This interpolation
|
43
43
|
# is used in the default :path to ease default specifications.
|
44
|
-
def url attachment,
|
44
|
+
def url attachment, style_name
|
45
45
|
raise InfiniteInterpolationError if attachment.options[:url].include?(":url")
|
46
|
-
attachment.url(
|
46
|
+
attachment.url(style_name, false)
|
47
47
|
end
|
48
48
|
|
49
49
|
# Returns the timestamp as defined by the <attachment>_updated_at field
|
50
|
-
def timestamp attachment,
|
50
|
+
def timestamp attachment, style_name
|
51
51
|
attachment.instance_read(:updated_at).to_s
|
52
52
|
end
|
53
53
|
|
54
54
|
# Returns the RAILS_ROOT constant.
|
55
|
-
def rails_root attachment,
|
55
|
+
def rails_root attachment, style_name
|
56
56
|
RAILS_ROOT
|
57
57
|
end
|
58
58
|
|
59
59
|
# Returns the RAILS_ENV constant.
|
60
|
-
def rails_env attachment,
|
60
|
+
def rails_env attachment, style_name
|
61
61
|
RAILS_ENV
|
62
62
|
end
|
63
63
|
|
@@ -65,44 +65,44 @@ module Paperclip
|
|
65
65
|
# e.g. "users" for the User class.
|
66
66
|
# NOTE: The arguments need to be optional, because some tools fetch
|
67
67
|
# all class names. Calling #class will return the expected class.
|
68
|
-
def class attachment = nil,
|
69
|
-
return super() if attachment.nil? &&
|
68
|
+
def class attachment = nil, style_name = nil
|
69
|
+
return super() if attachment.nil? && style_name.nil?
|
70
70
|
attachment.instance.class.to_s.underscore.pluralize
|
71
71
|
end
|
72
72
|
|
73
73
|
# Returns the basename of the file. e.g. "file" for "file.jpg"
|
74
|
-
def basename attachment,
|
74
|
+
def basename attachment, style_name
|
75
75
|
attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "")
|
76
76
|
end
|
77
77
|
|
78
78
|
# Returns the extension of the file. e.g. "jpg" for "file.jpg"
|
79
79
|
# If the style has a format defined, it will return the format instead
|
80
80
|
# of the actual extension.
|
81
|
-
def extension attachment,
|
82
|
-
((style = attachment.styles[
|
81
|
+
def extension attachment, style_name
|
82
|
+
((style = attachment.styles[style_name]) && style[:format]) ||
|
83
83
|
File.extname(attachment.original_filename).gsub(/^\.+/, "")
|
84
84
|
end
|
85
85
|
|
86
86
|
# Returns the id of the instance.
|
87
|
-
def id attachment,
|
87
|
+
def id attachment, style_name
|
88
88
|
attachment.instance.id
|
89
89
|
end
|
90
90
|
|
91
91
|
# Returns the id of the instance in a split path form. e.g. returns
|
92
92
|
# 000/001/234 for an id of 1234.
|
93
|
-
def id_partition attachment,
|
93
|
+
def id_partition attachment, style_name
|
94
94
|
("%09d" % attachment.instance.id).scan(/\d{3}/).join("/")
|
95
95
|
end
|
96
96
|
|
97
97
|
# Returns the pluralized form of the attachment name. e.g.
|
98
98
|
# "avatars" for an attachment of :avatar
|
99
|
-
def attachment attachment,
|
99
|
+
def attachment attachment, style_name
|
100
100
|
attachment.name.to_s.downcase.pluralize
|
101
101
|
end
|
102
102
|
|
103
103
|
# Returns the style, or the default style if nil is supplied.
|
104
|
-
def style attachment,
|
105
|
-
|
104
|
+
def style attachment, style_name
|
105
|
+
style_name || attachment.default_style
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
data/lib/paperclip/iostream.rb
CHANGED
@@ -4,7 +4,8 @@ module IOStream
|
|
4
4
|
|
5
5
|
# Returns a Tempfile containing the contents of the readable object.
|
6
6
|
def to_tempfile
|
7
|
-
|
7
|
+
name = respond_to?(:original_filename) ? original_filename : (respond_to?(:path) ? path : "stream")
|
8
|
+
tempfile = Paperclip::Tempfile.new(File.basename(name))
|
8
9
|
tempfile.binmode
|
9
10
|
self.stream_to(tempfile)
|
10
11
|
end
|
@@ -46,9 +46,8 @@ module Paperclip
|
|
46
46
|
types.all? do |type|
|
47
47
|
file = StringIO.new(".")
|
48
48
|
file.content_type = type
|
49
|
-
|
50
|
-
|
51
|
-
attachment.errors[:content_type].nil?
|
49
|
+
(subject = @subject.new).attachment_for(@attachment_name).assign(file)
|
50
|
+
subject.valid? && subject.errors.on(:"#{@attachment_name}_content_type").blank?
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
@@ -30,16 +30,16 @@ module Paperclip
|
|
30
30
|
protected
|
31
31
|
|
32
32
|
def error_when_not_valid?
|
33
|
-
|
34
|
-
|
35
|
-
not
|
33
|
+
(subject = @subject.new).send(@attachment_name).assign(nil)
|
34
|
+
subject.valid?
|
35
|
+
not subject.errors.on(:"#{@attachment_name}_file_name").blank?
|
36
36
|
end
|
37
37
|
|
38
38
|
def no_error_when_valid?
|
39
39
|
@file = StringIO.new(".")
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
(subject = @subject.new).send(@attachment_name).assign(@file)
|
41
|
+
subject.valid?
|
42
|
+
subject.errors.on(:"#{@attachment_name}_file_name").blank?
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -54,9 +54,11 @@ module Paperclip
|
|
54
54
|
def passes_validation_with_size(new_size)
|
55
55
|
file = StringIO.new(".")
|
56
56
|
override_method(file, :size){ new_size }
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
override_method(file, :to_tempfile){ file }
|
58
|
+
|
59
|
+
(subject = @subject.new).send(@attachment_name).assign(file)
|
60
|
+
subject.valid?
|
61
|
+
subject.errors.on(:"#{@attachment_name}_file_size").blank?
|
60
62
|
end
|
61
63
|
|
62
64
|
def lower_than_low?
|
data/lib/paperclip/storage.rb
CHANGED
@@ -20,9 +20,9 @@ module Paperclip
|
|
20
20
|
def self.extended base
|
21
21
|
end
|
22
22
|
|
23
|
-
def exists?(
|
23
|
+
def exists?(style_name = default_style)
|
24
24
|
if original_filename
|
25
|
-
File.exist?(path(
|
25
|
+
File.exist?(path(style_name))
|
26
26
|
else
|
27
27
|
false
|
28
28
|
end
|
@@ -30,17 +30,17 @@ module Paperclip
|
|
30
30
|
|
31
31
|
# Returns representation of the data of the file assigned to the given
|
32
32
|
# style, in the format most representative of the current storage.
|
33
|
-
def to_file
|
34
|
-
@queued_for_write[
|
33
|
+
def to_file style_name = default_style
|
34
|
+
@queued_for_write[style_name] || (File.new(path(style_name), 'rb') if exists?(style_name))
|
35
35
|
end
|
36
36
|
|
37
37
|
def flush_writes #:nodoc:
|
38
|
-
@queued_for_write.each do |
|
38
|
+
@queued_for_write.each do |style_name, file|
|
39
39
|
file.close
|
40
|
-
FileUtils.mkdir_p(File.dirname(path(
|
41
|
-
log("saving #{path(
|
42
|
-
FileUtils.mv(file.path, path(
|
43
|
-
FileUtils.chmod(0644, path(
|
40
|
+
FileUtils.mkdir_p(File.dirname(path(style_name)))
|
41
|
+
log("saving #{path(style_name)}")
|
42
|
+
FileUtils.mv(file.path, path(style_name))
|
43
|
+
FileUtils.chmod(0644, path(style_name))
|
44
44
|
end
|
45
45
|
@queued_for_write = {}
|
46
46
|
end
|
@@ -159,6 +159,10 @@ module Paperclip
|
|
159
159
|
"#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
|
160
160
|
end
|
161
161
|
end
|
162
|
+
|
163
|
+
def expiring_url(time = 3600)
|
164
|
+
AWS::S3::S3Object.url_for(path, bucket_name, :expires_in => time )
|
165
|
+
end
|
162
166
|
|
163
167
|
def bucket_name
|
164
168
|
@bucket
|
data/lib/paperclip/thumbnail.rb
CHANGED
@@ -12,6 +12,7 @@ module Paperclip
|
|
12
12
|
# set, the options will be appended to the convert command upon image conversion
|
13
13
|
def initialize file, options = {}, attachment = nil
|
14
14
|
super
|
15
|
+
|
15
16
|
geometry = options[:geometry]
|
16
17
|
@file = file
|
17
18
|
@crop = geometry[-1,1] == '#'
|
@@ -24,6 +25,7 @@ module Paperclip
|
|
24
25
|
|
25
26
|
@current_format = File.extname(@file.path)
|
26
27
|
@basename = File.basename(@file.path, @current_format)
|
28
|
+
|
27
29
|
end
|
28
30
|
|
29
31
|
# Returns true if the +target_geometry+ is meant to crop.
|
data/test/attachment_test.rb
CHANGED
@@ -14,18 +14,6 @@ class AttachmentTest < Test::Unit::TestCase
|
|
14
14
|
assert_equal "#{RAILS_ROOT}/public/fake_models/1234/fake", @attachment.path
|
15
15
|
end
|
16
16
|
|
17
|
-
should "call a proc sent to check_guard" do
|
18
|
-
@dummy = Dummy.new
|
19
|
-
@dummy.expects(:one).returns(:one)
|
20
|
-
assert_equal :one, @dummy.avatar.send(:check_guard, lambda{|x| x.one })
|
21
|
-
end
|
22
|
-
|
23
|
-
should "call a method name sent to check_guard" do
|
24
|
-
@dummy = Dummy.new
|
25
|
-
@dummy.expects(:one).returns(:one)
|
26
|
-
assert_equal :one, @dummy.avatar.send(:check_guard, :one)
|
27
|
-
end
|
28
|
-
|
29
17
|
context "Attachment default_options" do
|
30
18
|
setup do
|
31
19
|
rebuild_model
|
@@ -133,7 +121,7 @@ class AttachmentTest < Test::Unit::TestCase
|
|
133
121
|
:styles => { :default => ["100x100", :png] },
|
134
122
|
:default_style => :default
|
135
123
|
@file = StringIO.new("...")
|
136
|
-
@file.
|
124
|
+
@file.stubs(:original_filename).returns("file.jpg")
|
137
125
|
end
|
138
126
|
should "return the right extension for the path" do
|
139
127
|
@attachment.assign(@file)
|
@@ -162,11 +150,6 @@ class AttachmentTest < Test::Unit::TestCase
|
|
162
150
|
should "report the correct options when sent #extra_options_for(:large)" do
|
163
151
|
assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large)
|
164
152
|
end
|
165
|
-
|
166
|
-
before_should "call extra_options_for(:thumb/:large)" do
|
167
|
-
Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
|
168
|
-
Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
|
169
|
-
end
|
170
153
|
end
|
171
154
|
|
172
155
|
context "An attachment with :convert_options that is a proc" do
|
@@ -194,11 +177,6 @@ class AttachmentTest < Test::Unit::TestCase
|
|
194
177
|
should "report the correct options when sent #extra_options_for(:large)" do
|
195
178
|
assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
|
196
179
|
end
|
197
|
-
|
198
|
-
before_should "call extra_options_for(:thumb/:large)" do
|
199
|
-
Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
|
200
|
-
Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
|
201
|
-
end
|
202
180
|
end
|
203
181
|
|
204
182
|
context "An attachment with :path that is a proc" do
|
@@ -267,10 +245,6 @@ class AttachmentTest < Test::Unit::TestCase
|
|
267
245
|
@attachment = Dummy.new.avatar
|
268
246
|
end
|
269
247
|
|
270
|
-
should "not run the procs immediately" do
|
271
|
-
assert_kind_of Proc, @attachment.styles[:normal][:geometry]
|
272
|
-
end
|
273
|
-
|
274
248
|
context "when assigned" do
|
275
249
|
setup do
|
276
250
|
@file = StringIO.new(".")
|
@@ -307,10 +281,6 @@ class AttachmentTest < Test::Unit::TestCase
|
|
307
281
|
@attachment = Dummy.new.avatar
|
308
282
|
end
|
309
283
|
|
310
|
-
should "not run the proc immediately" do
|
311
|
-
assert_kind_of Proc, @attachment.styles[:normal][:processors]
|
312
|
-
end
|
313
|
-
|
314
284
|
context "when assigned" do
|
315
285
|
setup do
|
316
286
|
@attachment.assign(StringIO.new("."))
|
@@ -354,19 +324,22 @@ class AttachmentTest < Test::Unit::TestCase
|
|
354
324
|
setup { @dummy.avatar = @file }
|
355
325
|
|
356
326
|
before_should "call #make on all specified processors" do
|
327
|
+
Paperclip::Thumbnail.expects(:make).with(any_parameters).returns(@file)
|
328
|
+
Paperclip::Test.expects(:make).with(any_parameters).returns(@file)
|
329
|
+
end
|
330
|
+
|
331
|
+
before_should "call #make with the right parameters passed as second argument" do
|
357
332
|
expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => true, :convert_options => ""})
|
358
|
-
Paperclip::Thumbnail.expects(:make).with(
|
359
|
-
Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
|
333
|
+
Paperclip::Thumbnail.expects(:make).with(anything, expected_params, anything).returns(@file)
|
360
334
|
end
|
361
335
|
|
362
336
|
before_should "call #make with attachment passed as third argument" do
|
363
|
-
|
364
|
-
Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
|
337
|
+
Paperclip::Test.expects(:make).with(anything, anything, @dummy.avatar).returns(@file)
|
365
338
|
end
|
366
339
|
end
|
367
340
|
end
|
368
341
|
|
369
|
-
context "An attachment with no processors defined" do
|
342
|
+
context "An attachment with styles but no processors defined" do
|
370
343
|
setup do
|
371
344
|
rebuild_model :processors => [], :styles => {:something => 1}
|
372
345
|
@dummy = Dummy.new
|
@@ -377,6 +350,17 @@ class AttachmentTest < Test::Unit::TestCase
|
|
377
350
|
end
|
378
351
|
end
|
379
352
|
|
353
|
+
context "An attachment without styles and with no processors defined" do
|
354
|
+
setup do
|
355
|
+
rebuild_model :processors => [], :styles => {}
|
356
|
+
@dummy = Dummy.new
|
357
|
+
@file = StringIO.new("...")
|
358
|
+
end
|
359
|
+
should "not raise when assigned to" do
|
360
|
+
@dummy.avatar = @file
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
380
364
|
context "Assigning an attachment with post_process hooks" do
|
381
365
|
setup do
|
382
366
|
rebuild_model :styles => { :something => "100x100#" }
|
@@ -478,8 +462,6 @@ class AttachmentTest < Test::Unit::TestCase
|
|
478
462
|
@attachment.expects(:valid_assignment?).with(@not_file).returns(true)
|
479
463
|
@attachment.expects(:queue_existing_for_delete)
|
480
464
|
@attachment.expects(:post_process)
|
481
|
-
@attachment.expects(:valid?).returns(true)
|
482
|
-
@attachment.expects(:validate)
|
483
465
|
@dummy.avatar = @not_file
|
484
466
|
end
|
485
467
|
|
@@ -498,6 +480,7 @@ class AttachmentTest < Test::Unit::TestCase
|
|
498
480
|
FileUtils.rm_rf("tmp")
|
499
481
|
rebuild_model
|
500
482
|
@instance = Dummy.new
|
483
|
+
@instance.stubs(:id).returns 123
|
501
484
|
@attachment = Paperclip::Attachment.new(:avatar, @instance)
|
502
485
|
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
|
503
486
|
end
|
@@ -606,6 +589,7 @@ class AttachmentTest < Test::Unit::TestCase
|
|
606
589
|
should "commit the files to disk" do
|
607
590
|
[:large, :medium, :small].each do |style|
|
608
591
|
io = @attachment.to_file(style)
|
592
|
+
# p "in commit to disk test, io is #{io.inspect} and @instance.id is #{@instance.id}"
|
609
593
|
assert File.exists?(io)
|
610
594
|
assert ! io.is_a?(::Tempfile)
|
611
595
|
io.close
|
data/test/helper.rb
CHANGED
data/test/iostream_test.rb
CHANGED
@@ -58,8 +58,15 @@ class IOStreamTest < Test::Unit::TestCase
|
|
58
58
|
assert @tempfile = @file.to_tempfile
|
59
59
|
end
|
60
60
|
|
61
|
-
should "convert it to a Tempfile" do
|
62
|
-
assert @tempfile.is_a?(Tempfile)
|
61
|
+
should "convert it to a Paperclip Tempfile" do
|
62
|
+
assert @tempfile.is_a?(Paperclip::Tempfile)
|
63
|
+
end
|
64
|
+
|
65
|
+
should "have the name be based on the original_filename" do
|
66
|
+
name = File.basename(@file.path)
|
67
|
+
extension = File.extname(name)
|
68
|
+
basename = File.basename(name, extension)
|
69
|
+
assert_match %r[^#{Regexp.quote(basename)}.*?#{Regexp.quote(extension)}], File.basename(@tempfile.path)
|
63
70
|
end
|
64
71
|
|
65
72
|
should "have the Tempfile contain the same data as the file" do
|
@@ -3,7 +3,9 @@ require 'test/helper'
|
|
3
3
|
class ValidateAttachmentPresenceMatcherTest < Test::Unit::TestCase
|
4
4
|
context "validate_attachment_presence" do
|
5
5
|
setup do
|
6
|
-
reset_table("dummies")
|
6
|
+
reset_table("dummies") do |d|
|
7
|
+
d.string :avatar_file_name
|
8
|
+
end
|
7
9
|
@dummy_class = reset_class "Dummy"
|
8
10
|
@dummy_class.has_attached_file :avatar
|
9
11
|
@matcher = self.class.validate_attachment_presence(:avatar)
|
data/test/paperclip_test.rb
CHANGED
@@ -185,45 +185,23 @@ class PaperclipTest < Test::Unit::TestCase
|
|
185
185
|
should "be valid" do
|
186
186
|
assert @dummy.valid?
|
187
187
|
end
|
188
|
-
|
189
|
-
context "then has a validation added that makes it invalid" do
|
190
|
-
setup do
|
191
|
-
assert @dummy.save
|
192
|
-
Dummy.class_eval do
|
193
|
-
validates_attachment_content_type :avatar, :content_type => ["text/plain"]
|
194
|
-
end
|
195
|
-
@dummy2 = Dummy.find(@dummy.id)
|
196
|
-
end
|
197
|
-
|
198
|
-
should "be invalid when reloaded" do
|
199
|
-
assert ! @dummy2.valid?, @dummy2.errors.inspect
|
200
|
-
end
|
201
|
-
|
202
|
-
should "be able to call #valid? twice without having duplicate errors" do
|
203
|
-
@dummy2.avatar.valid?
|
204
|
-
first_errors = @dummy2.avatar.errors
|
205
|
-
@dummy2.avatar.valid?
|
206
|
-
assert_equal first_errors, @dummy2.avatar.errors
|
207
|
-
end
|
208
|
-
end
|
209
188
|
end
|
210
189
|
|
211
190
|
context "a validation with an if guard clause" do
|
212
191
|
setup do
|
213
192
|
Dummy.send(:"validates_attachment_presence", :avatar, :if => lambda{|i| i.foo })
|
214
193
|
@dummy = Dummy.new
|
194
|
+
@dummy.stubs(:avatar_file_name).returns(nil)
|
215
195
|
end
|
216
196
|
|
217
197
|
should "attempt validation if the guard returns true" do
|
218
198
|
@dummy.expects(:foo).returns(true)
|
219
|
-
@dummy.
|
220
|
-
@dummy.valid?
|
199
|
+
assert ! @dummy.valid?
|
221
200
|
end
|
222
201
|
|
223
202
|
should "not attempt validation if the guard returns false" do
|
224
203
|
@dummy.expects(:foo).returns(false)
|
225
|
-
@dummy.
|
226
|
-
@dummy.valid?
|
204
|
+
assert @dummy.valid?
|
227
205
|
end
|
228
206
|
end
|
229
207
|
|
@@ -231,18 +209,17 @@ class PaperclipTest < Test::Unit::TestCase
|
|
231
209
|
setup do
|
232
210
|
Dummy.send(:"validates_attachment_presence", :avatar, :unless => lambda{|i| i.foo })
|
233
211
|
@dummy = Dummy.new
|
212
|
+
@dummy.stubs(:avatar_file_name).returns(nil)
|
234
213
|
end
|
235
214
|
|
236
215
|
should "attempt validation if the guard returns true" do
|
237
216
|
@dummy.expects(:foo).returns(false)
|
238
|
-
@dummy.
|
239
|
-
@dummy.valid?
|
217
|
+
assert ! @dummy.valid?
|
240
218
|
end
|
241
219
|
|
242
220
|
should "not attempt validation if the guard returns false" do
|
243
221
|
@dummy.expects(:foo).returns(true)
|
244
|
-
@dummy.
|
245
|
-
@dummy.valid?
|
222
|
+
assert @dummy.valid?
|
246
223
|
end
|
247
224
|
end
|
248
225
|
|
@@ -259,11 +236,11 @@ class PaperclipTest < Test::Unit::TestCase
|
|
259
236
|
end
|
260
237
|
if validation == :presence
|
261
238
|
should "have an error on the attachment" do
|
262
|
-
assert @dummy.errors.on(:
|
239
|
+
assert @dummy.errors.on(:avatar_file_name)
|
263
240
|
end
|
264
241
|
else
|
265
242
|
should "not have an error on the attachment" do
|
266
|
-
assert_nil @dummy.errors.on(:
|
243
|
+
assert_nil @dummy.errors.on(:avatar_file_name), @dummy.errors.full_messages.join(", ")
|
267
244
|
end
|
268
245
|
end
|
269
246
|
end
|
@@ -273,10 +250,7 @@ class PaperclipTest < Test::Unit::TestCase
|
|
273
250
|
@dummy.valid?
|
274
251
|
end
|
275
252
|
should "not have an error when assigned a valid file" do
|
276
|
-
|
277
|
-
end
|
278
|
-
should "not have an error on the attachment" do
|
279
|
-
assert_nil @dummy.errors.on(:avatar)
|
253
|
+
assert_equal 0, @dummy.errors.length, @dummy.errors.full_messages.join(", ")
|
280
254
|
end
|
281
255
|
end
|
282
256
|
context "and assigned an invalid file" do
|
@@ -285,17 +259,14 @@ class PaperclipTest < Test::Unit::TestCase
|
|
285
259
|
@dummy.valid?
|
286
260
|
end
|
287
261
|
should "have an error when assigned a valid file" do
|
288
|
-
|
289
|
-
end
|
290
|
-
should "have an error on the attachment" do
|
291
|
-
assert @dummy.errors.on(:avatar)
|
262
|
+
assert @dummy.errors.length > 0
|
292
263
|
end
|
293
264
|
end
|
294
265
|
end
|
295
266
|
end
|
296
267
|
|
297
268
|
[[:presence, {}, "5k.png", nil],
|
298
|
-
[:size, {:in => 1..10240},
|
269
|
+
[:size, {:in => 1..10240}, "5k.png", "12k.png"],
|
299
270
|
[:size, {:less_than => 10240}, "5k.png", "12k.png"],
|
300
271
|
[:size, {:greater_than => 8096}, "12k.png", "5k.png"],
|
301
272
|
[:content_type, {:content_type => "image/png"}, "5k.png", "text.txt"],
|
@@ -318,7 +289,7 @@ class PaperclipTest < Test::Unit::TestCase
|
|
318
289
|
end
|
319
290
|
|
320
291
|
should "have a file size min/max error message" do
|
321
|
-
assert_match /between 0 and 10240 bytes/, @dummy.errors.on(:
|
292
|
+
assert_match %r/between 0 and 10240 bytes/, @dummy.errors.on(:avatar_file_size)
|
322
293
|
end
|
323
294
|
end
|
324
295
|
end
|
data/test/storage_test.rb
CHANGED
@@ -96,6 +96,33 @@ class StorageTest < Test::Unit::TestCase
|
|
96
96
|
assert_match %r{^http://something.something.com/avatars/stringio.txt}, @dummy.avatar.url
|
97
97
|
end
|
98
98
|
end
|
99
|
+
|
100
|
+
context "Generating a url with an expiration" do
|
101
|
+
setup do
|
102
|
+
AWS::S3::Base.stubs(:establish_connection!)
|
103
|
+
rebuild_model :storage => :s3,
|
104
|
+
:s3_credentials => {
|
105
|
+
:production => { :bucket => "prod_bucket" },
|
106
|
+
:development => { :bucket => "dev_bucket" }
|
107
|
+
},
|
108
|
+
:s3_host_alias => "something.something.com",
|
109
|
+
:path => ":attachment/:basename.:extension",
|
110
|
+
:url => ":s3_alias_url"
|
111
|
+
|
112
|
+
rails_env("production")
|
113
|
+
|
114
|
+
@dummy = Dummy.new
|
115
|
+
@dummy.avatar = StringIO.new(".")
|
116
|
+
|
117
|
+
AWS::S3::S3Object.expects(:url_for).with("avatars/stringio.txt", "prod_bucket", { :expires_in => 3600 })
|
118
|
+
|
119
|
+
@dummy.avatar.expiring_url
|
120
|
+
end
|
121
|
+
|
122
|
+
should "should succeed" do
|
123
|
+
assert true
|
124
|
+
end
|
125
|
+
end
|
99
126
|
|
100
127
|
context "Parsing S3 credentials with a bucket in them" do
|
101
128
|
setup do
|
data/test/thumbnail_test.rb
CHANGED
@@ -200,7 +200,7 @@ class ThumbnailTest < Test::Unit::TestCase
|
|
200
200
|
teardown { @file.close }
|
201
201
|
|
202
202
|
should "start with two pages with dimensions 612x792" do
|
203
|
-
cmd = %Q[identify -format "%wx%h" "#{@file.path}"]
|
203
|
+
cmd = %Q[identify -format "%wx%h" "#{@file.path}"]
|
204
204
|
assert_equal "612x792"*2, `#{cmd}`.chomp
|
205
205
|
end
|
206
206
|
|