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.
- checksums.yaml +15 -0
- data/.gitignore +21 -0
- data/.hound.yml +1066 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +26 -0
- data/Appraisals +27 -0
- data/CONTRIBUTING.md +86 -0
- data/Gemfile +15 -0
- data/LICENSE +24 -0
- data/NEWS +424 -0
- data/README.md +999 -0
- data/RELEASING.md +17 -0
- data/Rakefile +44 -0
- data/UPGRADING +17 -0
- data/features/basic_integration.feature +81 -0
- data/features/migration.feature +70 -0
- data/features/rake_tasks.feature +62 -0
- data/features/step_definitions/attachment_steps.rb +110 -0
- data/features/step_definitions/html_steps.rb +15 -0
- data/features/step_definitions/rails_steps.rb +230 -0
- data/features/step_definitions/s3_steps.rb +14 -0
- data/features/step_definitions/web_steps.rb +107 -0
- data/features/support/env.rb +11 -0
- data/features/support/fakeweb.rb +13 -0
- data/features/support/file_helpers.rb +34 -0
- data/features/support/fixtures/boot_config.txt +15 -0
- data/features/support/fixtures/gemfile.txt +5 -0
- data/features/support/fixtures/preinitializer.txt +20 -0
- data/features/support/paths.rb +28 -0
- data/features/support/rails.rb +63 -0
- data/features/support/selectors.rb +19 -0
- data/gemfiles/4.2.awsv2.0.gemfile +17 -0
- data/gemfiles/4.2.awsv2.1.gemfile +17 -0
- data/gemfiles/4.2.awsv2.gemfile +20 -0
- data/gemfiles/5.0.awsv2.0.gemfile +17 -0
- data/gemfiles/5.0.awsv2.1.gemfile +17 -0
- data/gemfiles/5.0.awsv2.gemfile +25 -0
- data/lib/generators/paperclip/USAGE +8 -0
- data/lib/generators/paperclip/paperclip_generator.rb +30 -0
- data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
- data/lib/paperclip.rb +211 -0
- data/lib/paperclip/attachment.rb +610 -0
- data/lib/paperclip/attachment_registry.rb +60 -0
- data/lib/paperclip/callbacks.rb +42 -0
- data/lib/paperclip/content_type_detector.rb +80 -0
- data/lib/paperclip/errors.rb +34 -0
- data/lib/paperclip/file_command_content_type_detector.rb +30 -0
- data/lib/paperclip/filename_cleaner.rb +16 -0
- data/lib/paperclip/geometry.rb +158 -0
- data/lib/paperclip/geometry_detector_factory.rb +48 -0
- data/lib/paperclip/geometry_parser_factory.rb +31 -0
- data/lib/paperclip/glue.rb +17 -0
- data/lib/paperclip/has_attached_file.rb +115 -0
- data/lib/paperclip/helpers.rb +60 -0
- data/lib/paperclip/interpolations.rb +197 -0
- data/lib/paperclip/interpolations/plural_cache.rb +18 -0
- data/lib/paperclip/io_adapters/abstract_adapter.rb +47 -0
- data/lib/paperclip/io_adapters/attachment_adapter.rb +36 -0
- data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
- data/lib/paperclip/io_adapters/empty_string_adapter.rb +18 -0
- data/lib/paperclip/io_adapters/file_adapter.rb +22 -0
- data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +15 -0
- data/lib/paperclip/io_adapters/identity_adapter.rb +12 -0
- data/lib/paperclip/io_adapters/nil_adapter.rb +34 -0
- data/lib/paperclip/io_adapters/registry.rb +32 -0
- data/lib/paperclip/io_adapters/stringio_adapter.rb +33 -0
- data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +42 -0
- data/lib/paperclip/io_adapters/uri_adapter.rb +44 -0
- data/lib/paperclip/locales/en.yml +18 -0
- data/lib/paperclip/logger.rb +21 -0
- data/lib/paperclip/matchers.rb +64 -0
- data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +100 -0
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +96 -0
- data/lib/paperclip/media_type_spoof_detector.rb +89 -0
- data/lib/paperclip/missing_attachment_styles.rb +79 -0
- data/lib/paperclip/processor.rb +48 -0
- data/lib/paperclip/processor_helpers.rb +50 -0
- data/lib/paperclip/rails_environment.rb +25 -0
- data/lib/paperclip/railtie.rb +31 -0
- data/lib/paperclip/schema.rb +77 -0
- data/lib/paperclip/storage.rb +4 -0
- data/lib/paperclip/storage/database.rb +140 -0
- data/lib/paperclip/storage/filesystem.rb +90 -0
- data/lib/paperclip/storage/fog.rb +244 -0
- data/lib/paperclip/storage/s3.rb +442 -0
- data/lib/paperclip/style.rb +109 -0
- data/lib/paperclip/tempfile.rb +43 -0
- data/lib/paperclip/tempfile_factory.rb +23 -0
- data/lib/paperclip/thumbnail.rb +121 -0
- data/lib/paperclip/url_generator.rb +72 -0
- data/lib/paperclip/validators.rb +74 -0
- data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
- data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
- data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
- data/lib/paperclip/validators/attachment_presence_validator.rb +30 -0
- data/lib/paperclip/validators/attachment_size_validator.rb +109 -0
- data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
- data/lib/paperclip/version.rb +3 -0
- data/lib/tasks/paperclip.rake +127 -0
- data/paperclip.gemspec +54 -0
- data/shoulda_macros/paperclip.rb +134 -0
- data/spec/database.yml +4 -0
- data/spec/paperclip/attachment_definitions_spec.rb +13 -0
- data/spec/paperclip/attachment_processing_spec.rb +80 -0
- data/spec/paperclip/attachment_registry_spec.rb +158 -0
- data/spec/paperclip/attachment_spec.rb +1517 -0
- data/spec/paperclip/content_type_detector_spec.rb +48 -0
- data/spec/paperclip/file_command_content_type_detector_spec.rb +26 -0
- data/spec/paperclip/filename_cleaner_spec.rb +14 -0
- data/spec/paperclip/geometry_detector_spec.rb +39 -0
- data/spec/paperclip/geometry_parser_spec.rb +73 -0
- data/spec/paperclip/geometry_spec.rb +255 -0
- data/spec/paperclip/glue_spec.rb +44 -0
- data/spec/paperclip/has_attached_file_spec.rb +158 -0
- data/spec/paperclip/integration_spec.rb +668 -0
- data/spec/paperclip/interpolations_spec.rb +262 -0
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +78 -0
- data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +139 -0
- data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +83 -0
- data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
- data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +113 -0
- data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
- data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
- data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
- data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
- data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +102 -0
- data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
- data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +109 -0
- data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
- data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
- data/spec/paperclip/media_type_spoof_detector_spec.rb +70 -0
- data/spec/paperclip/meta_class_spec.rb +30 -0
- data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +84 -0
- data/spec/paperclip/paperclip_spec.rb +192 -0
- data/spec/paperclip/plural_cache_spec.rb +37 -0
- data/spec/paperclip/processor_helpers_spec.rb +57 -0
- data/spec/paperclip/processor_spec.rb +26 -0
- data/spec/paperclip/rails_environment_spec.rb +33 -0
- data/spec/paperclip/rake_spec.rb +103 -0
- data/spec/paperclip/schema_spec.rb +248 -0
- data/spec/paperclip/storage/filesystem_spec.rb +79 -0
- data/spec/paperclip/storage/fog_spec.rb +545 -0
- data/spec/paperclip/storage/s3_live_spec.rb +186 -0
- data/spec/paperclip/storage/s3_spec.rb +1583 -0
- data/spec/paperclip/style_spec.rb +255 -0
- data/spec/paperclip/tempfile_factory_spec.rb +33 -0
- data/spec/paperclip/thumbnail_spec.rb +500 -0
- data/spec/paperclip/url_generator_spec.rb +211 -0
- data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
- data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +160 -0
- data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
- data/spec/paperclip/validators/attachment_size_validator_spec.rb +235 -0
- data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +52 -0
- data/spec/paperclip/validators_spec.rb +164 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/support/assertions.rb +78 -0
- data/spec/support/fake_model.rb +25 -0
- data/spec/support/fake_rails.rb +12 -0
- data/spec/support/fixtures/12k.png +0 -0
- data/spec/support/fixtures/50x50.png +0 -0
- data/spec/support/fixtures/5k.png +0 -0
- data/spec/support/fixtures/animated +0 -0
- data/spec/support/fixtures/animated.gif +0 -0
- data/spec/support/fixtures/animated.unknown +0 -0
- data/spec/support/fixtures/bad.png +1 -0
- data/spec/support/fixtures/empty.html +1 -0
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/fixtures/fog.yml +8 -0
- data/spec/support/fixtures/rotated.jpg +0 -0
- data/spec/support/fixtures/s3.yml +8 -0
- data/spec/support/fixtures/spaced file.jpg +0 -0
- data/spec/support/fixtures/spaced file.png +0 -0
- data/spec/support/fixtures/text.txt +1 -0
- data/spec/support/fixtures/twopage.pdf +0 -0
- data/spec/support/fixtures/uppercase.PNG +0 -0
- data/spec/support/matchers/accept.rb +5 -0
- data/spec/support/matchers/exist.rb +5 -0
- data/spec/support/matchers/have_column.rb +23 -0
- data/spec/support/mock_attachment.rb +22 -0
- data/spec/support/mock_interpolator.rb +24 -0
- data/spec/support/mock_url_generator_builder.rb +27 -0
- data/spec/support/model_reconstruction.rb +68 -0
- data/spec/support/reporting.rb +11 -0
- data/spec/support/test_data.rb +13 -0
- data/spec/support/version_helper.rb +9 -0
- 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,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
|
data/paperclip.gemspec
ADDED
|
@@ -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
|