paperclip 2.2.6 → 2.2.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of paperclip might be problematic. Click here for more details.

@@ -62,6 +62,9 @@ module Paperclip
62
62
  # If the file that is assigned is not valid, the processing (i.e.
63
63
  # thumbnailing, etc) will NOT be run.
64
64
  def assign uploaded_file
65
+ # This is because of changes in Rails 2.3 that cause blank fields to send nil
66
+ return nil if uploaded_file.nil?
67
+
65
68
  %w(file_name).each do |field|
66
69
  unless @instance.class.column_names.include?("#{name}_#{field}")
67
70
  raise PaperclipError.new("#{@instance.class} model does not have required column '#{name}_#{field}'")
@@ -92,7 +95,6 @@ module Paperclip
92
95
 
93
96
  @dirty = true
94
97
 
95
- solidify_style_definitions
96
98
  post_process if valid?
97
99
 
98
100
  # Reset the file size if the original file was reprocessed.
@@ -159,6 +161,23 @@ module Paperclip
159
161
  end
160
162
  end
161
163
 
164
+ # Clears out the attachment. Has the same effect as previously assigning
165
+ # nil to the attachment. Does NOT save. If you wish to clear AND save,
166
+ # use #destroy.
167
+ def clear
168
+ queue_existing_for_delete
169
+ @errors = {}
170
+ @validation_errors = nil
171
+ end
172
+
173
+ # Destroys the attachment. Has the same effect as previously assigning
174
+ # nil to the attachment *and saving*. This is permanent. If you wish to
175
+ # wipe out the existing attachment but not save, use #clear.
176
+ def destroy
177
+ clear
178
+ save
179
+ end
180
+
162
181
  # Returns the name of the file as originally assigned, and lives in the
163
182
  # <attachment>_file_name attribute of the model.
164
183
  def original_filename
@@ -274,7 +293,7 @@ module Paperclip
274
293
  end
275
294
 
276
295
  def valid_assignment? file #:nodoc:
277
- file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
296
+ file.respond_to?(:original_filename) && file.respond_to?(:content_type)
278
297
  end
279
298
 
280
299
  def validate #:nodoc:
@@ -314,9 +333,8 @@ module Paperclip
314
333
 
315
334
  def solidify_style_definitions #:nodoc:
316
335
  @styles.each do |name, args|
317
- if @styles[name][:geometry].respond_to?(:call)
318
- @styles[name][:geometry] = @styles[name][:geometry].call(instance)
319
- end
336
+ @styles[name][:geometry] = @styles[name][:geometry].call(instance) if @styles[name][:geometry].respond_to?(:call)
337
+ @styles[name][:processors] = @styles[name][:processors].call(instance) if @styles[name][:processors].respond_to?(:call)
320
338
  end
321
339
  end
322
340
 
@@ -336,6 +354,7 @@ module Paperclip
336
354
 
337
355
  def post_process #:nodoc:
338
356
  return if @queued_for_write[:original].nil?
357
+ solidify_style_definitions
339
358
  return if fire_events(:before)
340
359
  post_process_styles
341
360
  return if fire_events(:after)
@@ -346,6 +365,10 @@ module Paperclip
346
365
  return true if callback(:"#{which}_#{name}_post_process") == false
347
366
  end
348
367
 
368
+ def callback which #:nodoc:
369
+ instance.run_callbacks(which, @queued_for_write){|result, obj| result == false }
370
+ end
371
+
349
372
  def post_process_styles
350
373
  log("Post-processing #{name}")
351
374
  @styles.each do |name, args|
@@ -362,10 +385,6 @@ module Paperclip
362
385
  end
363
386
  end
364
387
 
365
- def callback which #:nodoc:
366
- instance.run_callbacks(which, @queued_for_write){|result, obj| result == false }
367
- end
368
-
369
388
  def interpolate pattern, style = default_style #:nodoc:
370
389
  interpolations = self.class.interpolations.sort{|a,b| a.first.to_s <=> b.first.to_s }
371
390
  interpolations.reverse.inject( pattern.dup ) do |result, interpolation|
@@ -108,11 +108,21 @@ module Paperclip
108
108
  # Paperclip will attempt to create it. The bucket name will not be interpolated.
109
109
  # You can define the bucket as a Proc if you want to determine it's name at runtime.
110
110
  # Paperclip will call that Proc with attachment as the only argument.
