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,43 @@
|
|
|
1
|
+
module Paperclip
|
|
2
|
+
# Overriding some implementation of Tempfile
|
|
3
|
+
class Tempfile < ::Tempfile
|
|
4
|
+
# Due to how ImageMagick handles its image format conversion and how
|
|
5
|
+
# Tempfile handles its naming scheme, it is necessary to override how
|
|
6
|
+
# Tempfile makes # its names so as to allow for file extensions. Idea
|
|
7
|
+
# taken from the comments on this blog post:
|
|
8
|
+
# http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
|
|
9
|
+
#
|
|
10
|
+
# This is Ruby 1.9.3's implementation.
|
|
11
|
+
def make_tmpname(prefix_suffix, n)
|
|
12
|
+
if RUBY_PLATFORM =~ /java/
|
|
13
|
+
case prefix_suffix
|
|
14
|
+
when String
|
|
15
|
+
prefix, suffix = prefix_suffix, ''
|
|
16
|
+
when Array
|
|
17
|
+
prefix, suffix = *prefix_suffix
|
|
18
|
+
else
|
|
19
|
+
raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
t = Time.now.strftime("%y%m%d")
|
|
23
|
+
path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
|
|
24
|
+
else
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module TempfileEncoding
|
|
31
|
+
# This overrides Tempfile#binmode to make sure that the extenal encoding
|
|
32
|
+
# for binary mode is ASCII-8BIT. This behavior is what's in CRuby, but not
|
|
33
|
+
# in JRuby
|
|
34
|
+
def binmode
|
|
35
|
+
set_encoding('ASCII-8BIT')
|
|
36
|
+
super
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if RUBY_PLATFORM =~ /java/
|
|
42
|
+
::Tempfile.send :include, Paperclip::TempfileEncoding
|
|
43
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Paperclip
|
|
2
|
+
class TempfileFactory
|
|
3
|
+
|
|
4
|
+
def generate(name = random_name)
|
|
5
|
+
@name = name
|
|
6
|
+
file = Tempfile.new([basename, extension])
|
|
7
|
+
file.binmode
|
|
8
|
+
file
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def extension
|
|
12
|
+
File.extname(@name)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def basename
|
|
16
|
+
Digest::MD5.hexdigest(File.basename(@name, extension))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def random_name
|
|
20
|
+
SecureRandom.uuid
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module Paperclip
|
|
2
|
+
# Handles thumbnailing images that are uploaded.
|
|
3
|
+
class Thumbnail < Processor
|
|
4
|
+
|
|
5
|
+
attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options,
|
|
6
|
+
:source_file_options, :animated, :auto_orient
|
|
7
|
+
|
|
8
|
+
# List of formats that we need to preserve animation
|
|
9
|
+
ANIMATED_FORMATS = %w(gif)
|
|
10
|
+
|
|
11
|
+
# Creates a Thumbnail object set to work on the +file+ given. It
|
|
12
|
+
# will attempt to transform the image into one defined by +target_geometry+
|
|
13
|
+
# which is a "WxH"-style string. +format+ will be inferred from the +file+
|
|
14
|
+
# unless specified. Thumbnail creation will raise no errors unless
|
|
15
|
+
# +whiny+ is true (which it is, by default. If +convert_options+ is
|
|
16
|
+
# set, the options will be appended to the convert command upon image conversion
|
|
17
|
+
#
|
|
18
|
+
# Options include:
|
|
19
|
+
#
|
|
20
|
+
# +geometry+ - the desired width and height of the thumbnail (required)
|
|
21
|
+
# +file_geometry_parser+ - an object with a method named +from_file+ that takes an image file and produces its geometry and a +transformation_to+. Defaults to Paperclip::Geometry
|
|
22
|
+
# +string_geometry_parser+ - an object with a method named +parse+ that takes a string and produces an object with +width+, +height+, and +to_s+ accessors. Defaults to Paperclip::Geometry
|
|
23
|
+
# +source_file_options+ - flags passed to the +convert+ command that influence how the source file is read
|
|
24
|
+
# +convert_options+ - flags passed to the +convert+ command that influence how the image is processed
|
|
25
|
+
# +whiny+ - whether to raise an error when processing fails. Defaults to true
|
|
26
|
+
# +format+ - the desired filename extension
|
|
27
|
+
# +animated+ - whether to merge all the layers in the image. Defaults to true
|
|
28
|
+
def initialize(file, options = {}, attachment = nil)
|
|
29
|
+
super
|
|
30
|
+
|
|
31
|
+
geometry = options[:geometry].to_s
|
|
32
|
+
@crop = geometry[-1,1] == '#'
|
|
33
|
+
@target_geometry = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
|
|
34
|
+
@current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
|
|
35
|
+
@source_file_options = options[:source_file_options]
|
|
36
|
+
@convert_options = options[:convert_options]
|
|
37
|
+
@whiny = options.fetch(:whiny, true)
|
|
38
|
+
@format = options[:format]
|
|
39
|
+
@animated = options.fetch(:animated, true)
|
|
40
|
+
@auto_orient = options.fetch(:auto_orient, true)
|
|
41
|
+
if @auto_orient && @current_geometry.respond_to?(:auto_orient)
|
|
42
|
+
@current_geometry.auto_orient
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@source_file_options = @source_file_options.split(/\s+/) if @source_file_options.respond_to?(:split)
|
|
46
|
+
@convert_options = @convert_options.split(/\s+/) if @convert_options.respond_to?(:split)
|
|
47
|
+
|
|
48
|
+
@current_format = File.extname(@file.path)
|
|
49
|
+
@basename = File.basename(@file.path, @current_format)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns true if the +target_geometry+ is meant to crop.
|
|
53
|
+
def crop?
|
|
54
|
+
@crop
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns true if the image is meant to make use of additional convert options.
|
|
58
|
+
def convert_options?
|
|
59
|
+
!@convert_options.nil? && !@convert_options.empty?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile
|
|
63
|
+
# that contains the new image.
|
|
64
|
+
def make
|
|
65
|
+
src = @file
|
|
66
|
+
filename = [@basename, @format ? ".#{@format}" : ""].join
|
|
67
|
+
dst = TempfileFactory.new.generate(filename)
|
|
68
|
+
|
|
69
|
+
begin
|
|
70
|
+
parameters = []
|
|
71
|
+
parameters << source_file_options
|
|
72
|
+
parameters << ":source"
|
|
73
|
+
parameters << transformation_command
|
|
74
|
+
parameters << convert_options
|
|
75
|
+
parameters << ":dest"
|
|
76
|
+
|
|
77
|
+
parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
|
|
78
|
+
|
|
79
|
+
success = convert(parameters, :source => "#{File.expand_path(src.path)}#{'[0]' unless animated?}", :dest => File.expand_path(dst.path))
|
|
80
|
+
rescue Cocaine::ExitStatusError => e
|
|
81
|
+
raise Paperclip::Error, "There was an error processing the thumbnail for #{@basename}" if @whiny
|
|
82
|
+
rescue Cocaine::CommandNotFoundError => e
|
|
83
|
+
raise Paperclip::Errors::CommandNotFoundError.new("Could not run the `convert` command. Please install ImageMagick.")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
dst
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Returns the command ImageMagick's +convert+ needs to transform the image
|
|
90
|
+
# into the thumbnail.
|
|
91
|
+
def transformation_command
|
|
92
|
+
scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
|
|
93
|
+
trans = []
|
|
94
|
+
trans << "-coalesce" if animated?
|
|
95
|
+
trans << "-auto-orient" if auto_orient
|
|
96
|
+
trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
|
|
97
|
+
trans << "-crop" << %["#{crop}"] << "+repage" if crop
|
|
98
|
+
trans << '-layers "optimize"' if animated?
|
|
99
|
+
trans
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
protected
|
|
103
|
+
|
|
104
|
+
# Return true if the format is animated
|
|
105
|
+
def animated?
|
|
106
|
+
@animated && (ANIMATED_FORMATS.include?(@format.to_s) || @format.blank?) && identified_as_animated?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Return true if ImageMagick's +identify+ returns an animated format
|
|
110
|
+
def identified_as_animated?
|
|
111
|
+
if @identified_as_animated.nil?
|
|
112
|
+
@identified_as_animated = ANIMATED_FORMATS.include? identify("-format %m :file", :file => "#{@file.path}[0]").to_s.downcase.strip
|
|
113
|
+
end
|
|
114
|
+
@identified_as_animated
|
|
115
|
+
rescue Cocaine::ExitStatusError => e
|
|
116
|
+
raise Paperclip::Error, "There was an error running `identify` for #{@basename}" if @whiny
|
|
117
|
+
rescue Cocaine::CommandNotFoundError => e
|
|
118
|
+
raise Paperclip::Errors::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
|
|
3
|
+
module Paperclip
|
|
4
|
+
class UrlGenerator
|
|
5
|
+
def initialize(attachment, attachment_options)
|
|
6
|
+
@attachment = attachment
|
|
7
|
+
@attachment_options = attachment_options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def for(style_name, options)
|
|
11
|
+
timestamp_as_needed(
|
|
12
|
+
escape_url_as_needed(
|
|
13
|
+
@attachment_options[:interpolator].interpolate(most_appropriate_url, @attachment, style_name),
|
|
14
|
+
options
|
|
15
|
+
), options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# This method is all over the place.
|
|
21
|
+
def default_url
|
|
22
|
+
if @attachment_options[:default_url].respond_to?(:call)
|
|
23
|
+
@attachment_options[:default_url].call(@attachment)
|
|
24
|
+
elsif @attachment_options[:default_url].is_a?(Symbol)
|
|
25
|
+
@attachment.instance.send(@attachment_options[:default_url])
|
|
26
|
+
else
|
|
27
|
+
@attachment_options[:default_url]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def most_appropriate_url
|
|
32
|
+
if @attachment.original_filename.nil?
|
|
33
|
+
default_url
|
|
34
|
+
else
|
|
35
|
+
@attachment_options[:url]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def timestamp_as_needed(url, options)
|
|
40
|
+
if options[:timestamp] && timestamp_possible?
|
|
41
|
+
delimiter_char = url.match(/\?.+=/) ? '&' : '?'
|
|
42
|
+
"#{url}#{delimiter_char}#{@attachment.updated_at.to_s}"
|
|
43
|
+
else
|
|
44
|
+
url
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def timestamp_possible?
|
|
49
|
+
@attachment.respond_to?(:updated_at) && @attachment.updated_at.present?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def escape_url_as_needed(url, options)
|
|
53
|
+
if options[:escape]
|
|
54
|
+
escape_url(url)
|
|
55
|
+
else
|
|
56
|
+
url
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def escape_url(url)
|
|
61
|
+
if url.respond_to?(:escape)
|
|
62
|
+
url.escape
|
|
63
|
+
else
|
|
64
|
+
URI.escape(url).gsub(escape_regex){|m| "%#{m.ord.to_s(16).upcase}" }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def escape_regex
|
|
69
|
+
/[\?\(\)\[\]\+]/
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'active_model'
|
|
2
|
+
require 'active_support/concern'
|
|
3
|
+
require 'active_support/core_ext/array/wrap'
|
|
4
|
+
require 'paperclip/validators/attachment_content_type_validator'
|
|
5
|
+
require 'paperclip/validators/attachment_file_name_validator'
|
|
6
|
+
require 'paperclip/validators/attachment_presence_validator'
|
|
7
|
+
require 'paperclip/validators/attachment_size_validator'
|
|
8
|
+
require 'paperclip/validators/media_type_spoof_detection_validator'
|
|
9
|
+
require 'paperclip/validators/attachment_file_type_ignorance_validator'
|
|
10
|
+
|
|
11
|
+
module Paperclip
|
|
12
|
+
module Validators
|
|
13
|
+
extend ActiveSupport::Concern
|
|
14
|
+
|
|
15
|
+
included do
|
|
16
|
+
extend HelperMethods
|
|
17
|
+
include HelperMethods
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
::Paperclip::REQUIRED_VALIDATORS = [AttachmentFileNameValidator, AttachmentContentTypeValidator, AttachmentFileTypeIgnoranceValidator]
|
|
21
|
+
|
|
22
|
+
module ClassMethods
|
|
23
|
+
# This method is a shortcut to validator classes that is in
|
|
24
|
+
# "Attachment...Validator" format. It is almost the same thing as the
|
|
25
|
+
# +validates+ method that shipped with Rails, but this is customized to
|
|
26
|
+
# be using with attachment validators. This is helpful when you're using
|
|
27
|
+
# multiple attachment validators on a single attachment.
|
|
28
|
+
#
|
|
29
|
+
# Example of using the validator:
|
|
30
|
+
#
|
|
31
|
+
# validates_attachment :avatar, :presence => true,
|
|
32
|
+
# :content_type => { :content_type => "image/jpg" },
|
|
33
|
+
# :size => { :in => 0..10.kilobytes }
|
|
34
|
+
#
|
|
35
|
+
def validates_attachment(*attributes)
|
|
36
|
+
options = attributes.extract_options!.dup
|
|
37
|
+
|
|
38
|
+
Paperclip::Validators.constants.each do |constant|
|
|
39
|
+
if constant.to_s =~ /\AAttachment(.+)Validator\Z/
|
|
40
|
+
validator_kind = $1.underscore.to_sym
|
|
41
|
+
|
|
42
|
+
if options.has_key?(validator_kind)
|
|
43
|
+
validator_options = options.delete(validator_kind)
|
|
44
|
+
validator_options = {} if validator_options == true
|
|
45
|
+
conditional_options = options.slice(:if, :unless)
|
|
46
|
+
Array.wrap(validator_options).each do |local_options|
|
|
47
|
+
method_name = Paperclip::Validators.const_get(constant.to_s).helper_method_name
|
|
48
|
+
send(method_name, attributes, local_options.merge(conditional_options))
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def validate_before_processing(validator_class, options)
|
|
56
|
+
options = options.dup
|
|
57
|
+
attributes = options.delete(:attributes)
|
|
58
|
+
attributes.each do |attribute|
|
|
59
|
+
options[:attributes] = [attribute]
|
|
60
|
+
create_validating_before_filter(attribute, validator_class, options)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def create_validating_before_filter(attribute, validator_class, options)
|
|
65
|
+
if_clause = options.delete(:if)
|
|
66
|
+
unless_clause = options.delete(:unless)
|
|
67
|
+
send(:"before_#{attribute}_post_process", :if => if_clause, :unless => unless_clause) do |*args|
|
|
68
|
+
validator_class.new(options.dup).validate(self)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -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.has_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.has_key?(:content_type) || options.has_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,80 @@
|
|
|
1
|
+
module Paperclip
|
|
2
|
+
module Validators
|
|
3
|
+
class AttachmentFileNameValidator < ActiveModel::EachValidator
|
|
4
|
+
def initialize(options)
|
|
5
|
+
options[:allow_nil] = true unless options.has_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
|
+
if allowed.present? && allowed.none? { |type| type === value }
|
|
32
|
+
mark_invalid record, attribute, allowed
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def validate_blacklist(record, attribute, value)
|
|
37
|
+
if forbidden.present? && forbidden.any? { |type| type === value }
|
|
38
|
+
mark_invalid record, attribute, forbidden
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def mark_invalid(record, attribute, patterns)
|
|
43
|
+
record.errors.add attribute, :invalid, options.merge(:names => patterns.join(', '))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def allowed
|
|
47
|
+
[options[:matches]].flatten.compact
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def forbidden
|
|
51
|
+
[options[:not]].flatten.compact
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def check_validity!
|
|
55
|
+
unless options.has_key?(:matches) || options.has_key?(:not)
|
|
56
|
+
raise ArgumentError, "You must pass in either :matches or :not to the validator"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
module HelperMethods
|
|
62
|
+
# Places ActiveModel validations on the name of the file
|
|
63
|
+
# assigned. The possible options are:
|
|
64
|
+
# * +matches+: Allowed filename patterns as Regexps. Can be a single one
|
|
65
|
+
# or an array.
|
|
66
|
+
# * +not+: Forbidden file name patterns, specified the same was as +matches+.
|
|
67
|
+
# * +message+: The message to display when the uploaded file has an invalid
|
|
68
|
+
# name.
|
|
69
|
+
# * +if+: A lambda or name of an instance method. Validation will only
|
|
70
|
+
# be run is this lambda or method returns true.
|
|
71
|
+
# * +unless+: Same as +if+ but validates if lambda or method returns false.
|
|
72
|
+
def validates_attachment_file_name(*attr_names)
|
|
73
|
+
options = _merge_attributes(attr_names)
|
|
74
|
+
validates_with AttachmentFileNameValidator, options.dup
|
|
75
|
+
validate_before_processing AttachmentFileNameValidator, options.dup
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|