paperclip 3.0.2 → 3.0.3

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.

Files changed (47) hide show
  1. data/CONTRIBUTING.md +34 -2
  2. data/NEWS +33 -1
  3. data/README.md +20 -6
  4. data/RUNNING_TESTS.md +4 -0
  5. data/features/basic_integration.feature +4 -2
  6. data/features/support/fakeweb.rb +7 -0
  7. data/lib/paperclip.rb +2 -1
  8. data/lib/paperclip/attachment.rb +28 -16
  9. data/lib/paperclip/glue.rb +8 -0
  10. data/lib/paperclip/helpers.rb +4 -16
  11. data/lib/paperclip/instance_methods.rb +1 -1
  12. data/lib/paperclip/io_adapters/attachment_adapter.rb +13 -4
  13. data/lib/paperclip/io_adapters/file_adapter.rb +5 -3
  14. data/lib/paperclip/io_adapters/stringio_adapter.rb +4 -2
  15. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +3 -1
  16. data/lib/paperclip/missing_attachment_styles.rb +5 -8
  17. data/lib/paperclip/railtie.rb +4 -7
  18. data/lib/paperclip/storage/fog.rb +11 -0
  19. data/lib/paperclip/storage/s3.rb +26 -0
  20. data/lib/paperclip/tempfile.rb +7 -5
  21. data/lib/paperclip/validators.rb +1 -0
  22. data/lib/paperclip/validators/attachment_content_type_validator.rb +8 -1
  23. data/lib/paperclip/version.rb +1 -1
  24. data/lib/tasks/paperclip.rake +1 -1
  25. data/paperclip.gemspec +3 -3
  26. data/test/attachment_test.rb +77 -23
  27. data/test/geometry_test.rb +3 -3
  28. data/test/helper.rb +10 -10
  29. data/test/integration_test.rb +103 -56
  30. data/test/{attachment_adapter_test.rb → io_adapters/attachment_adapter_test.rb} +4 -1
  31. data/test/io_adapters/file_adapter_test.rb +88 -0
  32. data/test/{identity_adapter_test.rb → io_adapters/identity_adapter_test.rb} +0 -0
  33. data/test/{nil_adapter_test.rb → io_adapters/nil_adapter_test.rb} +0 -0
  34. data/test/{adapter_registry_test.rb → io_adapters/registry_test.rb} +0 -0
  35. data/test/{stringio_adapter_test.rb → io_adapters/stringio_adapter_test.rb} +0 -0
  36. data/test/{uploaded_file_adapter_test.rb → io_adapters/uploaded_file_adapter_test.rb} +0 -0
  37. data/test/paperclip_missing_attachment_styles_test.rb +10 -12
  38. data/test/paperclip_test.rb +4 -2
  39. data/test/storage/filesystem_test.rb +24 -16
  40. data/test/storage/fog_test.rb +14 -5
  41. data/test/storage/s3_live_test.rb +40 -12
  42. data/test/storage/s3_test.rb +13 -9
  43. data/test/style_test.rb +1 -1
  44. data/test/thumbnail_test.rb +7 -3
  45. data/test/validators/attachment_content_type_validator_test.rb +53 -1
  46. metadata +30 -28
  47. data/test/file_adapter_test.rb +0 -43
@@ -1,7 +1,6 @@
1
1
 
2
2
  require 'set'
3
3
  module Paperclip
4
-
5
4
  class << self
6
5
  attr_accessor :classes_with_attachments
7
6
  attr_writer :registered_attachments_styles_path
@@ -12,7 +11,6 @@ module Paperclip
12
11
 
13
12
  self.classes_with_attachments = Set.new
14
13
 
15
-
16
14
  # Get list of styles saved on previous deploy (running rake paperclip:refresh:missing_styles)
17
15
  def self.get_registered_attachments_styles
18
16
  YAML.load_file(Paperclip.registered_attachments_styles_path)
