paperclip 4.2.2 → 5.2.1

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -0
  3. data/.hound.yml +1055 -0
  4. data/.rubocop.yml +1 -0
  5. data/.travis.yml +17 -15
  6. data/Appraisals +4 -16
  7. data/CONTRIBUTING.md +19 -8
  8. data/Gemfile +5 -9
  9. data/LICENSE +1 -1
  10. data/NEWS +148 -31
  11. data/README.md +327 -191
  12. data/RELEASING.md +17 -0
  13. data/Rakefile +2 -2
  14. data/UPGRADING +12 -9
  15. data/features/basic_integration.feature +10 -6
  16. data/features/migration.feature +0 -24
  17. data/features/step_definitions/attachment_steps.rb +33 -27
  18. data/features/step_definitions/html_steps.rb +2 -2
  19. data/features/step_definitions/rails_steps.rb +39 -38
  20. data/features/step_definitions/s3_steps.rb +2 -2
  21. data/features/step_definitions/web_steps.rb +1 -103
  22. data/features/support/env.rb +1 -0
  23. data/features/support/file_helpers.rb +2 -2
  24. data/features/support/paths.rb +1 -1
  25. data/features/support/rails.rb +0 -24
  26. data/gemfiles/4.2.gemfile +6 -8
  27. data/gemfiles/5.0.gemfile +17 -0
  28. data/lib/paperclip/attachment.rb +32 -20
  29. data/lib/paperclip/attachment_registry.rb +3 -2
  30. data/lib/paperclip/callbacks.rb +8 -6
  31. data/lib/paperclip/content_type_detector.rb +27 -11
  32. data/lib/paperclip/errors.rb +3 -1
  33. data/lib/paperclip/file_command_content_type_detector.rb +6 -8
  34. data/lib/paperclip/geometry_parser_factory.rb +1 -1
  35. data/lib/paperclip/glue.rb +1 -1
  36. data/lib/paperclip/has_attached_file.rb +9 -2
  37. data/lib/paperclip/helpers.rb +14 -10
  38. data/lib/paperclip/interpolations/plural_cache.rb +6 -5
  39. data/lib/paperclip/interpolations.rb +19 -14
  40. data/lib/paperclip/io_adapters/abstract_adapter.rb +26 -3
  41. data/lib/paperclip/io_adapters/attachment_adapter.rb +10 -5
  42. data/lib/paperclip/io_adapters/data_uri_adapter.rb +8 -8
  43. data/lib/paperclip/io_adapters/empty_string_adapter.rb +5 -4
  44. data/lib/paperclip/io_adapters/file_adapter.rb +12 -6
  45. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +7 -7
  46. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -6
  47. data/lib/paperclip/io_adapters/nil_adapter.rb +8 -5
  48. data/lib/paperclip/io_adapters/registry.rb +6 -2
  49. data/lib/paperclip/io_adapters/stringio_adapter.rb +9 -6
  50. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +10 -6
  51. data/lib/paperclip/io_adapters/uri_adapter.rb +41 -19
  52. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
  53. data/lib/paperclip/media_type_spoof_detector.rb +2 -2
  54. data/lib/paperclip/processor.rb +5 -4
  55. data/lib/paperclip/rails_environment.rb +25 -0
  56. data/lib/paperclip/schema.rb +3 -9
  57. data/lib/paperclip/storage/filesystem.rb +13 -2
  58. data/lib/paperclip/storage/fog.rb +30 -18
  59. data/lib/paperclip/storage/s3.rb +92 -65
  60. data/lib/paperclip/thumbnail.rb +16 -7
  61. data/lib/paperclip/url_generator.rb +16 -13
  62. data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
  63. data/lib/paperclip/validators.rb +1 -1
  64. data/lib/paperclip/version.rb +3 -1
  65. data/lib/paperclip.rb +25 -12
  66. data/lib/tasks/paperclip.rake +33 -3
  67. data/paperclip.gemspec +18 -15
  68. data/spec/paperclip/attachment_definitions_spec.rb +1 -1
  69. data/spec/paperclip/attachment_processing_spec.rb +2 -4
  70. data/spec/paperclip/attachment_registry_spec.rb +84 -13
  71. data/spec/paperclip/attachment_spec.rb +130 -39
  72. data/spec/paperclip/content_type_detector_spec.rb +8 -1
  73. data/spec/paperclip/file_command_content_type_detector_spec.rb +0 -1
  74. data/spec/paperclip/geometry_spec.rb +1 -1
  75. data/spec/paperclip/glue_spec.rb +44 -0
  76. data/spec/paperclip/has_attached_file_spec.rb +24 -8
  77. data/spec/paperclip/integration_spec.rb +4 -3
  78. data/spec/paperclip/interpolations_spec.rb +16 -13
  79. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +47 -23
  80. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +6 -3
  81. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +7 -1
  82. data/spec/paperclip/io_adapters/file_adapter_spec.rb +6 -3
  83. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +26 -6
  84. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +1 -1
  85. data/spec/paperclip/io_adapters/registry_spec.rb +2 -2
  86. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +5 -1
  87. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +5 -5
  88. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +77 -7
  89. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +10 -0
  90. data/spec/paperclip/media_type_spoof_detector_spec.rb +34 -11
  91. data/spec/paperclip/paperclip_spec.rb +4 -29
  92. data/spec/paperclip/plural_cache_spec.rb +17 -16
  93. data/spec/paperclip/rails_environment_spec.rb +33 -0
  94. data/spec/paperclip/storage/fog_spec.rb +58 -3
  95. data/spec/paperclip/storage/s3_live_spec.rb +20 -14
  96. data/spec/paperclip/storage/s3_spec.rb +398 -213
  97. data/spec/paperclip/tempfile_factory_spec.rb +4 -0
  98. data/spec/paperclip/tempfile_spec.rb +35 -0
  99. data/spec/paperclip/thumbnail_spec.rb +51 -32
  100. data/spec/paperclip/url_generator_spec.rb +55 -44
  101. data/spec/paperclip/validators/attachment_size_validator_spec.rb +26 -20
  102. data/spec/paperclip/validators_spec.rb +5 -5
  103. data/spec/spec_helper.rb +8 -1
  104. data/spec/support/assertions.rb +12 -1
  105. data/spec/support/conditional_filter_helper.rb +5 -0
  106. data/spec/support/fake_model.rb +4 -0
  107. data/spec/support/fixtures/empty.xlsx +0 -0
  108. data/spec/support/matchers/have_column.rb +11 -2
  109. data/spec/support/mock_attachment.rb +2 -0
  110. data/spec/support/mock_url_generator_builder.rb +2 -2
  111. data/spec/support/model_reconstruction.rb +9 -1
  112. data/spec/support/reporting.rb +11 -0
  113. metadata +109 -162
  114. data/RUNNING_TESTS.md +0 -4
  115. data/cucumber/paperclip_steps.rb +0 -6
  116. data/gemfiles/3.2.gemfile +0 -19
  117. data/gemfiles/4.0.gemfile +0 -19
  118. data/gemfiles/4.1.gemfile +0 -19
  119. data/lib/paperclip/locales/de.yml +0 -18
  120. data/lib/paperclip/locales/es.yml +0 -18
  121. data/lib/paperclip/locales/ja.yml +0 -18
  122. data/lib/paperclip/locales/pt-BR.yml +0 -18
  123. data/lib/paperclip/locales/zh-CN.yml +0 -18
  124. data/lib/paperclip/locales/zh-HK.yml +0 -18
  125. data/lib/paperclip/locales/zh-TW.yml +0 -18
  126. data/spec/support/mock_model.rb +0 -2
  127. data/spec/support/rails_helpers.rb +0 -7
