kt-paperclip 4.4.0 → 5.4.0

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.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -0
  3. data/.hound.yml +5 -16
  4. data/.travis.yml +15 -12
  5. data/Appraisals +4 -8
  6. data/CONTRIBUTING.md +16 -5
  7. data/Gemfile +3 -8
  8. data/LICENSE +1 -1
  9. data/NEWS +105 -31
  10. data/README.md +239 -153
  11. data/Rakefile +1 -1
  12. data/UPGRADING +12 -9
  13. data/features/basic_integration.feature +3 -2
  14. data/features/migration.feature +0 -24
  15. data/features/step_definitions/attachment_steps.rb +6 -6
  16. data/features/step_definitions/rails_steps.rb +29 -28
  17. data/features/step_definitions/s3_steps.rb +2 -2
  18. data/features/support/env.rb +1 -0
  19. data/features/support/paths.rb +1 -1
  20. data/features/support/rails.rb +0 -24
  21. data/gemfiles/4.2.gemfile +3 -5
  22. data/gemfiles/{3.2.gemfile → 5.0.gemfile} +4 -6
  23. data/lib/generators/paperclip/paperclip_generator.rb +9 -1
  24. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +1 -1
  25. data/lib/paperclip/attachment.rb +25 -14
  26. data/lib/paperclip/attachment_registry.rb +2 -1
  27. data/lib/paperclip/callbacks.rb +8 -6
  28. data/lib/paperclip/content_type_detector.rb +3 -2
  29. data/lib/paperclip/errors.rb +3 -1
  30. data/lib/paperclip/file_command_content_type_detector.rb +1 -1
  31. data/lib/paperclip/geometry_detector_factory.rb +2 -2
  32. data/lib/paperclip/glue.rb +1 -1
  33. data/lib/paperclip/has_attached_file.rb +7 -1
  34. data/lib/paperclip/helpers.rb +15 -11
  35. data/lib/paperclip/interpolations.rb +1 -1
  36. data/lib/paperclip/io_adapters/abstract_adapter.rb +29 -3
  37. data/lib/paperclip/io_adapters/attachment_adapter.rb +10 -5
  38. data/lib/paperclip/io_adapters/data_uri_adapter.rb +8 -8
  39. data/lib/paperclip/io_adapters/empty_string_adapter.rb +5 -4
  40. data/lib/paperclip/io_adapters/file_adapter.rb +12 -6
  41. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +7 -7
  42. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -6
  43. data/lib/paperclip/io_adapters/nil_adapter.rb +8 -5
  44. data/lib/paperclip/io_adapters/registry.rb +6 -2
  45. data/lib/paperclip/io_adapters/stringio_adapter.rb +9 -6
  46. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +10 -6
  47. data/lib/paperclip/io_adapters/uri_adapter.rb +17 -14
  48. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
  49. data/lib/paperclip/media_type_spoof_detector.rb +3 -2
  50. data/lib/paperclip/processor.rb +5 -4
  51. data/lib/paperclip/schema.rb +4 -10
  52. data/lib/paperclip/storage/filesystem.rb +13 -2
  53. data/lib/paperclip/storage/fog.rb +19 -13
  54. data/lib/paperclip/storage/s3.rb +87 -58
  55. data/lib/paperclip/thumbnail.rb +18 -8
  56. data/lib/paperclip/url_generator.rb +18 -14
  57. data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
  58. data/lib/paperclip/validators.rb +1 -1
  59. data/lib/paperclip/version.rb +3 -1
  60. data/lib/paperclip.rb +13 -12
  61. data/lib/tasks/paperclip.rake +18 -4
  62. data/paperclip.gemspec +13 -11
  63. data/spec/paperclip/attachment_processing_spec.rb +2 -4
  64. data/spec/paperclip/attachment_registry_spec.rb +28 -0
  65. data/spec/paperclip/attachment_spec.rb +72 -18
  66. data/spec/paperclip/content_type_detector_spec.rb +1 -1
  67. data/spec/paperclip/file_command_content_type_detector_spec.rb +15 -1
  68. data/spec/paperclip/has_attached_file_spec.rb +24 -8
  69. data/spec/paperclip/integration_spec.rb +4 -3
  70. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +76 -22
  71. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +6 -3
  72. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +7 -1
  73. data/spec/paperclip/io_adapters/file_adapter_spec.rb +2 -2
  74. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +18 -1
  75. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +1 -1
  76. data/spec/paperclip/io_adapters/registry_spec.rb +2 -2
  77. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +1 -1
  78. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +5 -5
  79. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +48 -3
  80. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +10 -0
  81. data/spec/paperclip/media_type_spoof_detector_spec.rb +15 -0
  82. data/spec/paperclip/paperclip_spec.rb +15 -45
  83. data/spec/paperclip/processor_spec.rb +4 -4
  84. data/spec/paperclip/storage/fog_spec.rb +26 -0
  85. data/spec/paperclip/storage/s3_live_spec.rb +20 -14
  86. data/spec/paperclip/storage/s3_spec.rb +357 -190
  87. data/spec/paperclip/tempfile_spec.rb +35 -0
  88. data/spec/paperclip/thumbnail_spec.rb +38 -35
  89. data/spec/paperclip/url_generator_spec.rb +53 -42
  90. data/spec/paperclip/validators/attachment_size_validator_spec.rb +26 -20
  91. data/spec/paperclip/validators_spec.rb +5 -5
  92. data/spec/spec_helper.rb +6 -2
  93. data/spec/support/assertions.rb +12 -1
  94. data/spec/support/conditional_filter_helper.rb +5 -0
  95. data/spec/support/mock_attachment.rb +2 -0
  96. data/spec/support/mock_url_generator_builder.rb +2 -2
  97. data/spec/support/model_reconstruction.rb +10 -2
  98. data/spec/support/reporting.rb +11 -0
  99. metadata +69 -75
  100. data/cucumber/paperclip_steps.rb +0 -6
  101. data/gemfiles/4.1.gemfile +0 -19
  102. data/lib/paperclip/deprecations.rb +0 -42
  103. data/lib/paperclip/locales/de.yml +0 -18
  104. data/lib/paperclip/locales/es.yml +0 -18
  105. data/lib/paperclip/locales/ja.yml +0 -18
  106. data/lib/paperclip/locales/pt-BR.yml +0 -18
  107. data/lib/paperclip/locales/zh-CN.yml +0 -18
  108. data/lib/paperclip/locales/zh-HK.yml +0 -18
  109. data/lib/paperclip/locales/zh-TW.yml +0 -18
  110. data/spec/paperclip/deprecations_spec.rb +0 -65
  111. data/spec/support/deprecations.rb +0 -9
  112. data/spec/support/rails_helpers.rb +0 -7
