kt-paperclip 6.2.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 (191) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +17 -0
  3. data/.github/issue_template.md +3 -0
  4. data/.gitignore +19 -0
  5. data/.hound.yml +1050 -0
  6. data/.rubocop.yml +1 -0
  7. data/.travis.yml +47 -0
  8. data/Appraisals +24 -0
  9. data/CONTRIBUTING.md +86 -0
  10. data/Gemfile +18 -0
  11. data/LICENSE +24 -0
  12. data/NEWS +515 -0
  13. data/README.md +1053 -0
  14. data/RELEASING.md +17 -0
  15. data/Rakefile +52 -0
  16. data/UPGRADING +17 -0
  17. data/features/basic_integration.feature +85 -0
  18. data/features/migration.feature +29 -0
  19. data/features/rake_tasks.feature +62 -0
  20. data/features/step_definitions/attachment_steps.rb +110 -0
  21. data/features/step_definitions/html_steps.rb +15 -0
  22. data/features/step_definitions/rails_steps.rb +257 -0
  23. data/features/step_definitions/s3_steps.rb +14 -0
  24. data/features/step_definitions/web_steps.rb +106 -0
  25. data/features/support/env.rb +12 -0
  26. data/features/support/fakeweb.rb +11 -0
  27. data/features/support/file_helpers.rb +34 -0
  28. data/features/support/fixtures/boot_config.txt +15 -0
  29. data/features/support/fixtures/gemfile.txt +5 -0
  30. data/features/support/fixtures/preinitializer.txt +20 -0
  31. data/features/support/paths.rb +28 -0
  32. data/features/support/rails.rb +39 -0
  33. data/features/support/selectors.rb +19 -0
  34. data/gemfiles/4.2.gemfile +20 -0
  35. data/gemfiles/5.0.gemfile +20 -0
  36. data/gemfiles/5.1.gemfile +20 -0
  37. data/gemfiles/5.2.gemfile +20 -0
  38. data/gemfiles/6.0.gemfile +20 -0
  39. data/lib/generators/paperclip/USAGE +8 -0
  40. data/lib/generators/paperclip/paperclip_generator.rb +36 -0
  41. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
  42. data/lib/paperclip.rb +215 -0
  43. data/lib/paperclip/attachment.rb +617 -0
  44. data/lib/paperclip/attachment_registry.rb +60 -0
  45. data/lib/paperclip/callbacks.rb +42 -0
  46. data/lib/paperclip/content_type_detector.rb +80 -0
  47. data/lib/paperclip/errors.rb +34 -0
  48. data/lib/paperclip/file_command_content_type_detector.rb +28 -0
  49. data/lib/paperclip/filename_cleaner.rb +15 -0
  50. data/lib/paperclip/geometry.rb +157 -0
  51. data/lib/paperclip/geometry_detector_factory.rb +45 -0
  52. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  53. data/lib/paperclip/glue.rb +17 -0
  54. data/lib/paperclip/has_attached_file.rb +116 -0
  55. data/lib/paperclip/helpers.rb +60 -0
  56. data/lib/paperclip/interpolations.rb +201 -0
  57. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  58. data/lib/paperclip/io_adapters/abstract_adapter.rb +75 -0
  59. data/lib/paperclip/io_adapters/attachment_adapter.rb +47 -0
  60. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  61. data/lib/paperclip/io_adapters/empty_string_adapter.rb +19 -0
  62. data/lib/paperclip/io_adapters/file_adapter.rb +26 -0
  63. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +16 -0
  64. data/lib/paperclip/io_adapters/identity_adapter.rb +17 -0
  65. data/lib/paperclip/io_adapters/nil_adapter.rb +37 -0
  66. data/lib/paperclip/io_adapters/registry.rb +36 -0
  67. data/lib/paperclip/io_adapters/stringio_adapter.rb +36 -0
  68. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +44 -0
  69. data/lib/paperclip/io_adapters/uri_adapter.rb +68 -0
  70. data/lib/paperclip/locales/en.yml +18 -0
  71. data/lib/paperclip/logger.rb +21 -0
  72. data/lib/paperclip/matchers.rb +64 -0
  73. data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
  74. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +101 -0
  75. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
  76. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +97 -0
  77. data/lib/paperclip/media_type_spoof_detector.rb +90 -0
  78. data/lib/paperclip/missing_attachment_styles.rb +84 -0
  79. data/lib/paperclip/processor.rb +56 -0
  80. data/lib/paperclip/processor_helpers.rb +52 -0
  81. data/lib/paperclip/rails_environment.rb +21 -0
  82. data/lib/paperclip/railtie.rb +31 -0
  83. data/lib/paperclip/schema.rb +81 -0
  84. data/lib/paperclip/storage.rb +3 -0
  85. data/lib/paperclip/storage/filesystem.rb +99 -0
  86. data/lib/paperclip/storage/fog.rb +252 -0
  87. data/lib/paperclip/storage/s3.rb +461 -0
  88. data/lib/paperclip/style.rb +106 -0
  89. data/lib/paperclip/tempfile.rb +42 -0
  90. data/lib/paperclip/tempfile_factory.rb +22 -0
  91. data/lib/paperclip/thumbnail.rb +131 -0
  92. data/lib/paperclip/url_generator.rb +76 -0
  93. data/lib/paperclip/validators.rb +73 -0
  94. data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
  95. data/lib/paperclip/validators/attachment_file_name_validator.rb +75 -0
  96. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +28 -0
  97. data/lib/paperclip/validators/attachment_presence_validator.rb +28 -0
  98. data/lib/paperclip/validators/attachment_size_validator.rb +109 -0
  99. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +29 -0
  100. data/lib/paperclip/version.rb +3 -0
  101. data/lib/tasks/paperclip.rake +140 -0
  102. data/paperclip.gemspec +50 -0
  103. data/shoulda_macros/paperclip.rb +134 -0
  104. data/spec/database.yml +4 -0
  105. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  106. data/spec/paperclip/attachment_processing_spec.rb +79 -0
  107. data/spec/paperclip/attachment_registry_spec.rb +158 -0
  108. data/spec/paperclip/attachment_spec.rb +1590 -0
  109. data/spec/paperclip/content_type_detector_spec.rb +47 -0
  110. data/spec/paperclip/file_command_content_type_detector_spec.rb +40 -0
  111. data/spec/paperclip/filename_cleaner_spec.rb +13 -0
  112. data/spec/paperclip/geometry_detector_spec.rb +38 -0
  113. data/spec/paperclip/geometry_parser_spec.rb +73 -0
  114. data/spec/paperclip/geometry_spec.rb +255 -0
  115. data/spec/paperclip/glue_spec.rb +42 -0
  116. data/spec/paperclip/has_attached_file_spec.rb +78 -0
  117. data/spec/paperclip/integration_spec.rb +702 -0
  118. data/spec/paperclip/interpolations_spec.rb +270 -0
  119. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +160 -0
  120. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +140 -0
  121. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +88 -0
  122. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  123. data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
  124. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +137 -0
  125. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  126. data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
  127. data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
  128. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
  129. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
  130. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +221 -0
  131. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  132. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +108 -0
  133. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  134. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  135. data/spec/paperclip/media_type_spoof_detector_spec.rb +120 -0
  136. data/spec/paperclip/meta_class_spec.rb +30 -0
  137. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +88 -0
  138. data/spec/paperclip/paperclip_spec.rb +196 -0
  139. data/spec/paperclip/plural_cache_spec.rb +37 -0
  140. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  141. data/spec/paperclip/processor_spec.rb +26 -0
  142. data/spec/paperclip/rails_environment_spec.rb +30 -0
  143. data/spec/paperclip/rake_spec.rb +103 -0
  144. data/spec/paperclip/schema_spec.rb +252 -0
  145. data/spec/paperclip/storage/filesystem_spec.rb +79 -0
  146. data/spec/paperclip/storage/fog_spec.rb +560 -0
  147. data/spec/paperclip/storage/s3_live_spec.rb +188 -0
  148. data/spec/paperclip/storage/s3_spec.rb +1695 -0
  149. data/spec/paperclip/style_spec.rb +251 -0
  150. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  151. data/spec/paperclip/tempfile_spec.rb +35 -0
  152. data/spec/paperclip/thumbnail_spec.rb +504 -0
  153. data/spec/paperclip/url_generator_spec.rb +221 -0
  154. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  155. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +159 -0
  156. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
  157. data/spec/paperclip/validators/attachment_size_validator_spec.rb +235 -0
  158. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +48 -0
  159. data/spec/paperclip/validators_spec.rb +164 -0
  160. data/spec/spec_helper.rb +45 -0
  161. data/spec/support/assertions.rb +84 -0
  162. data/spec/support/fake_model.rb +24 -0
  163. data/spec/support/fake_rails.rb +12 -0
  164. data/spec/support/fixtures/12k.png +0 -0
  165. data/spec/support/fixtures/50x50.png +0 -0
  166. data/spec/support/fixtures/5k.png +0 -0
  167. data/spec/support/fixtures/animated +0 -0
  168. data/spec/support/fixtures/animated.gif +0 -0
  169. data/spec/support/fixtures/animated.unknown +0 -0
  170. data/spec/support/fixtures/bad.png +1 -0
  171. data/spec/support/fixtures/empty.html +1 -0
  172. data/spec/support/fixtures/empty.xlsx +0 -0
  173. data/spec/support/fixtures/fog.yml +8 -0
  174. data/spec/support/fixtures/rotated.jpg +0 -0
  175. data/spec/support/fixtures/s3.yml +8 -0
  176. data/spec/support/fixtures/spaced file.jpg +0 -0
  177. data/spec/support/fixtures/spaced file.png +0 -0
  178. data/spec/support/fixtures/text.txt +1 -0
  179. data/spec/support/fixtures/twopage.pdf +0 -0
  180. data/spec/support/fixtures/uppercase.PNG +0 -0
  181. data/spec/support/matchers/accept.rb +5 -0
  182. data/spec/support/matchers/exist.rb +5 -0
  183. data/spec/support/matchers/have_column.rb +23 -0
  184. data/spec/support/mock_attachment.rb +24 -0
  185. data/spec/support/mock_interpolator.rb +24 -0
  186. data/spec/support/mock_url_generator_builder.rb +26 -0
  187. data/spec/support/model_reconstruction.rb +72 -0
  188. data/spec/support/reporting.rb +11 -0
  189. data/spec/support/test_data.rb +13 -0
  190. data/spec/support/version_helper.rb +9 -0
  191. metadata +586 -0
