kt-paperclip 5.4.0 → 7.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  4. data/.github/ISSUE_TEMPLATE/custom.md +10 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/.hound.yml +3 -1055
  7. data/.rubocop.yml +1061 -1
  8. data/.travis.yml +23 -4
  9. data/Appraisals +23 -0
  10. data/CONTRIBUTING.md +4 -5
  11. data/Gemfile +10 -7
  12. data/NEWS +52 -0
  13. data/README.md +58 -46
  14. data/Rakefile +29 -21
  15. data/UPGRADING +3 -3
  16. data/features/basic_integration.feature +4 -0
  17. data/features/migration.feature +10 -51
  18. data/features/step_definitions/attachment_steps.rb +23 -13
  19. data/features/step_definitions/html_steps.rb +5 -5
  20. data/features/step_definitions/rails_steps.rb +29 -9
  21. data/features/step_definitions/s3_steps.rb +3 -3
  22. data/features/step_definitions/web_steps.rb +5 -6
  23. data/features/support/env.rb +4 -4
  24. data/features/support/fakeweb.rb +3 -5
  25. data/features/support/file_helpers.rb +2 -2
  26. data/features/support/paths.rb +4 -4
  27. data/features/support/rails.rb +7 -7
  28. data/features/support/selectors.rb +1 -1
  29. data/gemfiles/4.2.gemfile +7 -4
  30. data/gemfiles/5.0.gemfile +7 -4
  31. data/gemfiles/5.1.gemfile +20 -0
  32. data/gemfiles/5.2.gemfile +20 -0
  33. data/gemfiles/6.0.gemfile +20 -0
  34. data/gemfiles/6.1.gemfile +21 -0
  35. data/gemfiles/7.0.gemfile +21 -0
  36. data/lib/generators/paperclip/paperclip_generator.rb +6 -8
  37. data/lib/paperclip/attachment.rb +103 -105
  38. data/lib/paperclip/attachment_registry.rb +2 -2
  39. data/lib/paperclip/content_type_detector.rb +10 -5
  40. data/lib/paperclip/file_command_content_type_detector.rb +1 -3
  41. data/lib/paperclip/filename_cleaner.rb +0 -1
  42. data/lib/paperclip/geometry.rb +18 -19
  43. data/lib/paperclip/geometry_detector_factory.rb +13 -16
  44. data/lib/paperclip/geometry_parser_factory.rb +5 -5
  45. data/lib/paperclip/glue.rb +3 -3
  46. data/lib/paperclip/has_attached_file.rb +5 -4
  47. data/lib/paperclip/helpers.rb +3 -3
  48. data/lib/paperclip/interpolations.rb +42 -38
  49. data/lib/paperclip/io_adapters/abstract_adapter.rb +16 -14
  50. data/lib/paperclip/io_adapters/attachment_adapter.rb +12 -6
  51. data/lib/paperclip/io_adapters/data_uri_adapter.rb +1 -1
  52. data/lib/paperclip/io_adapters/file_adapter.rb +1 -3
  53. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +1 -1
  54. data/lib/paperclip/io_adapters/identity_adapter.rb +1 -2
  55. data/lib/paperclip/io_adapters/registry.rb +1 -1
  56. data/lib/paperclip/io_adapters/stringio_adapter.rb +1 -1
  57. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +6 -8
  58. data/lib/paperclip/io_adapters/uri_adapter.rb +21 -9
  59. data/lib/paperclip/logger.rb +1 -1
  60. data/lib/paperclip/matchers/have_attached_file_matcher.rb +4 -4
  61. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +19 -18
  62. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +4 -4
  63. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +11 -10
  64. data/lib/paperclip/matchers.rb +4 -4
  65. data/lib/paperclip/media_type_spoof_detector.rb +13 -13
  66. data/lib/paperclip/missing_attachment_styles.rb +11 -6
  67. data/lib/paperclip/processor.rb +13 -6
  68. data/lib/paperclip/processor_helpers.rb +3 -1
  69. data/lib/paperclip/rails_environment.rb +1 -5
  70. data/lib/paperclip/railtie.rb +5 -5
  71. data/lib/paperclip/schema.rb +16 -12
  72. data/lib/paperclip/storage/filesystem.rb +6 -8
  73. data/lib/paperclip/storage/fog.rb +36 -32
  74. data/lib/paperclip/storage/s3.rb +68 -76
  75. data/lib/paperclip/style.rb +3 -6
  76. data/lib/paperclip/tempfile.rb +4 -5
  77. data/lib/paperclip/tempfile_factory.rb +0 -1
  78. data/lib/paperclip/thumbnail.rb +11 -11
  79. data/lib/paperclip/url_generator.rb +5 -5
  80. data/lib/paperclip/validators/attachment_content_type_validator.rb +11 -4
  81. data/lib/paperclip/validators/attachment_file_name_validator.rb +14 -12
  82. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +1 -2
  83. data/lib/paperclip/validators/attachment_presence_validator.rb +3 -5
  84. data/lib/paperclip/validators/attachment_size_validator.rb +28 -11
  85. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +3 -1
  86. data/lib/paperclip/validators.rb +16 -17
  87. data/lib/paperclip/version.rb +1 -3
  88. data/lib/paperclip.rb +49 -48
  89. data/lib/tasks/paperclip.rake +23 -24
  90. data/paperclip.gemspec +29 -33
  91. data/shoulda_macros/paperclip.rb +16 -16
  92. data/spec/paperclip/attachment_definitions_spec.rb +5 -5
  93. data/spec/paperclip/attachment_processing_spec.rb +22 -23
  94. data/spec/paperclip/attachment_registry_spec.rb +15 -15
  95. data/spec/paperclip/attachment_spec.rb +238 -196
  96. data/spec/paperclip/content_type_detector_spec.rb +18 -12
  97. data/spec/paperclip/file_command_content_type_detector_spec.rb +10 -10
  98. data/spec/paperclip/filename_cleaner_spec.rb +3 -4
  99. data/spec/paperclip/geometry_detector_spec.rb +7 -8
  100. data/spec/paperclip/geometry_parser_spec.rb +31 -31
  101. data/spec/paperclip/geometry_spec.rb +24 -24
  102. data/spec/paperclip/glue_spec.rb +3 -5
  103. data/spec/paperclip/has_attached_file_spec.rb +46 -126
  104. data/spec/paperclip/integration_spec.rb +111 -77
  105. data/spec/paperclip/interpolations_spec.rb +101 -93
  106. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +41 -13
  107. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +8 -10
  108. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +13 -14
  109. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +4 -4
  110. data/spec/paperclip/io_adapters/file_adapter_spec.rb +12 -12
  111. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +58 -37
  112. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +1 -1
  113. data/spec/paperclip/io_adapters/nil_adapter_spec.rb +2 -2
  114. data/spec/paperclip/io_adapters/registry_spec.rb +4 -4
  115. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +10 -10
  116. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +6 -6
  117. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +90 -31
  118. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +3 -3
  119. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +4 -5
  120. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +4 -4
  121. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +4 -4
  122. data/spec/paperclip/media_type_spoof_detector_spec.rb +50 -24
  123. data/spec/paperclip/meta_class_spec.rb +3 -3
  124. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +28 -24
  125. data/spec/paperclip/paperclip_spec.rb +15 -11
  126. data/spec/paperclip/plural_cache_spec.rb +8 -8
  127. data/spec/paperclip/processor_helpers_spec.rb +35 -35
  128. data/spec/paperclip/processor_spec.rb +8 -8
  129. data/spec/paperclip/rails_environment_spec.rb +7 -10
  130. data/spec/paperclip/rake_spec.rb +39 -39
  131. data/spec/paperclip/schema_spec.rb +57 -53
  132. data/spec/paperclip/storage/filesystem_spec.rb +29 -6
  133. data/spec/paperclip/storage/fog_spec.rb +122 -82
  134. data/spec/paperclip/storage/s3_live_spec.rb +22 -22
  135. data/spec/paperclip/storage/s3_spec.rb +649 -583
  136. data/spec/paperclip/style_spec.rb +67 -71
  137. data/spec/paperclip/tempfile_factory_spec.rb +5 -5
  138. data/spec/paperclip/thumbnail_spec.rb +68 -67
  139. data/spec/paperclip/url_generator_spec.rb +18 -19
  140. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +115 -27
  141. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +105 -16
  142. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +5 -5
  143. data/spec/paperclip/validators/attachment_size_validator_spec.rb +111 -21
  144. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +9 -13
  145. data/spec/paperclip/validators_spec.rb +61 -46
  146. data/spec/spec_helper.rb +21 -23
  147. data/spec/support/assertions.rb +8 -6
  148. data/spec/support/fake_model.rb +1 -2
  149. data/spec/support/fake_rails.rb +1 -1
  150. data/spec/support/fixtures/aws_s3.yml +13 -0
  151. data/spec/support/fixtures/sample.xlsm +0 -0
  152. data/spec/support/matchers/exist.rb +1 -1
  153. data/spec/support/matchers/have_column.rb +1 -1
  154. data/spec/support/mock_url_generator_builder.rb +2 -3
  155. data/spec/support/model_reconstruction.rb +16 -12
  156. data/spec/support/reporting.rb +1 -1
  157. data/spec/support/test_data.rb +2 -2
  158. metadata +58 -106
  159. data/spec/support/conditional_filter_helper.rb +0 -5