@@ -49,9 +47,9 @@ module Paperclip
49
47
  current_styles[klass_sym][attachment_name.to_sym] ||= Array.new
50
48
  current_styles[klass_sym][attachment_name.to_sym] << style_name.to_sym
51
49
  current_styles[klass_sym][attachment_name.to_sym].map!(&:to_s).sort!.map!(&:to_sym).uniq!
52
- end
53
- end
54
- end
50
+ end
51
+ end
52
+ end
55
53
  end
56
54
  end
57
55
  private_class_method :current_attachments_styles
@@ -71,7 +69,7 @@ module Paperclip
71
69
  current_styles.each do |klass, attachment_definitions|
72
70
  attachment_definitions.each do |attachment_name, styles|
73
71
  registered = registered_styles[klass][attachment_name] || [] rescue []
74
- missed = styles - registered
72
+ missed = styles - registered
75
73
  if missed.present?
76
74
  klass_sym = klass.to_s.to_sym
77
75
  missing_styles[klass_sym] ||= Hash.new
@@ -80,8 +78,7 @@ module Paperclip
80
78
  missing_styles[klass_sym][attachment_name.to_sym].map!(&:to_s).sort!.map!(&:to_sym).uniq!
81
79
  end
82
80
  end
83
- end
81
+ end
84
82
  end
85
83
  end
86
-
87
84
  end
@@ -3,27 +3,24 @@ require 'paperclip/schema'
3
3
 
4
4
  module Paperclip
5
5
  require 'rails'
6
+
6
7
  class Railtie < Rails::Railtie
7
8
  initializer 'paperclip.insert_into_active_record' do
8
9
  ActiveSupport.on_load :active_record do
9
10
  Paperclip::Railtie.insert
10
11
  end
11
12
  end
12
- rake_tasks do
13
- load "tasks/paperclip.rake"
14
- end
13
+
14
+ rake_tasks { load "tasks/paperclip.rake" }
15
15
  end
16
16
 
17
17
  class Railtie
18
18
  def self.insert
19
- Paperclip.options[:logger] = Rails.logger if defined?(Rails)
19
+ Paperclip.options[:logger] = Rails.logger
20
20
 
21
21
  if defined?(ActiveRecord)
22
22
  Paperclip.options[:logger] = ActiveRecord::Base.logger
23
23
  ActiveRecord::Base.send(:include, Paperclip::Glue)
24
- ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, Paperclip::Schema)
25
- ActiveRecord::ConnectionAdapters::Table.send(:include, Paperclip::Schema)
26
- ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Paperclip::Schema)
27
24
  end
28
25
  end
29
26
  end
@@ -135,6 +135,17 @@ module Paperclip
135
135
  (creds[env] || creds).symbolize_keys
136
136
  end
137
137
 
138
+ def copy_to_local_file(style, local_dest_path)
139
+ log("copying #{path(style)} to local file #{local_dest_path}")
140
+ local_file = ::File.open(local_dest_path, 'wb')
141
+ file = directory.files.get(path(style))
142
+ local_file.write(file.body)
143
+ local_file.close
144
+ rescue Fog::Errors::Error => e
145
+ warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
146
+ false
147
+ end
148
+
138
149
  private
139
150
 
140
151
  def find_credentials(creds)
@@ -90,6 +90,21 @@ module Paperclip
90
90
  raise e
91
91
  end unless defined?(AWS::Core)
92
92
 
93
+ # Overriding AWS::Core::LogFormatter to make sure it return a UTF-8 string
94
+ if AWS::VERSION >= "1.3.9"
95
+ AWS::Core::LogFormatter.class_eval do
96
+ def summarize_hash(hash)
97
+ hash.map { |key, value| ":#{key}=>#{summarize_value(value)}".force_encoding('UTF-8') }.sort.join(',')
98
+ end
99
+ end
100
+ else
101
+ AWS::Core::ClientLogging.class_eval do
102
+ def sanitize_hash(hash)
103
+ hash.map { |key, value| "#{sanitize_value(key)}=>#{sanitize_value(value)}".force_encoding('UTF-8') }.sort.join(',')
104
+ end
105
+ end
106
+ end
107
+
93
108
  base.instance_eval do