@@ -0,0 +1,88 @@
1
+ module Paperclip
2
+ module Validators
3
+ class AttachmentContentTypeValidator < ActiveModel::EachValidator
4
+ def initialize(options)
5
+ options[:allow_nil] = true unless options.key?(:allow_nil)
6
+ super
7
+ end
8
+
9
+ def self.helper_method_name
10
+ :validates_attachment_content_type
11
+ end
12
+
13
+ def validate_each(record, attribute, value)
14
+ base_attribute = attribute.to_sym
15
+ attribute = "#{attribute}_content_type".to_sym
16
+ value = record.send :read_attribute_for_validation, attribute
17
+
18
+ return if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
19
+
20
+ validate_whitelist(record, attribute, value)
21
+ validate_blacklist(record, attribute, value)
22
+
23
+ if record.errors.include? attribute
24
+ record.errors[attribute].each do |error|
25
+ record.errors.add base_attribute, error
26
+ end
27
+ end
28
+ end
29
+
30
+ def validate_whitelist(record, attribute, value)
31
+ if allowed_types.present? && allowed_types.none? { |type| type === value }
32
+ mark_invalid record, attribute, allowed_types
33
+ end
34
+ end
35
+
36
+ def validate_blacklist(record, attribute, value)
37
+ if forbidden_types.present? && forbidden_types.any? { |type| type === value }
38
+ mark_invalid record, attribute, forbidden_types
39
+ end
40
+ end
41
+
42
+ def mark_invalid(record, attribute, types)
43
+ record.errors.add attribute, :invalid, options.merge(types: types.join(", "))
44
+ end
45
+
46
+ def allowed_types
47
+ [options[:content_type]].flatten.compact
48
+ end
49
+
50
+ def forbidden_types
51
+ [options[:not]].flatten.compact
52
+ end
53
+
54
+ def check_validity!
55
+ unless options.key?(:content_type) || options.key?(:not)
56
+ raise ArgumentError, "You must pass in either :content_type or :not to the validator"
57
+ end
58
+ end
59
+ end
60
+
61
+ module HelperMethods
62
+ # Places ActiveModel validations on the content type of the file
63
+ # assigned. The possible options are:
64
+ # * +content_type+: Allowed content types. Can be a single content type
65
+ # or an array. Each type can be a String or a Regexp. It should be
66
+ # noted that Internet Explorer uploads files with content_types that you
67
+ # may not expect. For example, JPEG images are given image/pjpeg and
68
+ # PNGs are image/x-png, so keep that in mind when determining how you
69
+ # match. Allows all by default.
70
+ # * +not+: Forbidden content types.
71
+ # * +message+: The message to display when the uploaded file has an invalid
72
+ # content type.
73
+ # * +if+: A lambda or name of an instance method. Validation will only
74
+ # be run is this lambda or method returns true.
75
+ # * +unless+: Same as +if+ but validates if lambda or method returns false.
76
+ # NOTE: If you do not specify an [attachment]_content_type field on your
77
+ # model, content_type validation will work _ONLY upon assignment_ and
78
+ # re-validation after the instance has been reloaded will always succeed.
79
+ # You'll still need to have a virtual attribute (created by +attr_accessor+)
80
+ # name +[attachment]_content_type+ to be able to use this validator.
81
+ def validates_attachment_content_type(*attr_names)
82
+ options = _merge_attributes(attr_names)
83
+ validates_with AttachmentContentTypeValidator, options.dup
84
+ validate_before_processing AttachmentContentTypeValidator, options.dup
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,75 @@
1
+ module Paperclip
2
+ module Validators
3
+ class AttachmentFileNameValidator < ActiveModel::EachValidator
4
+ def initialize(options)
5
+ options[:allow_nil] = true unless options.key?(:allow_nil)
6
+ super
7
+ end
8
+
9
+ def self.helper_method_name
10
+ :validates_attachment_file_name
11
+ end
12
+
13
+ def validate_each(record, attribute, value)
14
+ base_attribute = attribute.to_sym
15
+ attribute = "#{attribute}_file_name".to_sym
16
+ value = record.send :read_attribute_for_validation, attribute
17
+
18
+ return if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
19
+
20
+ validate_whitelist(record, attribute, value)
21
+ validate_blacklist(record, attribute, value)
22
+
23
+ if record.errors.include? attribute
24
+ record.errors[attribute].each do |error|
25
+ record.errors.add base_attribute, error
26
+ end
27
+ end
28
+ end
29
+
30
+ def validate_whitelist(record, attribute, value)
31
+ mark_invalid record, attribute, allowed if allowed.present? && allowed.none? { |type| type === value }
32
+ end
33
+
34
+ def validate_blacklist(record, attribute, value)
35
+ mark_invalid record, attribute, forbidden if forbidden.present? && forbidden.any? { |type| type === value }
36
+ end
37
+
38
+ def mark_invalid(record, attribute, patterns)
39
+ record.errors.add attribute, :invalid, options.merge(names: patterns.join(", "))
40
+ end
41
+
42
+ def allowed
43
+ [options[:matches]].flatten.compact
44
+ end
45
+
46
+ def forbidden
47
+ [options[:not]].flatten.compact
48
+ end
49
+
50
+ def check_validity!
51
+ unless options.key?(:matches) || options.key?(:not)
52
+ raise ArgumentError, "You must pass in either :matches or :not to the validator"
53
+ end
54
+ end
55
+ end
56
+
57
+ module HelperMethods
58
+ # Places ActiveModel validations on the name of the file
59
+ # assigned. The possible options are:
60
+ # * +matches+: Allowed filename patterns as Regexps. Can be a single one
61
+ # or an array.
62
+ # * +not+: Forbidden file name patterns, specified the same was as +matches+.
63
+ # * +message+: The message to display when the uploaded file has an invalid
64
+ # name.
65
+ # * +if+: A lambda or name of an instance method. Validation will only
66
+ # be run is this lambda or method returns true.
67
+ # * +unless+: Same as +if+ but validates if lambda or method returns false.
68
+ def validates_attachment_file_name(*attr_names)
69
+ options = _merge_attributes(attr_names)
70
+ validates_with AttachmentFileNameValidator, options.dup
71
+ validate_before_processing AttachmentFileNameValidator, options.dup
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,28 @@
1
+ require "active_model/validations/presence"
2
+
3
+ module Paperclip
4
+ module Validators
5
+ class AttachmentFileTypeIgnoranceValidator < ActiveModel::EachValidator
6
+ def validate_each(record, attribute, value)
7
+ # This doesn't do anything. It's just to mark that you don't care about
8
+ # the file_names or content_types of your incoming attachments.
9
+ end
10
+
11
+ def self.helper_method_name
12
+ :do_not_validate_attachment_file_type
13
+ end
14
+ end
15
+
16
+ module HelperMethods
17
+ # Places ActiveModel validations on the presence of a file.
18
+ # Options:
19
+ # * +if+: A lambda or name of an instance method. Validation will only
20
+ # be run if this lambda or method returns true.
21
+ # * +unless+: Same as +if+ but validates if lambda or method returns false.
22
+ def do_not_validate_attachment_file_type(*attr_names)
23
+ options = _merge_attributes(attr_names)
24
+ validates_with AttachmentFileTypeIgnoranceValidator, options.dup
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require "active_model/validations/presence"
2
+
3
+ module Paperclip
4
+ module Validators
5
+ class AttachmentPresenceValidator < ActiveModel::EachValidator
6
+ def validate_each(record, attribute, _value)
7
+ record.errors.add(attribute, :blank, options) if record.send("#{attribute}_file_name").blank?
8
+ end
9
+
10
+ def self.helper_method_name
11
+ :validates_attachment_presence
12
+ end
13
+ end
14
+
15
+ module HelperMethods
16
+ # Places ActiveModel validations on the presence of a file.
17
+ # Options:
18
+ # * +if+: A lambda or name of an instance method. Validation will only
19
+ # be run if this lambda or method returns true.
20
+ # * +unless+: Same as +if+ but validates if lambda or method returns false.
21
+ def validates_attachment_presence(*attr_names)
22
+ options = _merge_attributes(attr_names)
23
+ validates_with AttachmentPresenceValidator, options.dup
24
+ validate_before_processing AttachmentPresenceValidator, options.dup
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,109 @@
1
+ require "active_model/validations/numericality"
2
+
3
+ module Paperclip
4
+ module Validators
5
+ class AttachmentSizeValidator < ActiveModel::Validations::NumericalityValidator
6
+ AVAILABLE_CHECKS = [:less_than, :less_than_or_equal_to, :greater_than, :greater_than_or_equal_to].freeze
7
+
8
+ def initialize(options)
9
+ extract_options(options)
10
+ super
11
+ end
12
+
13
+ def self.helper_method_name
14
+ :validates_attachment_size
15
+ end
16
+
17
+ def validate_each(record, attr_name, value)
18
+ base_attr_name = attr_name
19
+ attr_name = "#{attr_name}_file_size".to_sym
20
+ value = record.send(:read_attribute_for_validation, attr_name)
21
+
22
+ unless value.blank?
23
+ options.slice(*AVAILABLE_CHECKS).each do |option, option_value|
24
+ option_value = option_value.call(record) if option_value.is_a?(Proc)
25
+ option_value = extract_option_value(option, option_value)
26
+
27
+ unless value.send(CHECKS[option], option_value)
28
+ error_message_key = options[:in] ? :in_between : option
29
+ [attr_name, base_attr_name].each do |error_attr_name|
30
+ record.errors.add(error_attr_name, error_message_key, filtered_options(value).merge(
31
+ min: min_value_in_human_size(record),
32
+ max: max_value_in_human_size(record),
33
+ count: human_size(option_value)
34
+ ))
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def check_validity!
42
+ unless (AVAILABLE_CHECKS + [:in]).any? { |argument| options.key?(argument) }
43
+ raise ArgumentError, "You must pass either :less_than, :greater_than, or :in to the validator"
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def extract_options(options)
50
+ if range = options[:in]
51
+ if !options[:in].respond_to?(:call)
52
+ options[:less_than_or_equal_to] = range.max
53
+ options[:greater_than_or_equal_to] = range.min
54
+ else
55
+ options[:less_than_or_equal_to] = range
56
+ options[:greater_than_or_equal_to] = range
57
+ end
58
+ end
59
+ end
60
+
61
+ def extract_option_value(option, option_value)
62
+ if option_value.is_a?(Range)
63
+ if [:less_than, :less_than_or_equal_to].include?(option)
64
+ option_value.max
65
+ else
66
+ option_value.min
67
+ end
68
+ else
69
+ option_value
70
+ end
71
+ end
72
+
73
+ def human_size(size)
74
+ ActiveSupport::NumberHelper.number_to_human_size(size)
75
+ end
76
+
77
+ def min_value_in_human_size(record)
78
+ value = options[:greater_than_or_equal_to] || options[:greater_than]
79
+ value = value.call(record) if value.respond_to?(:call)
80
+ value = value.min if value.respond_to?(:min)
81
+ human_size(value)
82
+ end
83
+
84
+ def max_value_in_human_size(record)
85
+ value = options[:less_than_or_equal_to] || options[:less_than]
86
+ value = value.call(record) if value.respond_to?(:call)
87
+ value = value.max if value.respond_to?(:max)
88
+ human_size(value)
89
+ end
90
+ end
91
+
92
+ module HelperMethods
93
+ # Places ActiveModel validations on the size of the file assigned. The
94
+ # possible options are:
95
+ # * +in+: a Range of bytes (i.e. +1..1.megabyte+),
96
+ # * +less_than+: equivalent to :in => 0..options[:less_than]
97
+ # * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
98
+ # * +message+: error message to display, use :min and :max as replacements
99
+ # * +if+: A lambda or name of an instance method. Validation will only
100
+ # be run if this lambda or method returns true.
101
+ # * +unless+: Same as +if+ but validates if lambda or method returns false.
102
+ def validates_attachment_size(*attr_names)
103
+ options = _merge_attributes(attr_names)
104
+ validates_with AttachmentSizeValidator, options.dup
105
+ validate_before_processing AttachmentSizeValidator, options.dup
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,29 @@
1
+ require "active_model/validations/presence"
2
+
3
+ module Paperclip
4
+ module Validators
5
+ class MediaTypeSpoofDetectionValidator < ActiveModel::EachValidator
6
+ def validate_each(record, attribute, value)
7
+ adapter = Paperclip.io_adapters.for(value)
8
+ if Paperclip::MediaTypeSpoofDetector.using(adapter, value.original_filename, value.content_type).spoofed?
9
+ record.errors.add(attribute, :spoofed_media_type)
10
+ end
11
+
12
+ adapter.tempfile.close(true) if adapter.tempfile
13
+ end
14
+ end
15
+
16
+ module HelperMethods
17
+ # Places ActiveModel validations on the presence of a file.
18
+ # Options:
19
+ # * +if+: A lambda or name of an instance method. Validation will only
20
+ # be run if this lambda or method returns true.
21
+ # * +unless+: Same as +if+ but validates if lambda or method returns false.
22
+ def validates_media_type_spoof_detection(*attr_names)
23
+ options = _merge_attributes(attr_names)
24
+ validates_with MediaTypeSpoofDetectionValidator, options.dup
25
+ validate_before_processing MediaTypeSpoofDetectionValidator, options.dup
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Paperclip
2
+ VERSION = "6.2.0" unless defined?(Paperclip::VERSION)
3
+ end
@@ -0,0 +1,140 @@
1
+ require "paperclip/attachment_registry"
2
+
3
+ module Paperclip
4
+ module Task
5
+ def self.obtain_class
6
+ class_name = ENV["CLASS"] || ENV["class"]
7
+ raise "Must specify CLASS" unless class_name
8
+
9
+ class_name
10
+ end
11
+
12
+ def self.obtain_attachments(klass)
13
+ klass = Paperclip.class_for(klass.to_s)
14
+ name = ENV["ATTACHMENT"] || ENV["attachment"]
15
+
16
+ attachment_names = Paperclip::AttachmentRegistry.names_for(klass)
17
+
18
+ raise "Class #{klass.name} has no attachments specified" if attachment_names.empty?
19
+
20
+ if name.present? && attachment_names.map(&:to_s).include?(name.to_s)
21
+ [name]
22
+ else
23
+ attachment_names
24
+ end
25
+ end
26
+
27
+ def self.log_error(error)
28
+ $stderr.puts error
29
+ end
30
+ end
31
+ end
32
+
33
+ namespace :paperclip do
34
+ desc "Refreshes both metadata and thumbnails."
35
+ task refresh: ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]
36
+
37
+ namespace :refresh do
38
+ desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT and STYLES splitted by comma)."
39
+ task thumbnails: :environment do
40
+ klass = Paperclip::Task.obtain_class
41
+ names = Paperclip::Task.obtain_attachments(klass)
42
+ styles = (ENV["STYLES"] || ENV["styles"] || "").split(",").map(&:to_sym)
43
+ names.each do |name|
44
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
45
+ attachment = instance.send(name)
46
+ begin
47
+ attachment.reprocess!(*styles)
48
+ rescue StandardError => e
49
+ Paperclip::Task.log_error("exception while processing #{klass} ID #{instance.id}:")
50
+ Paperclip::Task.log_error(" " + e.message + "\n")
51
+ end
52
+ unless instance.errors.blank?
53
+ Paperclip::Task.log_error("errors while processing #{klass} ID #{instance.id}:")
54
+ Paperclip::Task.log_error(" " + instance.errors.full_messages.join("\n ") + "\n")
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
61
+ task metadata: :environment do
62
+ klass = Paperclip::Task.obtain_class
63
+ names = Paperclip::Task.obtain_attachments(klass)
64
+ names.each do |name|
65
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
66
+ attachment = instance.send(name)
67
+ if file = Paperclip.io_adapters.for(attachment, attachment.options[:adapter_options])
68
+ instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
69
+ instance.send("#{name}_content_type=", file.content_type.to_s.strip)
70
+ instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
71
+ instance.save(validate: false)
72
+ else
73
+ true
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ desc "Regenerates missing thumbnail styles for all classes using Paperclip."
80
+ task missing_styles: :environment do
81
+ Rails.application.eager_load!
82
+ Paperclip.missing_attachments_styles.each do |klass, attachment_definitions|
83
+ attachment_definitions.each do |attachment_name, missing_styles|
84
+ puts "Regenerating #{klass} -> #{attachment_name} -> #{missing_styles.inspect}"
85
+ ENV["CLASS"] = klass.to_s
86
+ ENV["ATTACHMENT"] = attachment_name.to_s
87
+ ENV["STYLES"] = missing_styles.join(",")
88
+ Rake::Task["paperclip:refresh:thumbnails"].execute
89
+ end
90
+ end
91
+ Paperclip.save_current_attachments_styles!
92
+ end
93
+
94
+ desc "Regenerates fingerprints for a given CLASS (and optional ATTACHMENT). Useful when changing digest."
95
+ task fingerprints: :environment do
96
+ klass = Paperclip::Task.obtain_class
97
+ names = Paperclip::Task.obtain_attachments(klass)
98
+ names.each do |name|
99
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
100
+ attachment = instance.send(name)
101
+ attachment.assign(attachment)
102
+ instance.save(validate: false)
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ desc "Cleans out invalid attachments. Useful after you've added new validations."
109
+ task clean: :environment do
110
+ klass = Paperclip::Task.obtain_class
111
+ names = Paperclip::Task.obtain_attachments(klass)
112
+ names.each do |name|
113
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
114
+ unless instance.valid?
115
+ attributes = %w(file_size file_name content_type).map { |suffix| "#{name}_#{suffix}".to_sym }
116
+ if attributes.any? { |attribute| instance.errors[attribute].present? }
117
+ instance.send("#{name}=", nil)
118
+ instance.save(validate: false)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ desc "find missing attachments. Useful to know which attachments are broken"
126
+ task find_broken_attachments: :environment do
127
+ klass = Paperclip::Task.obtain_class
128
+ names = Paperclip::Task.obtain_attachments(klass)
129
+ names.each do |name|
130
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
131
+ attachment = instance.send(name)
132
+ if attachment.exists?
133
+ print "."
134
+ else
135
+ Paperclip::Task.log_error("#{instance.class}##{attachment.name}, #{instance.id}, #{attachment.url}")
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end