@@ -8,27 +8,31 @@ module Paperclip
8
8
  Paperclip::Interpolations[key] = block
9
9
  end
10
10
 
11
- # The run method takes the name of a binary to run, the arguments to that binary
12
- # and some options:
11
+ # The run method takes the name of a binary to run, the arguments
12
+ # to that binary, the values to interpolate and some local options.
13
13
  #
14
- # :command_path -> A $PATH-like variable that defines where to look for the binary
15
- # on the filesystem. Colon-separated, just like $PATH.
14
+ # :cmd -> The name of a binary to run.
16
15
  #
17
- # :expected_outcodes -> An array of integers that defines the expected exit codes
18
- # of the binary. Defaults to [0].
16
+ # :arguments -> The command line arguments to that binary.
19
17
  #
20
- # :log_command -> Log the command being run when set to true (defaults to true).
21
- # This will only log if logging in general is set to true as well.
18
+ # :interpolation_values -> Values to be interpolated into the arguments.
22
19
  #
23
- # :swallow_stderr -> Set to true if you don't care what happens on STDERR.
20
+ # :local_options -> The options to be used by Cocain::CommandLine.
21
+ # These could be: runner
22
+ # logger
23
+ # swallow_stderr
24
+ # expected_outcodes
25
+ # environment
26
+ # runner_options
24
27
  #
25
28
  def run(cmd, arguments = "", interpolation_values = {}, local_options = {})
26
29
  command_path = options[:command_path]
27
- Cocaine::CommandLine.path = [Cocaine::CommandLine.path, command_path].flatten.compact.uniq
30
+ terrapin_path_array = Terrapin::CommandLine.path.try(:split, Terrapin::OS.path_separator)
31
+ Terrapin::CommandLine.path = [terrapin_path_array, command_path].flatten.compact.uniq
28
32
  if logging? && (options[:log_command] || local_options[:log_command])
