paperclip 2.8.0 → 3.0.2

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 (94) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +9 -7
  3. data/Appraisals +6 -12
  4. data/Gemfile +2 -0
  5. data/NEWS +24 -0
  6. data/README.md +53 -21
  7. data/Rakefile +7 -2
  8. data/UPGRADING +14 -0
  9. data/features/basic_integration.feature +8 -8
  10. data/features/rake_tasks.feature +1 -1
  11. data/features/step_definitions/attachment_steps.rb +11 -2
  12. data/features/step_definitions/rails_steps.rb +17 -79
  13. data/features/support/env.rb +3 -0
  14. data/features/support/file_helpers.rb +24 -0
  15. data/features/support/rails.rb +3 -3
  16. data/gemfiles/{rails3_1.gemfile → 3.0.gemfile} +3 -1
  17. data/gemfiles/{rails2.gemfile → 3.1.gemfile} +3 -1
  18. data/gemfiles/{rails3.gemfile → 3.2.gemfile} +3 -1
  19. data/images.rake +21 -0
  20. data/lib/generators/paperclip/paperclip_generator.rb +1 -2
  21. data/lib/paperclip.rb +48 -319
  22. data/lib/paperclip/attachment.rb +33 -81
  23. data/lib/paperclip/attachment_options.rb +0 -1
  24. data/lib/paperclip/callbacks.rb +30 -0
  25. data/lib/paperclip/errors.rb +27 -0
  26. data/lib/paperclip/geometry.rb +6 -4
  27. data/lib/paperclip/glue.rb +15 -0
  28. data/lib/paperclip/helpers.rb +71 -0
  29. data/lib/paperclip/instance_methods.rb +35 -0
  30. data/lib/paperclip/interpolations.rb +2 -2
  31. data/lib/paperclip/io_adapters/attachment_adapter.rb +62 -0
  32. data/lib/paperclip/io_adapters/file_adapter.rb +81 -0
  33. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
  34. data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
  35. data/lib/paperclip/io_adapters/registry.rb +32 -0
  36. data/lib/paperclip/io_adapters/stringio_adapter.rb +64 -0
  37. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +63 -0
  38. data/lib/paperclip/locales/en.yml +17 -0
  39. data/lib/paperclip/logger.rb +21 -0
  40. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +1 -1
  41. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +2 -2
  42. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +7 -7
  43. data/lib/paperclip/processor.rb +32 -17
  44. data/lib/paperclip/railtie.rb +10 -15
  45. data/lib/paperclip/storage/filesystem.rb +5 -14
  46. data/lib/paperclip/storage/fog.rb +2 -21
  47. data/lib/paperclip/storage/s3.rb +12 -29
  48. data/lib/paperclip/tempfile.rb +41 -0
  49. data/lib/paperclip/thumbnail.rb +2 -3
  50. data/lib/paperclip/validators.rb +45 -0
  51. data/lib/paperclip/validators/attachment_content_type_validator.rb +47 -0
  52. data/lib/paperclip/validators/attachment_presence_validator.rb +26 -0
  53. data/lib/paperclip/validators/attachment_size_validator.rb +102 -0
  54. data/lib/paperclip/version.rb +1 -1
  55. data/lib/tasks/paperclip.rake +3 -11
  56. data/paperclip.gemspec +15 -5
  57. data/test/adapter_registry_test.rb +32 -0
  58. data/test/attachment_adapter_test.rb +48 -0
  59. data/test/attachment_options_test.rb +0 -13
  60. data/test/attachment_test.rb +27 -55
  61. data/test/file_adapter_test.rb +43 -0
  62. data/test/generator_test.rb +78 -0
  63. data/test/geometry_test.rb +5 -5
  64. data/test/helper.rb +9 -11
  65. data/test/identity_adapter_test.rb +8 -0
  66. data/test/integration_test.rb +39 -94
  67. data/test/interpolations_test.rb +8 -1
  68. data/test/matchers/validate_attachment_size_matcher_test.rb +16 -2
  69. data/test/nil_adapter_test.rb +25 -0
  70. data/test/paperclip_test.rb +30 -189
  71. data/test/storage/filesystem_test.rb +0 -14
  72. data/test/storage/fog_test.rb +0 -14
  73. data/test/storage/s3_live_test.rb +22 -9
  74. data/test/storage/s3_test.rb +70 -34
  75. data/test/stringio_adapter_test.rb +42 -0
  76. data/test/style_test.rb +10 -16
  77. data/test/thumbnail_test.rb +16 -10
  78. data/test/uploaded_file_adapter_test.rb +98 -0
  79. data/test/validators/attachment_content_type_validator_test.rb +140 -0
  80. data/test/validators/attachment_presence_validator_test.rb +85 -0
  81. data/test/validators/attachment_size_validator_test.rb +207 -0
  82. data/test/validators_test.rb +25 -0
  83. metadata +152 -30
  84. data/gemfiles/rails3_2.gemfile +0 -9
  85. data/generators/paperclip/USAGE +0 -5
  86. data/generators/paperclip/paperclip_generator.rb +0 -27
  87. data/generators/paperclip/templates/paperclip_migration.rb.erb +0 -19
  88. data/init.rb +0 -4
  89. data/lib/paperclip/callback_compatibility.rb +0 -61
  90. data/lib/paperclip/iostream.rb +0 -45
  91. data/lib/paperclip/upfile.rb +0 -64
  92. data/rails/init.rb +0 -2
  93. data/test/iostream_test.rb +0 -71
  94. data/test/upfile_test.rb +0 -53