@@ -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,15 +1,15 @@
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)
7
- super(URI(target))
11
+ def initialize(target, options = {})
12
+ super(URI(URI.escape(target)), options)
8
13
  end
9
-
10
14
  end
11
15
  end
12
-
13
- Paperclip.io_adapters.register Paperclip::HttpUrlProxyAdapter do |target|
14
- String === target && target =~ Paperclip::HttpUrlProxyAdapter::REGEXP
15
- 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,35 +1,61 @@
1
- require 'open-uri'
1
+ require "open-uri"
2
2
 
3
3
  module Paperclip
4
4
  class UriAdapter < AbstractAdapter
5
- def initialize(target)
6
- @target = target
5
+ attr_writer :content_type
6
+
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
7
15
  @content = download_content
8
16
  cache_current_values
9
17
  @tempfile = copy_to_tempfile(@content)
10
18
  end
11
19
 
12
- attr_writer :content_type
13
-
14
20
  private
15
21
 
16
- def download_content
17
- open(@target)
22
+ def cache_current_values
23
+ self.content_type = content_type_from_content || "text/html"
24
+
25
+ self.original_filename = filename_from_content_disposition ||
26
+ filename_from_path || default_filename
27
+ @size = @content.size
18
28
  end
19
29
 
20
- def cache_current_values
21
- @original_filename = @target.path.split("/").last
22
- @original_filename ||= "index.html"
23
- self.original_filename = @original_filename.strip
30
+ def content_type_from_content
31
+ if @content.respond_to?(:content_type)
32
+ @content.content_type
33
+ end
34
+ end
35
+
36
+ def filename_from_content_disposition
37
+ if @content.meta.key?("content-disposition")
38
+ matches = @content.meta["content-disposition"].match(/filename="([^"]*)"/)
39
+ matches[1] if matches
40
+ end
41
+ end
24
42
 