29
33
  local_options = local_options.merge(:logger => logger)
30
34
  end
31
- Cocaine::CommandLine.new(cmd, arguments, local_options).run(interpolation_values)
35
+ Terrapin::CommandLine.new(cmd, arguments, local_options).run(interpolation_values)
32
36
  end
33
37
 
34
38
  # Find all instances of the given Active Record model +klass+ with attachment +name+.
@@ -141,7 +141,7 @@ module Paperclip
141
141
  # It's possible, though unlikely, that the mime type is not in the
142
142
  # database, so just use the part after the '/' in the mime type as the
143
143
  # extension.
144
- %r{/([^/]*)\Z}.match(attachment.content_type)[1]
144
+ %r{/([^/]*)\z}.match(attachment.content_type)[1]
145
145
  end
146
146
  end
147
147
 
@@ -5,11 +5,23 @@ module Paperclip
5
5
  OS_RESTRICTED_CHARACTERS = %r{[/:]}
6
6
 
7
7
  attr_reader :content_type, :original_filename, :size
8
- delegate :binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :rewind, :unlink, :to => :@tempfile
8
+ delegate :binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :readbyte, :rewind, :unlink, :to => :@tempfile
9
9
  alias :length :size
10
10
 
11
+ def initialize(target, options = {})
12
+ @target = target
13
+ @options = options
14
+ end
15
+
11
16
  def fingerprint
12
- @fingerprint ||= Digest::MD5.file(path).to_s
17
+ @fingerprint ||= begin
18
+ digest = @options.fetch(:hash_digest).new
19
+ File.open(path, "rb") do |f|
20
+ buf = ""
21
+ digest.update(buf) while f.read(16384, buf)
22
+ end
23
+ digest.hexdigest
24
+ end
13
25
  end
14
26
 
15
27
  def read(length = nil, buffer = nil)
@@ -40,8 +52,22 @@ module Paperclip
40
52
  end
41
53
 
42
54
  def copy_to_tempfile(src)
43
- FileUtils.cp(src.path, destination.path)
55
+ link_or_copy_file(src.path, destination.path)
44
56
  destination
45
57
  end
58
+
59
+ def link_or_copy_file(src, dest)
60
+ Paperclip.log("Trying to link #{src} to #{dest}")
61
+ FileUtils.ln(src, dest, force: true) # overwrite existing
62
+ @destination.close
63
+ @destination.open.binmode
64
+ rescue Errno::EXDEV, Errno::EPERM, Errno::ENOENT, Errno::EEXIST => e
65
+ Paperclip.log(
66
+ "Link failed with #{e.message}; copying link #{src} to #{dest}"
67
+ )
68
+ FileUtils.cp(src, dest)
69
+ @destination.close
70
+ @destination.open.binmode
71
+ end
46
72
  end
47
73
  end
@@ -1,6 +1,13 @@
1
1
  module Paperclip
2
2
  class AttachmentAdapter < AbstractAdapter
3
- def initialize(target)
3
+ def self.register
4
+ Paperclip.io_adapters.register self do |target|
5
+ Paperclip::Attachment === target || Paperclip::Style === target
6
+ end
7
+ end
8
+
9
+ def initialize(target, options = {})
10
+ super
4
11
  @target, @style = case target
5
12
  when Paperclip::Attachment
6
13
  [target, :original]
@@ -22,7 +29,7 @@ module Paperclip
22
29
 
23
30
  def copy_to_tempfile(source)
24
31
  if source.staged?
25
- FileUtils.cp(source.staged_path(@style), destination.path)
32
+ link_or_copy_file(source.staged_path(@style), destination.path)
26
33
  else
27
34
  source.copy_to_local_file(@style, destination.path)
28
35
  end
@@ -31,6 +38,4 @@ module Paperclip
31
38
  end
32
39
  end
33
40
 
34
- Paperclip.io_adapters.register Paperclip::AttachmentAdapter do |target|
35
- Paperclip::Attachment === target || Paperclip::Style === target
36
- end
41
+ Paperclip::AttachmentAdapter.register
@@ -1,22 +1,22 @@
1
1
  module Paperclip
2
2
  class DataUriAdapter < StringioAdapter
3
+ def self.register
4
+ Paperclip.io_adapters.register self do |target|
5
+ String === target && target =~ REGEXP
6
+ end
7
+ end
3
8
 