@@ -4,7 +4,7 @@ module Paperclip
4
4
  yield(self) if block_given?
5
5
  end
6
6
 
7
- def interpolates key, &block
7
+ def interpolates(key, &block)
8
8
  Paperclip::Interpolations[key] = block
9
9
  end
10
10
 
@@ -30,7 +30,7 @@ module Paperclip
30
30
  terrapin_path_array = Terrapin::CommandLine.path.try(:split, Terrapin::OS.path_separator)
31
31
  Terrapin::CommandLine.path = [terrapin_path_array, command_path].flatten.compact.uniq
32
32
  if logging? && (options[:log_command] || local_options[:log_command])
33
- local_options = local_options.merge(:logger => logger)
33
+ local_options = local_options.merge(logger: logger)
34
34
  end
35
35
  Terrapin::CommandLine.new(cmd, arguments, local_options).run(interpolation_values)
36
36
  end
@@ -44,7 +44,7 @@ module Paperclip
44
44
  end
45
45
 
46
46
  def class_for(class_name)
47
- class_name.split('::').inject(Object) do |klass, partial_class_name|
47
+ class_name.split("::").inject(Object) do |klass, partial_class_name|
48
48
  if klass.const_defined?(partial_class_name)
49
49
  klass.const_get(partial_class_name, false)
