paperclip-cloudfiles 2.3.8.1 → 2.3.8.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|