4
9
  REGEXP = /\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)/m
5
10
 
6
- def initialize(target_uri)
7
- super(extract_target(target_uri))
11
+ def initialize(target_uri, options = {})
12
+ super(extract_target(target_uri), options)
8
13
  end
9
14
 
10
15
  private
11
16
 
12
17
  def extract_target(uri)
13
18
  data_uri_parts = uri.match(REGEXP) || []
14
- StringIO.new(Base64.decode64(data_uri_parts[2] || ''))
19
+ StringIO.new(Base64.decode64(data_uri_parts[2] || ""))
15
20
  end
16
-
17
21
  end
18
22
  end
19
-
20
- Paperclip.io_adapters.register Paperclip::DataUriAdapter do |target|
21
- String === target && target =~ Paperclip::DataUriAdapter::REGEXP
22
- end
@@ -1,6 +1,9 @@
1
1
  module Paperclip
2
2
  class EmptyStringAdapter < AbstractAdapter
3
- def initialize(target)
3
+ def self.register
4
+ Paperclip.io_adapters.register self do |target|
5
+ target.is_a?(String) && target.empty?
6
+ end
4
7
  end
5
8
 
6
9
  def nil?
@@ -13,6 +16,4 @@ module Paperclip
13
16
  end
14
17
  end
15
18
 
16
- Paperclip.io_adapters.register Paperclip::EmptyStringAdapter do |target|
17
- target.is_a?(String) && target.empty?
18
- end
19
+ Paperclip::EmptyStringAdapter.register
@@ -1,14 +1,22 @@
1
1
  module Paperclip
2
2
  class FileAdapter < AbstractAdapter
3
- def initialize(target)
4
- @target = target
3
+ def self.register
4
+ Paperclip.io_adapters.register self do |target|
5
+ File === target || ::Tempfile === target
6
+ end
7
+ end
8
+
9
+ def initialize(target, options = {})
10
+ super
5
11
  cache_current_values
6
12
  end
7
13
 
8
14
  private
9
15
 
10
16
  def cache_current_values
11
- self.original_filename = @target.original_filename if @target.respond_to?(:original_filename)
17
+ if @target.respond_to?(:original_filename)
18
+ self.original_filename = @target.original_filename
19
+ end
12
20
  self.original_filename ||= File.basename(@target.path)
13
21
  @tempfile = copy_to_tempfile(@target)
14
22
  @content_type = ContentTypeDetector.new(@target.path).detect
@@ -17,6 +25,4 @@ module Paperclip
17
25
  end
18
26
  end
19
27
 
20
- Paperclip.io_adapters.register Paperclip::FileAdapter do |target|
21
- File === target || Tempfile === target
22
- end
28
+ Paperclip::FileAdapter.register
@@ -1,16 +1,16 @@
1
1
  module Paperclip
2
2
  class HttpUrlProxyAdapter < UriAdapter
3
+ def self.register
4
+ Paperclip.io_adapters.register self do |target|
5
+ String === target && target =~ REGEXP
6
+ end
7
+ end
3
8
 
4
9
  REGEXP = /\Ahttps?:\/\//
5
10
 
6
- def initialize(target)
11
+ def initialize(target, options = {})
7
12
  escaped = Paperclip::UrlGenerator.escape(target)
8
- super(URI(target == Paperclip::UrlGenerator.unescape(target) ? escaped : target))
13
+ super(URI(target == Paperclip::UrlGenerator.unescape(target) ? escaped : target), options)
9
14
  end
10
-
11
15
  end
12
16
  end
13
-
14
- Paperclip.io_adapters.register Paperclip::HttpUrlProxyAdapter do |target|
15
- String === target && target =~ Paperclip::HttpUrlProxyAdapter::REGEXP
16
- end
@@ -1,12 +1,18 @@
1
1
  module Paperclip
2
2
  class IdentityAdapter < AbstractAdapter
3
- def new(adapter)
4
- adapter
3
+ def self.register
4
+ Paperclip.io_adapters.register Paperclip::IdentityAdapter.new do |target|
5
+ Paperclip.io_adapters.registered?(target)
6
+ end
7
+ end
8
+
9
+ def initialize
5
10
  end
6
- end
7
- end
8
11
 