@@ -0,0 +1,81 @@
1
+ module Paperclip
2
+ class FileAdapter
3
+ def initialize(target)
4
+ @target = target
5
+ @tempfile = copy_to_tempfile(@target)
6
+ end
7
+
8
+ def original_filename
9
+ if @target.respond_to?(:original_filename)
10
+ @target.original_filename
11
+ else
12
+ File.basename(@target.path)
13
+ end
14
+ end
15
+
16
+ def content_type
17
+ types = MIME::Types.type_for(original_filename)
18
+ if types.length == 0
19
+ type_from_file_command
20
+ elsif types.length == 1
21
+ types.first.content_type
22
+ else
23
+ best_content_type_option(types)
24
+ end
25
+ end
26
+
27
+ def fingerprint
28
+ @fingerprint ||= Digest::MD5.file(path).to_s
29
+ end
30
+
31
+ def size
32
+ File.size(@tempfile)
33
+ end
34
+
35
+ def nil?
36
+ @target.nil?
37
+ end
38
+
39
+ def read(length = nil, buffer = nil)
40
+ @tempfile.read(length, buffer)
41
+ end
42
+
43
+ # We don't use this directly, but aws/sdk does.
44
+ def rewind
45
+ @tempfile.rewind
46
+ end
47
+
48
+ def eof?
49
+ @tempfile.eof?
50
+ end
51
+
52
+ def path
53
+ @tempfile.path
54
+ end
55
+
56
+ private
57
+
58
+ def copy_to_tempfile(src)
59
+ dest = Tempfile.new(original_filename)
60
+ dest.binmode
61
+ FileUtils.cp(src.path, dest.path)
62
+ dest
63
+ end
64
+
65
+ def best_content_type_option(types)
66
+ types.reject {|type| type.content_type.match(/\/x-/) }.first
67
+ end
68
+
69
+ def type_from_file_command
70
+ # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
71
+ type = (self.original_filename.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
72
+ mime_type = (Paperclip.run("file", "-b --mime :file", :file => self.path).split(/[:;]\s+/)[0] rescue "application/x-#{type}")
73
+ mime_type = "application/x-#{type}" if mime_type.match(/\(.*?\)/)
74
+ mime_type
75
+ end
76
+ end
77
+ end
78
+
79
+ Paperclip.io_adapters.register Paperclip::FileAdapter do |target|
80
+ File === target || Tempfile === target
81
+ end
@@ -0,0 +1,12 @@
1
+ module Paperclip
2
+ class IdentityAdapter
3
+ def new(adapter)
4
+ adapter
5
+ end
6
+ end
7
+ end
8
+
9
+ Paperclip.io_adapters.register Paperclip::IdentityAdapter.new do |target|
10
+ Paperclip.io_adapters.registered?(target)
11
+ end
12
+
@@ -0,0 +1,34 @@
1
+ module Paperclip
2
+ class NilAdapter
3
+ def initialize(target)
4
+ end
5
+
6
+ def original_filename
7
+ ""
8
+ end
9
+
10
+ def content_type
11
+ ""
12
+ end
13
+
14
+ def size
15
+ 0
16
+ end
17
+
18
+ def nil?
19
+ true
20
+ end
21
+
22
+ def read(*args)
23
+ nil
24
+ end
25
+
26
+ def eof?
27
+ true
28
+ end
29
+ end
30
+ end
31
+
32
+ Paperclip.io_adapters.register Paperclip::NilAdapter do |target|
33
+ target.nil?
34
+ end
@@ -0,0 +1,32 @@
1
+ module Paperclip
2
+ class AdapterRegistry
3
+ class NoHandlerError < Paperclip::Error; end
4
+
5
+ attr_reader :registered_handlers
6
+
7
+ def initialize
8
+ @registered_handlers = []
9
+ end
10
+
11
+ def register(handler_class, &block)
12
+ @registered_handlers << [block, handler_class]
13
+ end
14
+
15
+ def handler_for(target)
16
+ @registered_handlers.each do |tester, handler|
17
+ return handler if tester.call(target)
18
+ end
19
+ raise NoHandlerError.new("No handler found for #{target.inspect}")
20
+ end
21
+
22
+ def registered?(target)
23
+ @registered_handlers.any? do |tester, handler|
24
+ handler === target
25
+ end
26
+ end
27
+
28
+ def for(target)
29
+ handler_for(target).new(target)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,64 @@
1
+ module Paperclip
2
+ class StringioAdapter
3
+ def initialize(target)
4
+ @target = target
5
+ @tempfile = copy_to_tempfile(@target)
6
+ end
7
+
8
+ attr_writer :original_filename, :content_type
9
+
10
+ def original_filename
11
+ @original_filename ||= @target.original_filename if @target.respond_to?(:original_filename)
12
+ @original_filename ||= "stringio.txt"
13
+ @original_filename.strip
14
+ end
15
+
16
+ def content_type
17
+ @content_type ||= @target.content_type if @target.respond_to?(:content_type)
18
+ @content_type ||= "text/plain"
19
+ @content_type.strip
20
+ end
21
+
22
+ def size
23
+ @target.size
24
+ end
25
+
26
+ def fingerprint
27
+ Digest::MD5.hexdigest(read)
28
+ end
29
+
30
+ def read(length = nil, buffer = nil)
31
+ @tempfile.read(length, buffer)
32
+ end
33
+
34
+ # We don't use this directly, but aws/sdk does.
35
+ def rewind
36
+ @tempfile.rewind
37
+ end
38
+
39
+ def eof?
40
+ @tempfile.eof?
41
+ end
42
+
43
+ def path
44
+ @tempfile.path
45
+ end
46
+
47
+ private
48
+
49
+ def copy_to_tempfile(src)
50
+ dest = Tempfile.new(original_filename)
51
+ dest.binmode
52
+ while data = src.read(16*1024)
53
+ dest.write(data)
54
+ end
55
+ dest.rewind
56
+ dest
57
+ end
58
+
59
+ end
60
+ end
61
+
62
+ Paperclip.io_adapters.register Paperclip::StringioAdapter do |target|
63
+ StringIO === target
64
+ end
@@ -0,0 +1,63 @@
1
+ module Paperclip
2
+ class UploadedFileAdapter
3
+ def initialize(target)
4
+ @target = target
5
+
6
+ if @target.respond_to?(:tempfile)
7
+ @tempfile = copy_to_tempfile(@target.tempfile)
8
+ else
9
+ @tempfile = copy_to_tempfile(@target)
10
+ end
11
+ end
12
+
13
+ def original_filename
14
+ @target.original_filename
15
+ end
16
+
17
+ def content_type
18
+ @target.content_type
19
+ end
20
+
21
+ def fingerprint
22
+ @fingerprint ||= Digest::MD5.file(path).to_s
23
+ end
24
+
25
+ def size
26
+ File.size(path)
27
+ end
28
+
29
+ def nil?
30
+ false
31
+ end
32
+
33
+ def read(length = nil, buffer = nil)
34
+ @tempfile.read(length, buffer)
35
+ end
36
+
37
+ # We don't use this directly, but aws/sdk does.
38
+ def rewind
39
+ @tempfile.rewind
40
+ end
41
+
42
+ def eof?
43
+ @tempfile.eof?
44
+ end
45
+
46
+ def path
47
+ @tempfile.path
48
+ end
49
+
50
+ private
51
+
52
+ def copy_to_tempfile(src)
53
+ dest = Tempfile.new(original_filename)
54
+ dest.binmode
55
+ FileUtils.cp(src.path, dest.path)
56
+ dest
57
+ end
58
+ end
59
+ end
60
+
61
+ Paperclip.io_adapters.register Paperclip::UploadedFileAdapter do |target|
62
+ target.class.name.include?("UploadedFile")
63
+ end
@@ -0,0 +1,17 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ in_between: "must be in between %{min} and %{max}"
5
+
6
+ number:
7
+ human:
8
+ storage_units:
9
+ format: "%n %u"
10
+ units:
11
+ byte:
12
+ one: "Byte"
13
+ other: "Bytes"
14
+ kb: "KB"
15
+ mb: "MB"
16
+ gb: "GB"
17
+ tb: "TB"
@@ -0,0 +1,21 @@
1
+ module Paperclip
2
+ module Logger
3
+ # Log a paperclip-specific line. This will log to STDOUT
4
+ # by default. Set Paperclip.options[:log] to false to turn off.
5
+ def log message
6
+ logger.info("[paperclip] #{message}") if logging?
7
+ end
8
+
9
+ def logger #:nodoc:
10
+ @logger ||= options[:logger] || ::Logger.new(STDOUT)
11
+ end
12
+
13
+ def logger=(logger)
14
+ @logger = logger
15
+ end
16
+
17
+ def logging? #:nodoc:
18
+ options[:log]
19
+ end
20
+ end
21
+ end
@@ -61,7 +61,7 @@ module Paperclip
61
61
  protected
62
62
 
63
63
  def type_allowed?(type)
64
- file = StringIO.new(".")
64
+ file = Paperclip.io_adapters.for(StringIO.new("."))
65
65
  file.content_type = type
66
66
  @subject.attachment_for(@attachment_name).assign(file)
67
67
  @subject.valid?
@@ -39,14 +39,14 @@ module Paperclip
39
39
  def error_when_not_valid?
40
40
  @subject.send(@attachment_name).assign(nil)
41
41
  @subject.valid?
42
- not @subject.errors[:"#{@attachment_name}_file_name"].blank?
42
+ @subject.errors[:"#{@attachment_name}"].present?
43
43
  end
44
44
 
45
45
  def no_error_when_valid?
46
46
  @file = StringIO.new(".")
47
47
  @subject.send(@attachment_name).assign(@file)
48
48
  @subject.valid?
49
- @subject.errors[:"#{@attachment_name}_file_name"].blank?
49
+ @subject.errors[:"#{@attachment_name}"].blank?
50
50
  end
51
51
  end
52
52
  end
@@ -18,7 +18,6 @@ module Paperclip
18
18
  class ValidateAttachmentSizeMatcher
19
19
  def initialize attachment_name
20
20
  @attachment_name = attachment_name
21
- @low, @high = 0, (1.0/0)
22
21
  end
23
22
 
24
23
  def less_than size
@@ -67,27 +66,28 @@ module Paperclip
67
66
  override_method(file, :size){ new_size }
68
67
  override_method(file, :to_tempfile){ file }
69
68
 
69
+ @subject.send(@attachment_name).post_processing = false
70
70
  @subject.send(@attachment_name).assign(file)
71
71
  @subject.valid?
72
72
  @subject.errors[:"#{@attachment_name}_file_size"].blank?
73
+ ensure
74
+ @subject.send(@attachment_name).post_processing = true
73
75
  end
74
76
 
75
77
  def lower_than_low?
76
- not passes_validation_with_size(@low - 1)
78
+ @low.nil? || !passes_validation_with_size(@low - 1)
77
79
  end
78
80
 
79
81
  def higher_than_low?
80
- passes_validation_with_size(@low + 1)
82
+ @low.nil? || passes_validation_with_size(@low + 1)
81
83
  end
82
84
 
83
85
  def lower_than_high?
84
- return true if @high == (1.0/0)
85
- passes_validation_with_size(@high - 1)
86
+ @high.nil? || @high == Float::INFINITY || passes_validation_with_size(@high - 1)
86
87
  end
87
88
 
88
89
  def higher_than_high?
89
- return true if @high == (1.0/0)
90
- not passes_validation_with_size(@high + 1)
90
+ @high.nil? || @high == Float::INFINITY || !passes_validation_with_size(@high + 1)
91
91
  end
92
92
  end
93
93
  end
@@ -34,25 +34,40 @@ module Paperclip
34
34
  end
35
35
  end
36
36
 
37
- # Due to how ImageMagick handles its image format conversion and how Tempfile
38
- # handles its naming scheme, it is necessary to override how Tempfile makes
39
- # its names so as to allow for file extensions. Idea taken from the comments
40
- # on this blog post:
41
- # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
42
- class Tempfile < ::Tempfile
43
- # This is Ruby 1.8.7's implementation.
44
- if RUBY_VERSION <= "1.8.6" || RUBY_PLATFORM =~ /java/
45
- def make_tmpname(basename, n)
46
- case basename
47
- when Array
48
- prefix, suffix = *basename
49
- else
50
- prefix, suffix = basename, ''
51
- end
37
+ module ProcessorHelpers
38
+ def processor(name) #:nodoc:
39
+ @known_processors ||= {}
40
+ if @known_processors[name.to_s]
41
+ @known_processors[name.to_s]
42
+ else
43
+ name = name.to_s.camelize
44
+ load_processor(name) unless Paperclip.const_defined?(name)
45
+ processor = Paperclip.const_get(name)
46
+ @known_processors[name.to_s] = processor
47
+ end
48
+ end
52
49
 
53
- t = Time.now.strftime("%y%m%d")
54
- path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
50
+ def load_processor(name)
51
+ if defined?(Rails.root) && Rails.root
52
+ require File.expand_path(Rails.root.join("lib", "paperclip_processors", "#{name.underscore}.rb"))
55
53
  end
56
54
  end
55
+
56
+ def clear_processors!
57
+ @known_processors.try(:clear)
58
+ end
59
+
60
+ # You can add your own processor via the Paperclip configuration. Normally
61
+ # Paperclip will load all processors from the
62
+ # Rails.root/lib/paperclip_processors directory, but here you can add any
63
+ # existing class using this mechanism.
64
+ #
65
+ # Paperclip.configure do |c|
66
+ # c.register_processor :watermarker, WatermarkingProcessor.new
67
+ # end
68
+ def register_processor(name, processor)
69
+ @known_processors ||= {}
70
+ @known_processors[name.to_s] = processor
71
+ end
57
72
  end
58
73
  end