paperclip 3.5.4 → 4.3.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of paperclip might be problematic. Click here for more details.

Files changed (198) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -6
  3. data/.hound.yml +1066 -0
  4. data/.rubocop.yml +1 -0
  5. data/.travis.yml +11 -17
  6. data/Appraisals +6 -14
  7. data/CONTRIBUTING.md +13 -8
  8. data/Gemfile +16 -3
  9. data/LICENSE +1 -3
  10. data/NEWS +167 -49
  11. data/README.md +294 -75
  12. data/RELEASING.md +17 -0
  13. data/Rakefile +6 -8
  14. data/features/basic_integration.feature +24 -6
  15. data/features/step_definitions/attachment_steps.rb +30 -22
  16. data/features/step_definitions/html_steps.rb +2 -2
  17. data/features/step_definitions/rails_steps.rb +44 -14
  18. data/features/step_definitions/web_steps.rb +1 -103
  19. data/features/support/env.rb +2 -2
  20. data/features/support/file_helpers.rb +2 -2
  21. data/features/support/fixtures/gemfile.txt +1 -1
  22. data/features/support/rails.rb +2 -1
  23. data/gemfiles/3.2.gemfile +14 -6
  24. data/gemfiles/4.1.gemfile +19 -0
  25. data/gemfiles/4.2.gemfile +19 -0
  26. data/lib/generators/paperclip/paperclip_generator.rb +0 -2
  27. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +1 -1
  28. data/lib/paperclip/attachment.rb +132 -38
  29. data/lib/paperclip/attachment_registry.rb +1 -1
  30. data/lib/paperclip/callbacks.rb +11 -1
  31. data/lib/paperclip/content_type_detector.rb +25 -22
  32. data/lib/paperclip/deprecations.rb +42 -0
  33. data/lib/paperclip/errors.rb +5 -0
  34. data/lib/paperclip/file_command_content_type_detector.rb +6 -8
  35. data/lib/paperclip/geometry_detector_factory.rb +3 -1
  36. data/lib/paperclip/geometry_parser_factory.rb +1 -1
  37. data/lib/paperclip/has_attached_file.rb +10 -0
  38. data/lib/paperclip/interpolations/plural_cache.rb +6 -5
  39. data/lib/paperclip/interpolations.rb +25 -12
  40. data/lib/paperclip/io_adapters/abstract_adapter.rb +3 -1
  41. data/lib/paperclip/io_adapters/attachment_adapter.rb +4 -4
  42. data/lib/paperclip/io_adapters/data_uri_adapter.rb +5 -10
  43. data/lib/paperclip/io_adapters/stringio_adapter.rb +6 -10
  44. data/lib/paperclip/io_adapters/uri_adapter.rb +30 -11
  45. data/lib/paperclip/locales/de.yml +18 -0
  46. data/lib/paperclip/locales/en.yml +1 -0
  47. data/lib/paperclip/locales/es.yml +18 -0
  48. data/lib/paperclip/locales/ja.yml +18 -0
  49. data/lib/paperclip/locales/pt-BR.yml +18 -0
  50. data/lib/paperclip/locales/zh-CN.yml +18 -0
  51. data/lib/paperclip/locales/zh-HK.yml +18 -0
  52. data/lib/paperclip/locales/zh-TW.yml +18 -0
  53. data/lib/paperclip/matchers/have_attached_file_matcher.rb +2 -1
  54. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +2 -1
  55. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +2 -1
  56. data/lib/paperclip/media_type_spoof_detector.rb +89 -0
  57. data/lib/paperclip/processor.rb +0 -37
  58. data/lib/paperclip/processor_helpers.rb +50 -0
  59. data/lib/paperclip/rails_environment.rb +25 -0
  60. data/lib/paperclip/schema.rb +10 -2
  61. data/lib/paperclip/storage/filesystem.rb +1 -1
  62. data/lib/paperclip/storage/fog.rb +18 -7
  63. data/lib/paperclip/storage/s3.rb +53 -22
  64. data/lib/paperclip/style.rb +8 -2
  65. data/lib/paperclip/tempfile_factory.rb +5 -1
  66. data/lib/paperclip/thumbnail.rb +12 -10
  67. data/lib/paperclip/url_generator.rb +11 -3
  68. data/lib/paperclip/validators/attachment_content_type_validator.rb +4 -0
  69. data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
  70. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
  71. data/lib/paperclip/validators/attachment_presence_validator.rb +4 -0
  72. data/lib/paperclip/validators/attachment_size_validator.rb +11 -3
  73. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
  74. data/lib/paperclip/validators.rb +10 -3
  75. data/lib/paperclip/version.rb +1 -1
  76. data/lib/paperclip.rb +26 -8
  77. data/lib/tasks/paperclip.rake +17 -2
  78. data/paperclip.gemspec +16 -14
  79. data/shoulda_macros/paperclip.rb +0 -1
  80. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  81. data/{test/attachment_processing_test.rb → spec/paperclip/attachment_processing_spec.rb} +20 -21
  82. data/spec/paperclip/attachment_registry_spec.rb +130 -0
  83. data/{test/attachment_test.rb → spec/paperclip/attachment_spec.rb} +438 -397
  84. data/{test/content_type_detector_test.rb → spec/paperclip/content_type_detector_spec.rb} +16 -19
  85. data/spec/paperclip/deprecations_spec.rb +65 -0
  86. data/{test/file_command_content_type_detector_test.rb → spec/paperclip/file_command_content_type_detector_spec.rb} +5 -6
  87. data/spec/paperclip/filename_cleaner_spec.rb +14 -0
  88. data/spec/paperclip/geometry_detector_spec.rb +39 -0
  89. data/{test/geometry_parser_test.rb → spec/paperclip/geometry_parser_spec.rb} +27 -27
  90. data/{test/geometry_test.rb → spec/paperclip/geometry_spec.rb} +50 -52
  91. data/spec/paperclip/glue_spec.rb +44 -0
  92. data/{test/has_attached_file_test.rb → spec/paperclip/has_attached_file_spec.rb} +45 -28
  93. data/{test/integration_test.rb → spec/paperclip/integration_spec.rb} +134 -126
  94. data/{test/interpolations_test.rb → spec/paperclip/interpolations_spec.rb} +70 -46
  95. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +78 -0
  96. data/{test/io_adapters/attachment_adapter_test.rb → spec/paperclip/io_adapters/attachment_adapter_spec.rb} +27 -29
  97. data/{test/io_adapters/data_uri_adapter_test.rb → spec/paperclip/io_adapters/data_uri_adapter_spec.rb} +26 -17
  98. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  99. data/{test/io_adapters/file_adapter_test.rb → spec/paperclip/io_adapters/file_adapter_spec.rb} +36 -40
  100. data/{test/io_adapters/http_url_proxy_adapter_test.rb → spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb} +31 -29
  101. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  102. data/{test/io_adapters/nil_adapter_test.rb → spec/paperclip/io_adapters/nil_adapter_spec.rb} +7 -7
  103. data/{test/io_adapters/registry_test.rb → spec/paperclip/io_adapters/registry_spec.rb} +10 -7
  104. data/{test/io_adapters/stringio_adapter_test.rb → spec/paperclip/io_adapters/stringio_adapter_spec.rb} +20 -17
  105. data/{test/io_adapters/uploaded_file_adapter_test.rb → spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb} +41 -41
  106. data/{test/io_adapters/uri_adapter_test.rb → spec/paperclip/io_adapters/uri_adapter_spec.rb} +53 -28
  107. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  108. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +99 -0
  109. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  110. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  111. data/spec/paperclip/media_type_spoof_detector_spec.rb +79 -0
  112. data/spec/paperclip/meta_class_spec.rb +30 -0
  113. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
  114. data/{test/paperclip_test.rb → spec/paperclip/paperclip_spec.rb} +53 -48
  115. data/spec/paperclip/plural_cache_spec.rb +37 -0
  116. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  117. data/{test/processor_test.rb → spec/paperclip/processor_spec.rb} +5 -5
  118. data/spec/paperclip/rails_environment_spec.rb +33 -0
  119. data/{test/rake_test.rb → spec/paperclip/rake_spec.rb} +15 -15
  120. data/spec/paperclip/schema_spec.rb +248 -0
  121. data/{test/storage/filesystem_test.rb → spec/paperclip/storage/filesystem_spec.rb} +18 -18
  122. data/spec/paperclip/storage/fog_spec.rb +535 -0
  123. data/spec/paperclip/storage/s3_live_spec.rb +182 -0
  124. data/spec/paperclip/storage/s3_spec.rb +1526 -0
  125. data/spec/paperclip/style_spec.rb +255 -0
  126. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  127. data/{test/thumbnail_test.rb → spec/paperclip/thumbnail_spec.rb} +123 -107
  128. data/spec/paperclip/url_generator_spec.rb +211 -0
  129. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  130. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
  131. data/{test/validators/attachment_presence_validator_test.rb → spec/paperclip/validators/attachment_presence_validator_spec.rb} +20 -20
  132. data/{test/validators/attachment_size_validator_test.rb → spec/paperclip/validators/attachment_size_validator_spec.rb} +65 -58
  133. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
  134. data/spec/paperclip/validators_spec.rb +164 -0
  135. data/spec/spec_helper.rb +43 -0
  136. data/spec/support/assertions.rb +71 -0
  137. data/spec/support/deprecations.rb +9 -0
  138. data/spec/support/fake_model.rb +25 -0
  139. data/spec/support/fake_rails.rb +12 -0
  140. data/spec/support/fixtures/empty.html +1 -0
  141. data/spec/support/fixtures/empty.xlsx +0 -0
  142. data/spec/support/fixtures/spaced file.jpg +0 -0
  143. data/spec/support/matchers/accept.rb +5 -0
  144. data/spec/support/matchers/exist.rb +5 -0
  145. data/spec/support/matchers/have_column.rb +23 -0
  146. data/spec/support/model_reconstruction.rb +60 -0
  147. data/spec/support/rails_helpers.rb +7 -0
  148. data/spec/support/test_data.rb +13 -0
  149. data/spec/support/version_helper.rb +9 -0
  150. metadata +334 -219
  151. data/RUNNING_TESTS.md +0 -4
  152. data/gemfiles/3.0.gemfile +0 -11
  153. data/gemfiles/3.1.gemfile +0 -11
  154. data/gemfiles/4.0.gemfile +0 -11
  155. data/test/attachment_definitions_test.rb +0 -12
  156. data/test/attachment_registry_test.rb +0 -88
  157. data/test/filename_cleaner_test.rb +0 -14
  158. data/test/generator_test.rb +0 -84
  159. data/test/geometry_detector_test.rb +0 -24
  160. data/test/helper.rb +0 -232
  161. data/test/io_adapters/abstract_adapter_test.rb +0 -58
  162. data/test/io_adapters/empty_string_adapter_test.rb +0 -18
  163. data/test/io_adapters/identity_adapter_test.rb +0 -8
  164. data/test/matchers/have_attached_file_matcher_test.rb +0 -24
  165. data/test/matchers/validate_attachment_content_type_matcher_test.rb +0 -110
  166. data/test/matchers/validate_attachment_presence_matcher_test.rb +0 -69
  167. data/test/matchers/validate_attachment_size_matcher_test.rb +0 -86
  168. data/test/meta_class_test.rb +0 -32
  169. data/test/paperclip_missing_attachment_styles_test.rb +0 -90
  170. data/test/plural_cache_test.rb +0 -36
  171. data/test/schema_test.rb +0 -200
  172. data/test/storage/fog_test.rb +0 -473
  173. data/test/storage/s3_live_test.rb +0 -179
  174. data/test/storage/s3_test.rb +0 -1356
  175. data/test/style_test.rb +0 -213
  176. data/test/support/mock_model.rb +0 -2
  177. data/test/tempfile_factory_test.rb +0 -17
  178. data/test/url_generator_test.rb +0 -187
  179. data/test/validators/attachment_content_type_validator_test.rb +0 -324
  180. data/test/validators_test.rb +0 -61
  181. /data/{test → spec}/database.yml +0 -0
  182. /data/{test → spec/support}/fixtures/12k.png +0 -0
  183. /data/{test → spec/support}/fixtures/50x50.png +0 -0
  184. /data/{test → spec/support}/fixtures/5k.png +0 -0
  185. /data/{test → spec/support}/fixtures/animated +0 -0
  186. /data/{test → spec/support}/fixtures/animated.gif +0 -0
  187. /data/{test → spec/support}/fixtures/animated.unknown +0 -0
  188. /data/{test → spec/support}/fixtures/bad.png +0 -0
  189. /data/{test → spec/support}/fixtures/fog.yml +0 -0
  190. /data/{test → spec/support}/fixtures/rotated.jpg +0 -0
  191. /data/{test → spec/support}/fixtures/s3.yml +0 -0
  192. /data/{test → spec/support}/fixtures/spaced file.png +0 -0
  193. /data/{test → spec/support}/fixtures/text.txt +0 -0
  194. /data/{test → spec/support}/fixtures/twopage.pdf +0 -0
  195. /data/{test → spec/support}/fixtures/uppercase.PNG +0 -0
  196. /data/{test → spec}/support/mock_attachment.rb +0 -0
  197. /data/{test → spec}/support/mock_interpolator.rb +0 -0
  198. /data/{test → spec}/support/mock_url_generator_builder.rb +0 -0