111
- # * +url+: There are two options for the S3 url. You can choose to have the bucket's name
111
+ # * +s3_host_alias+: The fully-qualified domain name (FQDN) that is the alias to the
112
+ # S3 domain of your bucket. Used with the :s3_alias_url url interpolation. See the
113
+ # link in the +url+ entry for more information about S3 domains and buckets.
114
+ # * +url+: There are three options for the S3 url. You can choose to have the bucket's name
112
115
  # placed domain-style (bucket.s3.amazonaws.com) or path-style (s3.amazonaws.com/bucket).
116
+ # Lastly, you can specify a CNAME (which requires the CNAME to be specified as
117
+ # :s3_alias_url. You can read more about CNAMEs and S3 at
118
+ # http://docs.amazonwebservices.com/AmazonS3/latest/index.html?VirtualHosting.html
113
119
  # Normally, this won't matter in the slightest and you can leave the default (which is
114
120
  # path-style, or :s3_path_url). But in some cases paths don't work and you need to use
115
121
  # the domain-style (:s3_domain_url). Anything else here will be treated like path-style.
122
+ # NOTE: If you use a CNAME for use with CloudFront, you can NOT specify https as your
123
+ # :s3_protocol; This is *not supported* by S3/CloudFront. Finally, when using the host
124
+ # alias, the :bucket parameter is ignored, as the hostname is used as the bucket name
125
+ # by S3.
116
126
  # * +path+: This is the key under the bucket in which the file will be stored. The
117
127
  # URL will be constructed from the bucket and the path. This is what you will want
118
128
  # to interpolate. Keys should be unique, like filenames, and despite the fact that
@@ -129,8 +139,12 @@ module Paperclip
129
139
  @s3_permissions = @options[:s3_permissions] || 'public-read'
130
140
  @s3_protocol = @options[:s3_protocol] || (@s3_permissions == 'public-read' ? 'http' : 'https')
131
141
  @s3_headers = @options[:s3_headers] || {}
142
+ @s3_host_alias = @options[:s3_host_alias]
132
143
  @url = ":s3_path_url" unless @url.to_s.match(/^:s3.*url$/)
133
144
  end
145
+ base.class.interpolations[:s3_alias_url] = lambda do |attachment, style|
146
+ "#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
147
+ end
134
148
  base.class.interpolations[:s3_path_url] = lambda do |attachment, style|
135
149
  "#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
136
150
  end
@@ -154,6 +168,10 @@ module Paperclip
154
168
  @bucket
155
169
  end
156
170
 
171
+ def s3_host_alias
172
+ @s3_host_alias
173
+ end
174
+
157
175
  def parse_credentials creds
158
176
  creds = find_credentials(creds).stringify_keys
159
177
  (creds[ENV['RAILS_ENV']] || creds).symbolize_keys
data/lib/paperclip.rb CHANGED
@@ -43,7 +43,7 @@ end
43
43
  # documentation for Paperclip::ClassMethods for more useful information.
44
44
  module Paperclip
45
45
 
46
- VERSION = "2.2.6"
46
+ VERSION = "2.2.7"
47
47
 
48
48
  class << self
49
49
  # Provides configurability to Paperclip. There are a number of options available, such as:
@@ -38,7 +38,7 @@ module Paperclip
38
38
  klass = self.name.gsub(/Test$/, '').constantize
39
39
  valid = [options[:valid]].flatten
40
40
  invalid = [options[:invalid]].flatten
41
- matcher = validate_attachment_presence(name).allows(valid).rejects(invalid)
41
+ matcher = validate_attachment_content_type(name).allowing(valid).rejecting(invalid)
42
42
  should matcher.description do
43
43
  assert_accepts(matcher, klass)
44
44
  end
@@ -265,7 +265,28 @@ class AttachmentTest < Test::Unit::TestCase
265
265
  end
266
266
  end
267
267
  end
268
-
268
+
269
+ context "An attachment with :processors that is a proc" do
270
+ setup do
271
+ rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] }
272
+ @attachment = Dummy.new.avatar
273
+ end
274
+
275
+ should "not run the proc immediately" do
276
+ assert_kind_of Proc, @attachment.styles[:normal][:processors]
277
+ end
278
+
279
+ context "when assigned" do
280
+ setup do
281
+ @attachment.assign(StringIO.new("."))
282
+ end
283
+
284
+ should "have the correct processors" do
285
+ assert_equal [ :test ], @attachment.styles[:normal][:processors]
286
+ end
287
+ end
288
+ end
289
+
269
290
  context "An attachment with erroring processor" do