94
109
  @s3_options = @options[:s3_options] || {}
95
110
  @s3_permissions = set_permissions(@options[:s3_permissions])
@@ -307,6 +322,17 @@ module Paperclip
307
322
  @queued_for_delete = []
308
323
  end
309
324
 
325
+ def copy_to_local_file(style, local_dest_path)
326
+ log("copying #{path(style)} to local file #{local_dest_path}")
327
+ local_file = ::File.open(local_dest_path, 'wb')
328
+ file = s3_object(style)
329
+ local_file.write(file.read)
330
+ local_file.close
331
+ rescue AWS::Errors::Base => e
332
+ warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
333
+ false
334
+ end
335
+
310
336
  def find_credentials creds
311
337
  case creds
312
338
  when File
@@ -7,14 +7,16 @@ module Paperclip
7
7
  # taken from the comments on this blog post:
8
8
  # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
9
9
  #
10
- # This is Ruby 1.8.7's implementation.
11
- def make_tmpname(basename, n)
10
+ # This is Ruby 1.9.3's implementation.
11
+ def make_tmpname(prefix_suffix, n)
12
12
  if RUBY_PLATFORM =~ /java/
13
- case basename
13
+ case prefix_suffix
14
+ when String
15
+ prefix, suffix = prefix_suffix, ''
14
16
  when Array
15
- prefix, suffix = *basename
17
+ prefix, suffix = *prefix_suffix
16
18
  else
17
- prefix, suffix = basename, ''
19
+ raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
18
20
  end
19
21
 
20
22
  t = Time.now.strftime("%y%m%d")
@@ -1,3 +1,4 @@
1
+ require 'active_model'
1
2
  require 'active_support/concern'
2
3
  require 'paperclip/validators/attachment_content_type_validator'
3
4
  require 'paperclip/validators/attachment_presence_validator'
@@ -1,12 +1,19 @@
1
1
  module Paperclip
2
2
  module Validators
3
3
  class AttachmentContentTypeValidator < ActiveModel::EachValidator
4
+ def initialize(options)
5
+ options[:allow_nil] = true unless options.has_key?(:allow_nil)
6
+ super
7
+ end
8
+
4
9
  def validate_each(record, attribute, value)
5
10
  attribute = "#{attribute}_content_type".to_sym
6
11
  value = record.send(:read_attribute_for_validation, attribute)
7
12
  allowed_types = [options[:content_type]].flatten
8
13
 
9
- if value.present? && allowed_types.none? { |type| type === value }
14
+ return if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
15
+
16
+ if allowed_types.none? { |type| type === value }
10
17
  record.errors.add(attribute, :invalid, options.merge(
11
18
  :types => allowed_types.join(', ')
12
19
  ))
@@ -1,3 +1,3 @@
1
1
  module Paperclip
2
- VERSION = "3.0.2" unless defined? Paperclip::VERSION
2
+ VERSION = "3.0.3" unless defined? Paperclip::VERSION
3
3
  end
@@ -10,7 +10,7 @@ module Paperclip
10
10
  klass = Paperclip.class_for(klass.to_s)
11
11
  name = ENV['ATTACHMENT'] || ENV['attachment']
12
12
  raise "Class #{klass.name} has no attachments specified" unless klass.respond_to?(:attachment_definitions)
13
- if !name.blank? && klass.attachment_definitions.keys.include?(name)
13
+ if !name.blank? && klass.attachment_definitions.keys.map(&:to_s).include?(name.to_s)
14
14
  [ name ]
15
15
  else
16
16
  klass.attachment_definitions.keys
@@ -32,14 +32,14 @@ Gem::Specification.new do |s|
32
32
  s.add_dependency('mime-types')