@@ -20,11 +20,11 @@ module Paperclip
20
20
  @size = @tempfile.size || @target.size
21
21
  end
22
22
 
23
- def copy_to_tempfile(src)
24
- if src.respond_to? :copy_to_local_file
25
- src.copy_to_local_file(@style, destination.path)
23
+ def copy_to_tempfile(source)
24
+ if source.staged?
25
+ FileUtils.cp(source.staged_path(@style), destination.path)
26
26
  else
27
- FileUtils.cp(src.path(@style), destination.path)
27
+ source.copy_to_local_file(@style, destination.path)
28
28
  end
29
29
  destination
30
30
  end
@@ -1,22 +1,17 @@
1
1
  module Paperclip
2
2
  class DataUriAdapter < StringioAdapter
3
3
 
4
- REGEXP = /\Adata:([-\w]+\/[-\w\+]+);base64,(.*)/m
4
+ REGEXP = /\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)/m
5
5
 
6
6
  def initialize(target_uri)
7
- @target_uri = target_uri
8
- cache_current_values
9
- @tempfile = copy_to_tempfile
7
+ super(extract_target(target_uri))
10
8
  end
11
9
 
12
10
  private
13
11
 
14
- def cache_current_values
15
- self.original_filename = 'base64.txt'
16
- data_uri_parts ||= @target_uri.match(REGEXP) || []
17
- @content_type = data_uri_parts[1] || 'text/plain'
18
- @target = StringIO.new(Base64.decode64(data_uri_parts[2] || ''))
19
- @size = @target.size
12
+ def extract_target(uri)
13
+ data_uri_parts = uri.match(REGEXP) || []
14
+ StringIO.new(Base64.decode64(data_uri_parts[2] || ''))
20
15
  end
