paperclip 4.2.0 → 4.2.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of paperclip might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Appraisals +6 -1
- data/Gemfile +1 -1
- data/NEWS +33 -0
- data/README.md +85 -7
- data/features/basic_integration.feature +15 -0
- data/features/step_definitions/rails_steps.rb +29 -0
- data/gemfiles/3.2.gemfile +13 -7
- data/gemfiles/4.0.gemfile +13 -7
- data/gemfiles/4.1.gemfile +15 -9
- data/gemfiles/4.2.gemfile +19 -0
- data/lib/paperclip.rb +1 -0
- data/lib/paperclip/attachment.rb +16 -8
- data/lib/paperclip/has_attached_file.rb +5 -3
- data/lib/paperclip/interpolations.rb +1 -1
- data/lib/paperclip/io_adapters/abstract_adapter.rb +1 -0
- data/lib/paperclip/locales/ja.yml +18 -0
- data/lib/paperclip/locales/pt-BR.yml +18 -0
- data/lib/paperclip/locales/zh-CN.yml +18 -0
- data/lib/paperclip/locales/zh-HK.yml +18 -0
- data/lib/paperclip/locales/zh-TW.yml +18 -0
- data/lib/paperclip/processor.rb +0 -37
- data/lib/paperclip/processor_helpers.rb +50 -0
- data/lib/paperclip/schema.rb +11 -3
- data/lib/paperclip/storage/fog.rb +6 -1
- data/lib/paperclip/storage/s3.rb +16 -6
- data/lib/paperclip/url_generator.rb +11 -3
- data/lib/paperclip/version.rb +1 -1
- data/spec/paperclip/has_attached_file_spec.rb +24 -0
- data/spec/paperclip/interpolations_spec.rb +11 -4
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +7 -0
- data/spec/paperclip/meta_class_spec.rb +1 -1
- data/spec/paperclip/processor_helpers_spec.rb +57 -0
- data/spec/paperclip/schema_spec.rb +50 -8
- data/spec/paperclip/storage/fog_spec.rb +30 -2
- data/spec/paperclip/storage/s3_spec.rb +33 -0
- data/spec/paperclip/url_generator_spec.rb +25 -0
- data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +5 -0
- data/spec/support/matchers/have_column.rb +14 -0
- metadata +13 -2
@@ -0,0 +1,19 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "sqlite3", "1.3.8", :platforms => :ruby
|
6
|
+
gem "jruby-openssl", :platforms => :jruby
|
7
|
+
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
|
8
|
+
gem "rubysl", :platforms => :rbx
|
9
|
+
gem "racc", :platforms => :rbx
|
10
|
+
gem "pry"
|
11
|
+
gem "rails", "~> 4.2.0.rc2"
|
12
|
+
gem "paperclip", :path => "../"
|
13
|
+
|
14
|
+
group :development, :test do
|
15
|
+
gem "mime-types", "~> 1.16"
|
16
|
+
gem "builder"
|
17
|
+
end
|
18
|
+
|
19
|
+
gemspec :path => "../"
|
data/lib/paperclip.rb
CHANGED
@@ -33,6 +33,7 @@ require 'paperclip/geometry_parser_factory'
|
|
33
33
|
require 'paperclip/geometry_detector_factory'
|
34
34
|
require 'paperclip/geometry'
|
35
35
|
require 'paperclip/processor'
|
36
|
+
require 'paperclip/processor_helpers'
|
36
37
|
require 'paperclip/tempfile'
|
37
38
|
require 'paperclip/thumbnail'
|
38
39
|
require 'paperclip/interpolations/plural_cache'
|
data/lib/paperclip/attachment.rb
CHANGED
@@ -31,12 +31,13 @@ module Paperclip
|
|
31
31
|
:use_default_time_zone => true,
|
32
32
|
:use_timestamp => true,
|
33
33
|
:whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails],
|
34
|
+
:validate_media_type => true,
|
34
35
|
:check_validity_before_processing => true
|
35
36
|
}
|
36
37
|
end
|
37
38
|
|
38
39
|
attr_reader :name, :instance, :default_style, :convert_options, :queued_for_write, :whiny,
|
39
|
-
:options, :interpolator, :source_file_options
|
40
|
+
:options, :interpolator, :source_file_options
|
40
41
|
attr_accessor :post_processing
|
41
42
|
|
42
43
|
# Creates an Attachment object. +name+ is the name of the attachment,
|
@@ -389,12 +390,8 @@ module Paperclip
|
|
389
390
|
@instance.class.validators.map(&:class)
|
390
391
|
end
|
391
392
|
|
392
|
-
def required_validator_classes
|
393
|
-
Paperclip::REQUIRED_VALIDATORS + Paperclip::REQUIRED_VALIDATORS.flat_map(&:descendants)
|
394
|
-
end
|
395
|
-
|
396
393
|
def missing_required_validator?
|
397
|
-
(active_validator_classes &
|
394
|
+
(active_validator_classes.flat_map(&:ancestors) & Paperclip::REQUIRED_VALIDATORS).empty?
|
398
395
|
end
|
399
396
|
|
400
397
|
def ensure_required_validations!
|
@@ -519,9 +516,14 @@ module Paperclip
|
|
519
516
|
def post_process_style(name, style) #:nodoc:
|
520
517
|
begin
|
521
518
|
raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
|
519
|
+
intermediate_files = []
|
520
|
+
|
522
521
|
@queued_for_write[name] = style.processors.inject(@queued_for_write[:original]) do |file, processor|
|
523
|
-
Paperclip.processor(processor).make(file, style.processor_options, self)
|
522
|
+
file = Paperclip.processor(processor).make(file, style.processor_options, self)
|
523
|
+
intermediate_files << file
|
524
|
+
file
|
524
525
|
end
|
526
|
+
|
525
527
|
unadapted_file = @queued_for_write[name]
|
526
528
|
@queued_for_write[name] = Paperclip.io_adapters.for(@queued_for_write[name])
|
527
529
|
unadapted_file.close if unadapted_file.respond_to?(:close)
|
@@ -529,6 +531,8 @@ module Paperclip
|
|
529
531
|
rescue Paperclip::Error => e
|
530
532
|
log("An error was received while processing: #{e.inspect}")
|
531
533
|
(@errors[:processing] ||= []) << e.message if @options[:whiny]
|
534
|
+
ensure
|
535
|
+
unlink_files(intermediate_files)
|
532
536
|
end
|
533
537
|
end
|
534
538
|
|
@@ -569,7 +573,11 @@ module Paperclip
|
|
569
573
|
|
570
574
|
# called by storage after the writes are flushed and before @queued_for_write is cleared
|
571
575
|
def after_flush_writes
|
572
|
-
@queued_for_write.
|
576
|
+
unlink_files(@queued_for_write.values)
|
577
|
+
end
|
578
|
+
|
579
|
+
def unlink_files(files)
|
580
|
+
Array(files).each do |file|
|
573
581
|
file.close unless file.closed?
|
574
582
|
file.unlink if file.respond_to?(:unlink) && file.path.present? && File.exist?(file.path)
|
575
583
|
end
|
@@ -79,9 +79,11 @@ module Paperclip
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def add_required_validations
|
82
|
-
|
83
|
-
|
84
|
-
|
82
|
+
if @options[:validate_media_type] != false
|
83
|
+
name = @name
|
84
|
+
@klass.validates_media_type_spoof_detection name,
|
85
|
+
:if => ->(instance){ instance.send(name).dirty? }
|
86
|
+
end
|
85
87
|
end
|
86
88
|
|
87
89
|
def add_active_record_callbacks
|
@@ -0,0 +1,18 @@
|
|
1
|
+
ja:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
in_between: "の容量は%{min}以上%{max}以下にしてください。"
|
5
|
+
spoofed_media_type: "の拡張子と内容が一致していません。"
|
6
|
+
|
7
|
+
number:
|
8
|
+
human:
|
9
|
+
storage_units:
|
10
|
+
format: "%n %u"
|
11
|
+
units:
|
12
|
+
byte:
|
13
|
+
one: "Byte"
|
14
|
+
other: "Bytes"
|
15
|
+
kb: "KB"
|
16
|
+
mb: "MB"
|
17
|
+
gb: "GB"
|
18
|
+
tb: "TB"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
pt-BR:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
in_between: "deve ter entre %{min} e %{max}"
|
5
|
+
spoofed_media_type: "tem uma extensão que não corresponde ao seu conteúdo"
|
6
|
+
|
7
|
+
number:
|
8
|
+
human:
|
9
|
+
storage_units:
|
10
|
+
format: "%n %u"
|
11
|
+
units:
|
12
|
+
byte:
|
13
|
+
one: "Byte"
|
14
|
+
other: "Bytes"
|
15
|
+
kb: "KB"
|
16
|
+
mb: "MB"
|
17
|
+
gb: "GB"
|
18
|
+
tb: "TB"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
zh-CN:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
in_between: "文件大小必须介于 %{min} 到 %{max} 之间"
|
5
|
+
spoofed_media_type: "扩展名与内容类型不符"
|
6
|
+
|
7
|
+
number:
|
8
|
+
human:
|
9
|
+
storage_units:
|
10
|
+
format: "%n %u"
|
11
|
+
units:
|
12
|
+
byte:
|
13
|
+
one: "Byte"
|
14
|
+
other: "Bytes"
|
15
|
+
kb: "KB"
|
16
|
+
mb: "MB"
|
17
|
+
gb: "GB"
|
18
|
+
tb: "TB"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
zh-HK:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
in_between: "必須介於%{min}到%{max}之間"
|
5
|
+
spoofed_media_type: "副檔名與內容類型不匹配"
|
6
|
+
|
7
|
+
number:
|
8
|
+
human:
|
9
|
+
storage_units:
|
10
|
+
format: "%n %u"
|
11
|
+
units:
|
12
|
+
byte:
|
13
|
+
one: "Byte"
|
14
|
+
other: "Bytes"
|
15
|
+
kb: "KB"
|
16
|
+
mb: "MB"
|
17
|
+
gb: "GB"
|
18
|
+
tb: "TB"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
zh-TW:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
in_between: "檔案大小必須介於 %{min} 到 %{max} 之間"
|
5
|
+
spoofed_media_type: "副檔名與內容類型不符"
|
6
|
+
|
7
|
+
number:
|
8
|
+
human:
|
9
|
+
storage_units:
|
10
|
+
format: "%n %u"
|
11
|
+
units:
|
12
|
+
byte:
|
13
|
+
one: "Byte"
|
14
|
+
other: "Bytes"
|
15
|
+
kb: "KB"
|
16
|
+
mb: "MB"
|
17
|
+
gb: "GB"
|
18
|
+
tb: "TB"
|
data/lib/paperclip/processor.rb
CHANGED
@@ -45,41 +45,4 @@ module Paperclip
|
|
45
45
|
Paperclip.run('identify', arguments, local_options)
|
46
46
|
end
|
47
47
|
end
|
48
|
-
|
49
|
-
module ProcessorHelpers
|
50
|
-
def processor(name) #:nodoc:
|
51
|
-
@known_processors ||= {}
|
52
|
-
if @known_processors[name.to_s]
|
53
|
-
@known_processors[name.to_s]
|
54
|
-
else
|
55
|
-
name = name.to_s.camelize
|
56
|
-
load_processor(name) unless Paperclip.const_defined?(name)
|
57
|
-
processor = Paperclip.const_get(name)
|
58
|
-
@known_processors[name.to_s] = processor
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def load_processor(name)
|
63
|
-
if defined?(Rails.root) && Rails.root
|
64
|
-
require File.expand_path(Rails.root.join("lib", "paperclip_processors", "#{name.underscore}.rb"))
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def clear_processors!
|
69
|
-
@known_processors.try(:clear)
|
70
|
-
end
|
71
|
-
|
72
|
-
# You can add your own processor via the Paperclip configuration. Normally
|
73
|
-
# Paperclip will load all processors from the
|
74
|
-
# Rails.root/lib/paperclip_processors directory, but here you can add any
|
75
|
-
# existing class using this mechanism.
|
76
|
-
#
|
77
|
-
# Paperclip.configure do |c|
|
78
|
-
# c.register_processor :watermarker, WatermarkingProcessor.new
|
79
|
-
# end
|
80
|
-
def register_processor(name, processor)
|
81
|
-
@known_processors ||= {}
|
82
|
-
@known_processors[name.to_s] = processor
|
83
|
-
end
|
84
|
-
end
|
85
48
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Paperclip
|
2
|
+
module ProcessorHelpers
|
3
|
+
class NoSuchProcessor < StandardError; end
|
4
|
+
|
5
|
+
def processor(name) #:nodoc:
|
6
|
+
@known_processors ||= {}
|
7
|
+
if @known_processors[name.to_s]
|
8
|
+
@known_processors[name.to_s]
|
9
|
+
else
|
10
|
+
name = name.to_s.camelize
|
11
|
+
load_processor(name) unless Paperclip.const_defined?(name)
|
12
|
+
processor = Paperclip.const_get(name)
|
13
|
+
@known_processors[name.to_s] = processor
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_processor(name)
|
18
|
+
if defined?(Rails.root) && Rails.root
|
19
|
+
filename = "#{name.to_s.underscore}.rb"
|
20
|
+
directories = %w(lib/paperclip lib/paperclip_processors)
|
21
|
+
|
22
|
+
required = directories.map do |directory|
|
23
|
+
pathname = File.expand_path(Rails.root.join(directory, filename))
|
24
|
+
file_exists = File.exist?(pathname)
|
25
|
+
require pathname if file_exists
|
26
|
+
file_exists
|
27
|
+
end
|
28
|
+
|
29
|
+
raise LoadError, "Could not find the '#{name}' processor in any of these paths: #{directories.join(', ')}" unless required.any?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def clear_processors!
|
34
|
+
@known_processors.try(:clear)
|
35
|
+
end
|
36
|
+
|
37
|
+
# You can add your own processor via the Paperclip configuration. Normally
|
38
|
+
# Paperclip will load all processors from the
|
39
|
+
# Rails.root/lib/paperclip_processors directory, but here you can add any
|
40
|
+
# existing class using this mechanism.
|
41
|
+
#
|
42
|
+
# Paperclip.configure do |c|
|
43
|
+
# c.register_processor :watermarker, WatermarkingProcessor.new
|
44
|
+
# end
|
45
|
+
def register_processor(name, processor)
|
46
|
+
@known_processors ||= {}
|
47
|
+
@known_processors[name.to_s] = processor
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/paperclip/schema.rb
CHANGED
@@ -22,9 +22,12 @@ module Paperclip
|
|
22
22
|
def add_attachment(table_name, *attachment_names)
|
23
23
|
raise ArgumentError, "Please specify attachment name in your add_attachment call in your migration." if attachment_names.empty?
|
24
24
|
|
25
|
+
options = attachment_names.extract_options!
|
26
|
+
|
25
27
|
attachment_names.each do |attachment_name|
|
26
28
|
COLUMNS.each_pair do |column_name, column_type|
|
27
|
-
|
29
|
+
column_options = options.merge(options[column_name.to_sym] || {})
|
30
|
+
add_column(table_name, "#{attachment_name}_#{column_name}", column_type, column_options)
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|
@@ -32,9 +35,12 @@ module Paperclip
|
|
32
35
|
def remove_attachment(table_name, *attachment_names)
|
33
36
|
raise ArgumentError, "Please specify attachment name in your remove_attachment call in your migration." if attachment_names.empty?
|
34
37
|
|
38
|
+
options = attachment_names.extract_options!
|
39
|
+
|
35
40
|
attachment_names.each do |attachment_name|
|
36
41
|
COLUMNS.each_pair do |column_name, column_type|
|
37
|
-
|
42
|
+
column_options = options.merge(options[column_name.to_sym] || {})
|
43
|
+
remove_column(table_name, "#{attachment_name}_#{column_name}", column_type, column_options)
|
38
44
|
end
|
39
45
|
end
|
40
46
|
end
|
@@ -47,9 +53,11 @@ module Paperclip
|
|
47
53
|
|
48
54
|
module TableDefinition
|
49
55
|
def attachment(*attachment_names)
|
56
|
+
options = attachment_names.extract_options!
|
50
57
|
attachment_names.each do |attachment_name|
|
51
58
|
COLUMNS.each_pair do |column_name, column_type|
|
52
|
-
|
59
|
+
column_options = options.merge(options[column_name.to_sym] || {})
|
60
|
+
column("#{attachment_name}_#{column_name}", column_type, column_options)
|
53
61
|
end
|
54
62
|
end
|
55
63
|
end
|
@@ -14,6 +14,7 @@ module Paperclip
|
|
14
14
|
# aws_secret_access_key: '<your aws_secret_access_key>'
|
15
15
|
# provider: 'AWS'
|
16
16
|
# region: 'eu-west-1'
|
17
|
+
# scheme: 'https'
|
17
18
|
# * +fog_directory+: This is the name of the S3 bucket that will
|
18
19
|
# store your files. Remember that the bucket must be unique across
|
19
20
|
# all of Amazon S3. If the bucket does not exist, Paperclip will
|
@@ -131,7 +132,7 @@ module Paperclip
|
|
131
132
|
"#{dynamic_fog_host_for_style(style)}/#{path(style)}"
|
132
133
|
else
|
133
134
|
if fog_credentials[:provider] == 'AWS'
|
134
|
-
"
|
135
|
+
"#{scheme}://#{host_name_for_directory}/#{path(style)}"
|
135
136
|
else
|
136
137
|
directory.files.new(:key => path(style)).public_url
|
137
138
|
end
|
@@ -225,6 +226,10 @@ module Paperclip
|
|
225
226
|
|
226
227
|
@directory ||= connection.directories.new(:key => dir)
|
227
228
|
end
|
229
|
+
|
230
|
+
def scheme
|
231
|
+
@scheme ||= fog_credentials[:scheme] || 'https'
|
232
|
+
end
|
228
233
|
end
|
229
234
|
end
|
230
235
|
end
|
data/lib/paperclip/storage/s3.rb
CHANGED
@@ -158,7 +158,7 @@ module Paperclip
|
|
158
158
|
end
|
159
159
|
|
160
160
|
unless @options[:url].to_s.match(/\A:s3.*url\Z/) || @options[:url] == ":asset_host"
|
161
|
-
@options[:path] =
|
161
|
+
@options[:path] = path_option.gsub(/:url/, @options[:url]).gsub(/\A:rails_root\/public\/system/, '')
|
162
162
|
@options[:url] = ":s3_path_url"
|
163
163
|
end
|
164
164
|
@options[:url] = @options[:url].inspect if @options[:url].is_a?(Symbol)
|
@@ -329,6 +329,7 @@ module Paperclip
|
|
329
329
|
|
330
330
|
def flush_writes #:nodoc:
|
331
331
|
@queued_for_write.each do |style, file|
|
332
|
+
retries = 0
|
332
333
|
begin
|
333
334
|
log("saving #{path(style)}")
|
334
335
|
acl = @s3_permissions[style] || @s3_permissions[:default]
|
@@ -357,9 +358,17 @@ module Paperclip
|
|
357
358
|
write_options.merge!(@s3_headers)
|
358
359
|
|
359
360
|
s3_object(style).write(file, write_options)
|
360
|
-
rescue AWS::S3::Errors::NoSuchBucket
|
361
|
+
rescue AWS::S3::Errors::NoSuchBucket
|
361
362
|
create_bucket
|
362
363
|
retry
|
364
|
+
rescue AWS::S3::Errors::SlowDown
|
365
|
+
retries += 1
|
366
|
+
if retries <= 5
|
367
|
+
sleep((2 ** retries) * 0.5)
|
368
|
+
retry
|
369
|
+
else
|
370
|
+
raise
|
371
|
+
end
|
363
372
|
ensure
|
364
373
|
file.rewind
|
365
374
|
end
|
@@ -384,10 +393,11 @@ module Paperclip
|
|
384
393
|
|
385
394
|
def copy_to_local_file(style, local_dest_path)
|
386
395
|
log("copying #{path(style)} to local file #{local_dest_path}")
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
396
|
+
::File.open(local_dest_path, 'wb') do |local_file|
|
397
|
+
s3_object(style).read do |chunk|
|
398
|
+
local_file.write(chunk)
|
399
|
+
end
|
400
|
+
end
|
391
401
|
rescue AWS::Errors::Base => e
|
392
402
|
warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
|
393
403
|
false
|
@@ -8,8 +8,8 @@ module Paperclip
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def for(style_name, options)
|
11
|
-
|
12
|
-
|
11
|
+
timestamp_as_needed(
|
12
|
+
escape_url_as_needed(
|
13
13
|
@attachment_options[:interpolator].interpolate(most_appropriate_url, @attachment, style_name),
|
14
14
|
options
|
15
15
|
), options)
|
@@ -58,7 +58,15 @@ module Paperclip
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def escape_url(url)
|
61
|
-
|
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
|
+
/[\?\(\)\[\]\+]/
|
62
70
|
end
|
63
71
|
end
|
64
72
|
end
|