270
291
  setup do
271
292
  rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
@@ -571,20 +592,39 @@ class AttachmentTest < Test::Unit::TestCase
571
592
  file.close
572
593
  end
573
594
 
574
- context "and deleted" do
595
+ context "and trying to delete" do
575
596
  setup do
576
597
  @existing_names = @attachment.styles.keys.collect do |style|
577
598
  @attachment.path(style)
578
599
  end
600
+ end
601
+
602
+ should "not delete the files saving in a deprecated manner" do
603
+ @attachment.expects(:instance_write).with(:file_name, nil).never
604
+ @attachment.expects(:instance_write).with(:content_type, nil).never
605
+ @attachment.expects(:instance_write).with(:file_size, nil).never
606
+ @attachment.expects(:instance_write).with(:updated_at, nil).never
607
+ @attachment.assign nil
608
+ @attachment.save
609
+ @existing_names.each{|f| assert File.exists?(f) }
610
+ end
611
+
612
+ should "delete the files when you call #clear and #save" do
579
613
  @attachment.expects(:instance_write).with(:file_name, nil)
580
614
  @attachment.expects(:instance_write).with(:content_type, nil)
581
615
  @attachment.expects(:instance_write).with(:file_size, nil)
582
616
  @attachment.expects(:instance_write).with(:updated_at, nil)
583
- @attachment.assign nil
617
+ @attachment.clear
584
618
  @attachment.save
619
+ @existing_names.each{|f| assert ! File.exists?(f) }
585
620
  end
586
621
 
587
- should "delete the files" do
622
+ should "delete the files when you call #delete" do
623
+ @attachment.expects(:instance_write).with(:file_name, nil)
624
+ @attachment.expects(:instance_write).with(:content_type, nil)
625
+ @attachment.expects(:instance_write).with(:file_size, nil)
626
+ @attachment.expects(:instance_write).with(:updated_at, nil)
627
+ @attachment.destroy
588
628
  @existing_names.each{|f| assert ! File.exists?(f) }
589
629
  end
590
630
  end
@@ -39,6 +39,7 @@ class IntegrationTest < Test::Unit::TestCase
39
39
  setup do
40
40
  Dummy.class_eval do
41
41
  has_attached_file :avatar, :styles => { :thumb => "150x25#" }
42
+ has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } }
42
43
  end
43
44
  @d2 = Dummy.find(@dummy.id)
44
45
  @d2.avatar.reprocess!
@@ -47,6 +48,7 @@ class IntegrationTest < Test::Unit::TestCase
47
48
 
48
49
  should "create its thumbnails properly" do
49
50
  assert_match /\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
51
+ assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`
50
52
  end
51
53
  end
52
54
  end
@@ -94,7 +96,7 @@ class IntegrationTest < Test::Unit::TestCase
94
96
 
95
97
  context "and deleted" do
96
98
  setup do
97
- @dummy.avatar = nil
99
+ @dummy.avatar.clear
98
100
  @dummy.save
99
101
  end
100
102
 
@@ -233,7 +235,7 @@ class IntegrationTest < Test::Unit::TestCase
233
235
  assert File.exists?(p)
234
236
  end
235
237
 
236
- @dummy.avatar = nil
238
+ @dummy.avatar.clear
237
239
  assert_nil @dummy.avatar_file_name
238
240
  assert @dummy.valid?
239
241
  assert @dummy.save
@@ -256,7 +258,7 @@ class IntegrationTest < Test::Unit::TestCase
256
258
 
257
259
  saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
258
260
 
259
- @d2.avatar = nil
261
+ @d2.avatar.clear
260
262
  assert @d2.save
261
263
 
262
264
  saved_paths.each do |p|
@@ -264,7 +266,7 @@ class IntegrationTest < Test::Unit::TestCase
264
266
  end
265
267
  end
266
268
 
267
- should "know the difference between good files, bad files, not files, and nil" do
269
+ should "know the difference between good files, bad files, and not files" do
268
270
  expected = @dummy.avatar.to_file
269
271
  @dummy.avatar = "not a file"
270
272
  assert @dummy.valid?
@@ -273,25 +275,21 @@ class IntegrationTest < Test::Unit::TestCase
273
275
 
274
276
  @dummy.avatar = @bad_file
275
277
  assert ! @dummy.valid?
276
- @dummy.avatar = nil
277
- assert @dummy.valid?, @dummy.errors.inspect
278
278
  end
279
279
 
280
- should "know the difference between good files, bad files, not files, and nil when validating" do
280
+ should "know the difference between good files, bad files, and not files when validating" do
281
281
  Dummy.validates_attachment_presence :avatar
282
282
  @d2 = Dummy.find(@dummy.id)
283
283
  @d2.avatar = @file
284
284
  assert @d2.valid?, @d2.errors.full_messages.inspect
285
285
  @d2.avatar = @bad_file
286
286
  assert ! @d2.valid?
287
- @d2.avatar = nil
288
- assert ! @d2.valid?
289
287
  end
290
288
 
291
289
  should "be able to reload without saving and not have the file disappear" do
292
290
  @dummy.avatar = @file
293
291
  assert @dummy.save
294
- @dummy.avatar = nil
292
+ @dummy.avatar.clear
295
293
  assert_nil @dummy.avatar_file_name
296
294
  @dummy.reload
297
295
  assert_equal "5k.png", @dummy.avatar_file_name
@@ -314,16 +312,6 @@ class IntegrationTest < Test::Unit::TestCase
314
312
  assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
315
313
  `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