21
16
 
22
17
  end
@@ -3,7 +3,6 @@ module Paperclip
3
3
  def initialize(target)
4
4
  @target = target
5
5
  cache_current_values
6
- @tempfile = copy_to_tempfile
7
6
  end
8
7
 
9
8
  attr_writer :content_type
@@ -11,18 +10,15 @@ module Paperclip
11
10
  private
12
11
 
13
12
  def cache_current_values
14
- @original_filename = @target.original_filename if @target.respond_to?(:original_filename)
15
- @original_filename ||= "stringio.txt"
16
- self.original_filename = @original_filename.strip
17
-
18
- @content_type = @target.content_type if @target.respond_to?(:content_type)
19
- @content_type ||= "text/plain"
20
-
13
+ self.original_filename = @target.original_filename if @target.respond_to?(:original_filename)
14
+ self.original_filename ||= "data"
15
+ @tempfile = copy_to_tempfile(@target)
16
+ @content_type = ContentTypeDetector.new(@tempfile.path).detect
21
17
  @size = @target.size
22
18
  end
23
19
 
24
- def copy_to_tempfile
25
- while data = @target.read(16*1024)
20
+ def copy_to_tempfile(source)
21
+ while data = source.read(16*1024)
26
22
  destination.write(data)
27
23
  end