50
50
  else
@@ -5,31 +5,32 @@ module Paperclip
5
5
  # Paperclip.interpolates method.
6
6
  module Interpolations
7
7
  extend self
8
+ ID_PARTITION_LIMIT = 1_000_000_000
8
9
 
9
10
  # Hash assignment of interpolations. Included only for compatibility,
10
11
  # and is not intended for normal use.
11
- def self.[]= name, block
12
+ def self.[]=(name, block)
12
13
  define_method(name, &block)
13
14
  @interpolators_cache = nil
14
15
  end
15
16
 
16
17
  # Hash access of interpolations. Included only for compatibility,
17
18
  # and is not intended for normal use.
18
- def self.[] name
19
+ def self.[](name)
19
20
  method(name)
20
21
  end
21
22
 
22
23
  # Returns a sorted list of all interpolations.
23
24
  def self.all
24
- self.instance_methods(false).sort!
25
+ instance_methods(false).sort!
25
26
  end
26
27
 
27
28
  # Perform the actual interpolation. Takes the pattern to interpolate
28
29
  # and the arguments to pass, which are the attachment and style name.
29
30
  # You can pass a method name on your record as a symbol, which should turn
30
31
  # an interpolation pattern for Paperclip to use.
31
- def self.interpolate pattern, *args
32
- pattern = args.first.instance.send(pattern) if pattern.kind_of? Symbol
32
+ def self.interpolate(pattern, *args)
33
+ pattern = args.first.instance.send(pattern) if pattern.is_a? Symbol
33
34
  result = pattern.dup
34
35
  interpolators_cache.each do |method, token|
35
36
  result.gsub!(token) { send(method, *args) } if result.include?(token)
