paperclip-fix 4.3.7

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 (197) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.hound.yml +1066 -0
  4. data/.rubocop.yml +1 -0
  5. data/.travis.yml +22 -0
  6. data/Appraisals +11 -0
  7. data/CONTRIBUTING.md +75 -0
  8. data/Gemfile +21 -0
  9. data/LICENSE +24 -0
  10. data/NEWS +420 -0
  11. data/README.md +979 -0
  12. data/RELEASING.md +17 -0
  13. data/Rakefile +44 -0
  14. data/UPGRADING +14 -0
  15. data/cucumber/paperclip_steps.rb +6 -0
  16. data/features/basic_integration.feature +80 -0
  17. data/features/migration.feature +94 -0
  18. data/features/rake_tasks.feature +62 -0
  19. data/features/step_definitions/attachment_steps.rb +110 -0
  20. data/features/step_definitions/html_steps.rb +15 -0
  21. data/features/step_definitions/rails_steps.rb +236 -0
  22. data/features/step_definitions/s3_steps.rb +14 -0
  23. data/features/step_definitions/web_steps.rb +107 -0
  24. data/features/support/env.rb +11 -0
  25. data/features/support/fakeweb.rb +13 -0
  26. data/features/support/file_helpers.rb +34 -0
  27. data/features/support/fixtures/boot_config.txt +15 -0
  28. data/features/support/fixtures/gemfile.txt +5 -0
  29. data/features/support/fixtures/preinitializer.txt +20 -0
  30. data/features/support/paths.rb +28 -0
  31. data/features/support/rails.rb +63 -0
  32. data/features/support/selectors.rb +19 -0
  33. data/gemfiles/3.2.gemfile +19 -0
  34. data/gemfiles/4.1.gemfile +19 -0
  35. data/gemfiles/4.2.gemfile +19 -0
  36. data/lib/generators/paperclip/USAGE +8 -0
  37. data/lib/generators/paperclip/paperclip_generator.rb +30 -0
  38. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
  39. data/lib/paperclip/attachment.rb +608 -0
  40. data/lib/paperclip/attachment_registry.rb +59 -0
  41. data/lib/paperclip/callbacks.rb +40 -0
  42. data/lib/paperclip/content_type_detector.rb +79 -0
  43. data/lib/paperclip/deprecations.rb +42 -0
  44. data/lib/paperclip/errors.rb +32 -0
  45. data/lib/paperclip/file_command_content_type_detector.rb +30 -0
  46. data/lib/paperclip/filename_cleaner.rb +16 -0
  47. data/lib/paperclip/geometry.rb +158 -0
  48. data/lib/paperclip/geometry_detector_factory.rb +48 -0
  49. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  50. data/lib/paperclip/glue.rb +17 -0
  51. data/lib/paperclip/has_attached_file.rb +109 -0
  52. data/lib/paperclip/helpers.rb +56 -0
  53. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  54. data/lib/paperclip/interpolations.rb +197 -0
  55. data/lib/paperclip/io_adapters/abstract_adapter.rb +47 -0
  56. data/lib/paperclip/io_adapters/attachment_adapter.rb +36 -0
  57. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  58. data/lib/paperclip/io_adapters/empty_string_adapter.rb +18 -0
  59. data/lib/paperclip/io_adapters/file_adapter.rb +22 -0
  60. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +15 -0
  61. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
  62. data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
  63. data/lib/paperclip/io_adapters/registry.rb +32 -0
  64. data/lib/paperclip/io_adapters/stringio_adapter.rb +33 -0
  65. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +42 -0
  66. data/lib/paperclip/io_adapters/uri_adapter.rb +63 -0
  67. data/lib/paperclip/locales/de.yml +18 -0
  68. data/lib/paperclip/locales/en.yml +18 -0
  69. data/lib/paperclip/locales/es.yml +18 -0
  70. data/lib/paperclip/locales/ja.yml +18 -0
  71. data/lib/paperclip/locales/pt-BR.yml +18 -0
  72. data/lib/paperclip/locales/zh-CN.yml +18 -0
  73. data/lib/paperclip/locales/zh-HK.yml +18 -0
  74. data/lib/paperclip/locales/zh-TW.yml +18 -0
  75. data/lib/paperclip/logger.rb +21 -0
  76. data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
  77. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +100 -0
  78. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
  79. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +96 -0
  80. data/lib/paperclip/matchers.rb +64 -0
  81. data/lib/paperclip/media_type_spoof_detector.rb +89 -0
  82. data/lib/paperclip/missing_attachment_styles.rb +79 -0
  83. data/lib/paperclip/processor.rb +48 -0
  84. data/lib/paperclip/processor_helpers.rb +50 -0
  85. data/lib/paperclip/rails_environment.rb +25 -0
  86. data/lib/paperclip/railtie.rb +31 -0
  87. data/lib/paperclip/schema.rb +83 -0
  88. data/lib/paperclip/storage/filesystem.rb +90 -0
  89. data/lib/paperclip/storage/fog.rb +241 -0
  90. data/lib/paperclip/storage/s3.rb +440 -0
  91. data/lib/paperclip/storage.rb +3 -0
  92. data/lib/paperclip/style.rb +109 -0
  93. data/lib/paperclip/tempfile.rb +43 -0
  94. data/lib/paperclip/tempfile_factory.rb +23 -0
  95. data/lib/paperclip/thumbnail.rb +121 -0
  96. data/lib/paperclip/url_generator.rb +72 -0
  97. data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
  98. data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
  99. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
  100. data/lib/paperclip/validators/attachment_presence_validator.rb +30 -0
  101. data/lib/paperclip/validators/attachment_size_validator.rb +115 -0
  102. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
  103. data/lib/paperclip/validators.rb +74 -0
  104. data/lib/paperclip/version.rb +3 -0
  105. data/lib/paperclip.rb +213 -0
  106. data/lib/tasks/paperclip.rake +127 -0
  107. data/paperclip.gemspec +51 -0
  108. data/shoulda_macros/paperclip.rb +134 -0
  109. data/spec/database.yml +4 -0
  110. data/spec/paperclip/attachment_definitions_spec.rb +13 -0
  111. data/spec/paperclip/attachment_processing_spec.rb +82 -0
  112. data/spec/paperclip/attachment_registry_spec.rb +130 -0
  113. data/spec/paperclip/attachment_spec.rb +1494 -0
  114. data/spec/paperclip/content_type_detector_spec.rb +48 -0
  115. data/spec/paperclip/deprecations_spec.rb +65 -0
  116. data/spec/paperclip/file_command_content_type_detector_spec.rb +26 -0
  117. data/spec/paperclip/filename_cleaner_spec.rb +14 -0
  118. data/spec/paperclip/geometry_detector_spec.rb +39 -0
  119. data/spec/paperclip/geometry_parser_spec.rb +73 -0
  120. data/spec/paperclip/geometry_spec.rb +255 -0
  121. data/spec/paperclip/glue_spec.rb +44 -0
  122. data/spec/paperclip/has_attached_file_spec.rb +142 -0
  123. data/spec/paperclip/integration_spec.rb +667 -0
  124. data/spec/paperclip/interpolations_spec.rb +262 -0
  125. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +78 -0
  126. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +139 -0
  127. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +83 -0
  128. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
  129. data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
  130. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +104 -0
  131. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
  132. data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
  133. data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
  134. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
  135. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
  136. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +127 -0
  137. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
  138. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +99 -0
  139. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
  140. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
  141. data/spec/paperclip/media_type_spoof_detector_spec.rb +79 -0
  142. data/spec/paperclip/meta_class_spec.rb +30 -0
  143. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
  144. data/spec/paperclip/paperclip_spec.rb +222 -0
  145. data/spec/paperclip/plural_cache_spec.rb +37 -0
  146. data/spec/paperclip/processor_helpers_spec.rb +57 -0
  147. data/spec/paperclip/processor_spec.rb +26 -0
  148. data/spec/paperclip/rails_environment_spec.rb +33 -0
  149. data/spec/paperclip/rake_spec.rb +103 -0
  150. data/spec/paperclip/schema_spec.rb +248 -0
  151. data/spec/paperclip/storage/filesystem_spec.rb +79 -0
  152. data/spec/paperclip/storage/fog_spec.rb +535 -0
  153. data/spec/paperclip/storage/s3_live_spec.rb +182 -0
  154. data/spec/paperclip/storage/s3_spec.rb +1526 -0
  155. data/spec/paperclip/style_spec.rb +255 -0
  156. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  157. data/spec/paperclip/thumbnail_spec.rb +500 -0
  158. data/spec/paperclip/url_generator_spec.rb +211 -0
  159. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  160. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
  161. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
  162. data/spec/paperclip/validators/attachment_size_validator_spec.rb +229 -0
  163. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
  164. data/spec/paperclip/validators_spec.rb +164 -0
  165. data/spec/spec_helper.rb +43 -0
  166. data/spec/support/assertions.rb +71 -0
  167. data/spec/support/deprecations.rb +9 -0
  168. data/spec/support/fake_model.rb +25 -0
  169. data/spec/support/fake_rails.rb +12 -0
  170. data/spec/support/fixtures/12k.png +0 -0
  171. data/spec/support/fixtures/50x50.png +0 -0
  172. data/spec/support/fixtures/5k.png +0 -0
  173. data/spec/support/fixtures/animated +0 -0
  174. data/spec/support/fixtures/animated.gif +0 -0
  175. data/spec/support/fixtures/animated.unknown +0 -0
  176. data/spec/support/fixtures/bad.png +1 -0
  177. data/spec/support/fixtures/empty.html +1 -0
  178. data/spec/support/fixtures/empty.xlsx +0 -0
  179. data/spec/support/fixtures/fog.yml +8 -0
  180. data/spec/support/fixtures/rotated.jpg +0 -0
  181. data/spec/support/fixtures/s3.yml +8 -0
  182. data/spec/support/fixtures/spaced file.jpg +0 -0
  183. data/spec/support/fixtures/spaced file.png +0 -0
  184. data/spec/support/fixtures/text.txt +1 -0
  185. data/spec/support/fixtures/twopage.pdf +0 -0
  186. data/spec/support/fixtures/uppercase.PNG +0 -0
  187. data/spec/support/matchers/accept.rb +5 -0
  188. data/spec/support/matchers/exist.rb +5 -0
  189. data/spec/support/matchers/have_column.rb +23 -0
  190. data/spec/support/mock_attachment.rb +22 -0
  191. data/spec/support/mock_interpolator.rb +24 -0
  192. data/spec/support/mock_url_generator_builder.rb +27 -0
  193. data/spec/support/model_reconstruction.rb +60 -0
  194. data/spec/support/rails_helpers.rb +7 -0
  195. data/spec/support/test_data.rb +13 -0
  196. data/spec/support/version_helper.rb +9 -0
  197. metadata +606 -0