28
24
  destination.rewind
@@ -2,6 +2,8 @@ require 'open-uri'
2
2
 
3
3
  module Paperclip
4
4
  class UriAdapter < AbstractAdapter
5
+ attr_writer :content_type
6
+
5
7
  def initialize(target)
6
8
  @target = target
7
9
  @content = download_content
@@ -9,23 +11,40 @@ module Paperclip
9
11
  @tempfile = copy_to_tempfile(@content)
10
12
  end
11
13
 
12
- attr_writer :content_type
13
-
14
14
  private
15
15
 
16
- def download_content
17
- open(@target)
16
+ def cache_current_values
17
+ self.content_type = content_type_from_content || "text/html"
18
+
19
+ self.original_filename = filename_from_content_disposition ||
20
+ filename_from_path ||
21
+ default_filename
22
+ @size = @content.size
18
23
  end
19
24
 
20
- def cache_current_values
21
- @original_filename = @target.path.split("/").last
22
- @original_filename ||= "index.html"
23
- self.original_filename = @original_filename.strip
25
+ def content_type_from_content
26
+ if @content.respond_to?(:content_type)
27
+ @content.content_type
28
+ end
29
+ end
24
30
 
25
- @content_type = @content.content_type if @content.respond_to?(:content_type)
26
- @content_type ||= "text/html"
31
+ def filename_from_content_disposition
32
+ if @content.meta.has_key?("content-disposition")
33
+ @content.meta["content-disposition"].
34
+ match(/filename="([^"]*)"/)[1]
35
+ end
36
+ end
27
37
 