@@ -46,17 +47,17 @@ module Paperclip
46
47
  end
47
48
 
48
49
  # Returns the filename, the same way as ":basename.:extension" would.
49
- def filename attachment, style_name
50
- [ basename(attachment, style_name), extension(attachment, style_name) ].delete_if(&:empty?).join(".".freeze)
50
+ def filename(attachment, style_name)
51
+ [basename(attachment, style_name), extension(attachment, style_name)].delete_if(&:empty?).join(".")
51
52
  end
52
53
 
53
54
  # Returns the interpolated URL. Will raise an error if the url itself
54
55
  # contains ":url" to prevent infinite recursion. This interpolation
55
56
  # is used in the default :path to ease default specifications.
56
- RIGHT_HERE = "#{__FILE__.gsub(%r{\A\./}, "")}:#{__LINE__ + 3}"
57
- def url attachment, style_name
58
- raise Errors::InfiniteInterpolationError if caller.any?{|b| b.index(RIGHT_HERE) }
59
- attachment.url(style_name, :timestamp => false, :escape => false)
57
+ RIGHT_HERE = "#{__FILE__.gsub(%r{\A\./}, '')}:#{__LINE__ + 3}"
58
+ def url(attachment, style_name)
59
+ raise Errors::InfiniteInterpolationError if caller.any? { |b| b.index(RIGHT_HERE) }
60
+ attachment.url(style_name, timestamp: false, escape: false)
60
61
  end
61
62
 
62
63
  # Returns the timestamp as defined by the <attachment>_updated_at field
@@ -64,23 +65,23 @@ module Paperclip
64
65
  # to false. Note that a Rails.config.time_zone change will still
65
66
  # invalidate any path or URL that uses :timestamp. For a
66
67
  # time_zone-agnostic timestamp, use #updated_at.
67
- def timestamp attachment, style_name
68
+ def timestamp(attachment, _style_name)
68
69
  attachment.instance_read(:updated_at).in_time_zone(attachment.time_zone).to_s
69
70
  end
70
71
 
71
72
  # Returns an integer timestamp that is time zone-neutral, so that paths
72
73
  # remain valid even if a server's time zone changes.
73
- def updated_at attachment, style_name
74
+ def updated_at(attachment, _style_name)
74
75
  attachment.updated_at
75
76
  end
76
77
 
77
78
  # Returns the Rails.root constant.
78
- def rails_root attachment, style_name
79
+ def rails_root(_attachment, _style_name)
79
80
  Rails.root
80
81
  end
81
82
 
82
83
  # Returns the Rails.env constant.
83
- def rails_env attachment, style_name
84
+ def rails_env(_attachment, _style_name)
84
85
  Rails.env
85
86
  end
86
87
 
@@ -88,28 +89,29 @@ module Paperclip
88
89
  # e.g. "users" for the User class.
89
90
  # NOTE: The arguments need to be optional, because some tools fetch
90
91
  # all class names. Calling #class will return the expected class.
91
- def class attachment = nil, style_name = nil
92
+ def class(attachment = nil, style_name = nil)
92
93
  return super() if attachment.nil? && style_name.nil?
94
+
93
95
  plural_cache.underscore_and_pluralize_class(attachment.instance.class)
94
96
  end
95
97
 
96
98
  # Returns the basename of the file. e.g. "file" for "file.jpg"
97
- def basename attachment, style_name
98
- File.basename(attachment.original_filename, ".*".freeze)
99
+ def basename(attachment, _style_name)
100
+ File.basename(attachment.original_filename, ".*")
99
101
  end
100
102
 
101
103
  # Returns the extension of the file. e.g. "jpg" for "file.jpg"
102
104
  # If the style has a format defined, it will return the format instead
103
105
  # of the actual extension.
104
- def extension attachment, style_name
106
+ def extension(attachment, style_name)
105
107
  ((style = attachment.styles[style_name.to_s.to_sym]) && style[:format]) ||
106
- File.extname(attachment.original_filename).sub(/\A\.+/, "".freeze)
108
+ File.extname(attachment.original_filename).sub(/\A\.+/, "")
107
109
  end
108
110
 
109
111
  # Returns the dot+extension of the file. e.g. ".jpg" for "file.jpg"
110
112
  # If the style has a format defined, it will return the format instead
111
113
  # of the actual extension. If the extension is empty, no dot is added.
112
- def dotextension attachment, style_name
114
+ def dotextension(attachment, style_name)
113
115
  ext = extension(attachment, style_name)
114
116
  ext.empty? ? ext : ".#{ext}"
115
117
  end
@@ -121,13 +123,13 @@ module Paperclip
121
123
  # Each mime type generally has multiple extensions associated with it, so
122
124
  # if the extension from the original filename is one of these extensions,
123
125
  # that extension is used, otherwise, the first in the list is used.
124
- def content_type_extension attachment, style_name
126
+ def content_type_extension(attachment, style_name)
125
127
  mime_type = MIME::Types[attachment.content_type]
126
- extensions_for_mime_type = unless mime_type.empty?
127
- mime_type.first.extensions
128
- else
129
- []
130
- end
128
+ extensions_for_mime_type = if mime_type.empty?
129
+ []
130
+ else
131
+ mime_type.first.extensions
132
+ end
131
133
 
132
134
  original_extension = extension(attachment, style_name)
133
135
  style = attachment.styles[style_name.to_s.to_sym]
@@ -146,23 +148,23 @@ module Paperclip
146
148
  end
147
149
 
148
150
  # Returns the id of the instance.
149
- def id attachment, style_name
151
+ def id(attachment, _style_name)
150
152
  attachment.instance.id
151
153
  end
152
154
 
153
155
  # Returns the #to_param of the instance.
154
- def param attachment, style_name
156
+ def param(attachment, _style_name)
155
157
  attachment.instance.to_param
156
158
  end
157
159
 
158
160
  # Returns the fingerprint of the instance.
159
- def fingerprint attachment, style_name
161
+ def fingerprint(attachment, _style_name)
160
162
  attachment.fingerprint
161
163
  end
162
164
 
163
165
  # Returns a the attachment hash. See Paperclip::Attachment#hash_key for
164
166
  # more details.
165
- def hash attachment=nil, style_name=nil
167
+ def hash(attachment = nil, style_name = nil)
166
168
  if attachment && style_name
167
169
  attachment.hash_key(style_name)
168
170
  else
@@ -172,25 +174,27 @@ module Paperclip
172
174
 
173
175
  # Returns the id of the instance in a split path form. e.g. returns
174
176
  # 000/001/234 for an id of 1234.
175
- def id_partition attachment, style_name
177
+ def id_partition(attachment, _style_name)
176
178
  case id = attachment.instance.id
177
179
  when Integer
178
- ("%09d".freeze % id).scan(/\d{3}/).join("/".freeze)
180
+ if id < ID_PARTITION_LIMIT
181
+ ("%09d" % id).scan(/\d{3}/).join("/")
182
+ else
183
+ ("%012d" % id).scan(/\d{3}/).join("/")
184
+ end
179
185
  when String
180
- id.scan(/.{3}/).first(3).join("/".freeze)
181
- else
182
- nil
186
+ id.scan(/.{3}/).first(3).join("/")
183
187
  end
184
188
  end
185
189
 
186
190
  # Returns the pluralized form of the attachment name. e.g.
187
191
  # "avatars" for an attachment of :avatar
188
- def attachment attachment, style_name
192
+ def attachment(attachment, _style_name)
189
193
  plural_cache.pluralize_symbol(attachment.name)
190
194
  end
191
195
 
192
196
  # Returns the style, or the default style if nil is supplied.
193
- def style attachment, style_name
197
+ def style(attachment, style_name)
194
198
  style_name || attachment.default_style
195
199
  end
196
200
  end
@@ -1,11 +1,11 @@
1
- require 'active_support/core_ext/module/delegation'
1
+ require "active_support/core_ext/module/delegation"
2
2
 
3
3
  module Paperclip
4
4
  class AbstractAdapter
5
- OS_RESTRICTED_CHARACTERS = %r{[/:]}
5
+ OS_RESTRICTED_CHARACTERS = %r{[/:]}.freeze
6
6
 
7
- attr_reader :content_type, :original_filename, :size
8
- delegate :binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :readbyte, :rewind, :unlink, :to => :@tempfile
7
+ attr_reader :content_type, :original_filename, :size, :tempfile
8
+ delegate :binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :readbyte, :rewind, :unlink, to: :@tempfile
9
9
  alias :length :size
10
10
 
11
11
  def initialize(target, options = {})
@@ -29,11 +29,12 @@ module Paperclip
29
29
  end
30
30
 
31
31
  def inspect
32
- "#{self.class}: #{self.original_filename}"
32
+ "#{self.class}: #{original_filename}"
33
33
  end
34
34
 
35
35
  def original_filename=(new_filename)
36
36
  return unless new_filename
37
+
37
38
  @original_filename = new_filename.gsub(OS_RESTRICTED_CHARACTERS, "_")
38
39
  end
39
40
 
@@ -57,15 +58,16 @@ module Paperclip
57
58
  end
58
59
 
59
60
  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)
61
+ begin
62
+ Paperclip.log("Trying to link #{src} to #{dest}")
63
+ FileUtils.ln(src, dest, force: true) # overwrite existing
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
+ end
70
+
69
71
  @destination.close
70
72
  @destination.open.binmode
71
73
  end
@@ -9,11 +9,11 @@ module Paperclip
9
9
  def initialize(target, options = {})
10
10
  super
11
11
  @target, @style = case target
12
- when Paperclip::Attachment
13
- [target, :original]
14
- when Paperclip::Style
15
- [target.attachment, target.name]
16
- end
12
+ when Paperclip::Attachment
13
+ [target, :original]
14
+ when Paperclip::Style
15
+ [target.attachment, target.name]
16
+ end
17
17
 
18
18
  cache_current_values
19
19
  end
@@ -31,7 +31,13 @@ module Paperclip
31
31
  if source.staged?
32
32
  link_or_copy_file(source.staged_path(@style), destination.path)
33
33
  else
34
- source.copy_to_local_file(@style, destination.path)
34
+ begin
35
+ source.copy_to_local_file(@style, destination.path)
36
+ rescue Errno::EACCES
37
+ # clean up lingering tempfile if we cannot access source file
38
+ destination.close(true)
39
+ raise
40
+ end
35
41
  end
36
42
  destination
37
43
  end
@@ -6,7 +6,7 @@ module Paperclip
6
6
  end
7
7
  end
8
8
 
9
- REGEXP = /\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)/m
9
+ REGEXP = /\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)/m.freeze
10
10
 
11
11
  def initialize(target_uri, options = {})
12
12
  super(extract_target(target_uri), options)
@@ -14,9 +14,7 @@ module Paperclip
14
14
  private
15
15
 
16
16
  def cache_current_values
17
- if @target.respond_to?(:original_filename)
18
- self.original_filename = @target.original_filename
19
- end
17
+ self.original_filename = @target.original_filename if @target.respond_to?(:original_filename)
20
18
  self.original_filename ||= File.basename(@target.path)
21
19
  @tempfile = copy_to_tempfile(@target)
22
20
  @content_type = ContentTypeDetector.new(@target.path).detect
@@ -6,7 +6,7 @@ module Paperclip
6
6
  end
7
7
  end
8
8
 
9
- REGEXP = /\Ahttps?:\/\//
9
+ REGEXP = /\Ahttps?:\/\//.freeze
10
10
 
11
11
  def initialize(target, options = {})
12
12
  escaped = Paperclip::UrlGenerator.escape(target)
@@ -6,8 +6,7 @@ module Paperclip
6
6
  end
7
7
  end
8
8
 
9
- def initialize
10
- end
9
+ def initialize; end
11
10
 
12
11
  def new(target, _)
13
12
  target
@@ -24,7 +24,7 @@ module Paperclip
24
24
  end
25
25
 
26
26
  def registered?(target)
27
- @registered_handlers.any? do |tester, handler|
27
+ @registered_handlers.any? do |_tester, handler|
28
28
  handler === target
29
29
  end
30
30
  end
@@ -24,7 +24,7 @@ module Paperclip
24
24
  end
25
25
 
26
26
  def copy_to_tempfile(source)
27
- while data = source.read(16*1024)
27
+ while data = source.read(16 * 1024)
28
28
  destination.write(data)
29
29
  end
30
30
  destination.rewind
@@ -10,11 +10,11 @@ module Paperclip
10
10
  super
11
11
  cache_current_values
12
12
 
13
- if @target.respond_to?(:tempfile)
14
- @tempfile = copy_to_tempfile(@target.tempfile)
15
- else
16
- @tempfile = copy_to_tempfile(@target)
17
- end
13
+ @tempfile = if @target.respond_to?(:tempfile)
14
+ copy_to_tempfile(@target.tempfile)
15
+ else
16
+ copy_to_tempfile(@target)
17
+ end
18
18
  end
19
19
 
20
20
  class << self
@@ -35,9 +35,7 @@ module Paperclip
35
35
 
36
36
  def determine_content_type
37
37
  content_type = @target.content_type.to_s.strip
38
- if content_type_detector
39
- content_type = content_type_detector.new(@target.path).detect
40
- end
38
+ content_type = content_type_detector.new(@target.path).detect if content_type_detector
41
39
  content_type
42
40
  end
43
41
  end
@@ -28,15 +28,17 @@ module Paperclip
28
28
  end
29
29
 
30
30
  def content_type_from_content
31
- if @content.respond_to?(:content_type)
32
- @content.content_type
33
- end
31
+ @content.meta["content-type"].presence
34
32
  end
35
33
 
36
34
  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
35
+ if @content.meta.key?("content-disposition") && @content.meta["content-disposition"].match(/filename/i)
36
+ # can include both filename and filename* values according to RCF6266. filename should come first
37
+ _, filename = @content.meta["content-disposition"].split(/filename\*?\s*=\s*/i)
38
+
39
+ # filename can be enclosed in quotes or not
40
+ matches = filename.match(/"(.*)"/)
41
+ matches ? matches[1] : filename.split(";")[0]
40
42
  end
41
43
  end
42
44
 
@@ -48,10 +50,20 @@ module Paperclip
48
50
  "index.html"
49
51
  end
50
52
 
51
- def download_content
52
- options = { read_timeout: Paperclip.options[:read_timeout] }.compact
53
+ if RUBY_VERSION < '2.5'
54
+ def download_content
55
+ options = { read_timeout: Paperclip.options[:read_timeout] }.compact
53
56
 
54
- self.open(@target, options)
57
+ # rubocop:disable Security/Open
58
+ open(@target, options)
59
+ # rubocop:enable Security/Open
60
+ end
61
+ else
62
+ def download_content
63
+ options = { read_timeout: Paperclip.options[:read_timeout] }.compact
64
+
65
+ URI.open(@target, options)
66
+ end
55
67
  end
56
68
 
57
69
  def copy_to_tempfile(src)
@@ -2,7 +2,7 @@ module Paperclip
2
2
  module Logger
3
3
  # Log a paperclip-specific line. This will log to STDOUT
4
4
  # by default. Set Paperclip.options[:log] to false to turn off.
5
- def log message
5
+ def log(message)
6
6
  logger.info("[paperclip] #{message}") if logging?
7
7
  end
8
8
 
@@ -8,16 +8,16 @@ module Paperclip
8
8
  # describe User do
9
9
  # it { should have_attached_file(:avatar) }
10
10
  # end
11
- def have_attached_file name
11
+ def have_attached_file(name)
12
12
  HaveAttachedFileMatcher.new(name)
13
13
  end
14
14
 
15
15
  class HaveAttachedFileMatcher
16
- def initialize attachment_name
16
+ def initialize(attachment_name)
17
17
  @attachment_name = attachment_name
18
18
  end
19
19
 
20
- def matches? subject
20
+ def matches?(subject)
21
21
  @subject = subject
22
22
  @subject = @subject.class unless Class === @subject
23
23
  responds? && has_column?
@@ -40,7 +40,7 @@ module Paperclip
40
40
 
41
41
  def responds?
42
42
  methods = @subject.instance_methods.map(&:to_s)
