jr-paperclip 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +7 -0
  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/.github/workflows/reviewdog.yml +23 -0
  7. data/.github/workflows/test.yml +46 -0
  8. data/.gitignore +19 -0
  9. data/.qlty/.gitignore +7 -0
  10. data/.qlty/qlty.toml +89 -0
  11. data/.rubocop.yml +1060 -0
  12. data/Appraisals +29 -0
  13. data/CONTRIBUTING.md +85 -0
  14. data/Gemfile +17 -0
  15. data/LICENSE +25 -0
  16. data/NEWS +567 -0
  17. data/README.md +1083 -0
  18. data/RELEASING.md +17 -0
  19. data/Rakefile +52 -0
  20. data/bin/console +11 -0
  21. data/features/basic_integration.feature +85 -0
  22. data/features/migration.feature +29 -0
  23. data/features/rake_tasks.feature +62 -0
  24. data/features/step_definitions/attachment_steps.rb +121 -0
  25. data/features/step_definitions/html_steps.rb +15 -0
  26. data/features/step_definitions/rails_steps.rb +271 -0
  27. data/features/step_definitions/s3_steps.rb +16 -0
  28. data/features/step_definitions/web_steps.rb +106 -0
  29. data/features/support/env.rb +12 -0
  30. data/features/support/file_helpers.rb +34 -0
  31. data/features/support/fixtures/boot_config.txt +15 -0
  32. data/features/support/fixtures/gemfile.txt +5 -0
  33. data/features/support/fixtures/preinitializer.txt +20 -0
  34. data/features/support/paths.rb +28 -0
  35. data/features/support/rails.rb +39 -0
  36. data/features/support/selectors.rb +19 -0
  37. data/features/support/webmock_setup.rb +8 -0
  38. data/gemfiles/7.0.gemfile +20 -0
  39. data/gemfiles/7.1.gemfile +20 -0
  40. data/gemfiles/7.2.gemfile +20 -0
  41. data/gemfiles/8.0.gemfile +20 -0
  42. data/gemfiles/8.1.gemfile +20 -0
  43. data/lib/generators/paperclip/USAGE +8 -0
  44. data/lib/generators/paperclip/paperclip_generator.rb +36 -0
  45. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
  46. data/lib/jr-paperclip.rb +1 -0
  47. data/lib/paperclip/attachment.rb +634 -0
  48. data/lib/paperclip/attachment_registry.rb +60 -0
  49. data/lib/paperclip/callbacks.rb +42 -0
  50. data/lib/paperclip/content_type_detector.rb +85 -0
  51. data/lib/paperclip/errors.rb +34 -0
  52. data/lib/paperclip/file_command_content_type_detector.rb +28 -0
  53. data/lib/paperclip/filename_cleaner.rb +15 -0
  54. data/lib/paperclip/geometry.rb +157 -0
  55. data/lib/paperclip/geometry_detector_factory.rb +45 -0
  56. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  57. data/lib/paperclip/glue.rb +18 -0
  58. data/lib/paperclip/has_attached_file.rb +116 -0
  59. data/lib/paperclip/helpers.rb +60 -0
  60. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  61. data/lib/paperclip/interpolations.rb +205 -0
  62. data/lib/paperclip/io_adapters/abstract_adapter.rb +75 -0
  63. data/lib/paperclip/io_adapters/attachment_adapter.rb +56 -0
  64. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  65. data/lib/paperclip/io_adapters/empty_string_adapter.rb +19 -0
  66. data/lib/paperclip/io_adapters/file_adapter.rb +26 -0
  67. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +16 -0
  68. data/lib/paperclip/io_adapters/identity_adapter.rb +17 -0
  69. data/lib/paperclip/io_adapters/nil_adapter.rb +37 -0
  70. data/lib/paperclip/io_adapters/registry.rb +36 -0
  71. data/lib/paperclip/io_adapters/stringio_adapter.rb +36 -0
  72. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +44 -0
  73. data/lib/paperclip/io_adapters/uri_adapter.rb +78 -0
  74. data/lib/paperclip/locales/en.yml +18 -0
  75. data/lib/paperclip/locales/fr.yml +18 -0
  76. data/lib/paperclip/locales/gd.yml +20 -0
  77. data/lib/paperclip/logger.rb +21 -0
  78. data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
  79. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +101 -0
  80. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
  81. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +97 -0
  82. data/lib/paperclip/matchers.rb +64 -0
  83. data/lib/paperclip/media_type_spoof_detector.rb +93 -0
  84. data/lib/paperclip/missing_attachment_styles.rb +84 -0
  85. data/lib/paperclip/processor.rb +56 -0
  86. data/lib/paperclip/processor_helpers.rb +52 -0
  87. data/lib/paperclip/rails_environment.rb +21 -0
  88. data/lib/paperclip/railtie.rb +31 -0
  89. data/lib/paperclip/schema.rb +104 -0
  90. data/lib/paperclip/storage/filesystem.rb +99 -0
  91. data/lib/paperclip/storage/fog.rb +262 -0
  92. data/lib/paperclip/storage/s3.rb +497 -0
  93. data/lib/paperclip/storage.rb +3 -0
  94. data/lib/paperclip/style.rb +106 -0
  95. data/lib/paperclip/tempfile.rb +42 -0
  96. data/lib/paperclip/tempfile_factory.rb +22 -0
  97. data/lib/paperclip/thumbnail.rb +131 -0
  98. data/lib/paperclip/url_generator.rb +83 -0
  99. data/lib/paperclip/validators/attachment_content_type_validator.rb +95 -0
  100. data/lib/paperclip/validators/attachment_file_name_validator.rb +82 -0
  101. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +28 -0
  102. data/lib/paperclip/validators/attachment_presence_validator.rb +28 -0
  103. data/lib/paperclip/validators/attachment_size_validator.rb +126 -0
  104. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +29 -0
  105. data/lib/paperclip/validators.rb +73 -0
  106. data/lib/paperclip/version.rb +3 -0
  107. data/lib/paperclip.rb +215 -0
  108. data/lib/tasks/paperclip.rake +140 -0
  109. data/paperclip.gemspec +51 -0
  110. data/shoulda_macros/paperclip.rb +134 -0
  111. data/spec/database.yml +4 -0
  112. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  113. data/spec/paperclip/attachment_processing_spec.rb +79 -0
  114. data/spec/paperclip/attachment_registry_spec.rb +158 -0
  115. data/spec/paperclip/attachment_spec.rb +1617 -0
  116. data/spec/paperclip/content_type_detector_spec.rb +58 -0
  117. data/spec/paperclip/file_command_content_type_detector_spec.rb +40 -0
  118. data/spec/paperclip/filename_cleaner_spec.rb +13 -0
  119. data/spec/paperclip/geometry_detector_spec.rb +47 -0
  120. data/spec/paperclip/geometry_parser_spec.rb +73 -0
  121. data/spec/paperclip/geometry_spec.rb +267 -0
  122. data/spec/paperclip/glue_spec.rb +63 -0
  123. data/spec/paperclip/has_attached_file_spec.rb +78 -0
  124. data/spec/paperclip/integration_spec.rb +702 -0
  125. data/spec/paperclip/interpolations_spec.rb +270 -0
  126. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +160 -0
  127. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +167 -0
  128. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +88 -0
  129. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  130. data/spec/paperclip/io_adapters/file_adapter_spec.rb +134 -0
  131. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +142 -0
  132. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  133. data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
  134. data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
  135. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
  136. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
  137. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +231 -0
  138. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  139. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +108 -0
  140. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  141. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  142. data/spec/paperclip/media_type_spoof_detector_spec.rb +126 -0
  143. data/spec/paperclip/meta_class_spec.rb +30 -0
  144. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +88 -0
  145. data/spec/paperclip/paperclip_spec.rb +196 -0
  146. data/spec/paperclip/plural_cache_spec.rb +37 -0
  147. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  148. data/spec/paperclip/processor_spec.rb +26 -0
  149. data/spec/paperclip/rails_environment_spec.rb +30 -0
  150. data/spec/paperclip/rake_spec.rb +103 -0
  151. data/spec/paperclip/schema_spec.rb +298 -0
  152. data/spec/paperclip/storage/filesystem_spec.rb +102 -0
  153. data/spec/paperclip/storage/fog_spec.rb +606 -0
  154. data/spec/paperclip/storage/s3_live_spec.rb +188 -0
  155. data/spec/paperclip/storage/s3_spec.rb +1974 -0
  156. data/spec/paperclip/style_spec.rb +251 -0
  157. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  158. data/spec/paperclip/tempfile_spec.rb +35 -0
  159. data/spec/paperclip/thumbnail_spec.rb +504 -0
  160. data/spec/paperclip/url_generator_spec.rb +231 -0
  161. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +410 -0
  162. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +249 -0
  163. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
  164. data/spec/paperclip/validators/attachment_size_validator_spec.rb +325 -0
  165. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +48 -0
  166. data/spec/paperclip/validators_spec.rb +179 -0
  167. data/spec/spec_helper.rb +52 -0
  168. data/spec/support/assertions.rb +84 -0
  169. data/spec/support/fake_model.rb +24 -0
  170. data/spec/support/fake_rails.rb +12 -0
  171. data/spec/support/fixtures/12k.png +0 -0
  172. data/spec/support/fixtures/50x50.png +0 -0
  173. data/spec/support/fixtures/5k.png +0 -0
  174. data/spec/support/fixtures/animated +0 -0
  175. data/spec/support/fixtures/animated.gif +0 -0
  176. data/spec/support/fixtures/animated.unknown +0 -0
  177. data/spec/support/fixtures/aws_s3.yml +13 -0
  178. data/spec/support/fixtures/bad.png +1 -0
  179. data/spec/support/fixtures/empty.html +1 -0
  180. data/spec/support/fixtures/empty.xlsx +0 -0
  181. data/spec/support/fixtures/fog.yml +8 -0
  182. data/spec/support/fixtures/rotated.jpg +0 -0
  183. data/spec/support/fixtures/s3.yml +8 -0
  184. data/spec/support/fixtures/sample.xlsm +0 -0
  185. data/spec/support/fixtures/spaced file.jpg +0 -0
  186. data/spec/support/fixtures/spaced file.png +0 -0
  187. data/spec/support/fixtures/text.txt +1 -0
  188. data/spec/support/fixtures/twopage.pdf +0 -0
  189. data/spec/support/fixtures/uppercase.PNG +0 -0
  190. data/spec/support/matchers/accept.rb +5 -0
  191. data/spec/support/matchers/exist.rb +5 -0
  192. data/spec/support/matchers/have_column.rb +23 -0
  193. data/spec/support/mock_attachment.rb +24 -0
  194. data/spec/support/mock_interpolator.rb +24 -0
  195. data/spec/support/mock_url_generator_builder.rb +26 -0
  196. data/spec/support/model_reconstruction.rb +72 -0
  197. data/spec/support/reporting.rb +11 -0
  198. data/spec/support/test_data.rb +13 -0
  199. data/spec/support/version_helper.rb +9 -0
  200. metadata +702 -0