28
- @size = @content.size
38
+ def filename_from_path
39
+ @target.path.split("/").last
40
+ end
41
+
42
+ def default_filename
43
+ "index.html"
44
+ end
45
+
46
+ def download_content
47
+ open(@target)
29
48
  end
30
49
 
31
50
  def copy_to_tempfile(src)
@@ -0,0 +1,18 @@
1
+ de:
2
+ errors:
3
+ messages:
4
+ in_between: "muss zwischen %{min} und %{max} sein"
5
+ spoofed_media_type: "trägt eine Dateiendung, die nicht mit dem Inhalt der Datei übereinstimmt"
6
+
7
+ number:
8
+ human:
9
+ storage_units:
10
+ format: "%n %u"
11
+ units:
12
+ byte:
13
+ one: "Byte"
14
+ other: "Bytes"
15
+ kb: "KB"
16
+ mb: "MB"
17
+ gb: "GB"
18
+ tb: "TB"
@@ -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:
@@ -0,0 +1,18 @@
1
+ es:
2
+ errors:
3
+ messages:
4
+ in_between: "debe estar entre %{min} y %{max}"
5
+ spoofed_media_type: "tiene una extensión que no coincide con su contenido"
6
+
7
+ number:
8
+ human:
9
+ storage_units:
10
+ format: "%n %u"
11
+ units:
12
+ byte:
13
+ one: "Byte"
14
+ other: "Bytes"
15
+ kb: "KB"
16
+ mb: "MB"
17
+ gb: "GB"
18
+ tb: "TB"
@@ -0,0 +1,18 @@
1
+ ja:
2
+ errors:
3
+ messages:
4
+ in_between: "の容量は%{min}以上%{max}以下にしてください。"
5
+ spoofed_media_type: "の拡張子と内容が一致していません。"
6
+
7
+ number:
8
+ human:
9
+ storage_units:
10
+ format: "%n %u"
11
+ units:
12
+ byte:
13
+ one: "Byte"
14
+ other: "Bytes"
15
+ kb: "KB"
16
+ mb: "MB"
17
+ gb: "GB"
18
+ tb: "TB"
@@ -0,0 +1,18 @@
1
+ pt-BR:
2
+ errors:
3
+ messages:
4
+ in_between: "deve ter entre %{min} e %{max}"
5
+ spoofed_media_type: "tem uma extensão que não corresponde ao seu conteúdo"
6
+
7
+ number:
8
+ human:
9
+ storage_units:
10
+ format: "%n %u"
11
+ units:
12
+ byte:
13
+ one: "Byte"
14
+ other: "Bytes"
15
+ kb: "KB"
16
+ mb: "MB"
17
+ gb: "GB"
18
+ tb: "TB"
@@ -0,0 +1,18 @@
1
+ zh-CN:
2
+ errors:
3
+ messages:
4
+ in_between: "文件大小必须介于 %{min} 到 %{max} 之间"
5
+ spoofed_media_type: "扩展名与内容类型不符"
6
+
7
+ number:
8
+ human:
9
+ storage_units:
10
+ format: "%n %u"
11
+ units:
12
+ byte:
13
+ one: "Byte"
14
+ other: "Bytes"
15
+ kb: "KB"
16
+ mb: "MB"
17
+ gb: "GB"
18
+ tb: "TB"
@@ -0,0 +1,18 @@
1
+ zh-HK:
2
+ errors:
3
+ messages:
4
+ in_between: "必須介於%{min}到%{max}之間"
5
+ spoofed_media_type: "副檔名與內容類型不匹配"
6
+
7
+ number:
8
+ human:
9
+ storage_units:
10
+ format: "%n %u"
11
+ units:
12
+ byte:
13
+ one: "Byte"
14
+ other: "Bytes"
15
+ kb: "KB"
16
+ mb: "MB"
17
+ gb: "GB"
18
+ tb: "TB"
@@ -0,0 +1,18 @@
1
+ zh-TW:
2
+ errors:
3
+ messages:
4
+ in_between: "檔案大小必須介於 %{min} 到 %{max} 之間"
5
+ spoofed_media_type: "副檔名與內容類型不符"
6
+
7
+ number:
8
+ human:
9
+ storage_units:
10
+ format: "%n %u"
11
+ units:
12
+ byte:
13
+ one: "Byte"
14
+ other: "Bytes"
15
+ kb: "KB"
16
+ mb: "MB"
17
+ gb: "GB"
18
+ tb: "TB"
@@ -27,9 +27,10 @@ module Paperclip
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}"
@@ -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}"
@@ -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}"
@@ -0,0 +1,89 @@
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? && 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.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
+ supplied_type_mismatch? || calculated_type_mismatch?
34
+ end
35
+
36
+ def supplied_type_mismatch?
37
+ supplied_media_type.present? && !media_types_from_name.include?(supplied_media_type)
38
+ end
39
+
40
+ def calculated_type_mismatch?
41
+ !media_types_from_name.include?(calculated_media_type)
42
+ end
43
+
44
+ def mapping_override_mismatch?
45
+ !Array(mapped_content_type).include?(calculated_content_type)
46
+ end
47
+
48
+
49
+ def supplied_content_type
50
+ @content_type
51
+ end
52
+
53
+ def supplied_media_type
54
+ @content_type.split("/").first
55
+ end
56
+
57
+ def content_types_from_name
58
+ @content_types_from_name ||= MIME::Types.type_for(@name)
59
+ end
60
+
61
+ def media_types_from_name
62
+ @media_types_from_name ||= content_types_from_name.collect(&:media_type)
63
+ end
64
+
65
+ def calculated_content_type
66
+ @calculated_content_type ||= type_from_file_command.chomp
67
+ end
68
+
69
+ def calculated_media_type
70
+ @calculated_media_type ||= calculated_content_type.split("/").first
71
+ end
72
+
73
+ def type_from_file_command
74
+ begin
75
+ Paperclip.run("file", "-b --mime :file", :file => @file.path).split(/[:;]\s+/).first
76
+ rescue Cocaine::CommandLineError
77
+ ""
78
+ end
79
+ end
80
+
81
+ def mapped_content_type
82
+ Paperclip.options[:content_type_mappings][filename_extension]
83
+ end
84
+
85
+ def filename_extension
86
+ File.extname(@name.to_s.downcase).sub(/^\./, '').to_sym
87
+ end
88
+ end
89
+ end
@@ -45,41 +45,4 @@ module Paperclip
45
45
  Paperclip.run('identify', arguments, local_options)