@@ -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"
@@ -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}") &&
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,100 @@
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
44
+ message << "\n\n" if @allowed_types.present? && @rejected_types.present?
45
+ message << rejected_types_and_failures
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
+ if @missing_allowed_types.any?
59
+ message << " #{@missing_allowed_types.join(", ")} were rejected."
60
+ else
61
+ message << " All were accepted successfully."
62
+ end
63
+ end
64
+ end
65
+ end
66
+ def rejected_types_and_failures
67
+ if @rejected_types.present?
68
+ "Reject content types: #{@rejected_types.join(", ")}\n".tap do |message|
69
+ if @missing_rejected_types.any?
70
+ message << " #{@missing_rejected_types.join(", ")} were accepted."
71
+ else
72
+ message << " All were rejected successfully."
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def expected_attachment
79
+ "Expected #{@attachment_name}:\n"
80
+ end
81
+
82
+ def type_allowed?(type)
83
+ @subject.send("#{@attachment_name}_content_type=", type)
84
+ @subject.valid?
85
+ @subject.errors[:"#{@attachment_name}_content_type"].blank?
86
+ end
87
+
88
+ def allowed_types_allowed?
89
+ @missing_allowed_types ||= @allowed_types.reject { |type| type_allowed?(type) }
90
+ @missing_allowed_types.none?
91
+ end
92
+
93
+ def rejected_types_rejected?
94
+ @missing_rejected_types ||= @rejected_types.select { |type| type_allowed?(type) }
95
+ @missing_rejected_types.none?
96
+ end
97
+ end
98
+ end
99
+ end
100
+ 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,96 @@
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, @high = range.first, range.last
35
+ self
36
+ end
37
+
38
+ def matches? subject
39
+ @subject = subject
40
+ @subject = @subject.new if @subject.class == Class
41
+ lower_than_low? && higher_than_low? && lower_than_high? && higher_than_high?
42
+ end
43
+
44
+ def failure_message
45
+ "Attachment #{@attachment_name} must be between #{@low} and #{@high} bytes"
46
+ end
47
+
48
+ def failure_message_when_negated
49
+ "Attachment #{@attachment_name} cannot be between #{@low} and #{@high} bytes"
50
+ end
51
+ alias negative_failure_message failure_message_when_negated
52
+
53
+ def description
54
+ "validate the size of attachment #{@attachment_name}"
55
+ end
56
+
57
+ protected
58
+
59
+ def override_method object, method, &replacement
60
+ (class << object; self; end).class_eval do
61
+ define_method(method, &replacement)
62
+ end
63
+ end
64
+
65
+ def passes_validation_with_size(new_size)
66
+ file = StringIO.new(".")
67
+ override_method(file, :size){ new_size }
68
+ override_method(file, :to_tempfile){ file }
69
+
70
+ @subject.send(@attachment_name).post_processing = false
71
+ @subject.send(@attachment_name).assign(file)
72
+ @subject.valid?
73
+ @subject.errors[:"#{@attachment_name}_file_size"].blank?
74
+ ensure
75
+ @subject.send(@attachment_name).post_processing = true
76
+ end
77
+
78
+ def lower_than_low?
79
+ @low.nil? || !passes_validation_with_size(@low - 1)
80
+ end
81
+
82
+ def higher_than_low?
83
+ @low.nil? || passes_validation_with_size(@low + 1)
84
+ end
85
+
86
+ def lower_than_high?
87
+ @high.nil? || @high == Float::INFINITY || passes_validation_with_size(@high - 1)
88
+ end
89
+
90
+ def higher_than_high?
91
+ @high.nil? || @high == Float::INFINITY || !passes_validation_with_size(@high + 1)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ 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,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
@@ -0,0 +1,79 @@
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].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!
46
+ end
47
+ end
48
+ end
49
+ end
50
+ private_class_method :current_attachments_styles
51
+
52
+ # Returns hash with styles missing from recent run of rake paperclip:refresh:missing_styles
53
+ # {
54
+ # :User => {:avatar => [:big]},
55
+ # :Book => {
56
+ # :cover => [:croppable]},
57
+ # }
58
+ # }
59
+ def self.missing_attachments_styles
60
+ current_styles = current_attachments_styles
61
+ registered_styles = get_registered_attachments_styles
62
+
63
+ Hash.new.tap do |missing_styles|
64
+ current_styles.each do |klass, attachment_definitions|
65
+ attachment_definitions.each do |attachment_name, styles|
66
+ registered = registered_styles[klass][attachment_name] || [] rescue []
67
+ missed = styles - registered
68
+ if missed.present?
69
+ klass_sym = klass.to_s.to_sym
70
+ missing_styles[klass_sym] ||= Hash.new
71
+ missing_styles[klass_sym][attachment_name.to_sym] ||= Array.new
72
+ missing_styles[klass_sym][attachment_name.to_sym].concat(missed.to_a)
73
+ missing_styles[klass_sym][attachment_name.to_sym].map!(&:to_s).sort!.map!(&:to_sym).uniq!
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,48 @@
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. Both methods accept 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.
14
+ #
15
+ # All #make needs to return is an instance of File (Tempfile is
16
+ # acceptable) which contains the results of the processing.
17
+ #
18
+ # See Paperclip.run for more information about using command-line
19
+ # utilities from within Processors.
20
+ class Processor
21
+ attr_accessor :file, :options, :attachment
22
+
23
+ def initialize file, options = {}, attachment = nil
24
+ @file = file
25
+ @options = options
26
+ @attachment = attachment
27
+ end
28
+
29
+ def make
30
+ 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('convert', arguments, local_options)
40
+ end
41
+
42
+ # The identify method runs the identify binary with the provided arguments.
43
+ # See Paperclip.run for the available options.
44
+ def identify(arguments = "", local_options = {})
45
+ Paperclip.run('identify', arguments, local_options)
46
+ end
47
+ end
48
+ end