33
33
 
34
34
  s.add_development_dependency('shoulda')
35
- s.add_development_dependency('appraisal', '~> 0.4.0')
35
+ s.add_development_dependency('appraisal')
36
36
  s.add_development_dependency('mocha')
37
- s.add_development_dependency('aws-sdk', '~> 1.3.8')
37
+ s.add_development_dependency('aws-sdk')
38
38
  s.add_development_dependency('bourne')
39
39
  s.add_development_dependency('sqlite3', '~> 1.3.4')
40
40
  s.add_development_dependency('cucumber', '~> 1.1.0')
41
41
  s.add_development_dependency('aruba')
42
- s.add_development_dependency('nokogiri', '~> 1.4.7')
42
+ s.add_development_dependency('nokogiri')
43
43
  s.add_development_dependency('capybara')
44
44
  s.add_development_dependency('bundler')
45
45
  s.add_development_dependency('cocaine', '~> 0.2')
@@ -7,7 +7,7 @@ class Dummy; end
7
7
  class AttachmentTest < Test::Unit::TestCase
8
8
 
9
9
  should "process :original style first" do
10
- file = File.new(File.join(File.dirname(__FILE__), "fixtures", "50x50.png"), 'rb')
10
+ file = File.new(fixture_file("50x50.png"), 'rb')
11
11
  rebuild_class :styles => { :small => '100x>', :original => '42x42#' }
12
12
  dummy = Dummy.new
13
13
  dummy.avatar = file
@@ -19,6 +19,26 @@ class AttachmentTest < Test::Unit::TestCase
19
19
  file.close
20
20
  end
21
21
 
22
+ should "not delete styles that don't get reprocessed" do
23
+ file = File.new(fixture_file("50x50.png"), 'rb')
24
+ rebuild_class :styles => { :small => '100x>',
25
+ :large => '500x>',
26
+ :original => '42x42#' }
27
+ dummy = Dummy.new
28
+ dummy.avatar = file
29
+ dummy.save
30
+
31
+ assert File.exists?(dummy.avatar.path(:small))
32
+ assert File.exists?(dummy.avatar.path(:large))
33
+ assert File.exists?(dummy.avatar.path(:original))
34
+
35
+ dummy.avatar.reprocess!(:small)
36
+
37
+ assert File.exists?(dummy.avatar.path(:small))
38
+ assert File.exists?(dummy.avatar.path(:large))
39
+ assert File.exists?(dummy.avatar.path(:original))
40
+ end
41
+
22
42
  should "handle a boolean second argument to #url" do
23
43
  mock_url_generator_builder = MockUrlGeneratorBuilder.new
24
44
  attachment = Paperclip::Attachment.new(:name, :instance, :url_generator => mock_url_generator_builder)
@@ -182,9 +202,7 @@ class AttachmentTest < Test::Unit::TestCase
182
202
  rebuild_model :path => ":id.omg/:id-bbq/:idwhat/:id_partition.wtf"
183
203
  @dummy = Dummy.new
184
204
  @dummy.stubs(:id).returns(1024)
185
- @file = File.new(File.join(File.dirname(__FILE__),
186
- "fixtures",
187
- "5k.png"), 'rb')
205
+ @file = File.new(fixture_file("5k.png"), 'rb')
188
206
  @dummy.avatar = @file
189
207
  end
190
208
 
@@ -398,9 +416,7 @@ class AttachmentTest < Test::Unit::TestCase
398
416
  setup do
399
417
  rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
400
418
 
401
- @file = File.new(File.join(File.dirname(__FILE__),
402
- "fixtures",
403
- "5k.png"), 'rb')
419
+ @file = File.new(fixture_file("5k.png"), 'rb')
404
420
  @dummyA = Dummy.new(:other => 'a')
405
421
  @dummyA.avatar = @file
406
422
  @dummyB = Dummy.new(:other => 'b')
