paperclip_jk 5.0.0.beta2

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 (190) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +21 -0
  3. data/.hound.yml +1066 -0
  4. data/.rubocop.yml +1 -0
  5. data/.travis.yml +26 -0
  6. data/Appraisals +27 -0
  7. data/CONTRIBUTING.md +86 -0
  8. data/Gemfile +15 -0
  9. data/LICENSE +24 -0
  10. data/NEWS +424 -0
  11. data/README.md +999 -0
  12. data/RELEASING.md +17 -0
  13. data/Rakefile +44 -0
  14. data/UPGRADING +17 -0
  15. data/features/basic_integration.feature +81 -0
  16. data/features/migration.feature +70 -0
  17. data/features/rake_tasks.feature +62 -0
  18. data/features/step_definitions/attachment_steps.rb +110 -0
  19. data/features/step_definitions/html_steps.rb +15 -0
  20. data/features/step_definitions/rails_steps.rb +230 -0
  21. data/features/step_definitions/s3_steps.rb +14 -0
  22. data/features/step_definitions/web_steps.rb +107 -0
  23. data/features/support/env.rb +11 -0
  24. data/features/support/fakeweb.rb +13 -0
  25. data/features/support/file_helpers.rb +34 -0
  26. data/features/support/fixtures/boot_config.txt +15 -0
  27. data/features/support/fixtures/gemfile.txt +5 -0
  28. data/features/support/fixtures/preinitializer.txt +20 -0
  29. data/features/support/paths.rb +28 -0
  30. data/features/support/rails.rb +63 -0
  31. data/features/support/selectors.rb +19 -0
  32. data/gemfiles/4.2.awsv2.0.gemfile +17 -0
  33. data/gemfiles/4.2.awsv2.1.gemfile +17 -0
  34. data/gemfiles/4.2.awsv2.gemfile +20 -0
  35. data/gemfiles/5.0.awsv2.0.gemfile +17 -0
  36. data/gemfiles/5.0.awsv2.1.gemfile +17 -0
  37. data/gemfiles/5.0.awsv2.gemfile +25 -0
  38. data/lib/generators/paperclip/USAGE +8 -0
  39. data/lib/generators/paperclip/paperclip_generator.rb +30 -0
  40. data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
  41. data/lib/paperclip.rb +211 -0
  42. data/lib/paperclip/attachment.rb +610 -0
  43. data/lib/paperclip/attachment_registry.rb +60 -0
  44. data/lib/paperclip/callbacks.rb +42 -0
  45. data/lib/paperclip/content_type_detector.rb +80 -0
  46. data/lib/paperclip/errors.rb +34 -0
  47. data/lib/paperclip/file_command_content_type_detector.rb +30 -0
  48. data/lib/paperclip/filename_cleaner.rb +16 -0
  49. data/lib/paperclip/geometry.rb +158 -0
  50. data/lib/paperclip/geometry_detector_factory.rb +48 -0
  51. data/lib/paperclip/geometry_parser_factory.rb +31 -0
  52. data/lib/paperclip/glue.rb +17 -0
  53. data/lib/paperclip/has_attached_file.rb +115 -0
  54. data/lib/paperclip/helpers.rb +60 -0
  55. data/lib/paperclip/interpolations.rb +197 -0
  56. data/lib/paperclip/interpolations/plural_cache.rb +18 -0
  57. data/lib/paperclip/io_adapters/abstract_adapter.rb +47 -0
  58. data/lib/paperclip/io_adapters/attachment_adapter.rb +36 -0
  59. data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
  60. data/lib/paperclip/io_adapters/empty_string_adapter.rb +18 -0
  61. data/lib/paperclip/io_adapters/file_adapter.rb +22 -0
  62. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +15 -0
  63. data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
  64. data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
  65. data/lib/paperclip/io_adapters/registry.rb +32 -0
  66. data/lib/paperclip/io_adapters/stringio_adapter.rb +33 -0
  67. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +42 -0
  68. data/lib/paperclip/io_adapters/uri_adapter.rb +44 -0
  69. data/lib/paperclip/locales/en.yml +18 -0
  70. data/lib/paperclip/logger.rb +21 -0
  71. data/lib/paperclip/matchers.rb +64 -0
  72. data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
  73. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +100 -0
  74. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
  75. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +96 -0
  76. data/lib/paperclip/media_type_spoof_detector.rb +89 -0
  77. data/lib/paperclip/missing_attachment_styles.rb +79 -0
  78. data/lib/paperclip/processor.rb +48 -0
  79. data/lib/paperclip/processor_helpers.rb +50 -0
  80. data/lib/paperclip/rails_environment.rb +25 -0
  81. data/lib/paperclip/railtie.rb +31 -0
  82. data/lib/paperclip/schema.rb +77 -0
  83. data/lib/paperclip/storage.rb +4 -0
  84. data/lib/paperclip/storage/database.rb +140 -0
  85. data/lib/paperclip/storage/filesystem.rb +90 -0
  86. data/lib/paperclip/storage/fog.rb +244 -0
  87. data/lib/paperclip/storage/s3.rb +442 -0
  88. data/lib/paperclip/style.rb +109 -0
  89. data/lib/paperclip/tempfile.rb +43 -0
  90. data/lib/paperclip/tempfile_factory.rb +23 -0
  91. data/lib/paperclip/thumbnail.rb +121 -0
  92. data/lib/paperclip/url_generator.rb +72 -0
  93. data/lib/paperclip/validators.rb +74 -0
  94. data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
  95. data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
  96. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
  97. data/lib/paperclip/validators/attachment_presence_validator.rb +30 -0
  98. data/lib/paperclip/validators/attachment_size_validator.rb +109 -0
  99. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
  100. data/lib/paperclip/version.rb +3 -0
  101. data/lib/tasks/paperclip.rake +127 -0
  102. data/paperclip.gemspec +54 -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 +80 -0
  107. data/spec/paperclip/attachment_registry_spec.rb +158 -0
  108. data/spec/paperclip/attachment_spec.rb +1517 -0
  109. data/spec/paperclip/content_type_detector_spec.rb +48 -0
  110. data/spec/paperclip/file_command_content_type_detector_spec.rb +26 -0
  111. data/spec/paperclip/filename_cleaner_spec.rb +14 -0
  112. data/spec/paperclip/geometry_detector_spec.rb +39 -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 +44 -0
  116. data/spec/paperclip/has_attached_file_spec.rb +158 -0
  117. data/spec/paperclip/integration_spec.rb +668 -0
  118. data/spec/paperclip/interpolations_spec.rb +262 -0
  119. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +78 -0
  120. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +139 -0
  121. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +83 -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 +113 -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 +102 -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 +109 -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 +70 -0
  136. data/spec/paperclip/meta_class_spec.rb +30 -0
  137. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
  138. data/spec/paperclip/paperclip_spec.rb +192 -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 +33 -0
  143. data/spec/paperclip/rake_spec.rb +103 -0
  144. data/spec/paperclip/schema_spec.rb +248 -0
  145. data/spec/paperclip/storage/filesystem_spec.rb +79 -0
  146. data/spec/paperclip/storage/fog_spec.rb +545 -0
  147. data/spec/paperclip/storage/s3_live_spec.rb +186 -0
  148. data/spec/paperclip/storage/s3_spec.rb +1583 -0
  149. data/spec/paperclip/style_spec.rb +255 -0
  150. data/spec/paperclip/tempfile_factory_spec.rb +33 -0
  151. data/spec/paperclip/thumbnail_spec.rb +500 -0
  152. data/spec/paperclip/url_generator_spec.rb +211 -0
  153. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
  154. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
  155. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
  156. data/spec/paperclip/validators/attachment_size_validator_spec.rb +235 -0
  157. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
  158. data/spec/paperclip/validators_spec.rb +164 -0
  159. data/spec/spec_helper.rb +45 -0
  160. data/spec/support/assertions.rb +78 -0
  161. data/spec/support/fake_model.rb +25 -0
  162. data/spec/support/fake_rails.rb +12 -0
  163. data/spec/support/fixtures/12k.png +0 -0
  164. data/spec/support/fixtures/50x50.png +0 -0
  165. data/spec/support/fixtures/5k.png +0 -0
  166. data/spec/support/fixtures/animated +0 -0
  167. data/spec/support/fixtures/animated.gif +0 -0
  168. data/spec/support/fixtures/animated.unknown +0 -0
  169. data/spec/support/fixtures/bad.png +1 -0
  170. data/spec/support/fixtures/empty.html +1 -0
  171. data/spec/support/fixtures/empty.xlsx +0 -0
  172. data/spec/support/fixtures/fog.yml +8 -0
  173. data/spec/support/fixtures/rotated.jpg +0 -0
  174. data/spec/support/fixtures/s3.yml +8 -0
  175. data/spec/support/fixtures/spaced file.jpg +0 -0
  176. data/spec/support/fixtures/spaced file.png +0 -0
  177. data/spec/support/fixtures/text.txt +1 -0
  178. data/spec/support/fixtures/twopage.pdf +0 -0
  179. data/spec/support/fixtures/uppercase.PNG +0 -0
  180. data/spec/support/matchers/accept.rb +5 -0
  181. data/spec/support/matchers/exist.rb +5 -0
  182. data/spec/support/matchers/have_column.rb +23 -0
  183. data/spec/support/mock_attachment.rb +22 -0
  184. data/spec/support/mock_interpolator.rb +24 -0
  185. data/spec/support/mock_url_generator_builder.rb +27 -0
  186. data/spec/support/model_reconstruction.rb +68 -0
  187. data/spec/support/reporting.rb +11 -0
  188. data/spec/support/test_data.rb +13 -0
  189. data/spec/support/version_helper.rb +9 -0
  190. metadata +713 -0