316
314
  end
317
-
318
- should "work when assigned a nil file" do
319
- @dummy2.avatar = nil
320
- @dummy2.save
321
-
322
- @dummy.avatar = @dummy2.avatar
323
- @dummy.save
324
-
325
- assert !@dummy.avatar?
326
- end
327
315
  end
328
316
 
329
317
  end
@@ -421,7 +409,7 @@ class IntegrationTest < Test::Unit::TestCase
421
409
  assert key.exists?
422
410
  end
423
411
 
424
- @dummy.avatar = nil
412
+ @dummy.avatar.clear
425
413
  assert_nil @dummy.avatar_file_name
426
414
  assert @dummy.valid?
427
415
  assert @dummy.save
@@ -444,7 +432,7 @@ class IntegrationTest < Test::Unit::TestCase
444
432
 
445
433
  saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
446
434
 
447
- @d2.avatar = nil
435
+ @d2.avatar.clear
448
436
  assert @d2.save
449
437
 
450
438
  saved_keys.each do |key|
data/test/storage_test.rb CHANGED
@@ -37,6 +37,55 @@ class StorageTest < Test::Unit::TestCase
37
37
  end
38
38
  end
39
39
 
40
+ context "" do
41
+ setup do
42
+ rebuild_model :storage => :s3,
43
+ :s3_credentials => {},
44
+ :bucket => "bucket",
45
+ :path => ":attachment/:basename.:extension",
46
+ :url => ":s3_path_url"
47
+ @dummy = Dummy.new
48
+ @dummy.avatar = StringIO.new(".")
49
+ end
50
+
51
+ should "return a url based on an S3 path" do
52
+ assert_match %r{^http://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
53
+ end
54
+ end
55
+ context "" do
56
+ setup do
57
+ rebuild_model :storage => :s3,
58
+ :s3_credentials => {},
59
+ :bucket => "bucket",
60
+ :path => ":attachment/:basename.:extension",
61
+ :url => ":s3_domain_url"
62
+ @dummy = Dummy.new
63
+ @dummy.avatar = StringIO.new(".")
64
+ end
65
+
66
+ should "return a url based on an S3 subdomain" do
67
+ assert_match %r{^http://bucket.s3.amazonaws.com/avatars/stringio.txt}, @dummy.avatar.url
68
+ end
69
+ end
70
+ context "" do
71
+ setup do
72
+ rebuild_model :storage => :s3,
73
+ :s3_credentials => {
74
+ :production => { :bucket => "prod_bucket" },
75
+ :development => { :bucket => "dev_bucket" }
76
+ },
77
+ :s3_host_alias => "something.something.com",
78
+ :path => ":attachment/:basename.:extension",
79
+ :url => ":s3_alias_url"
80
+ @dummy = Dummy.new
81
+ @dummy.avatar = StringIO.new(".")
82
+ end
83
+
84
+ should "return a url based on the host_alias" do
85
+ assert_match %r{^http://something.something.com/avatars/stringio.txt}, @dummy.avatar.url
86
+ end
87
+ end
88
+
40
89
  context "Parsing S3 credentials with a bucket in them" do
41
90
  setup do
42
91
  rebuild_model :storage => :s3,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paperclip
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.6
4
+ version: 2.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Yurek
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-17 00:00:00 -05:00
12
+ date: 2009-03-12 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency