paperclip 3.4.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (220) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +17 -0
  3. data/.github/issue_template.md +3 -0
  4. data/.gitignore +0 -6
  5. data/.hound.yml +1055 -0
  6. data/.rubocop.yml +1 -0
  7. data/.travis.yml +19 -12
  8. data/Appraisals +4 -11
  9. data/CONTRIBUTING.md +29 -13
  10. data/Gemfile +13 -4
  11. data/LICENSE +1 -3
  12. data/MIGRATING-ES.md +317 -0
  13. data/MIGRATING.md +375 -0
  14. data/NEWS +390 -71
  15. data/README.md +607 -152
  16. data/RELEASING.md +17 -0
  17. data/Rakefile +6 -8
  18. data/UPGRADING +12 -9
  19. data/features/basic_integration.feature +34 -21
  20. data/features/migration.feature +0 -24
  21. data/features/rake_tasks.feature +2 -3
  22. data/features/step_definitions/attachment_steps.rb +44 -36
  23. data/features/step_definitions/html_steps.rb +2 -2
  24. data/features/step_definitions/rails_steps.rb +125 -26
  25. data/features/step_definitions/s3_steps.rb +3 -3
  26. data/features/step_definitions/web_steps.rb +1 -103
  27. data/features/support/env.rb +3 -2
  28. data/features/support/fakeweb.rb +4 -1
  29. data/features/support/file_helpers.rb +12 -2
  30. data/features/support/fixtures/gemfile.txt +1 -1
  31. data/features/support/paths.rb +1 -1
  32. data/features/support/rails.rb +4 -11
  33. data/gemfiles/4.2.gemfile +17 -0
  34. data/gemfiles/5.0.gemfile +17 -0
  35. data/lib/generators/paperclip/paperclip_generator.rb +9 -3
  36. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +2 -2
  37. data/lib/paperclip/attachment.rb +215 -82
  38. data/lib/paperclip/attachment_registry.rb +60 -0
  39. data/lib/paperclip/callbacks.rb +13 -1
  40. data/lib/paperclip/content_type_detector.rb +48 -24
  41. data/lib/paperclip/errors.rb +8 -1
  42. data/lib/paperclip/file_command_content_type_detector.rb +6 -8
  43. data/lib/paperclip/filename_cleaner.rb +15 -0
  44. data/lib/paperclip/geometry_detector_factory.rb +12 -5
  45. data/lib/paperclip/geometry_parser_factory.rb +1 -1
  46. data/lib/paperclip/glue.rb +1 -2
  47. data/lib/paperclip/has_attached_file.rb +115 -0
  48. data/lib/paperclip/helpers.rb +15 -20
  49. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  50. data/lib/paperclip/interpolations.rb +36 -14
  51. data/lib/paperclip/io_adapters/abstract_adapter.rb +42 -5
  52. data/lib/paperclip/io_adapters/attachment_adapter.rb +20 -9
  53. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  54. data/lib/paperclip/io_adapters/empty_string_adapter.rb +19 -0
  55. data/lib/paperclip/io_adapters/file_adapter.rb +13 -7
  56. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +16 -0
  57. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -6
  58. data/lib/paperclip/io_adapters/nil_adapter.rb +8 -5
  59. data/lib/paperclip/io_adapters/registry.rb +6 -2
  60. data/lib/paperclip/io_adapters/stringio_adapter.rb +15 -16
  61. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +11 -7
  62. data/lib/paperclip/io_adapters/uri_adapter.rb +43 -19
  63. data/lib/paperclip/locales/en.yml +1 -0
  64. data/lib/paperclip/logger.rb +1 -1
  65. data/lib/paperclip/matchers/have_attached_file_matcher.rb +3 -6
  66. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
  67. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +7 -2
  68. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +2 -1
  69. data/lib/paperclip/matchers.rb +1 -1
  70. data/lib/paperclip/media_type_spoof_detector.rb +93 -0
  71. data/lib/paperclip/missing_attachment_styles.rb +11 -16
  72. data/lib/paperclip/processor.rb +15 -43
  73. data/lib/paperclip/processor_helpers.rb +50 -0
  74. data/lib/paperclip/rails_environment.rb +25 -0
  75. data/lib/paperclip/schema.rb +10 -8
  76. data/lib/paperclip/storage/filesystem.rb +20 -5
  77. data/lib/paperclip/storage/fog.rb +49 -23
  78. data/lib/paperclip/storage/s3.rb +153 -82
  79. data/lib/paperclip/style.rb +8 -3
  80. data/lib/paperclip/tempfile_factory.rb +6 -4
  81. data/lib/paperclip/thumbnail.rb +35 -19
  82. data/lib/paperclip/url_generator.rb +26 -14
  83. data/lib/paperclip/validators/attachment_content_type_validator.rb +15 -2
  84. data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
  85. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
  86. data/lib/paperclip/validators/attachment_presence_validator.rb +12 -8
  87. data/lib/paperclip/validators/attachment_size_validator.rb +17 -10
  88. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +31 -0
  89. data/lib/paperclip/validators.rb +31 -3
  90. data/lib/paperclip/version.rb +3 -1
  91. data/lib/paperclip.rb +41 -55
  92. data/lib/tasks/paperclip.rake +56 -9
  93. data/paperclip.gemspec +18 -17
  94. data/shoulda_macros/paperclip.rb +13 -3
  95. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  96. data/spec/paperclip/attachment_processing_spec.rb +79 -0
  97. data/spec/paperclip/attachment_registry_spec.rb +158 -0
  98. data/{test/attachment_test.rb → spec/paperclip/attachment_spec.rb} +597 -389
  99. data/spec/paperclip/content_type_detector_spec.rb +48 -0
  100. data/spec/paperclip/file_command_content_type_detector_spec.rb +40 -0
  101. data/spec/paperclip/filename_cleaner_spec.rb +13 -0
  102. data/spec/paperclip/geometry_detector_spec.rb +39 -0
  103. data/{test/geometry_parser_test.rb → spec/paperclip/geometry_parser_spec.rb} +27 -27
  104. data/{test/geometry_test.rb → spec/paperclip/geometry_spec.rb} +50 -52
  105. data/spec/paperclip/glue_spec.rb +44 -0
  106. data/spec/paperclip/has_attached_file_spec.rb +158 -0
  107. data/{test/integration_test.rb → spec/paperclip/integration_spec.rb} +179 -199
  108. data/{test/interpolations_test.rb → spec/paperclip/interpolations_spec.rb} +79 -46
  109. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +160 -0
  110. data/{test/io_adapters/attachment_adapter_test.rb → spec/paperclip/io_adapters/attachment_adapter_spec.rb} +54 -25
  111. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +89 -0
  112. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  113. data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
  114. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +138 -0
  115. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  116. data/{test/io_adapters/nil_adapter_test.rb → spec/paperclip/io_adapters/nil_adapter_spec.rb} +7 -7
  117. data/{test/io_adapters/registry_test.rb → spec/paperclip/io_adapters/registry_spec.rb} +12 -9
  118. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
  119. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
  120. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +220 -0
  121. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  122. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +109 -0
  123. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  124. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  125. data/spec/paperclip/media_type_spoof_detector_spec.rb +120 -0
  126. data/spec/paperclip/meta_class_spec.rb +30 -0
  127. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
  128. data/spec/paperclip/paperclip_spec.rb +192 -0
  129. data/spec/paperclip/plural_cache_spec.rb +37 -0
  130. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  131. data/{test/processor_test.rb → spec/paperclip/processor_spec.rb} +7 -7
  132. data/spec/paperclip/rails_environment_spec.rb +33 -0
  133. data/spec/paperclip/rake_spec.rb +103 -0
  134. data/spec/paperclip/schema_spec.rb +248 -0
  135. data/{test/storage/filesystem_test.rb → spec/paperclip/storage/filesystem_spec.rb} +18 -18
  136. data/spec/paperclip/storage/fog_spec.rb +566 -0
  137. data/spec/paperclip/storage/s3_live_spec.rb +188 -0
  138. data/spec/paperclip/storage/s3_spec.rb +1693 -0
  139. data/spec/paperclip/style_spec.rb +254 -0
  140. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  141. data/spec/paperclip/tempfile_spec.rb +35 -0
  142. data/{test/thumbnail_test.rb → spec/paperclip/thumbnail_spec.rb} +186 -141
  143. data/spec/paperclip/url_generator_spec.rb +221 -0
  144. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  145. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
  146. data/{test/validators/attachment_presence_validator_test.rb → spec/paperclip/validators/attachment_presence_validator_spec.rb} +20 -20
  147. data/{test/validators/attachment_size_validator_test.rb → spec/paperclip/validators/attachment_size_validator_spec.rb} +87 -59
  148. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
  149. data/spec/paperclip/validators_spec.rb +164 -0
  150. data/spec/spec_helper.rb +46 -0
  151. data/spec/support/assertions.rb +82 -0
  152. data/spec/support/fake_model.rb +25 -0
  153. data/spec/support/fake_rails.rb +12 -0
  154. data/spec/support/fixtures/empty.html +1 -0
  155. data/spec/support/fixtures/empty.xlsx +0 -0
  156. data/spec/support/fixtures/spaced file.jpg +0 -0
  157. data/spec/support/matchers/accept.rb +5 -0
  158. data/spec/support/matchers/exist.rb +5 -0
  159. data/spec/support/matchers/have_column.rb +23 -0
  160. data/{test → spec}/support/mock_attachment.rb +2 -0
  161. data/{test → spec}/support/mock_url_generator_builder.rb +2 -2
  162. data/spec/support/model_reconstruction.rb +68 -0
  163. data/spec/support/reporting.rb +11 -0
  164. data/spec/support/test_data.rb +13 -0
  165. data/spec/support/version_helper.rb +9 -0
  166. metadata +395 -346
  167. data/Gemfile.lock +0 -200
  168. data/RUNNING_TESTS.md +0 -4
  169. data/cucumber/paperclip_steps.rb +0 -6
  170. data/gemfiles/3.0.gemfile +0 -11
  171. data/gemfiles/3.1.gemfile +0 -11
  172. data/gemfiles/3.2.gemfile +0 -11
  173. data/lib/paperclip/attachment_options.rb +0 -9
  174. data/lib/paperclip/instance_methods.rb +0 -35
  175. data/test/attachment_options_test.rb +0 -27
  176. data/test/attachment_processing_test.rb +0 -29
  177. data/test/content_type_detector_test.rb +0 -40
  178. data/test/file_command_content_type_detector_test.rb +0 -25
  179. data/test/generator_test.rb +0 -80
  180. data/test/geometry_detector_test.rb +0 -24
  181. data/test/helper.rb +0 -199
  182. data/test/io_adapters/abstract_adapter_test.rb +0 -50
  183. data/test/io_adapters/file_adapter_test.rb +0 -100
  184. data/test/io_adapters/identity_adapter_test.rb +0 -8
  185. data/test/io_adapters/stringio_adapter_test.rb +0 -51
  186. data/test/io_adapters/uploaded_file_adapter_test.rb +0 -123
  187. data/test/io_adapters/uri_adapter_test.rb +0 -86
  188. data/test/matchers/have_attached_file_matcher_test.rb +0 -24
  189. data/test/matchers/validate_attachment_content_type_matcher_test.rb +0 -110
  190. data/test/matchers/validate_attachment_presence_matcher_test.rb +0 -47
  191. data/test/matchers/validate_attachment_size_matcher_test.rb +0 -86
  192. data/test/meta_class_test.rb +0 -32
  193. data/test/paperclip_missing_attachment_styles_test.rb +0 -94
  194. data/test/paperclip_test.rb +0 -259
  195. data/test/schema_test.rb +0 -200
  196. data/test/storage/fog_test.rb +0 -453
  197. data/test/storage/s3_live_test.rb +0 -179
  198. data/test/storage/s3_test.rb +0 -1236
  199. data/test/style_test.rb +0 -213
  200. data/test/support/mock_model.rb +0 -2
  201. data/test/tempfile_factory_test.rb +0 -13
  202. data/test/url_generator_test.rb +0 -187
  203. data/test/validators/attachment_content_type_validator_test.rb +0 -292
  204. data/test/validators_test.rb +0 -25
  205. /data/{test → spec}/database.yml +0 -0
  206. /data/{test → spec/support}/fixtures/12k.png +0 -0
  207. /data/{test → spec/support}/fixtures/50x50.png +0 -0
  208. /data/{test → spec/support}/fixtures/5k.png +0 -0
  209. /data/{test → spec/support}/fixtures/animated +0 -0
  210. /data/{test → spec/support}/fixtures/animated.gif +0 -0
  211. /data/{test → spec/support}/fixtures/animated.unknown +0 -0
  212. /data/{test → spec/support}/fixtures/bad.png +0 -0
  213. /data/{test → spec/support}/fixtures/fog.yml +0 -0
  214. /data/{test → spec/support}/fixtures/rotated.jpg +0 -0
  215. /data/{test → spec/support}/fixtures/s3.yml +0 -0
  216. /data/{test → spec/support}/fixtures/spaced file.png +0 -0
  217. /data/{test → spec/support}/fixtures/text.txt +0 -0
  218. /data/{test → spec/support}/fixtures/twopage.pdf +0 -0
  219. /data/{test → spec/support}/fixtures/uppercase.PNG +0 -0
  220. /data/{test → spec}/support/mock_interpolator.rb +0 -0
@@ -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,9 +1,14 @@
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
- @tempfile = copy_to_tempfile(@target)
7
12
  end
8
13
 
9
14
  attr_writer :content_type
@@ -11,27 +16,21 @@ module Paperclip
11
16
  private
12
17
 
13
18
  def cache_current_values
14
- @original_filename = @target.original_filename if @target.respond_to?(:original_filename)
15
- @original_filename ||= "stringio.txt"
16
- @original_filename = @original_filename.strip
17
-
18
- @content_type = @target.content_type if @target.respond_to?(:content_type)
19
- @content_type ||= "text/plain"
20
-
19
+ self.original_filename = @target.original_filename if @target.respond_to?(:original_filename)
20
+ self.original_filename ||= "data"
21
+ @tempfile = copy_to_tempfile(@target)
22
+ @content_type = ContentTypeDetector.new(@tempfile.path).detect
21
23
  @size = @target.size
22
24
  end
23
25
 
24
- def copy_to_tempfile(src)
25
- while data = src.read(16*1024)
26
+ def copy_to_tempfile(source)
27
+ while data = source.read(16*1024)
26
28
  destination.write(data)
27
29
  end
28
30
  destination.rewind
29
31
  destination
30
32
  end
31
-
32
33
  end
33
34
  end
34
35
 
35
- Paperclip.io_adapters.register Paperclip::StringioAdapter do |target|
36
- StringIO === target
37
- 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)
@@ -18,13 +24,13 @@ module Paperclip
18
24
  private
19
25
 
20
26
  def cache_current_values
21
- @original_filename = @target.original_filename
27
+ self.original_filename = @target.original_filename
22
28
  @content_type = determine_content_type
23
29
  @size = File.size(@target.path)
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,63 @@
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
- @original_filename = @original_filename.strip
30
+ def content_type_from_content
31
+ @content.meta["content-type"].presence
32
+ end
24
33
 
25
- @content_type = @content.content_type if @content.respond_to?(:content_type)
26
- @content_type ||= "text/html"
34
+ def filename_from_content_disposition
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)
27
38
 
28
- @size = @content.size
39
+ # filename can be enclosed in quotes or not
40
+ matches = filename.match(/"(.*)"/)
41
+ matches ? matches[1] : filename.split(';')[0]
42
+ end
43
+ end
44
+
45
+ def filename_from_path
46
+ @target.path.split("/").last
47
+ end
48
+
49
+ def default_filename
50
+ "index.html"
51
+ end
52
+
53
+ def download_content
54
+ options = { read_timeout: Paperclip.options[:read_timeout] }.compact
55
+
56
+ open(@target, **options)
29
57
  end
30
58
 
31
59
  def copy_to_tempfile(src)
32
- while data = src.read(16*1024)
60
+ while data = src.read(16 * 1024)
33
61
  destination.write(data)
34
62
  end
35
63
  src.close
@@ -38,7 +66,3 @@ module Paperclip
38
66
  end
39
67
  end
40
68
  end
41
-
42
- Paperclip.io_adapters.register Paperclip::UriAdapter do |target|
43
- target.kind_of?(URI)
44
- end
@@ -2,6 +2,7 @@ en:
2
2
  errors:
3
3
  messages:
4
4
  in_between: "must be in between %{min} and %{max}"
5
+ spoofed_media_type: "has contents that are not what they are reported to be"
5
6
 
6
7
  number:
7
8
  human:
@@ -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
 
@@ -20,16 +20,17 @@ module Paperclip
20
20
  def matches? subject
21
21
  @subject = subject
22
22
  @subject = @subject.class unless Class === @subject
23
- responds? && has_column? && included?
23
+ responds? && has_column?
24
24
  end
25
25
 
26
26
  def failure_message
27
27
  "Should have an attachment named #{@attachment_name}"
28
28
  end
29
29
 
30
- def negative_failure_message
30
+ def failure_message_when_negated
31
31
  "Should not have an attachment named #{@attachment_name}"
32
32
  end
33
+ alias negative_failure_message failure_message_when_negated
33
34
 
34
35
  def description
35
36
  "have an attachment named #{@attachment_name}"
@@ -47,10 +48,6 @@ module Paperclip
47
48
  def has_column?
48
49
  @subject.column_names.include?("#{@attachment_name}_file_name")
49
50
  end
50
-
51
- def included?
52
- @subject.ancestors.include?(Paperclip::InstanceMethods)
53
- end
54
51
  end
55
52
  end
56
53
  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."
@@ -26,9 +26,10 @@ module Paperclip
26
26
  "Attachment #{@attachment_name} should be required"
27
27
  end
28
28
 
29
- def negative_failure_message
29
+ def failure_message_when_negated
30
30
  "Attachment #{@attachment_name} should not be required"
31
31
  end
32
+ alias negative_failure_message failure_message_when_negated
32
33
 
33
34
  def description
34
35
  "require presence of attachment #{@attachment_name}"
@@ -46,7 +47,11 @@ module Paperclip
46
47
  @file = StringIO.new(".")
47
48
  @subject.send(@attachment_name).assign(@file)
48
49
  @subject.valid?
49
- @subject.errors[:"#{@attachment_name}"].blank?
50
+ expected_message = [
51
+ @attachment_name.to_s.titleize,
52
+ I18n.t(:blank, scope: [:errors, :messages])
53
+ ].join(' ')
54
+ @subject.errors.full_messages.exclude?(expected_message)
50
55
  end
51
56
  end
52
57
  end
@@ -45,9 +45,10 @@ module Paperclip
45
45
  "Attachment #{@attachment_name} must be between #{@low} and #{@high} bytes"
46
46
  end
47
47
 
48
- def negative_failure_message
48
+ def failure_message_when_negated
49
49
  "Attachment #{@attachment_name} cannot be between #{@low} and #{@high} bytes"
50
50
  end
51
+ alias negative_failure_message failure_message_when_negated
51
52
 
52
53
  def description
53
54
  "validate the size of attachment #{@attachment_name}"
@@ -15,7 +15,7 @@ module Paperclip
15
15
  #
16
16
  # And _include_ the module:
17
17
  #
18
- # Spec::Runner.configure do |config|
18
+ # RSpec.configure do |config|
19
19
  # config.include Paperclip::Shoulda::Matchers
20
20
  # end
21
21
  #
@@ -0,0 +1,93 @@
1
+ module Paperclip
2
+ class MediaTypeSpoofDetector
3
+ def self.using(file, name, content_type)
4
+ new(file, name, content_type)
5
+ end
6
+
7
+ def initialize(file, name, content_type)
8
+ @file = file
9
+ @name = name
10
+ @content_type = content_type || ""
11
+ end
12
+
13
+ def spoofed?
14
+ if has_name? && 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.map(&:to_s)} from Extension), content type discovered from file command: #{calculated_content_type}. See documentation to allow this combination.")
16
+ true
17
+ else
18
+ false
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def has_name?
25
+ @name.present?
26
+ end
27
+
28
+ def has_extension?
29
+ File.extname(@name).present?
30
+ end
31
+
32
+ def media_type_mismatch?
33
+ extension_type_mismatch? || calculated_type_mismatch?
34
+ end
35
+
36
+ def extension_type_mismatch?
37
+ supplied_media_type.present? &&
38
+ has_extension? &&
39
+ !media_types_from_name.include?(supplied_media_type)
40
+ end
41
+
42
+ def calculated_type_mismatch?
43
+ supplied_media_type.present? &&
44
+ !calculated_content_type.include?(supplied_media_type)
45
+ end
46
+
47
+ def mapping_override_mismatch?
48
+ !Array(mapped_content_type).include?(calculated_content_type)
49
+ end
50
+
51
+
52
+ def supplied_content_type
53
+ @content_type
54
+ end
55
+
56
+ def supplied_media_type
57
+ @content_type.split("/").first
58
+ end
59
+
60
+ def content_types_from_name
61
+ @content_types_from_name ||= MIME::Types.type_for(@name)
62
+ end
63
+
64
+ def media_types_from_name
65
+ @media_types_from_name ||= content_types_from_name.collect(&:media_type)
66
+ end
67
+
68
+ def calculated_content_type
69
+ @calculated_content_type ||= type_from_file_command.chomp
70
+ end
71
+
72
+ def calculated_media_type
73
+ @calculated_media_type ||= calculated_content_type.split("/").first
74
+ end
75
+
76
+ def type_from_file_command
77
+ begin
78
+ Paperclip.run("file", "-b --mime :file", file: @file.path).
79
+ split(/[:;\s]+/).first
80
+ rescue Terrapin::CommandLineError
81
+ ""
82
+ end
83
+ end
84
+
85
+ def mapped_content_type
86
+ Paperclip.options[:content_type_mappings][filename_extension]
87
+ end
88
+
89
+ def filename_extension
90
+ File.extname(@name.to_s.downcase).sub(/^\./, '').to_sym
91
+ end
92
+ end
93
+ end
@@ -1,16 +1,14 @@
1
-
1
+ require 'paperclip/attachment_registry'
2
2
  require 'set'
3
+
3
4
  module Paperclip
4
5
  class << self
5
- attr_accessor :classes_with_attachments
6
6
  attr_writer :registered_attachments_styles_path
7
7
  def registered_attachments_styles_path
8
8
  @registered_attachments_styles_path ||= Rails.root.join('public/system/paperclip_attachments.yml').to_s
9
9
  end
10
10
  end
11
11
 
12
- self.classes_with_attachments = Set.new
13
-
14
12
  # Get list of styles saved on previous deploy (running rake paperclip:refresh:missing_styles)
15
13
  def self.get_registered_attachments_styles
16
14
  YAML.load_file(Paperclip.registered_attachments_styles_path)
@@ -36,18 +34,15 @@ module Paperclip
36
34
  # }
37
35
  def self.current_attachments_styles
38
36
  Hash.new.tap do |current_styles|
39
- Paperclip.classes_with_attachments.each do |klass_name|
40
- klass = Paperclip.class_for(klass_name)
41
- klass.attachment_definitions.each do |attachment_name, attachment_attributes|
42
- # TODO: is it even possible to take into account Procs?
43
- next if attachment_attributes[:styles].kind_of?(Proc)
44
- attachment_attributes[:styles].try(:keys).try(:each) do |style_name|
45
- klass_sym = klass.to_s.to_sym
46
- current_styles[klass_sym] ||= Hash.new
47
- current_styles[klass_sym][attachment_name.to_sym] ||= Array.new
48
- current_styles[klass_sym][attachment_name.to_sym] << style_name.to_sym
49
- current_styles[klass_sym][attachment_name.to_sym].map!(&:to_s).sort!.map!(&:to_sym).uniq!
50
- end
37
+ Paperclip::AttachmentRegistry.each_definition do |klass, attachment_name, attachment_attributes|
38
+ # TODO: is it even possible to take into account Procs?
39
+ next if attachment_attributes[:styles].kind_of?(Proc)
40
+ attachment_attributes[:styles].try(:keys).try(:each) do |style_name|
41
+ klass_sym = klass.to_s.to_sym
42
+ current_styles[klass_sym] ||= Hash.new
43
+ current_styles[klass_sym][attachment_name.to_sym] ||= Array.new
44
+ current_styles[klass_sym][attachment_name.to_sym] << style_name.to_sym
45
+ current_styles[klass_sym][attachment_name.to_sym].map!(&:to_s).sort!.map!(&:to_sym).uniq!
51
46
  end