43
- methods.include?("#{@attachment_name}") &&
43
+ methods.include?(@attachment_name.to_s) &&
44
44
  methods.include?("#{@attachment_name}=") &&
45
45
  methods.include?("#{@attachment_name}?")
46
46
  end
@@ -10,32 +10,32 @@ module Paperclip
10
10
  # allowing('image/png', 'image/gif').
11
11
  # rejecting('text/plain', 'text/xml') }
12
12
  # end
13
- def validate_attachment_content_type name
13
+ def validate_attachment_content_type(name)
14
14
  ValidateAttachmentContentTypeMatcher.new(name)
15
15
  end
16
16
 
17
17
  class ValidateAttachmentContentTypeMatcher
18
- def initialize attachment_name
18
+ def initialize(attachment_name)
19
19
  @attachment_name = attachment_name
20
20
  @allowed_types = []
21
21
  @rejected_types = []
22
22
  end
23
23
 
24
- def allowing *types
24
+ def allowing(*types)
25
25
  @allowed_types = types.flatten
26
26
  self
27
27
  end
28
28
 
29
- def rejecting *types
29
+ def rejecting(*types)
30
30
  @rejected_types = types.flatten
31
31
  self
32
32
  end
33
33
 
34
- def matches? subject
34
+ def matches?(subject)
35
35
  @subject = subject
36
36
  @subject = @subject.new if @subject.class == Class
37
37
  @allowed_types && @rejected_types &&
38
- allowed_types_allowed? && rejected_types_rejected?
38
+ allowed_types_allowed? && rejected_types_rejected?
39
39
  end
40
40
 
41
41
  def failure_message
@@ -54,23 +54,24 @@ module Paperclip
54
54
 
55
55
  def accepted_types_and_failures
56
56
  if @allowed_types.present?
57
- "Accept content types: #{@allowed_types.join(", ")}\n".tap do |message|
58
- if @missing_allowed_types.present?
59
- message << " #{@missing_allowed_types.join(", ")} were rejected."
60
- else
61
- message << " All were accepted successfully."
62
- end
57
+ "Accept content types: #{@allowed_types.join(', ')}\n".tap do |message|
58
+ message << if @missing_allowed_types.present?
59
+ " #{@missing_allowed_types.join(', ')} were rejected."
60
+ else
61
+ " All were accepted successfully."
62
+ end
63
63
  end
64
64
  end
65
65
  end
66
+
66
67
  def rejected_types_and_failures
67
68
  if @rejected_types.present?
68
- "Reject content types: #{@rejected_types.join(", ")}\n".tap do |message|
69
- if @missing_rejected_types.present?
70
- message << " #{@missing_rejected_types.join(", ")} were accepted."
71
- else
72
- message << " All were rejected successfully."
73
- end
69
+ "Reject content types: #{@rejected_types.join(', ')}\n".tap do |message|
70
+ message << if @missing_rejected_types.present?
71
+ " #{@missing_rejected_types.join(', ')} were accepted."
72
+ else
73
+ " All were rejected successfully."
74
+ end
74
75
  end
75
76
  end
76
77
  end
@@ -7,16 +7,16 @@ module Paperclip
7
7
  # describe User do
8
8
  # it { should validate_attachment_presence(:avatar) }
9
9
  # end
10
- def validate_attachment_presence name
10
+ def validate_attachment_presence(name)
11
11
  ValidateAttachmentPresenceMatcher.new(name)
12
12
  end
13
13
 
14
14
  class ValidateAttachmentPresenceMatcher
15
- def initialize attachment_name
15
+ def initialize(attachment_name)
16
16
  @attachment_name = attachment_name
17
17
  end
18
18
 
19
- def matches? subject
19
+ def matches?(subject)
20
20
  @subject = subject
21
21
  @subject = subject.new if subject.class == Class
22
22
  error_when_not_valid? && no_error_when_valid?
@@ -50,7 +50,7 @@ module Paperclip
50
50
  expected_message = [
51
51
  @attachment_name.to_s.titleize,
52
52
  I18n.t(:blank, scope: [:errors, :messages])
53
- ].join(' ')
53
+ ].join(" ")
54
54
  @subject.errors.full_messages.exclude?(expected_message)
55
55
  end
56
56
  end