46
46
  end
47
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
83
- end
84
- end
85
48
  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
@@ -22,9 +22,12 @@ module Paperclip
22
22
  def add_attachment(table_name, *attachment_names)
23
23
  raise ArgumentError, "Please specify attachment name in your add_attachment call in your migration." if attachment_names.empty?
24
24
 
25
+ options = attachment_names.extract_options!
26
+
25
27
  attachment_names.each do |attachment_name|
26
28
  COLUMNS.each_pair do |column_name, column_type|
27
- add_column(table_name, "#{attachment_name}_#{column_name}", column_type)
29
+ column_options = options.merge(options[column_name.to_sym] || {})
30
+ add_column(table_name, "#{attachment_name}_#{column_name}", column_type, column_options)
28
31
  end
29
32
  end
30
33
  end
@@ -32,8 +35,11 @@ module Paperclip
32
35
  def remove_attachment(table_name, *attachment_names)
33
36
  raise ArgumentError, "Please specify attachment name in your remove_attachment call in your migration." if attachment_names.empty?
34
37
 
38
+ options = attachment_names.extract_options!
39
+
35
40
  attachment_names.each do |attachment_name|
36
41
  COLUMNS.each_pair do |column_name, column_type|
42
+ column_options = options.merge(options[column_name.to_sym] || {})
37
43
  remove_column(table_name, "#{attachment_name}_#{column_name}")
38
44
  end
39
45
  end
@@ -47,9 +53,11 @@ module Paperclip
47
53
 
48
54
  module TableDefinition
49
55
  def attachment(*attachment_names)