9
- Paperclip.io_adapters.register Paperclip::IdentityAdapter.new do |target|
10
- Paperclip.io_adapters.registered?(target)
12
+ def new(target, _)
13
+ target
14
+ end
15
+ end
11
16
  end
12
17
 
18
+ Paperclip::IdentityAdapter.register
@@ -1,8 +1,13 @@
1
1
  module Paperclip
2
2
  class NilAdapter < AbstractAdapter
3
- def initialize(target)
3
+ def self.register
4
+ Paperclip.io_adapters.register self do |target|
5
+ target.nil? || ((Paperclip::Attachment === target) && !target.present?)
6
+ end
4
7
  end
5
8
 
9
+ def initialize(_target, _options = {}); end
10
+
6
11
  def original_filename
7
12
  ""
8
13
  end
@@ -19,7 +24,7 @@ module Paperclip
19
24
  true
20
25
  end
21
26
 
22
- def read(*args)
27
+ def read(*_args)
23
28
  nil
24
29
  end
25
30
 
@@ -29,6 +34,4 @@ module Paperclip
29
34
  end
30
35
  end
31
36
 
32
- Paperclip.io_adapters.register Paperclip::NilAdapter do |target|
33
- target.nil? || ( (Paperclip::Attachment === target) && !target.present? )
34
- end
37
+ Paperclip::NilAdapter.register
@@ -12,6 +12,10 @@ module Paperclip
12
12
  @registered_handlers << [block, handler_class]
13
13
  end
14
14
 
15
+ def unregister(handler_class)
16
+ @registered_handlers.reject! { |_, klass| klass == handler_class }
17
+ end
18
+
15
19
  def handler_for(target)
16
20
  @registered_handlers.each do |tester, handler|
17
21
  return handler if tester.call(target)
@@ -25,8 +29,8 @@ module Paperclip
25
29
  end
26
30
  end
27
31
 
28
- def for(target)
29
- handler_for(target).new(target)
32
+ def for(target, options = {})
33
+ handler_for(target).new(target, options)
30
34
  end
31
35
  end
32
36
  end
@@ -1,7 +1,13 @@
1
1
  module Paperclip
2
2
  class StringioAdapter < AbstractAdapter
3
- def initialize(target)
4
- @target = target
3
+ def self.register
4
+ Paperclip.io_adapters.register self do |target|
5
+ StringIO === target
6
+ end
7
+ end
8
+
9
+ def initialize(target, options = {})
10
+ super
5
11
  cache_current_values
6
12
  end
7
13
 
@@ -24,10 +30,7 @@ module Paperclip
24
30
  destination.rewind
25
31
  destination
26
32
  end
27
-
28
33
  end
29
34
  end
30
35
 
31
- Paperclip.io_adapters.register Paperclip::StringioAdapter do |target|
32
- StringIO === target
33
- end
36
+ Paperclip::StringioAdapter.register
@@ -1,7 +1,13 @@
1
1
  module Paperclip
2
2
  class UploadedFileAdapter < AbstractAdapter
3
- def initialize(target)
4
- @target = target
3
+ def self.register
4
+ Paperclip.io_adapters.register self do |target|
5
+ target.class.name.include?("UploadedFile")
6
+ end
7
+ end
8
+
9
+ def initialize(target, options = {})
10
+ super
5
11
  cache_current_values
6
12
 
7
13
  if @target.respond_to?(:tempfile)
@@ -24,7 +30,7 @@ module Paperclip
24
30
  end
25
31
 
26
32
  def content_type_detector
27
- self.class.content_type_detector
33
+ self.class.content_type_detector || Paperclip::ContentTypeDetector
28
34
  end
29
35
 
30
36
  def determine_content_type
@@ -37,6 +43,4 @@ module Paperclip
37
43
  end
38
44
  end
39
45
 
40
- Paperclip.io_adapters.register Paperclip::UploadedFileAdapter do |target|
41
- target.class.name.include?("UploadedFile")
42
- end
46
+ Paperclip::UploadedFileAdapter.register
@@ -1,11 +1,17 @@
1
- require 'open-uri'
1
+ require "open-uri"
2
2
 
3
3
  module Paperclip
4
4
  class UriAdapter < AbstractAdapter
5
5
  attr_writer :content_type
6
6
 