25
- @content_type = @content.content_type if @content.respond_to?(:content_type)
26
- @content_type ||= "text/html"
43
+ def filename_from_path
44
+ @target.path.split("/").last
45
+ end
27
46
 
28
- @size = @content.size
47
+ def default_filename
48
+ "index.html"
49
+ end
50
+
51
+ def download_content
52
+ options = { read_timeout: Paperclip.options[:read_timeout] }.compact
53
+
54
+ open(@target, **options)
29
55
  end
30
56
 
31
57
  def copy_to_tempfile(src)
32
- while data = src.read(16*1024)
58
+ while data = src.read(16 * 1024)
33
59
  destination.write(data)
34
60
  end
35
61
  src.close
@@ -38,7 +64,3 @@ module Paperclip
38
64
  end
39
65
  end
40
66
  end
41
-
42
- Paperclip.io_adapters.register Paperclip::UriAdapter do |target|
43
- target.kind_of?(URI)
44
- 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."
@@ -12,7 +12,7 @@ module Paperclip
12
12
 
13
13
  def spoofed?
14
14
  if has_name? && has_extension? && media_type_mismatch? && mapping_override_mismatch?
15
- Paperclip.log("Content Type Spoof: Filename #{File.basename(@name)} (#{supplied_content_type} from Headers, #{content_types_from_name} from Extension), content type discovered from file command: #{calculated_content_type}. See documentation to allow this combination.")
15
+ Paperclip.log("Content Type Spoof: Filename #{File.basename(@name)} (#{supplied_content_type} from Headers, #{content_types_from_name.map(&:to_s)} from Extension), content type discovered from file command: #{calculated_content_type}. See documentation to allow this combination.")
16
16
  true
17
17
  else
18
18
  false
@@ -42,7 +42,7 @@ module Paperclip
42
42
  end
43
43
 
44
44
  def mapping_override_mismatch?
45
- mapped_content_type != calculated_content_type
45
+ !Array(mapped_content_type).include?(calculated_content_type)
46
46
  end
47
47
 
48
48
 
@@ -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.
@@ -0,0 +1,25 @@
1
+ module Paperclip
2
+ class RailsEnvironment
3
+ def self.get
4
+ new.get
5
+ end
6
+
7
+ def get
8
+ if rails_exists? && rails_environment_exists?
9
+ Rails.env
10
+ else
11
+ nil
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def rails_exists?
18
+ Object.const_defined?(:Rails)
19
+ end
20
+
21
+ def rails_environment_exists?
22
+ Rails.respond_to?(:env)
23
+ end
24
+ end
25
+ end
@@ -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
@@ -35,12 +32,9 @@ 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] || {})
43
- remove_column(table_name, "#{attachment_name}_#{column_name}", column_type, column_options)
36
+ COLUMNS.keys.each do |column_name|
37
+ remove_column(table_name, "#{attachment_name}_#{column_name}")
44
38
  end
45
39
  end
46
40
  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
@@ -33,6 +33,10 @@ module Paperclip
33
33
  # that is the alias to the S3 domain of your bucket, e.g.
34
34
  # 'http://images.example.com'. This can also be used in
35
35
  # conjunction with Cloudfront (http://aws.amazon.com/cloudfront)
36
+ # * +fog_options+: (optional) A hash of options that are passed
37
+ # to fog when the file is created. For example, you could set
38
+ # the multipart-chunk size to 100MB with a hash:
39
+ # { :multipart_chunk_size => 104857600 }
36
40
 
37
41
  module Fog
38
42
  def self.extended base
@@ -44,7 +48,7 @@ module Paperclip
44
48
  end unless defined?(Fog)
45
49
 
46
50
  base.instance_eval do