@@ -691,6 +707,21 @@ class AttachmentTest < Test::Unit::TestCase
691
707
  end
692
708
  end
693
709
 
710
+ context "Assigning an attachment" do
711
+ setup do
712
+ rebuild_model :styles => { :something => "100x100#" }
713
+ @file = StringIO.new(".")
714
+ @file.stubs(:original_filename).returns("5k.png\n\n")
715
+ @file.stubs(:content_type).returns(MIME::Type.new("image/png"))
716
+ @dummy = Dummy.new
717
+ @dummy.avatar = @file
718
+ end
719
+
720
+ should "make sure the content_type is a string" do
721
+ assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
722
+ end
723
+ end
724
+
694
725
  context "Attachment with strange letters" do
695
726
  setup do
696
727
  rebuild_model
@@ -733,19 +764,38 @@ class AttachmentTest < Test::Unit::TestCase
733
764
  context "with specified regexp replacement" do
734
765
  setup do
735
766
  @old_defaults = Paperclip::Attachment.default_options.dup
736
- Paperclip::Attachment.default_options.merge! :restricted_characters => /o/
737
-
738
- @file.stubs(:original_filename).returns("goood.png")
739
- @dummy = Dummy.new
740
- @dummy.avatar = @file
741
767
  end
742
768
 
743
769
  teardown do
744
770
  Paperclip::Attachment.default_options.merge! @old_defaults
745
771
  end
746
772
 
747
- should "match and convert that character" do
748
- assert_equal "g___d.png", @dummy.avatar.original_filename
773
+ context 'as another regexp' do
774
+ setup do
775
+ Paperclip::Attachment.default_options.merge! :restricted_characters => /o/
776
+
777
+ @file.stubs(:original_filename).returns("goood.png")
778
+ @dummy = Dummy.new
779
+ @dummy.avatar = @file
780
+ end
781
+
782
+ should "match and convert that character" do
783
+ assert_equal "g___d.png", @dummy.avatar.original_filename
784
+ end
785
+ end
786
+
787
+ context 'as nil' do
788
+ setup do
789
+ Paperclip::Attachment.default_options.merge! :restricted_characters => nil
790
+
791
+ @file.stubs(:original_filename).returns("goood.png")
792
+ @dummy = Dummy.new
793
+ @dummy.avatar = @file
794
+ end
795
+
796
+ should "ignore and return the original file name" do
797
+ assert_equal "goood.png", @dummy.avatar.original_filename
798
+ end
749
799
  end
750
800
  end
751
801
  end
@@ -754,14 +804,14 @@ class AttachmentTest < Test::Unit::TestCase
754
804
  setup do
755
805
  @old_defaults = Paperclip::Attachment.default_options.dup
756
806
  Paperclip::Attachment.default_options.merge!({
757
- :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
807
+ :path => ":rails_root/:attachment/:class/:style/:id/:basename.:extension"
758
808
  })
759
809
  FileUtils.rm_rf("tmp")
760
810
  rebuild_model
761
811
  @instance = Dummy.new
762
812
  @instance.stubs(:id).returns 123
763
813
 
764
- @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "uppercase.PNG"), 'rb')
814
+ @file = File.new(fixture_file("uppercase.PNG"), 'rb')
765
815
 