7
- def initialize(target)
8
- @target = target
7
+ def self.register
8
+ Paperclip.io_adapters.register self do |target|
9
+ target.is_a?(URI)
10
+ end
11
+ end
12
+
13
+ def initialize(target, options = {})
14
+ super
9
15
  @content = download_content
10
16
  cache_current_values
11
17
  @tempfile = copy_to_tempfile(@content)
@@ -17,8 +23,7 @@ module Paperclip
17
23
  self.content_type = content_type_from_content || "text/html"
18
24
 
19
25
  self.original_filename = filename_from_content_disposition ||
20
- filename_from_path ||
21
- default_filename
26
+ filename_from_path || default_filename
22
27
  @size = @content.size
23
28
  end
24
29
 
@@ -29,9 +34,9 @@ module Paperclip
29
34
  end
30
35
 
31
36
  def filename_from_content_disposition
32
- if @content.meta.has_key?("content-disposition")
33
- @content.meta["content-disposition"].
34
- match(/filename="([^"]*)"/)[1]
37
+ if @content.meta.key?("content-disposition")
38
+ matches = @content.meta["content-disposition"].match(/filename="([^"]*)"/)
39
+ matches[1] if matches
35
40
  end
36
41
  end
37
42
 
@@ -44,11 +49,13 @@ module Paperclip
44
49
  end
45
50
 
46
51
  def download_content
47
- open(@target)
52
+ options = { read_timeout: Paperclip.options[:read_timeout] }.compact
53
+
54
+ self.open(@target, options)
48
55
  end
49
56
 
50
57
  def copy_to_tempfile(src)
51
- while data = src.read(16*1024)
58
+ while data = src.read(16 * 1024)
52
59
  destination.write(data)
53
60
  end
54
61
  src.close
@@ -57,7 +64,3 @@ module Paperclip
57
64
  end
58
65
  end
59
66
  end
60
-
61
- Paperclip.io_adapters.register Paperclip::UriAdapter do |target|
62
- target.kind_of?(URI)
63
- end
@@ -40,9 +40,9 @@ module Paperclip
40
40
 
41
41
  def failure_message
42
42
  "#{expected_attachment}\n".tap do |message|
43
- message << accepted_types_and_failures
43
+ message << accepted_types_and_failures.to_s
44
44
  message << "\n\n" if @allowed_types.present? && @rejected_types.present?
45
- message << rejected_types_and_failures
45
+ message << rejected_types_and_failures.to_s
46
46
  end
47
47
  end
48
48
 
@@ -55,7 +55,7 @@ module Paperclip
55
55
  def accepted_types_and_failures
56
56
  if @allowed_types.present?
57
57
  "Accept content types: #{@allowed_types.join(", ")}\n".tap do |message|
58
- if @missing_allowed_types.any?
58
+ if @missing_allowed_types.present?
59
59
  message << " #{@missing_allowed_types.join(", ")} were rejected."
60
60
  else
61
61
  message << " All were accepted successfully."
@@ -66,7 +66,7 @@ module Paperclip
66
66
  def rejected_types_and_failures
67
67
  if @rejected_types.present?
68
68
  "Reject content types: #{@rejected_types.join(", ")}\n".tap do |message|
69
- if @missing_rejected_types.any?
69
+ if @missing_rejected_types.present?
70
70
  message << " #{@missing_rejected_types.join(", ")} were accepted."
71
71
  else
72
72
  message << " All were rejected successfully."
@@ -72,8 +72,9 @@ module Paperclip
72
72
 
73
73
  def type_from_file_command
74
74
  begin
75
- Paperclip.run("file", "-b --mime :file", :file => @file.path).split(/[:;]\s+/).first
76
- rescue Cocaine::CommandLineError
75
+ Paperclip.run("file", "-b --mime :file", file: @file.path).
76
+ split(/[:;\s]+/).first
77
+ rescue Terrapin::CommandLineError
77
78
  ""
78
79
  end
79
80
  end
@@ -7,13 +7,14 @@ module Paperclip
7
7
  # Processors are required to be defined inside the Paperclip module and
8
8
  # are also required to be a subclass of Paperclip::Processor. There is
9
9
  # only one method you *must* implement to properly be a subclass:
10
- # #make, but #initialize may also be of use. Both methods accept 3
10
+ # #make, but #initialize may also be of use. #initialize accepts 3
11
11
  # arguments: the file that will be operated on (which is an instance of
12
12
  # File), a hash of options that were defined in has_attached_file's