47
- unless @options[:url].to_s.match(/\A:fog.*url\Z/)
51
+ unless @options[:url].to_s.match(/\A:fog.*url\z/)
48
52
  @options[:path] = @options[:path].gsub(/:url/, @options[:url]).gsub(/\A:rails_root\/public\/system\//, '')
49
53
  @options[:url] = ':fog_public_url'
50
54
  end
@@ -54,7 +58,7 @@ module Paperclip
54
58
  end
55
59
  end
56
60
 
57
- AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX = /\A(?:[a-z]|\d(?!\d{0,2}(?:\.\d{1,3}){3}\Z))(?:[a-z0-9]|\.(?![\.\-])|\-(?![\.])){1,61}[a-z0-9]\Z/
61
+ AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX = /\A(?:[a-z]|\d(?!\d{0,2}(?:\.\d{1,3}){3}\z))(?:[a-z0-9]|\.(?![\.\-])|\-(?![\.])){1,61}[a-z0-9]\z/
58
62
 
59
63
  def exists?(style = default_style)
60
64
  if original_filename
@@ -82,11 +86,14 @@ module Paperclip
82
86
  end
83
87
 
84
88
  def fog_public(style = default_style)
85
- if @options.has_key?(:fog_public)
86
- if @options[:fog_public].respond_to?(:has_key?) && @options[:fog_public].has_key?(style)
87
- @options[:fog_public][style]
89
+ if @options.key?(:fog_public)
90
+ value = @options[:fog_public]
91
+ if value.respond_to?(:key?) && value.key?(style)
92
+ value[style]
93
+ elsif value.respond_to?(:call)
94
+ value.call(self)
88
95
  else
89
- @options[:fog_public]
96
+ value
90
97
  end
91
98
  else
92
99
  true
@@ -98,12 +105,14 @@ module Paperclip
98
105
  log("saving #{path(style)}")
99
106
  retried = false
100
107
  begin
101
- directory.files.create(fog_file.merge(
108
+ attributes = fog_file.merge(
102
109
  :body => file,
103
110
  :key => path(style),
104
111
  :public => fog_public(style),
105
112
  :content_type => file.content_type
106
- ))
113
+ )
114
+ attributes.merge!(@options[:fog_options]) if @options[:fog_options]
115
+ directory.files.create(attributes)
107
116
  rescue Excon::Errors::NotFound
108
117
  raise if retried
109
118
  retried = true
@@ -141,8 +150,9 @@ module Paperclip
141
150
 
142
151
  def expiring_url(time = (Time.now + 3600), style_name = default_style)
143
152
  time = convert_time(time)
144
- if path(style_name) && directory.files.respond_to?(:get_http_url)
145
- expiring_url = directory.files.get_http_url(path(style_name), time)
153
+ http_url_method = "get_#{scheme}_url"
154
+ if path(style_name) && directory.files.respond_to?(http_url_method)
155
+ expiring_url = directory.files.public_send(http_url_method, path(style_name), time)
146
156
 
147
157
  if @options[:fog_host]
148
158
  expiring_url.gsub!(/#{host_name_for_directory}/, dynamic_fog_host_for_style(style_name))
@@ -156,14 +166,14 @@ module Paperclip
156
166
 
157
167
  def parse_credentials(creds)
158
168
  creds = find_credentials(creds).stringify_keys
159
- env = Object.const_defined?(:Rails) ? Rails.env : nil
160
- (creds[env] || creds).symbolize_keys
169
+ (creds[RailsEnvironment.get] || creds).symbolize_keys
161
170
  end
162
171
 
163
172
  def copy_to_local_file(style, local_dest_path)
164
173
  log("copying #{path(style)} to local file #{local_dest_path}")
165
174
  ::File.open(local_dest_path, 'wb') do |local_file|
166
175
  file = directory.files.get(path(style))
176
+ return false unless file
167
177
  local_file.write(file.body)
168
178
  end
169
179
  rescue ::Fog::Errors::Error => e
@@ -189,10 +199,10 @@ module Paperclip
189
199
  end
190
200
 
191
201
  def host_name_for_directory
192
- if @options[:fog_directory].to_s =~ Fog::AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX
193
- "#{@options[:fog_directory]}.s3.amazonaws.com"
202
+ if directory_name.to_s =~ Fog::AWS_BUCKET_SUBDOMAIN_RESTRICTON_REGEX
203
+ "#{directory_name}.s3.amazonaws.com"
194
204
  else
195
- "s3.amazonaws.com/#{@options[:fog_directory]}"
205
+ "s3.amazonaws.com/#{directory_name}"
196
206
  end
197
207
  end
198
208
 
@@ -218,13 +228,15 @@ module Paperclip
218
228
  end
219
229
 
220
230
  def directory
221
- dir = if @options[:fog_directory].respond_to?(:call)
231
+ @directory ||= connection.directories.new(key: directory_name)
232
+ end
233
+
234
+ def directory_name
235
+ if @options[:fog_directory].respond_to?(:call)
222
236
  @options[:fog_directory].call(self)
223
237
  else
224
238
  @options[:fog_directory]
225
239
  end
226
-
227
- @directory ||= connection.directories.new(:key => dir)
228
240
  end
229
241
 
230
242
  def scheme