766
816
  styles = {:styles => { :large => ["400x400", :jpg],
767
817
  :medium => ["100x100", :jpg],
@@ -791,14 +841,14 @@ class AttachmentTest < Test::Unit::TestCase
791
841
  setup do
792
842
  @old_defaults = Paperclip::Attachment.default_options.dup
793
843
  Paperclip::Attachment.default_options.merge!({
794
- :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
844
+ :path => ":rails_root/:attachment/:class/:style/:id/:basename.:extension"
795
845
  })
796
846
  FileUtils.rm_rf("tmp")
797
847
  rebuild_model
798
848
  @instance = Dummy.new
799
849
  @instance.stubs(:id).returns 123
800
850
  @attachment = Paperclip::Attachment.new(:avatar, @instance)
801
- @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
851
+ @file = File.new(fixture_file("5k.png"), 'rb')
802
852
  end
803
853
 
804
854
  teardown do
@@ -839,12 +889,12 @@ class AttachmentTest < Test::Unit::TestCase
839
889
  end
840
890
 
841
891
  should "return the proper path when filename has a single .'s" do
842
- assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
892
+ assert_equal File.expand_path("tmp/avatars/dummies/original/#{@instance.id}/5k.png"), File.expand_path(@attachment.path)
843
893
  end
844
894
 
845
895
  should "return the proper path when filename has multiple .'s" do
846
896
  @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
847
- assert_equal File.expand_path("./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
897
+ assert_equal File.expand_path("tmp/avatars/dummies/original/#{@instance.id}/5k.old.png"), File.expand_path(@attachment.path)
848
898
  end
849
899
 
850
900
  context "when expecting three styles" do
@@ -992,7 +1042,7 @@ class AttachmentTest < Test::Unit::TestCase
992
1042
  end
993
1043
  rebuild_class
994
1044
  @dummy = Dummy.new
995
- @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
1045
+ @file = File.new(fixture_file("5k.png"), 'rb')
996
1046
  end
997
1047
 
998
1048
  teardown { @file.close }
@@ -1113,13 +1163,15 @@ class AttachmentTest < Test::Unit::TestCase
1113
1163
  setup do
1114
1164
  rebuild_model :preserve_files => true
1115
1165
  @dummy = Dummy.new
1116
- @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
1166
+ @file = File.new(fixture_file("5k.png"), 'rb')
1117
1167
  @dummy.avatar = @file
1118
1168
  @dummy.save!
1119
1169
  @attachment = @dummy.avatar
1120
1170
  @path = @attachment.path
1121
1171
  end
1122
1172
 
1173
+ teardown { @file.close }
1174
+
1123
1175
  should "not delete the files from storage when attachment is destroyed" do
1124
1176
  @attachment.destroy
1125
1177
  assert File.exists?(@path)
@@ -1135,13 +1187,15 @@ class AttachmentTest < Test::Unit::TestCase
1135
1187
  setup do
1136
1188
  rebuild_model
1137
1189
  @dummy = Dummy.new
1138
- @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
1190
+ @file = File.new(fixture_file("5k.png"), 'rb')
1139
1191
  @dummy.avatar = @file
1140
1192
  @dummy.save!
1141
1193
  @attachment = @dummy.avatar
1142
1194
  @path = @attachment.path
1143
1195
  end
1144
1196
 
1197
+ teardown { @file.close }
1198
+
1145
1199
  should "not be deleted when the model fails to destroy" do
1146
1200
  @dummy.stubs(:destroy).raises(Exception)
1147
1201
 
@@ -101,7 +101,7 @@ class GeometryTest < Test::Unit::TestCase
101
101
  end
102
102
 
103
103
  should "be generated from a file" do
104
- file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
104
+ file = fixture_file("5k.png")
105
105
  file = File.new(file, 'rb')
106
106
  assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
107
107
  assert @geo.height > 0
@@ -109,7 +109,7 @@ class GeometryTest < Test::Unit::TestCase
109
109
  end
110
110
 
111
111
  should "be generated from a file path" do
112
- file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
112
+ file = fixture_file("5k.png")
113
113
  assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
114
114
  assert @geo.height > 0
115
115
  assert @geo.width > 0
@@ -141,7 +141,7 @@ class GeometryTest < Test::Unit::TestCase
141
141
  begin
142
142
  ENV['PATH'] = ''
143
143
  assert_raises(Paperclip::Errors::CommandNotFoundError) do
144
- file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
144
+ file = fixture_file("5k.png")
145
145
  @geo = Paperclip::Geometry.from_file(file)
146
146
  end
147
147
  ensure