56
+ options = attachment_names.extract_options!
50
57
  attachment_names.each do |attachment_name|
51
58
  COLUMNS.each_pair do |column_name, column_type|
52
- column("#{attachment_name}_#{column_name}", column_type)
59
+ column_options = options.merge(options[column_name.to_sym] || {})
60
+ column("#{attachment_name}_#{column_name}", column_type, column_options)
53
61
  end
54
62
  end
55
63
  end
@@ -69,7 +69,7 @@ module Paperclip
69
69
  while(true)
70
70
  path = File.dirname(path)
71
71
  FileUtils.rmdir(path)
72
- break if File.exists?(path) # Ruby 1.9.2 does not raise if the removal failed.
72
+ break if File.exist?(path) # Ruby 1.9.2 does not raise if the removal failed.
73
73
  end
74
74
  rescue Errno::EEXIST, Errno::ENOTEMPTY, Errno::ENOENT, Errno::EINVAL, Errno::ENOTDIR, Errno::EACCES
75
75
  # Stop trying to remove parent directories
@@ -14,6 +14,7 @@ module Paperclip
14
14
  # aws_secret_access_key: '<your aws_secret_access_key>'
15
15
  # provider: 'AWS'
16
16
  # region: 'eu-west-1'
17
+ # scheme: 'https'
17
18
  # * +fog_directory+: This is the name of the S3 bucket that will
18
19
  # store your files. Remember that the bucket must be unique across
19
20
  # all of Amazon S3. If the bucket does not exist, Paperclip will
@@ -32,6 +33,10 @@ module Paperclip
32
33
  # that is the alias to the S3 domain of your bucket, e.g.
33
34
  # 'http://images.example.com'. This can also be used in
34
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 }
35
40
 
36
41
  module Fog
37
42
  def self.extended base
@@ -97,12 +102,14 @@ module Paperclip
97
102
  log("saving #{path(style)}")
98
103
  retried = false
99
104
  begin
100
- directory.files.create(fog_file.merge(
105
+ attributes = fog_file.merge(
101
106
  :body => file,
102
107
  :key => path(style),
103
108
  :public => fog_public(style),
104
109
  :content_type => file.content_type
105
- ))
110
+ )
111
+ attributes.merge!(@options[:fog_options]) if @options[:fog_options]
112
+ directory.files.create(attributes)
106
113
  rescue Excon::Errors::NotFound
107
114
  raise if retried
108
115
  retried = true
@@ -131,7 +138,7 @@ module Paperclip
131
138
  "#{dynamic_fog_host_for_style(style)}/#{path(style)}"
132
139
  else
133
140
  if fog_credentials[:provider] == 'AWS'
134
- "https://#{host_name_for_directory}/#{path(style)}"
141
+ "#{scheme}://#{host_name_for_directory}/#{path(style)}"
135
142
  else
136
143
  directory.files.new(:key => path(style)).public_url
137
144
  end
@@ -140,8 +147,9 @@ module Paperclip
140
147
 
141
148
  def expiring_url(time = (Time.now + 3600), style_name = default_style)
142
149
  time = convert_time(time)
143
- if path(style_name) && directory.files.respond_to?(:get_http_url)
144
- expiring_url = directory.files.get_http_url(path(style_name), time)
150
+ http_url_method = "get_#{scheme}_url"
151
+ if path(style_name) && directory.files.respond_to?(http_url_method)
152
+ expiring_url = directory.files.public_send(http_url_method, path(style_name), time)
145
153
 
146
154
  if @options[:fog_host]
147
155
  expiring_url.gsub!(/#{host_name_for_directory}/, dynamic_fog_host_for_style(style_name))
@@ -155,8 +163,7 @@ module Paperclip
155
163
 
156
164
  def parse_credentials(creds)
157
165
  creds = find_credentials(creds).stringify_keys
158
- env = Object.const_defined?(:Rails) ? Rails.env : nil
159
- (creds[env] || creds).symbolize_keys
166
+ (creds[RailsEnvironment.get] || creds).symbolize_keys
160
167
  end
161
168
 
162
169
  def copy_to_local_file(style, local_dest_path)
@@ -225,6 +232,10 @@ module Paperclip
225
232
 
226
233
  @directory ||= connection.directories.new(:key => dir)
227
234
  end
235
+
236
+ def scheme
237
+ @scheme ||= fog_credentials[:scheme] || 'https'
238
+ end
228
239
  end
229
240
  end
230
241
  end