@@ -0,0 +1,29 @@
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
29
+
@@ -0,0 +1,30 @@
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
+ if record.send("#{attribute}_file_name").blank?
8
+ record.errors.add(attribute, :blank, options)
9
+ end
10
+ end
11
+
12
+ def self.helper_method_name
13
+ :validates_attachment_presence
14
+ end
15
+ end
16
+
17
+ module HelperMethods
18
+ # Places ActiveModel validations on the presence of a file.
19
+ # Options:
20
+ # * +if+: A lambda or name of an instance method. Validation will only
21
+ # be run if this lambda or method returns true.
22
+ # * +unless+: Same as +if+ but validates if lambda or method returns false.
23
+ def validates_attachment_presence(*attr_names)
24
+ options = _merge_attributes(attr_names)
25
+ validates_with AttachmentPresenceValidator, options.dup
26
+ validate_before_processing AttachmentPresenceValidator, options.dup
27
+ end
28
+ end
29
+ end
30
+ 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]
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.has_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,27 @@
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
+ end
12
+ end
13
+
14
+ module HelperMethods
15
+ # Places ActiveModel validations on the presence of a file.
16
+ # Options:
17
+ # * +if+: A lambda or name of an instance method. Validation will only
18
+ # be run if this lambda or method returns true.
19
+ # * +unless+: Same as +if+ but validates if lambda or method returns false.
20
+ def validates_media_type_spoof_detection(*attr_names)
21
+ options = _merge_attributes(attr_names)
22
+ validates_with MediaTypeSpoofDetectionValidator, options.dup
23
+ validate_before_processing MediaTypeSpoofDetectionValidator, options.dup
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module Paperclip
2
+ VERSION = "5.0.0.beta2" unless defined? Paperclip::VERSION
3
+ end
@@ -0,0 +1,127 @@
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
+ class_name
9
+ end
10
+
11
+ def self.obtain_attachments(klass)
12
+ klass = Paperclip.class_for(klass.to_s)
13
+ name = ENV['ATTACHMENT'] || ENV['attachment']
14
+
15
+ attachment_names = Paperclip::AttachmentRegistry.names_for(klass)
16
+
17
+ if attachment_names.empty?
18
+ raise "Class #{klass.name} has no attachments specified"
19
+ end
20
+
21
+ if !name.blank? && attachment_names.map(&:to_s).include?(name.to_s)
22
+ [ name ]
23
+ else
24
+ attachment_names
25
+ end
26
+ end
27
+
28
+ def self.log_error(error)
29
+ $stderr.puts error
30
+ end
31
+ end
32
+ end
33
+
34
+ namespace :paperclip do
35
+ desc "Refreshes both metadata and thumbnails."
36
+ task :refresh => ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]
37
+
38
+ namespace :refresh do
39
+ desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT and STYLES splitted by comma)."
40
+ task :thumbnails => :environment do
41
+ klass = Paperclip::Task.obtain_class
42
+ names = Paperclip::Task.obtain_attachments(klass)
43
+ styles = (ENV['STYLES'] || ENV['styles'] || '').split(',').map(&:to_sym)
44
+ names.each do |name|
45
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
46
+ attachment = instance.send(name)
47
+ begin
48
+ attachment.reprocess!(*styles)
49
+ rescue Exception => e
50
+ Paperclip::Task.log_error("exception while processing #{klass} ID #{instance.id}:")
51
+ Paperclip::Task.log_error(" " + e.message + "\n")
52
+ end
53
+ unless instance.errors.blank?
54
+ Paperclip::Task.log_error("errors while processing #{klass} ID #{instance.id}:")
55
+ Paperclip::Task.log_error(" " + instance.errors.full_messages.join("\n ") + "\n")
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
62
+ task :metadata => :environment do
63
+ klass = Paperclip::Task.obtain_class
64
+ names = Paperclip::Task.obtain_attachments(klass)
65
+ names.each do |name|
66
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
67
+ if file = Paperclip.io_adapters.for(instance.send(name))
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
+ end
94
+
95
+ desc "Cleans out invalid attachments. Useful after you've added new validations."
96
+ task :clean => :environment do
97
+ klass = Paperclip::Task.obtain_class
98
+ names = Paperclip::Task.obtain_attachments(klass)
99
+ names.each do |name|
100
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
101
+ unless instance.valid?
102
+ attributes = %w(file_size file_name content_type).map{ |suffix| "#{name}_#{suffix}".to_sym }
103
+ if attributes.any?{ |attribute| instance.errors[attribute].present? }
104
+ instance.send("#{name}=", nil)
105
+ instance.save(:validate => false)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ desc "find missing attachments. Useful to know which attachments are broken"
113
+ task :find_broken_attachments => :environment do
114
+ klass = Paperclip::Task.obtain_class
115
+ names = Paperclip::Task.obtain_attachments(klass)
116
+ names.each do |name|
117
+ Paperclip.each_instance_with_attachment(klass, name) do |instance|
118
+ attachment = instance.send(name)
119
+ if attachment.exists?
120
+ print "."
121
+ else
122
+ Paperclip::Task.log_error("#{instance.class}##{attachment.name}, #{instance.id}, #{attachment.url}")
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,54 @@
1
+ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
2
+ require 'paperclip/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "paperclip_jk"
6
+ s.version = Paperclip::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.author = "Jon Yurek"
9
+ s.email = ["jyurek@thoughtbot.com"]
10
+ s.homepage = "https://github.com/thoughtbot/paperclip"
11
+ s.summary = "File attachments as attributes for ActiveRecord"
12
+ s.description = "Easy upload management for ActiveRecord"
13
+ s.license = "MIT"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ if File.exist?('UPGRADING')
21
+ s.post_install_message = File.read("UPGRADING")
22
+ end
23
+
24
+ s.requirements << "ImageMagick"
25
+ s.required_ruby_version = ">= 2.1.0"
26
+
27
+ s.add_dependency('activemodel', '>= 4.2.0')
28
+ s.add_dependency('activesupport', '>= 4.2.0')
29
+ s.add_dependency('cocaine', '~> 0.5.5')
30
+ s.add_dependency('mime-types')
31
+ s.add_dependency('mimemagic', '~> 0.3.0')
32
+
33
+ s.add_development_dependency('activerecord', '>= 4.2.0')
34
+ s.add_development_dependency('shoulda')
35
+ s.add_development_dependency('rspec', '~> 3.0')
36
+ s.add_development_dependency('appraisal')
37
+ s.add_development_dependency('mocha')
38
+ s.add_development_dependency('aws-sdk', '>= 2.0.34', '< 3.0')
39
+ s.add_development_dependency('bourne')
40
+ s.add_development_dependency('cucumber', '~> 1.3.18')
41
+ s.add_development_dependency('aruba', '~> 0.9.0')
42
+ s.add_development_dependency('nokogiri')
43
+ s.add_development_dependency('capybara')
44
+ s.add_development_dependency('bundler')
45
+ s.add_development_dependency('fog-aws')
46
+ s.add_development_dependency('fog-local')
47
+ s.add_development_dependency('launchy')
48
+ s.add_development_dependency('rake')
49
+ s.add_development_dependency('fakeweb')
50
+ s.add_development_dependency('railties')
51
+ s.add_development_dependency('actionmailer', '>= 4.2.0')
52
+ s.add_development_dependency('generator_spec')
53
+ s.add_development_dependency('timecop')
54
+ end
@@ -0,0 +1,134 @@
1
+ require 'paperclip/matchers'
2
+
3
+ module Paperclip
4
+ # =Paperclip Shoulda Macros
5
+ #
6
+ # These macros are intended for use with shoulda, and will be included into
7
+ # your tests automatically. All of the macros use the standard shoulda
8
+ # assumption that the name of the test is based on the name of the model
9
+ # you're testing (that is, UserTest is the test for the User model), and
10
+ # will load that class for testing purposes.
11
+ module Shoulda
12
+ include Matchers
13
+ # This will test whether you have defined your attachment correctly by
14
+ # checking for all the required fields exist after the definition of the
15
+ # attachment.
16
+ def should_have_attached_file name
17
+ klass = self.name.gsub(/Test$/, '').constantize
18
+ matcher = have_attached_file name
19
+ should matcher.description do
20
+ assert_accepts(matcher, klass)
21
+ end
22
+ end
23
+
24
+ # Tests for validations on the presence of the attachment.
25
+ def should_validate_attachment_presence name
26
+ klass = self.name.gsub(/Test$/, '').constantize
27
+ matcher = validate_attachment_presence name
28
+ should matcher.description do
29
+ assert_accepts(matcher, klass)
30
+ end
31
+ end
32
+
33
+ # Tests that you have content_type validations specified. There are two
34
+ # options, :valid and :invalid. Both accept an array of strings. The
35
+ # strings should be a list of content types which will pass and fail
36
+ # validation, respectively.
37
+ def should_validate_attachment_content_type name, options = {}
38
+ klass = self.name.gsub(/Test$/, '').constantize
39
+ valid = [options[:valid]].flatten
40
+ invalid = [options[:invalid]].flatten
41
+ matcher = validate_attachment_content_type(name).allowing(valid).rejecting(invalid)
42
+ should matcher.description do
43
+ assert_accepts(matcher, klass)
44
+ end
45
+ end
46
+
47
+ # Tests to ensure that you have file size validations turned on. You
48
+ # can pass the same options to this that you can to
49
+ # validate_attachment_file_size - :less_than, :greater_than, and :in.
50
+ # :less_than checks that a file is less than a certain size, :greater_than
51
+ # checks that a file is more than a certain size, and :in takes a Range or
52
+ # Array which specifies the lower and upper limits of the file size.
53
+ def should_validate_attachment_size name, options = {}
54
+ klass = self.name.gsub(/Test$/, '').constantize
55
+ min = options[:greater_than] || (options[:in] && options[:in].first) || 0
56
+ max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
57
+ range = (min..max)
58
+ matcher = validate_attachment_size(name).in(range)
59
+ should matcher.description do
60
+ assert_accepts(matcher, klass)
61
+ end
62
+ end
63
+
64
+ # Stubs the HTTP PUT for an attachment using S3 storage.
65
+ #
66
+ # @example
67
+ # stub_paperclip_s3('user', 'avatar', 'png')
68
+ def stub_paperclip_s3(model, attachment, extension)
69
+ definition = model.gsub(" ", "_").classify.constantize.
70
+ attachment_definitions[attachment.to_sym]
71
+
72
+ path = "http://s3.amazonaws.com/:id/#{definition[:path]}"
73
+ path.gsub!(/:([^\/\.]+)/) do |match|
74
+ "([^\/\.]+)"
75
+ end
76
+
77
+ begin
78
+ FakeWeb.register_uri(:put, Regexp.new(path), :body => "OK")
79
+ rescue NameError
80
+ raise NameError, "the stub_paperclip_s3 shoulda macro requires the fakeweb gem."
81
+ end
82
+ end
83
+
84
+ # Stub S3 and return a file for attachment. Best with Factory Girl.
85
+ # Uses a strict directory convention:
86
+ #
87
+ # features/support/paperclip
88
+ #
89
+ # This method is used by the Paperclip-provided Cucumber step:
90
+ #
91
+ # When I attach a "demo_tape" "mp3" file to a "band" on S3
92
+ #
93
+ # @example
94
+ # Factory.define :band_with_demo_tape, :parent => :band do |band|
95
+ # band.demo_tape { band.paperclip_fixture("band", "demo_tape", "png") }
96
+ # end
97
+ def paperclip_fixture(model, attachment, extension)
98
+ stub_paperclip_s3(model, attachment, extension)
99
+ base_path = File.join(File.dirname(__FILE__), "..", "..",
100
+ "features", "support", "paperclip")
101
+ File.new(File.join(base_path, model, "#{attachment}.#{extension}"))
102
+ end
103
+ end
104
+ end
105
+
106
+ if defined?(ActionDispatch::Integration::Session)
107
+ class ActionDispatch::IntegrationTest::Session #:nodoc:
108
+ include Paperclip::Shoulda
109
+ end
110
+ elsif defined?(ActionController::Integration::Session)
111
+ class ActionController::Integration::Session #:nodoc:
112
+ include Paperclip::Shoulda
113
+ end
114
+ end
115
+
116
+ if defined?(FactoryGirl::Factory)
117
+ class FactoryGirl::Factory
118
+ include Paperclip::Shoulda #:nodoc:
119
+ end
120
+ else
121
+ class Factory
122
+ include Paperclip::Shoulda #:nodoc:
123
+ end
124
+ end
125
+
126
+ if defined?(Minitest)
127
+ class Minitest::Unit::TestCase #:nodoc:
128
+ extend Paperclip::Shoulda
129
+ end
130
+ elsif defined?(Test)
131
+ class Test::Unit::TestCase #:nodoc:
132
+ extend Paperclip::Shoulda
133
+ end
134
+ end