paperclip-cloudfiles 2.3.8.1 → 2.3.8.3
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/README.md +15 -0
- data/lib/paperclip/attachment.rb +69 -105
- data/lib/paperclip/command_line.rb +7 -1
- data/lib/paperclip/interpolations.rb +17 -1
- data/lib/paperclip/storage/cloud_files.rb +12 -2
- data/lib/paperclip/storage/s3.rb +4 -4
- data/lib/paperclip/version.rb +1 -1
- data/lib/paperclip.rb +3 -1
- data/test/attachment_test.rb +117 -14
- data/test/command_line_test.rb +5 -0
- data/test/fixtures/uppercase.PNG +0 -0
- data/test/helper.rb +1 -1
- data/test/integration_test.rb +72 -0
- data/test/interpolations_test.rb +17 -1
- data/test/paperclip_test.rb +30 -21
- data/test/storage_test.rb +4 -3
- metadata +8 -7
data/README.md
CHANGED
|
@@ -25,6 +25,21 @@ on Gemcutter's gem server.
|
|
|
25
25
|
|
|
26
26
|
The complete [RDoc](http://rdoc.info/github/minter/paperclip) is online.
|
|
27
27
|
|
|
28
|
+
Requirements
|
|
29
|
+
------------
|
|
30
|
+
|
|
31
|
+
ImageMagick must be installed and Paperclip must have access to it. To ensure
|
|
32
|
+
that it does, on your command line, run `which convert` (one of the ImageMagick
|
|
33
|
+
utilities). This will give you the path where that utility is installed. For
|
|
34
|
+
example, it might return `/usr/local/bin/convert`.
|
|
35
|
+
|
|
36
|
+
Then, in your environment config file, let Paperclip know to look there by adding that
|
|
37
|
+
directory to its path.
|
|
38
|
+
|
|
39
|
+
In development mode, you might add this line to `config/environments/development.rb)`:
|
|
40
|
+
|
|
41
|
+
Paperclip.options[:command_path] = "/usr/local/bin/"
|
|
42
|
+
|
|
28
43
|
Installation
|
|
29
44
|
------------
|
|
30
45
|
|
data/lib/paperclip/attachment.rb
CHANGED
|
@@ -8,21 +8,24 @@ module Paperclip
|
|
|
8
8
|
|
|
9
9
|
def self.default_options
|
|
10
10
|
@default_options ||= {
|
|
11
|
-
:url
|
|
12
|
-
:path
|
|
13
|
-
:styles
|
|
14
|
-
:processors
|
|
15
|
-
:convert_options
|
|
16
|
-
:default_url
|
|
17
|
-
:default_style
|
|
18
|
-
:
|
|
19
|
-
:
|
|
20
|
-
:
|
|
21
|
-
:
|
|
11
|
+
:url => "/system/:attachment/:id/:style/:filename",
|
|
12
|
+
:path => ":rails_root/public:url",
|
|
13
|
+
:styles => {},
|
|
14
|
+
:processors => [:thumbnail],
|
|
15
|
+
:convert_options => {},
|
|
16
|
+
:default_url => "/:attachment/:style/missing.png",
|
|
17
|
+
:default_style => :original,
|
|
18
|
+
:storage => :filesystem,
|
|
19
|
+
:use_timestamp => true,
|
|
20
|
+
:whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails],
|
|
21
|
+
:use_default_time_zone => true,
|
|
22
|
+
:hash_digest => "SHA1",
|
|
23
|
+
:hash_data => ":class/:attachment/:id/:style/:updated_at"
|
|
22
24
|
}
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
attr_reader :name, :instance, :default_style, :convert_options, :queued_for_write, :whiny, :options
|
|
28
|
+
attr_accessor :post_processing
|
|
26
29
|
|
|
27
30
|
# Creates an Attachment object. +name+ is the name of the attachment,
|
|
28
31
|
# +instance+ is the ActiveRecord object instance it's attached to, and
|
|
@@ -33,26 +36,29 @@ module Paperclip
|
|
|
33
36
|
|
|
34
37
|
options = self.class.default_options.merge(options)
|
|
35
38
|
|
|
36
|
-
@url
|
|
37
|
-
@url
|
|
38
|
-
@path
|
|
39
|
-
@path
|
|
40
|
-
@styles
|
|
41
|
-
@normalized_styles
|
|
42
|
-
@default_url
|
|
43
|
-
@
|
|
44
|
-
@
|
|
45
|
-
@
|
|
46
|
-
@
|
|
47
|
-
@
|
|
48
|
-
@
|
|
49
|
-
@
|
|
50
|
-
@
|
|
51
|
-
@
|
|
52
|
-
@
|
|
53
|
-
@
|
|
54
|
-
@
|
|
55
|
-
@
|
|
39
|
+
@url = options[:url]
|
|
40
|
+
@url = @url.call(self) if @url.is_a?(Proc)
|
|
41
|
+
@path = options[:path]
|
|
42
|
+
@path = @path.call(self) if @path.is_a?(Proc)
|
|
43
|
+
@styles = options[:styles]
|
|
44
|
+
@normalized_styles = nil
|
|
45
|
+
@default_url = options[:default_url]
|
|
46
|
+
@default_style = options[:default_style]
|
|
47
|
+
@storage = options[:storage]
|
|
48
|
+
@use_timestamp = options[:use_timestamp]
|
|
49
|
+
@whiny = options[:whiny_thumbnails] || options[:whiny]
|
|
50
|
+
@use_default_time_zone = options[:use_default_time_zone]
|
|
51
|
+
@hash_digest = options[:hash_digest]
|
|
52
|
+
@hash_data = options[:hash_data]
|
|
53
|
+
@hash_secret = options[:hash_secret]
|
|
54
|
+
@convert_options = options[:convert_options]
|
|
55
|
+
@processors = options[:processors]
|
|
56
|
+
@options = options
|
|
57
|
+
@post_processing = true
|
|
58
|
+
@queued_for_delete = []
|
|
59
|
+
@queued_for_write = {}
|
|
60
|
+
@errors = {}
|
|
61
|
+
@dirty = false
|
|
56
62
|
|
|
57
63
|
initialize_storage
|
|
58
64
|
end
|
|
@@ -103,14 +109,13 @@ module Paperclip
|
|
|
103
109
|
|
|
104
110
|
@dirty = true
|
|
105
111
|
|
|
106
|
-
post_process if
|
|
107
|
-
|
|
112
|
+
post_process if @post_processing
|
|
113
|
+
|
|
108
114
|
# Reset the file size if the original file was reprocessed.
|
|
109
115
|
instance_write(:file_size, @queued_for_write[:original].size.to_i)
|
|
110
116
|
instance_write(:fingerprint, generate_fingerprint(@queued_for_write[:original]))
|
|
111
117
|
ensure
|
|
112
118
|
uploaded_file.close if close_uploaded_file
|
|
113
|
-
validate
|
|
114
119
|
end
|
|
115
120
|
|
|
116
121
|
# Returns the public URL of the attachment, with a given style. Note that
|
|
@@ -133,16 +138,10 @@ module Paperclip
|
|
|
133
138
|
end
|
|
134
139
|
|
|
135
140
|
# Alias to +url+
|
|
136
|
-
def to_s style_name =
|
|
141
|
+
def to_s style_name = default_style
|
|
137
142
|
url(style_name)
|
|
138
143
|
end
|
|
139
144
|
|
|
140
|
-
# Returns true if there are no errors on this attachment.
|
|
141
|
-
def valid?
|
|
142
|
-
validate
|
|
143
|
-
errors.empty?
|
|
144
|
-
end
|
|
145
|
-
|
|
146
145
|
# Returns an array containing the errors on this attachment.
|
|
147
146
|
def errors
|
|
148
147
|
@errors
|
|
@@ -156,15 +155,10 @@ module Paperclip
|
|
|
156
155
|
# Saves the file, if there are no errors. If there are, it flushes them to
|
|
157
156
|
# the instance's errors and returns false, cancelling the save.
|
|
158
157
|
def save
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
true
|
|
164
|
-
else
|
|
165
|
-
flush_errors
|
|
166
|
-
false
|
|
167
|
-
end
|
|
158
|
+
flush_deletes
|
|
159
|
+
flush_writes
|
|
160
|
+
@dirty = false
|
|
161
|
+
true
|
|
168
162
|
end
|
|
169
163
|
|
|
170
164
|
# Clears out the attachment. Has the same effect as previously assigning
|
|
@@ -173,7 +167,6 @@ module Paperclip
|
|
|
173
167
|
def clear
|
|
174
168
|
queue_existing_for_delete
|
|
175
169
|
@errors = {}
|
|
176
|
-
@validation_errors = nil
|
|
177
170
|
end
|
|
178
171
|
|
|
179
172
|
# Destroys the attachment. Has the same effect as previously assigning
|
|
@@ -215,6 +208,21 @@ module Paperclip
|
|
|
215
208
|
time && time.to_f.to_i
|
|
216
209
|
end
|
|
217
210
|
|
|
211
|
+
# The time zone to use for timestamp interpolation. Using the default
|
|
212
|
+
# time zone ensures that results are consistent across all threads.
|
|
213
|
+
def time_zone
|
|
214
|
+
@use_default_time_zone ? Time.zone_default : Time.zone
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Returns a unique hash suitable for obfuscating the URL of an otherwise
|
|
218
|
+
# publicly viewable attachment.
|
|
219
|
+
def hash(style_name = default_style)
|
|
220
|
+
raise ArgumentError, "Unable to generate hash without :hash_secret" unless @hash_secret
|
|
221
|
+
require 'openssl' unless defined?(OpenSSL)
|
|
222
|
+
data = interpolate(@hash_data, style_name)
|
|
223
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@hash_digest).new, @hash_secret, data)
|
|
224
|
+
end
|
|
225
|
+
|
|
218
226
|
def generate_fingerprint(source)
|
|
219
227
|
data = source.read
|
|
220
228
|
source.rewind if source.respond_to?(:rewind)
|
|
@@ -237,7 +245,7 @@ module Paperclip
|
|
|
237
245
|
# in the paperclip:refresh rake task and that's it. It will regenerate all
|
|
238
246
|
# thumbnails forcefully, by reobtaining the original file and going through
|
|
239
247
|
# the post-process again.
|
|
240
|
-
def reprocess!
|
|
248
|
+
def reprocess!(*style_args)
|
|
241
249
|
new_original = Tempfile.new("paperclip-reprocess")
|
|
242
250
|
new_original.binmode
|
|
243
251
|
if old_original = to_file(:original)
|
|
@@ -245,7 +253,7 @@ module Paperclip
|
|
|
245
253
|
new_original.rewind
|
|
246
254
|
|
|
247
255
|
@queued_for_write = { :original => new_original }
|
|
248
|
-
post_process
|
|
256
|
+
post_process(*style_args)
|
|
249
257
|
|
|
250
258
|
old_original.close if old_original.respond_to?(:close)
|
|
251
259
|
|
|
@@ -301,52 +309,6 @@ module Paperclip
|
|
|
301
309
|
file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
|
|
302
310
|
end
|
|
303
311
|
|
|
304
|
-
def validate #:nodoc:
|
|
305
|
-
unless @validation_errors
|
|
306
|
-
@validation_errors = @validations.inject({}) do |errors, validation|
|
|
307
|
-
name, options = validation
|
|
308
|
-
errors[name] = send(:"validate_#{name}", options) if allow_validation?(options)
|
|
309
|
-
errors
|
|
310
|
-
end
|
|
311
|
-
@validation_errors.reject!{|k,v| v == nil }
|
|
312
|
-
@errors.merge!(@validation_errors)
|
|
313
|
-
end
|
|
314
|
-
@validation_errors
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
def allow_validation? options #:nodoc:
|
|
318
|
-
(options[:if].nil? || check_guard(options[:if])) && (options[:unless].nil? || !check_guard(options[:unless]))
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
def check_guard guard #:nodoc:
|
|
322
|
-
if guard.respond_to? :call
|
|
323
|
-
guard.call(instance)
|
|
324
|
-
elsif ! guard.blank?
|
|
325
|
-
instance.send(guard.to_s)
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
def validate_size options #:nodoc:
|
|
330
|
-
if file? && !options[:range].include?(size.to_i)
|
|
331
|
-
options[:message].gsub(/:min/, options[:min].to_s).gsub(/:max/, options[:max].to_s)
|
|
332
|
-
end
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
def validate_presence options #:nodoc:
|
|
336
|
-
options[:message] unless file?
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
def validate_content_type options #:nodoc:
|
|
340
|
-
valid_types = [options[:content_type]].flatten
|
|
341
|
-
unless original_filename.blank?
|
|
342
|
-
unless valid_types.blank?
|
|
343
|
-
content_type = instance_read(:content_type)
|
|
344
|
-
unless valid_types.any?{|t| content_type.nil? || t === content_type }
|
|
345
|
-
options[:message] || "is not one of the allowed file types."
|
|
346
|
-
end
|
|
347
|
-
end
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
312
|
|
|
351
313
|
def initialize_storage #:nodoc:
|
|
352
314
|
storage_class_name = @storage.to_s.capitalize
|
|
@@ -367,21 +329,23 @@ module Paperclip
|
|
|
367
329
|
[ style_options, all_options ].compact.join(" ")
|
|
368
330
|
end
|
|
369
331
|
|
|
370
|
-
def post_process #:nodoc:
|
|
332
|
+
def post_process(*style_args) #:nodoc:
|
|
371
333
|
return if @queued_for_write[:original].nil?
|
|
372
334
|
instance.run_paperclip_callbacks(:post_process) do
|
|
373
335
|
instance.run_paperclip_callbacks(:"#{name}_post_process") do
|
|
374
|
-
post_process_styles
|
|
336
|
+
post_process_styles(*style_args)
|
|
375
337
|
end
|
|
376
338
|
end
|
|
377
339
|
end
|
|
378
340
|
|
|
379
|
-
def post_process_styles #:nodoc:
|
|
341
|
+
def post_process_styles(*style_args) #:nodoc:
|
|
380
342
|
styles.each do |name, style|
|
|
381
343
|
begin
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
344
|
+
if style_args.empty? || style_args.include?(name)
|
|
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)
|
|
348
|
+
end
|
|
385
349
|
end
|
|
386
350
|
rescue PaperclipError => e
|
|
387
351
|
log("An error was received while processing: #{e.inspect}")
|
|
@@ -52,7 +52,7 @@ module Paperclip
|
|
|
52
52
|
raise PaperclipCommandLineError,
|
|
53
53
|
"Interpolation of #{key} isn't allowed."
|
|
54
54
|
end
|
|
55
|
-
|
|
55
|
+
interpolation(vars, key) || match
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
@@ -60,6 +60,12 @@ module Paperclip
|
|
|
60
60
|
%w(expected_outcodes swallow_stderr)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
def interpolation(vars, key)
|
|
64
|
+
if vars.key?(key.to_sym)
|
|
65
|
+
shell_quote(vars[key.to_sym])
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
63
69
|
def shell_quote(string)
|
|
64
70
|
return "" if string.nil? or string.blank?
|
|
65
71
|
if self.class.unix?
|
|
@@ -48,8 +48,18 @@ module Paperclip
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
# Returns the timestamp as defined by the <attachment>_updated_at field
|
|
51
|
+
# in the server default time zone unless :use_global_time_zone is set
|
|
52
|
+
# to false. Note that a Rails.config.time_zone change will still
|
|
53
|
+
# invalidate any path or URL that uses :timestamp. For a
|
|
54
|
+
# time_zone-agnostic timestamp, use #updated_at.
|
|
51
55
|
def timestamp attachment, style_name
|
|
52
|
-
attachment.instance_read(:updated_at).to_s
|
|
56
|
+
attachment.instance_read(:updated_at).in_time_zone(attachment.time_zone).to_s
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns an integer timestamp that is time zone-neutral, so that paths
|
|
60
|
+
# remain valid even if a server's time zone changes.
|
|
61
|
+
def updated_at attachment, style_name
|
|
62
|
+
attachment.updated_at
|
|
53
63
|
end
|
|
54
64
|
|
|
55
65
|
# Returns the Rails.root constant.
|
|
@@ -94,6 +104,12 @@ module Paperclip
|
|
|
94
104
|
attachment.fingerprint
|
|
95
105
|
end
|
|
96
106
|
|
|
107
|
+
# Returns a the attachment hash. See Paperclip::Attachment#hash for
|
|
108
|
+
# more details.
|
|
109
|
+
def hash attachment, style_name
|
|
110
|
+
attachment.hash(style_name)
|
|
111
|
+
end
|
|
112
|
+
|
|
97
113
|
# Returns the id of the instance in a split path form. e.g. returns
|
|
98
114
|
# 000/001/234 for an id of 1234.
|
|
99
115
|
def id_partition attachment, style_name
|
|
@@ -43,9 +43,17 @@ module Paperclip
|
|
|
43
43
|
# * +auth_url+: The URL to the authentication endpoint. If blank, defaults to the Rackspace Cloud Files
|
|
44
44
|
# USA endpoint. You can use this to specify things like the Rackspace Cloud Files UK infrastructure, or
|
|
45
45
|
# a non-Rackspace OpenStack Swift installation. Requires 1.4.11 or higher of the Cloud Files gem.
|
|
46
|
+
# * +ssl+: Whether or not to serve this content over SSL. If set to true, serves content as https, otherwise
|
|
47
|
+
# not. Can also take a lambda that returns true or false (for example, if the attachment object has a user object
|
|
48
|
+
# and that user has ssl enabled)
|
|
46
49
|
module Cloud_files
|
|
47
50
|
def self.extended base
|
|
48
|
-
|
|
51
|
+
begin
|
|
52
|
+
require 'cloudfiles'
|
|
53
|
+
rescue LoadError => e
|
|
54
|
+
e.message << " (You may need to install the cloudfiles gem)"
|
|
55
|
+
raise e
|
|
56
|
+
end unless defined?(CloudFiles)
|
|
49
57
|
@@container ||= {}
|
|
50
58
|
base.instance_eval do
|
|
51
59
|
@cloudfiles_credentials = parse_credentials(@options[:cloudfiles_credentials])
|
|
@@ -53,8 +61,10 @@ module Paperclip
|
|
|
53
61
|
@container_name = @container_name.call(self) if @container_name.is_a?(Proc)
|
|
54
62
|
@cloudfiles_options = @options[:cloudfiles_options] || {}
|
|
55
63
|
@@cdn_url = cloudfiles_container.cdn_url
|
|
64
|
+
@@ssl_url = cloudfiles_container.cdn_ssl_url
|
|
65
|
+
@use_ssl = @options[:ssl] || false
|
|
56
66
|
@path_filename = ":cf_path_filename" unless @url.to_s.match(/^:cf.*filename$/)
|
|
57
|
-
@url = @@cdn_url + "/#{URI.encode(@path_filename).gsub(/&/,'%26')}"
|
|
67
|
+
@url = (@use_ssl == true ? @@ssl_url : @@cdn_url) + "/#{URI.encode(@path_filename).gsub(/&/,'%26')}"
|
|
58
68
|
@path = (Paperclip::Attachment.default_options[:path] == @options[:path]) ? ":attachment/:id/:style/:basename.:extension" : @options[:path]
|
|
59
69
|
end
|
|
60
70
|
Paperclip.interpolates(:cf_path_filename) do |attachment, style|
|
data/lib/paperclip/storage/s3.rb
CHANGED
|
@@ -63,7 +63,7 @@ module Paperclip
|
|
|
63
63
|
rescue LoadError => e
|
|
64
64
|
e.message << " (You may need to install the aws-s3 gem)"
|
|
65
65
|
raise e
|
|
66
|
-
end
|
|
66
|
+
end unless defined?(AWS::S3)
|
|
67
67
|
|
|
68
68
|
base.instance_eval do
|
|
69
69
|
@s3_credentials = parse_credentials(@options[:s3_credentials])
|
|
@@ -85,13 +85,13 @@ module Paperclip
|
|
|
85
85
|
end
|
|
86
86
|
Paperclip.interpolates(:s3_alias_url) do |attachment, style|
|
|
87
87
|
"#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
|
88
|
-
end
|
|
88
|
+
end unless Paperclip::Interpolations.respond_to? :s3_alias_url
|
|
89
89
|
Paperclip.interpolates(:s3_path_url) do |attachment, style|
|
|
90
90
|
"#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
|
|
91
|
-
end
|
|
91
|
+
end unless Paperclip::Interpolations.respond_to? :s3_path_url
|
|
92
92
|
Paperclip.interpolates(:s3_domain_url) do |attachment, style|
|
|
93
93
|
"#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
|
|
94
|
-
end
|
|
94
|
+
end unless Paperclip::Interpolations.respond_to? :s3_domain_url
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def expiring_url(time = 3600)
|
data/lib/paperclip/version.rb
CHANGED
data/lib/paperclip.rb
CHANGED
|
@@ -258,7 +258,7 @@ module Paperclip
|
|
|
258
258
|
|
|
259
259
|
validates_each(name) do |record, attr, value|
|
|
260
260
|
attachment = record.attachment_for(name)
|
|
261
|
-
attachment.send(:flush_errors)
|
|
261
|
+
attachment.send(:flush_errors)
|
|
262
262
|
end
|
|
263
263
|
end
|
|
264
264
|
|
|
@@ -276,6 +276,7 @@ module Paperclip
|
|
|
276
276
|
max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
|
|
277
277
|
range = (min..max)
|
|
278
278
|
message = options[:message] || "file size must be between :min and :max bytes."
|
|
279
|
+
message = message.call if message.respond_to?(:call)
|
|
279
280
|
message = message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
|
|
280
281
|
|
|
281
282
|
validates_inclusion_of :"#{name}_file_size",
|
|
@@ -330,6 +331,7 @@ module Paperclip
|
|
|
330
331
|
if !allowed_types.any?{|t| t === value } && !(value.nil? || value.blank?)
|
|
331
332
|
if record.errors.method(:add).arity == -2
|
|
332
333
|
message = options[:message] || "is not one of #{allowed_types.join(", ")}"
|
|
334
|
+
message = message.call if message.respond_to?(:call)
|
|
333
335
|
record.errors.add(:"#{name}_content_type", message)
|
|
334
336
|
else
|
|
335
337
|
record.errors.add(:"#{name}_content_type", :inclusion, :default => options[:message], :value => value)
|
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
|
|
@@ -109,6 +97,83 @@ class AttachmentTest < Test::Unit::TestCase
|
|
|
109
97
|
end
|
|
110
98
|
end
|
|
111
99
|
|
|
100
|
+
context "An attachment with :timestamp interpolations" do
|
|
101
|
+
setup do
|
|
102
|
+
@file = StringIO.new("...")
|
|
103
|
+
@zone = 'UTC'
|
|
104
|
+
Time.stubs(:zone).returns(@zone)
|
|
105
|
+
@zone_default = 'Eastern Time (US & Canada)'
|
|
106
|
+
Time.stubs(:zone_default).returns(@zone_default)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context "using default time zone" do
|
|
110
|
+
setup do
|
|
111
|
+
rebuild_model :path => ":timestamp", :use_default_time_zone => true
|
|
112
|
+
@dummy = Dummy.new
|
|
113
|
+
@dummy.avatar = @file
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
should "return a time in the default zone" do
|
|
117
|
+
assert_equal @dummy.avatar_updated_at.in_time_zone(@zone_default).to_s, @dummy.avatar.path
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
context "using per-thread time zone" do
|
|
122
|
+
setup do
|
|
123
|
+
rebuild_model :path => ":timestamp", :use_default_time_zone => false
|
|
124
|
+
@dummy = Dummy.new
|
|
125
|
+
@dummy.avatar = @file
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
should "return a time in the per-thread zone" do
|
|
129
|
+
assert_equal @dummy.avatar_updated_at.in_time_zone(@zone).to_s, @dummy.avatar.path
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context "An attachment with :hash interpolations" do
|
|
135
|
+
setup do
|
|
136
|
+
@file = StringIO.new("...")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
should "raise if no secret is provided" do
|
|
140
|
+
@attachment = attachment :path => ":hash"
|
|
141
|
+
@attachment.assign @file
|
|
142
|
+
|
|
143
|
+
assert_raise ArgumentError do
|
|
144
|
+
@attachment.path
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
context "when secret is set" do
|
|
149
|
+
setup do
|
|
150
|
+
@attachment = attachment :path => ":hash", :hash_secret => "w00t"
|
|
151
|
+
@attachment.stubs(:instance_read).with(:updated_at).returns(Time.at(1234567890))
|
|
152
|
+
@attachment.stubs(:instance_read).with(:file_name).returns("bla.txt")
|
|
153
|
+
@attachment.instance.id = 1234
|
|
154
|
+
@attachment.assign @file
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
should "interpolate the hash data" do
|
|
158
|
+
@attachment.expects(:interpolate).with(@attachment.options[:hash_data],anything).returns("interpolated_stuff")
|
|
159
|
+
@attachment.hash
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
should "result in the correct interpolation" do
|
|
163
|
+
assert_equal "fake_models/avatars/1234/original/1234567890", @attachment.send(:interpolate,@attachment.options[:hash_data])
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
should "result in a correct hash" do
|
|
167
|
+
assert_equal "d22b617d1bf10016aa7d046d16427ae203f39fce", @attachment.path
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
should "generate a hash digest with the correct style" do
|
|
171
|
+
OpenSSL::HMAC.expects(:hexdigest).with(anything, anything, "fake_models/avatars/1234/medium/1234567890")
|
|
172
|
+
@attachment.path("medium")
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
112
177
|
context "An attachment with a :rails_env interpolation" do
|
|
113
178
|
setup do
|
|
114
179
|
@rails_env = "blah"
|
|
@@ -488,8 +553,6 @@ class AttachmentTest < Test::Unit::TestCase
|
|
|
488
553
|
@attachment.expects(:valid_assignment?).with(@not_file).returns(true)
|
|
489
554
|
@attachment.expects(:queue_existing_for_delete)
|
|
490
555
|
@attachment.expects(:post_process)
|
|
491
|
-
@attachment.expects(:valid?).returns(true)
|
|
492
|
-
@attachment.expects(:validate)
|
|
493
556
|
@attachment.expects(:to_tempfile).returns(@tempfile)
|
|
494
557
|
@attachment.expects(:generate_fingerprint).with(@tempfile).returns("12345")
|
|
495
558
|
@attachment.expects(:generate_fingerprint).with(@not_file).returns("12345")
|
|
@@ -501,6 +564,46 @@ class AttachmentTest < Test::Unit::TestCase
|
|
|
501
564
|
end
|
|
502
565
|
end
|
|
503
566
|
|
|
567
|
+
context "Attachment with uppercase extension and a default style" do
|
|
568
|
+
setup do
|
|
569
|
+
@old_defaults = Paperclip::Attachment.default_options.dup
|
|
570
|
+
Paperclip::Attachment.default_options.merge!({
|
|
571
|
+
:path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
|
|
572
|
+
})
|
|
573
|
+
FileUtils.rm_rf("tmp")
|
|
574
|
+
rebuild_model
|
|
575
|
+
@instance = Dummy.new
|
|
576
|
+
@instance.stubs(:id).returns 123
|
|
577
|
+
|
|
578
|
+
@file = File.new(File.join(File.dirname(__FILE__), "fixtures", "uppercase.PNG"), 'rb')
|
|
579
|
+
|
|
580
|
+
styles = {:styles => { :large => ["400x400", :jpg],
|
|
581
|
+
:medium => ["100x100", :jpg],
|
|
582
|
+
:small => ["32x32#", :jpg]},
|
|
583
|
+
:default_style => :small}
|
|
584
|
+
@attachment = Paperclip::Attachment.new(:avatar,
|
|
585
|
+
@instance,
|
|
586
|
+
styles)
|
|
587
|
+
now = Time.now
|
|
588
|
+
Time.stubs(:now).returns(now)
|
|
589
|
+
@attachment.assign(@file)
|
|
590
|
+
@attachment.save
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
teardown do
|
|
594
|
+
@file.close
|
|
595
|
+
Paperclip::Attachment.default_options.merge!(@old_defaults)
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
should "should have matching to_s and url methods" do
|
|
599
|
+
file = @attachment.to_file
|
|
600
|
+
assert file
|
|
601
|
+
assert_match @attachment.to_s, @attachment.url
|
|
602
|
+
assert_match @attachment.to_s(:small), @attachment.url(:small)
|
|
603
|
+
file.close
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
504
607
|
context "An attachment" do
|
|
505
608
|
setup do
|
|
506
609
|
@old_defaults = Paperclip::Attachment.default_options.dup
|
data/test/command_line_test.rb
CHANGED
|
@@ -6,6 +6,11 @@ class CommandLineTest < Test::Unit::TestCase
|
|
|
6
6
|
File.stubs(:exist?).with("/dev/null").returns(true)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
should "allow colons in parameters" do
|
|
10
|
+
cmd = Paperclip::CommandLine.new("convert", "'a.jpg' -resize 175x220> -size 175x220 xc:black +swap -gravity center -composite 'b.jpg'", :swallow_stderr => false)
|
|
11
|
+
assert_equal "convert 'a.jpg' -resize 175x220> -size 175x220 xc:black +swap -gravity center -composite 'b.jpg'", cmd.command
|
|
12
|
+
end
|
|
13
|
+
|
|
9
14
|
should "take a command and parameters and produce a shell command for bash" do
|
|
10
15
|
cmd = Paperclip::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
|
|
11
16
|
assert_equal "convert a.jpg b.png", cmd.command
|
|
Binary file
|
data/test/helper.rb
CHANGED
data/test/integration_test.rb
CHANGED
|
@@ -69,6 +69,78 @@ class IntegrationTest < Test::Unit::TestCase
|
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
context "Attachment" do
|
|
73
|
+
setup do
|
|
74
|
+
@thumb_path = "./test/../public/system/avatars/1/thumb/5k.png"
|
|
75
|
+
File.delete(@thumb_path) if File.exists?(@thumb_path)
|
|
76
|
+
rebuild_model :styles => { :thumb => "50x50#" }
|
|
77
|
+
@dummy = Dummy.new
|
|
78
|
+
@file = File.new(File.join(File.dirname(__FILE__),
|
|
79
|
+
"fixtures",
|
|
80
|
+
"5k.png"), 'rb')
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
teardown { @file.close }
|
|
85
|
+
|
|
86
|
+
should "not create the thumbnails upon saving when post-processing is disabled" do
|
|
87
|
+
@dummy.avatar.post_processing = false
|
|
88
|
+
@dummy.avatar = @file
|
|
89
|
+
assert @dummy.save
|
|
90
|
+
assert !File.exists?(@thumb_path)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
should "create the thumbnails upon saving when post_processing is enabled" do
|
|
94
|
+
@dummy.avatar.post_processing = true
|
|
95
|
+
@dummy.avatar = @file
|
|
96
|
+
assert @dummy.save
|
|
97
|
+
assert File.exists?(@thumb_path)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context "Attachment with no generated thumbnails" do
|
|
102
|
+
setup do
|
|
103
|
+
@thumb_small_path = "./test/../public/system/avatars/1/thumb_small/5k.png"
|
|
104
|
+
@thumb_large_path = "./test/../public/system/avatars/1/thumb_large/5k.png"
|
|
105
|
+
File.delete(@thumb_small_path) if File.exists?(@thumb_small_path)
|
|
106
|
+
File.delete(@thumb_large_path) if File.exists?(@thumb_large_path)
|
|
107
|
+
rebuild_model :styles => { :thumb_small => "50x50#", :thumb_large => "60x60#" }
|
|
108
|
+
@dummy = Dummy.new
|
|
109
|
+
@file = File.new(File.join(File.dirname(__FILE__),
|
|
110
|
+
"fixtures",
|
|
111
|
+
"5k.png"), 'rb')
|
|
112
|
+
|
|
113
|
+
@dummy.avatar.post_processing = false
|
|
114
|
+
@dummy.avatar = @file
|
|
115
|
+
assert @dummy.save
|
|
116
|
+
@dummy.avatar.post_processing = true
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
teardown { @file.close }
|
|
120
|
+
|
|
121
|
+
should "allow us to create all thumbnails in one go" do
|
|
122
|
+
assert !File.exists?(@thumb_small_path)
|
|
123
|
+
assert !File.exists?(@thumb_large_path)
|
|
124
|
+
|
|
125
|
+
@dummy.avatar.reprocess!
|
|
126
|
+
|
|
127
|
+
assert File.exists?(@thumb_small_path)
|
|
128
|
+
assert File.exists?(@thumb_large_path)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
should "allow us to selectively create each thumbnail" do
|
|
132
|
+
assert !File.exists?(@thumb_small_path)
|
|
133
|
+
assert !File.exists?(@thumb_large_path)
|
|
134
|
+
|
|
135
|
+
@dummy.avatar.reprocess! :thumb_small
|
|
136
|
+
assert File.exists?(@thumb_small_path)
|
|
137
|
+
assert !File.exists?(@thumb_large_path)
|
|
138
|
+
|
|
139
|
+
@dummy.avatar.reprocess! :thumb_large
|
|
140
|
+
assert File.exists?(@thumb_large_path)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
72
144
|
context "A model that modifies its original" do
|
|
73
145
|
setup do
|
|
74
146
|
rebuild_model :styles => { :original => "2x2#" }
|
data/test/interpolations_test.rb
CHANGED
|
@@ -112,9 +112,25 @@ class InterpolationsTest < Test::Unit::TestCase
|
|
|
112
112
|
|
|
113
113
|
should "return the timestamp" do
|
|
114
114
|
now = Time.now
|
|
115
|
+
zone = 'UTC'
|
|
115
116
|
attachment = mock
|
|
116
117
|
attachment.expects(:instance_read).with(:updated_at).returns(now)
|
|
117
|
-
|
|
118
|
+
attachment.expects(:time_zone).returns(zone)
|
|
119
|
+
assert_equal now.in_time_zone(zone).to_s, Paperclip::Interpolations.timestamp(attachment, :style)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
should "return updated_at" do
|
|
123
|
+
attachment = mock
|
|
124
|
+
seconds_since_epoch = 1234567890
|
|
125
|
+
attachment.expects(:updated_at).returns(seconds_since_epoch)
|
|
126
|
+
assert_equal seconds_since_epoch, Paperclip::Interpolations.updated_at(attachment, :style)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
should "return hash" do
|
|
130
|
+
attachment = mock
|
|
131
|
+
fake_hash = "a_wicked_secure_hash"
|
|
132
|
+
attachment.expects(:hash).returns(fake_hash)
|
|
133
|
+
assert_equal fake_hash, Paperclip::Interpolations.hash(attachment, :style)
|
|
118
134
|
end
|
|
119
135
|
|
|
120
136
|
should "call all expected interpolations with the given arguments" do
|
data/test/paperclip_test.rb
CHANGED
|
@@ -151,27 +151,6 @@ class PaperclipTest < Test::Unit::TestCase
|
|
|
151
151
|
should "be valid" do
|
|
152
152
|
assert @dummy.valid?
|
|
153
153
|
end
|
|
154
|
-
|
|
155
|
-
context "then has a validation added that makes it invalid" do
|
|
156
|
-
setup do
|
|
157
|
-
assert @dummy.save
|
|
158
|
-
Dummy.class_eval do
|
|
159
|
-
validates_attachment_content_type :avatar, :content_type => ["text/plain"]
|
|
160
|
-
end
|
|
161
|
-
@dummy2 = Dummy.find(@dummy.id)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
should "be invalid when reloaded" do
|
|
165
|
-
assert ! @dummy2.valid?, @dummy2.errors.inspect
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
should "be able to call #valid? twice without having duplicate errors" do
|
|
169
|
-
@dummy2.avatar.valid?
|
|
170
|
-
first_errors = @dummy2.avatar.errors
|
|
171
|
-
@dummy2.avatar.valid?
|
|
172
|
-
assert_equal first_errors, @dummy2.avatar.errors
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
154
|
end
|
|
176
155
|
|
|
177
156
|
context "a validation with an if guard clause" do
|
|
@@ -273,6 +252,21 @@ class PaperclipTest < Test::Unit::TestCase
|
|
|
273
252
|
should_validate validation, options, valid_file, invalid_file
|
|
274
253
|
end
|
|
275
254
|
|
|
255
|
+
context "with content_type validation and lambda message" do
|
|
256
|
+
context "and assigned an invalid file" do
|
|
257
|
+
setup do
|
|
258
|
+
Dummy.send(:"validates_attachment_content_type", :avatar, :content_type => %r{image/.*}, :message => lambda {'lambda content type message'})
|
|
259
|
+
@dummy = Dummy.new
|
|
260
|
+
@dummy.avatar &&= File.open(File.join(FIXTURES_DIR, "text.txt"), "rb")
|
|
261
|
+
@dummy.valid?
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
should "have a content type error message" do
|
|
265
|
+
assert [@dummy.errors[:avatar_content_type]].flatten.any?{|error| error =~ %r/lambda content type message/ }
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
276
270
|
context "with size validation and less_than 10240 option" do
|
|
277
271
|
context "and assigned an invalid file" do
|
|
278
272
|
setup do
|
|
@@ -288,5 +282,20 @@ class PaperclipTest < Test::Unit::TestCase
|
|
|
288
282
|
end
|
|
289
283
|
end
|
|
290
284
|
|
|
285
|
+
context "with size validation and less_than 10240 option with lambda message" do
|
|
286
|
+
context "and assigned an invalid file" do
|
|
287
|
+
setup do
|
|
288
|
+
Dummy.send(:"validates_attachment_size", :avatar, :less_than => 10240, :message => lambda {'lambda between 0 and 10240 bytes'})
|
|
289
|
+
@dummy = Dummy.new
|
|
290
|
+
@dummy.avatar &&= File.open(File.join(FIXTURES_DIR, "12k.png"), "rb")
|
|
291
|
+
@dummy.valid?
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
should "have a file size min/max error message" do
|
|
295
|
+
assert [@dummy.errors[:avatar_file_size]].flatten.any?{|error| error =~ %r/lambda between 0 and 10240 bytes/ }
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
291
300
|
end
|
|
292
301
|
end
|
data/test/storage_test.rb
CHANGED
|
@@ -122,8 +122,9 @@ class StorageTest < Test::Unit::TestCase
|
|
|
122
122
|
setup do
|
|
123
123
|
container = mock
|
|
124
124
|
container.stubs(:make_public).returns(true)
|
|
125
|
-
container.stubs(:public_url).returns('http://c0010181.cdn.cloudfiles.rackspacecloud.com/avatars/stringio.txt')
|
|
126
|
-
container.stubs(:cdn_url).returns('http://
|
|
125
|
+
container.stubs(:public_url).returns('http://c186397.r97.cf1.rackcdn.com/c0010181.cdn.cloudfiles.rackspacecloud.com/avatars/stringio.txt')
|
|
126
|
+
container.stubs(:cdn_url).returns('http://c186397.r97.cf1.rackcdn.com')
|
|
127
|
+
container.stubs(:cdn_ssl_url).returns('https://c186397.ssl.cf1.rackcdn.com')
|
|
127
128
|
connection = mock
|
|
128
129
|
connection.stubs(:create_container).returns(container)
|
|
129
130
|
CloudFiles::Connection.expects(:new).returns(connection)
|
|
@@ -137,7 +138,7 @@ class StorageTest < Test::Unit::TestCase
|
|
|
137
138
|
end
|
|
138
139
|
|
|
139
140
|
should "return a url based on an Cloud Files path" do
|
|
140
|
-
assert_match %r{^http://
|
|
141
|
+
assert_match %r{^http://c186397.r97.cf1.rackcdn.com/avatars/stringio.txt}, @dummy.avatar.url
|
|
141
142
|
end
|
|
142
143
|
end
|
|
143
144
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: paperclip-cloudfiles
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 81
|
|
5
5
|
prerelease:
|
|
6
6
|
segments:
|
|
7
7
|
- 2
|
|
8
8
|
- 3
|
|
9
9
|
- 8
|
|
10
|
-
-
|
|
11
|
-
version: 2.3.8.
|
|
10
|
+
- 3
|
|
11
|
+
version: 2.3.8.3
|
|
12
12
|
platform: ruby
|
|
13
13
|
authors:
|
|
14
14
|
- Jon Yurek
|
|
@@ -17,7 +17,7 @@ autorequire:
|
|
|
17
17
|
bindir: bin
|
|
18
18
|
cert_chain: []
|
|
19
19
|
|
|
20
|
-
date: 2011-
|
|
20
|
+
date: 2011-03-10 00:00:00 -05:00
|
|
21
21
|
default_executable:
|
|
22
22
|
dependencies:
|
|
23
23
|
- !ruby/object:Gem::Dependency
|
|
@@ -112,12 +112,12 @@ dependencies:
|
|
|
112
112
|
requirements:
|
|
113
113
|
- - ">="
|
|
114
114
|
- !ruby/object:Gem::Version
|
|
115
|
-
hash:
|
|
115
|
+
hash: 25
|
|
116
116
|
segments:
|
|
117
117
|
- 1
|
|
118
118
|
- 4
|
|
119
|
-
-
|
|
120
|
-
version: 1.4.
|
|
119
|
+
- 15
|
|
120
|
+
version: 1.4.15
|
|
121
121
|
name: cloudfiles
|
|
122
122
|
version_requirements: *id007
|
|
123
123
|
- !ruby/object:Gem::Dependency
|
|
@@ -186,6 +186,7 @@ files:
|
|
|
186
186
|
- test/fixtures/s3.yml
|
|
187
187
|
- test/fixtures/text.txt
|
|
188
188
|
- test/fixtures/twopage.pdf
|
|
189
|
+
- test/fixtures/uppercase.PNG
|
|
189
190
|
- test/geometry_test.rb
|
|
190
191
|
- test/helper.rb
|
|
191
192
|
- test/integration_test.rb
|