@@ -0,0 +1,18 @@
1
+ fr:
2
+ errors:
3
+ messages:
4
+ in_between: "doit être entre %{min} et %{max}"
5
+ spoofed_media_type: "à du contenu qui ne correspond pas au type déclaré"
6
+
7
+ number:
8
+ human:
9
+ storage_units:
10
+ format: "%n %u"
11
+ units:
12
+ byte:
13
+ one: "Octet"
14
+ other: "Octets"
15
+ kb: "ko"
16
+ mb: "Mo"
17
+ gb: "Go"
18
+ tb: "To"
@@ -0,0 +1,20 @@
1
+ gd:
2
+ errors:
3
+ messages:
4
+ in_between: "– feumaidh seo a bhith eadar %{min} ’s %{max}"
5
+ spoofed_media_type: "– tha susbaint ann nach eil a-rèir na chaidh aithris"
6
+
7
+ number:
8
+ human:
9
+ storage_units:
10
+ format: "%n %u"
11
+ units:
12
+ byte:
13
+ one: "bhaidht"
14
+ two: "bhaidht"
15
+ few: "baidhtean"
16
+ other: "baidht"
17
+ kb: "KB"
18
+ mb: "MB"
19
+ gb: "GB"
20
+ tb: "TB"
@@ -0,0 +1,21 @@
1
+ module Paperclip
2
+ module Logger
3
+ # Log a paperclip-specific line. This will log to STDOUT
4
+ # by default. Set Paperclip.options[:log] to false to turn off.
5
+ def log(message)
6
+ logger.info("[paperclip] #{message}") if logging?
7
+ end
8
+
9
+ def logger #:nodoc:
10
+ @logger ||= options[:logger] || ::Logger.new(STDOUT)
11
+ end
12
+
13
+ def logger=(logger)
14
+ @logger = logger
15
+ end
16
+
17
+ def logging? #:nodoc:
18
+ options[:log]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ module Paperclip
2
+ module Shoulda
3
+ module Matchers
4
+ # Ensures that the given instance or class has an attachment with the
5
+ # given name.
6
+ #
7
+ # Example:
8
+ # describe User do
9
+ # it { should have_attached_file(:avatar) }
10
+ # end
11
+ def have_attached_file(name)
12
+ HaveAttachedFileMatcher.new(name)
13
+ end
14
+
15
+ class HaveAttachedFileMatcher
16
+ def initialize(attachment_name)
17
+ @attachment_name = attachment_name
18
+ end
19
+
20
+ def matches?(subject)
21
+ @subject = subject
22
+ @subject = @subject.class unless Class === @subject
23
+ responds? && has_column?
24
+ end
25
+
26
+ def failure_message
27
+ "Should have an attachment named #{@attachment_name}"
28
+ end
29
+
30
+ def failure_message_when_negated
31
+ "Should not have an attachment named #{@attachment_name}"
32
+ end
33
+ alias negative_failure_message failure_message_when_negated
34
+
35
+ def description
36
+ "have an attachment named #{@attachment_name}"
37
+ end
38
+
39
+ protected
40
+
41
+ def responds?
42
+ methods = @subject.instance_methods.map(&:to_s)
43
+ methods.include?(@attachment_name.to_s) &&
44
+ methods.include?("#{@attachment_name}=") &&
45
+ methods.include?("#{@attachment_name}?")
46
+ end
47
+
48
+ def has_column?
49
+ @subject.column_names.include?("#{@attachment_name}_file_name")
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,101 @@
1
+ module Paperclip
2
+ module Shoulda
3
+ module Matchers
4
+ # Ensures that the given instance or class validates the content type of
5
+ # the given attachment as specified.
6
+ #
7
+ # Example:
8
+ # describe User do
9
+ # it { should validate_attachment_content_type(:icon).
10
+ # allowing('image/png', 'image/gif').
11
+ # rejecting('text/plain', 'text/xml') }
12
+ # end
13
+ def validate_attachment_content_type(name)
14
+ ValidateAttachmentContentTypeMatcher.new(name)
15
+ end
16
+
17
+ class ValidateAttachmentContentTypeMatcher
18
+ def initialize(attachment_name)
19
+ @attachment_name = attachment_name
20
+ @allowed_types = []
21
+ @rejected_types = []
22
+ end
23
+
24
+ def allowing(*types)
25
+ @allowed_types = types.flatten
26
+ self
27
+ end
28
+
29
+ def rejecting(*types)
30
+ @rejected_types = types.flatten
31
+ self
32
+ end
33
+
34
+ def matches?(subject)
35
+ @subject = subject
36
+ @subject = @subject.new if @subject.class == Class
37
+ @allowed_types && @rejected_types &&
38
+ allowed_types_allowed? && rejected_types_rejected?
39
+ end
40
+
41
+ def failure_message
42
+ "#{expected_attachment}\n".tap do |message|
43
+ message << accepted_types_and_failures.to_s
44
+ message << "\n\n" if @allowed_types.present? && @rejected_types.present?
45
+ message << rejected_types_and_failures.to_s
46
+ end
47
+ end
48
+
49
+ def description
50
+ "validate the content types allowed on attachment #{@attachment_name}"
51
+ end
52
+
53
+ protected
54
+
55
+ def accepted_types_and_failures
56
+ if @allowed_types.present?
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
+ end
64
+ end
65
+ end
66
+
67
+ def rejected_types_and_failures
68
+ if @rejected_types.present?
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
75
+ end
76
+ end
77
+ end
78
+
79
+ def expected_attachment
80
+ "Expected #{@attachment_name}:\n"
81
+ end
82
+
83
+ def type_allowed?(type)
84
+ @subject.send("#{@attachment_name}_content_type=", type)
85
+ @subject.valid?
86
+ @subject.errors[:"#{@attachment_name}_content_type"].blank?
87
+ end
88
+
89
+ def allowed_types_allowed?
90
+ @missing_allowed_types ||= @allowed_types.reject { |type| type_allowed?(type) }
91
+ @missing_allowed_types.none?
92
+ end
93
+
94
+ def rejected_types_rejected?
95
+ @missing_rejected_types ||= @rejected_types.select { |type| type_allowed?(type) }
96
+ @missing_rejected_types.none?
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,59 @@
1
+ module Paperclip
2
+ module Shoulda
3
+ module Matchers
4
+ # Ensures that the given instance or class validates the presence of the
5
+ # given attachment.
6
+ #
7
+ # describe User do
8
+ # it { should validate_attachment_presence(:avatar) }
9
+ # end
10
+ def validate_attachment_presence(name)
11
+ ValidateAttachmentPresenceMatcher.new(name)
12
+ end
13
+
14
+ class ValidateAttachmentPresenceMatcher
15
+ def initialize(attachment_name)
16
+ @attachment_name = attachment_name
17
+ end
18
+
19
+ def matches?(subject)
20
+ @subject = subject
21
+ @subject = subject.new if subject.class == Class
22
+ error_when_not_valid? && no_error_when_valid?
23
+ end
24
+
25
+ def failure_message
26
+ "Attachment #{@attachment_name} should be required"
27
+ end
28
+
29
+ def failure_message_when_negated
30
+ "Attachment #{@attachment_name} should not be required"
31
+ end
32
+ alias negative_failure_message failure_message_when_negated
33
+
34
+ def description
35
+ "require presence of attachment #{@attachment_name}"
36
+ end
37
+
38
+ protected
39
+
40
+ def error_when_not_valid?
41
+ @subject.send(@attachment_name).assign(nil)
42
+ @subject.valid?
43
+ @subject.errors[:"#{@attachment_name}"].present?
44
+ end
45
+
46
+ def no_error_when_valid?
47
+ @file = StringIO.new(".")
48
+ @subject.send(@attachment_name).assign(@file)
49
+ @subject.valid?
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)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,97 @@
1
+ module Paperclip
2
+ module Shoulda
3
+ module Matchers
4
+ # Ensures that the given instance or class validates the size of the
5
+ # given attachment as specified.
6
+ #
7
+ # Examples:
8
+ # it { should validate_attachment_size(:avatar).
9
+ # less_than(2.megabytes) }
10
+ # it { should validate_attachment_size(:icon).
11
+ # greater_than(1024) }
12
+ # it { should validate_attachment_size(:icon).
13
+ # in(0..100) }
14
+ def validate_attachment_size(name)
15
+ ValidateAttachmentSizeMatcher.new(name)
16
+ end
17
+
18
+ class ValidateAttachmentSizeMatcher
19
+ def initialize(attachment_name)
20
+ @attachment_name = attachment_name
21
+ end
22
+
23
+ def less_than(size)
24
+ @high = size
25
+ self
26
+ end
27
+
28
+ def greater_than(size)
29
+ @low = size
30
+ self
31
+ end
32
+
33
+ def in(range)
34
+ @low = range.first
35
+ @high = range.last
36
+ self
37
+ end
38
+
39
+ def matches?(subject)
40
+ @subject = subject
41
+ @subject = @subject.new if @subject.class == Class
42
+ lower_than_low? && higher_than_low? && lower_than_high? && higher_than_high?
43
+ end
44
+
45
+ def failure_message
46
+ "Attachment #{@attachment_name} must be between #{@low} and #{@high} bytes"
47
+ end
48
+
49
+ def failure_message_when_negated
50
+ "Attachment #{@attachment_name} cannot be between #{@low} and #{@high} bytes"
51
+ end
52
+ alias negative_failure_message failure_message_when_negated
53
+
54
+ def description
55
+ "validate the size of attachment #{@attachment_name}"
56
+ end
57
+
58
+ protected
59
+
60
+ def override_method(object, method, &replacement)
61
+ (class << object; self; end).class_eval do
62
+ define_method(method, &replacement)
63
+ end
64
+ end
65
+
66
+ def passes_validation_with_size(new_size)
67
+ file = StringIO.new(".")
68
+ override_method(file, :size) { new_size }
69
+ override_method(file, :to_tempfile) { file }
70
+
71
+ @subject.send(@attachment_name).post_processing = false
72
+ @subject.send(@attachment_name).assign(file)
73
+ @subject.valid?
74
+ @subject.errors[:"#{@attachment_name}_file_size"].blank?
75
+ ensure
76
+ @subject.send(@attachment_name).post_processing = true
77
+ end
78
+
79
+ def lower_than_low?
80
+ @low.nil? || !passes_validation_with_size(@low - 1)
81
+ end
82
+
83
+ def higher_than_low?
84
+ @low.nil? || passes_validation_with_size(@low + 1)
85
+ end
86
+
87
+ def lower_than_high?
88
+ @high.nil? || @high == Float::INFINITY || passes_validation_with_size(@high - 1)
89
+ end
90
+
91
+ def higher_than_high?
92
+ @high.nil? || @high == Float::INFINITY || !passes_validation_with_size(@high + 1)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,64 @@
1
+ require "paperclip/matchers/have_attached_file_matcher"
2
+ require "paperclip/matchers/validate_attachment_presence_matcher"
3
+ require "paperclip/matchers/validate_attachment_content_type_matcher"
4
+ require "paperclip/matchers/validate_attachment_size_matcher"
5
+
6
+ module Paperclip
7
+ module Shoulda
8
+ # Provides RSpec-compatible & Test::Unit-compatible matchers for testing Paperclip attachments.
9
+ #
10
+ # *RSpec*
11
+ #
12
+ # In spec_helper.rb, you'll need to require the matchers:
13
+ #
14
+ # require "paperclip/matchers"
15
+ #
16
+ # And _include_ the module:
17
+ #
18
+ # RSpec.configure do |config|
19
+ # config.include Paperclip::Shoulda::Matchers
20
+ # end
21
+ #
22
+ # Example:
23
+ # describe User do
24
+ # it { should have_attached_file(:avatar) }
25
+ # it { should validate_attachment_presence(:avatar) }
26
+ # it { should validate_attachment_content_type(:avatar).
27
+ # allowing('image/png', 'image/gif').
28
+ # rejecting('text/plain', 'text/xml') }
29
+ # it { should validate_attachment_size(:avatar).
30
+ # less_than(2.megabytes) }
31
+ # end
32
+ #
33
+ #
34
+ # *TestUnit*
35
+ #
36
+ # In test_helper.rb, you'll need to require the matchers as well:
37
+ #
38
+ # require "paperclip/matchers"
39
+ #
40
+ # And _extend_ the module:
41
+ #
42
+ # class ActiveSupport::TestCase
43
+ # extend Paperclip::Shoulda::Matchers
44
+ #
45
+ # #...other initializers...#
46
+ # end
47
+ #
48
+ # Example:
49
+ # require 'test_helper'
50
+ #
51
+ # class UserTest < ActiveSupport::TestCase
52
+ # should have_attached_file(:avatar)
53
+ # should validate_attachment_presence(:avatar)
54
+ # should validate_attachment_content_type(:avatar).
55
+ # allowing('image/png', 'image/gif').
56
+ # rejecting('text/plain', 'text/xml')
57
+ # should validate_attachment_size(:avatar).
58
+ # less_than(2.megabytes)
59
+ # end
60
+ #
61
+ module Matchers
62
+ end
63
+ end
64
+ end
@@ -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
+ def supplied_content_type
52
+ @content_type
53
+ end
54
+
55
+ def supplied_media_type
56
+ @content_type.split("/").first
57
+ end
58
+
59
+ def content_types_from_name
60
+ @content_types_from_name ||= MIME::Types.type_for(@name)
61
+ end
62
+
63
+ def media_types_from_name
64
+ @media_types_from_name ||= content_types_from_name.collect(&:media_type)
65
+ end
66
+
67
+ def calculated_content_type
68
+ @calculated_content_type ||= type_from_file_command.chomp
69
+ end
70
+
71
+ def calculated_media_type
72
+ @calculated_media_type ||= calculated_content_type.split("/").first
73
+ end
74
+
75
+ def type_from_file_command
76
+ Paperclip.run("file", "-b --mime :file", file: @file.path).
77
+ split(/[:;\s]+/).first
78
+ rescue Terrapin::CommandLineError
79
+ Paperclip.log("Problem getting type from `file` command. Possible that `file` doesn't exist on this system. Content Type validations don't work without this.")
80
+
81
+ ""
82
+ end
83
+
84
+ def mapped_content_type
85
+ content_type_mappings = Paperclip.options[:content_type_mappings]
86
+ content_type_mappings[filename_extension] || content_type_mappings[filename_extension.to_s]
87
+ end
88
+
89
+ def filename_extension
90
+ File.extname(@name.to_s.downcase).sub(/^\./, "").to_sym
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,84 @@
1
+ require "paperclip/attachment_registry"
2
+ require "set"
3
+
4
+ module Paperclip
5
+ class << self
6
+ attr_writer :registered_attachments_styles_path
7
+ def registered_attachments_styles_path
8
+ @registered_attachments_styles_path ||= Rails.root.join("public/system/paperclip_attachments.yml").to_s
9
+ end
10
+ end
11
+
12
+ # Get list of styles saved on previous deploy (running rake paperclip:refresh:missing_styles)
13
+ def self.get_registered_attachments_styles
14
+ YAML.load_file(Paperclip.registered_attachments_styles_path)
15
+ rescue Errno::ENOENT
16
+ nil
17
+ end
18
+ private_class_method :get_registered_attachments_styles
19
+
20
+ def self.save_current_attachments_styles!
21
+ File.open(Paperclip.registered_attachments_styles_path, "w") do |f|
22
+ YAML.dump(current_attachments_styles, f)
23
+ end
24
+ end
25
+
26
+ # Returns hash with styles for all classes using Paperclip.
27
+ # Unfortunately current version does not work with lambda styles:(
28
+ # {
29
+ # :User => {:avatar => [:small, :big]},
30
+ # :Book => {
31
+ # :cover => [:thumb, :croppable]},
32
+ # :sample => [:thumb, :big]},
33
+ # }
34
+ # }
35
+ def self.current_attachments_styles
36
+ Hash.new.tap do |current_styles|
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].is_a?(Proc)
40
+
41
+ attachment_attributes[:styles].try(:keys).try(:each) do |style_name|
42
+ klass_sym = klass.to_s.to_sym
43
+ current_styles[klass_sym] ||= Hash.new
44
+ current_styles[klass_sym][attachment_name.to_sym] ||= Array.new
45
+ current_styles[klass_sym][attachment_name.to_sym] << style_name.to_sym
46
+ current_styles[klass_sym][attachment_name.to_sym].map!(&:to_s).sort!.map!(&:to_sym).uniq!
47
+ end
48
+ end
49
+ end
50
+ end
51
+ private_class_method :current_attachments_styles
52
+
53
+ # Returns hash with styles missing from recent run of rake paperclip:refresh:missing_styles
54
+ # {
55
+ # :User => {:avatar => [:big]},
56
+ # :Book => {
57
+ # :cover => [:croppable]},
58
+ # }
59
+ # }
60
+ def self.missing_attachments_styles
61
+ current_styles = current_attachments_styles
62
+ registered_styles = get_registered_attachments_styles
63
+
64
+ Hash.new.tap do |missing_styles|
65
+ current_styles.each do |klass, attachment_definitions|
66
+ attachment_definitions.each do |attachment_name, styles|
67
+ registered = begin
68
+ registered_styles[klass][attachment_name] || []
69
+ rescue StandardError
70
+ []
71
+ end
72
+ missed = styles - registered
73
+ if missed.present?
74
+ klass_sym = klass.to_s.to_sym
75
+ missing_styles[klass_sym] ||= Hash.new
76
+ missing_styles[klass_sym][attachment_name.to_sym] ||= Array.new
77
+ missing_styles[klass_sym][attachment_name.to_sym].concat(missed.to_a)
78
+ missing_styles[klass_sym][attachment_name.to_sym].map!(&:to_s).sort!.map!(&:to_sym).uniq!
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,56 @@
1
+ module Paperclip
2
+ # Paperclip processors allow you to modify attached files when they are
3
+ # attached in any way you are able. Paperclip itself uses command-line
4
+ # programs for its included Thumbnail processor, but custom processors
5
+ # are not required to follow suit.
6
+ #
7
+ # Processors are required to be defined inside the Paperclip module and
8
+ # are also required to be a subclass of Paperclip::Processor. There is
9
+ # only one method you *must* implement to properly be a subclass:
10
+ # #make, but #initialize may also be of use. #initialize accepts 3
11
+ # arguments: the file that will be operated on (which is an instance of
12
+ # File), a hash of options that were defined in has_attached_file's
13
+ # style hash, and the Paperclip::Attachment itself. These are set as
14
+ # instance variables that can be used within `#make`.
15
+ #
16
+ # #make must return an instance of File (Tempfile is acceptable) which
17
+ # contains the results of the processing.
18
+ #
19
+ # See Paperclip.run for more information about using command-line
20
+ # utilities from within Processors.
21
+ class Processor
22
+ attr_accessor :file, :options, :attachment
23
+
24
+ def initialize(file, options = {}, attachment = nil)
25
+ @file = file
26
+ @options = options
27
+ @attachment = attachment
28
+ end
29
+
30
+ def make; end
31
+
32
+ def self.make(file, options = {}, attachment = nil)
33
+ new(file, options, attachment).make
34
+ end
35
+
36
+ # The convert method runs the convert binary with the provided arguments.
37
+ # See Paperclip.run for the available options.
38
+ def convert(arguments = "", local_options = {})
39
+ Paperclip.run(
40
+ Paperclip.options[:is_windows] ? "magick convert" : "convert",
41
+ arguments,
42
+ local_options
43
+ )
44
+ end
45
+
46
+ # The identify method runs the identify binary with the provided arguments.
47
+ # See Paperclip.run for the available options.
48
+ def identify(arguments = "", local_options = {})
49
+ Paperclip.run(
50
+ Paperclip.options[:is_windows] ? "magick identify" : "identify",
51
+ arguments,
52
+ local_options
53
+ )
54
+ end
55
+ end
56
+ end