13
- # style hash, and the Paperclip::Attachment itself.
13
+ # style hash, and the Paperclip::Attachment itself. These are set as
14
+ # instance variables that can be used within `#make`.
14
15
  #
15
- # All #make needs to return is an instance of File (Tempfile is
16
- # acceptable) which contains the results of the processing.
16
+ # #make must return an instance of File (Tempfile is acceptable) which
17
+ # contains the results of the processing.
17
18
  #
18
19
  # See Paperclip.run for more information about using command-line
19
20
  # utilities from within Processors.
@@ -12,10 +12,7 @@ module Paperclip
12
12
  ActiveRecord::ConnectionAdapters::Table.send :include, TableDefinition
13
13
  ActiveRecord::ConnectionAdapters::TableDefinition.send :include, TableDefinition
14
14
  ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, Statements
15
-
16
- if defined?(ActiveRecord::Migration::CommandRecorder) # Rails 3.1+
17
- ActiveRecord::Migration::CommandRecorder.send :include, CommandRecorder
18
- end
15
+ ActiveRecord::Migration::CommandRecorder.send :include, CommandRecorder
19
16
  end
20
17
 
21
18
  module Statements
@@ -27,7 +24,7 @@ module Paperclip
27
24
  attachment_names.each do |attachment_name|
28
25
  COLUMNS.each_pair do |column_name, column_type|
29
26
  column_options = options.merge(options[column_name.to_sym] || {})
30
- add_column(table_name, "#{attachment_name}_#{column_name}", column_type, column_options)
27
+ add_column(table_name, "#{attachment_name}_#{column_name}", column_type, **column_options)
31
28
  end
32
29
  end
33
30
  end
@@ -35,11 +32,8 @@ module Paperclip
35
32
  def remove_attachment(table_name, *attachment_names)
36
33
  raise ArgumentError, "Please specify attachment name in your remove_attachment call in your migration." if attachment_names.empty?
37
34
 
38
- options = attachment_names.extract_options!
39
-
40
35
  attachment_names.each do |attachment_name|
41
- COLUMNS.each_pair do |column_name, column_type|
42
- column_options = options.merge(options[column_name.to_sym] || {})
36
+ COLUMNS.keys.each do |column_name|
43
37
  remove_column(table_name, "#{attachment_name}_#{column_name}")
44
38
  end
45
39
  end
@@ -57,7 +51,7 @@ module Paperclip
57
51
  attachment_names.each do |attachment_name|
58
52
  COLUMNS.each_pair do |column_name, column_type|
59
53
  column_options = options.merge(options[column_name.to_sym] || {})
60
- column("#{attachment_name}_#{column_name}", column_type, column_options)
54
+ column("#{attachment_name}_#{column_name}", column_type, **column_options)
61
55
  end
62
56
  end
63
57
  end
@@ -37,7 +37,7 @@ module Paperclip
37
37
  @queued_for_write.each do |style_name, file|
38
38
  FileUtils.mkdir_p(File.dirname(path(style_name)))
39
39
  begin
40
- FileUtils.mv(file.path, path(style_name))
40
+ move_file(file.path, path(style_name))
41
41
  rescue SystemCallError
42
42
  File.open(path(style_name), "wb") do |new_file|
43
43
  while chunk = file.read(16 * 1024)
@@ -46,7 +46,7 @@ module Paperclip
46
46
  end
47
47
  end
48
48
  unless @options[:override_file_permissions] == false
49
- resolved_chmod = (@options[:override_file_permissions] &~ 0111) || (0666 &~ File.umask)
49
+ resolved_chmod = (@options[:override_file_permissions] & ~0111) || (0666 & ~File.umask)
50
50
  FileUtils.chmod( resolved_chmod, path(style_name) )
51
51
  end
52
52
  file.rewind
@@ -84,6 +84,17 @@ module Paperclip
84
84
  def copy_to_local_file(style, local_dest_path)
85
85
  FileUtils.cp(path(style), local_dest_path)
86
86
  end
87
+
88
+ private
89
+
90
+ def move_file(src, dest)
91
+ # Support hardlinked files
92
+ if File.identical?(src, dest)
93
+ File.unlink(src)
94
+ else
95
+ FileUtils.mv(src, dest)
96
+ end
97
+ end
87
98
  end
88
99
 
89
100
  end