52
47
  end
53
48
  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.
@@ -36,50 +37,21 @@ module Paperclip
36
37
  # The convert method runs the convert binary with the provided arguments.
37
38
  # See Paperclip.run for the available options.
38
39
  def convert(arguments = "", local_options = {})
39
- Paperclip.run('convert', arguments, local_options)
40
+ Paperclip.run(
41
+ Paperclip.options[:is_windows] ? "magick convert" : "convert",
42
+ arguments,
43
+ local_options,
44
+ )
40
45
  end
41
46
 
42
47
  # The identify method runs the identify binary with the provided arguments.
43
48
  # See Paperclip.run for the available options.
44
49
  def identify(arguments = "", local_options = {})
45
- Paperclip.run('identify', arguments, local_options)
46
- end
47
- end
48
-
49
- module ProcessorHelpers
50
- def processor(name) #:nodoc:
51
- @known_processors ||= {}
52
- if @known_processors[name.to_s]
53
- @known_processors[name.to_s]
54
- else
55
- name = name.to_s.camelize
56
- load_processor(name) unless Paperclip.const_defined?(name)
57
- processor = Paperclip.const_get(name)
58
- @known_processors[name.to_s] = processor
59
- end
60
- end
61
-
62
- def load_processor(name)
63
- if defined?(Rails.root) && Rails.root
64
- require File.expand_path(Rails.root.join("lib", "paperclip_processors", "#{name.underscore}.rb"))
65
- end
66
- end
67
-
68
- def clear_processors!
69
- @known_processors.try(:clear)
70
- end
71
-
72
- # You can add your own processor via the Paperclip configuration. Normally
73
- # Paperclip will load all processors from the
74
- # Rails.root/lib/paperclip_processors directory, but here you can add any
75
- # existing class using this mechanism.
76
- #
77
- # Paperclip.configure do |c|
78
- # c.register_processor :watermarker, WatermarkingProcessor.new
79
- # end
80
- def register_processor(name, processor)
81
- @known_processors ||= {}
82
- @known_processors[name.to_s] = processor
50
+ Paperclip.run(
51
+ Paperclip.options[:is_windows] ? "magick identify" : "identify",
52
+ arguments,
53
+ local_options,
54
+ )
83
55
  end
84
56
  end
85
57
  end
@@ -0,0 +1,50 @@
1
+ module Paperclip
2
+ module ProcessorHelpers
3
+ class NoSuchProcessor < StandardError; end
4
+
5
+ def processor(name) #:nodoc:
6
+ @known_processors ||= {}
7
+ if @known_processors[name.to_s]
8
+ @known_processors[name.to_s]
9
+ else
10
+ name = name.to_s.camelize
11
+ load_processor(name) unless Paperclip.const_defined?(name)
12
+ processor = Paperclip.const_get(name)
13
+ @known_processors[name.to_s] = processor
14
+ end
15
+ end
16
+
17
+ def load_processor(name)
18
+ if defined?(Rails.root) && Rails.root
19
+ filename = "#{name.to_s.underscore}.rb"
20
+ directories = %w(lib/paperclip lib/paperclip_processors)
21
+
22
+ required = directories.map do |directory|
23
+ pathname = File.expand_path(Rails.root.join(directory, filename))
24
+ file_exists = File.exist?(pathname)
25
+ require pathname if file_exists
26
+ file_exists
27
+ end
28
+
29
+ raise LoadError, "Could not find the '#{name}' processor in any of these paths: #{directories.join(', ')}" unless required.any?
30
+ end
31
+ end
32
+
33
+ def clear_processors!
34
+ @known_processors.try(:clear)
35
+ end
36
+
37
+ # You can add your own processor via the Paperclip configuration. Normally
38
+ # Paperclip will load all processors from the
39
+ # Rails.root/lib/paperclip_processors directory, but here you can add any
40
+ # existing class using this mechanism.
41
+ #
42
+ # Paperclip.configure do |c|
43
+ # c.register_processor :watermarker, WatermarkingProcessor.new
44
+ # end
45
+ def register_processor(name, processor)
46
+ @known_processors ||= {}
47
+ @known_processors[name.to_s] = processor
48
+ end
49
+ end
50
+ end
@@ -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