paperclip 5.1.0 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +17 -0
- data/.hound.yml +5 -16
- data/.travis.yml +14 -15
- data/Appraisals +3 -23
- data/Gemfile +1 -0
- data/NEWS +36 -1
- data/README.md +58 -10
- data/Rakefile +1 -1
- data/UPGRADING +1 -1
- data/features/step_definitions/attachment_steps.rb +6 -6
- data/features/step_definitions/rails_steps.rb +29 -22
- data/features/step_definitions/s3_steps.rb +1 -1
- data/features/support/env.rb +1 -0
- data/features/support/paths.rb +1 -1
- data/features/support/rails.rb +0 -24
- data/gemfiles/{4.2.awsv2.0.gemfile → 4.2.gemfile} +1 -1
- data/gemfiles/{5.0.awsv2.1.gemfile → 5.0.gemfile} +2 -2
- data/lib/paperclip.rb +12 -10
- data/lib/paperclip/attachment.rb +10 -4
- data/lib/paperclip/io_adapters/abstract_adapter.rb +24 -2
- data/lib/paperclip/io_adapters/attachment_adapter.rb +10 -5
- data/lib/paperclip/io_adapters/data_uri_adapter.rb +8 -8
- data/lib/paperclip/io_adapters/empty_string_adapter.rb +5 -4
- data/lib/paperclip/io_adapters/file_adapter.rb +12 -6
- data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +7 -7
- data/lib/paperclip/io_adapters/identity_adapter.rb +12 -6
- data/lib/paperclip/io_adapters/nil_adapter.rb +8 -5
- data/lib/paperclip/io_adapters/registry.rb +6 -2
- data/lib/paperclip/io_adapters/stringio_adapter.rb +9 -6
- data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +9 -5
- data/lib/paperclip/io_adapters/uri_adapter.rb +13 -11
- data/lib/paperclip/storage/filesystem.rb +13 -2
- data/lib/paperclip/storage/fog.rb +7 -4
- data/lib/paperclip/storage/s3.rb +31 -3
- data/lib/paperclip/thumbnail.rb +14 -4
- data/lib/paperclip/version.rb +1 -1
- data/lib/tasks/paperclip.rake +17 -3
- data/paperclip.gemspec +3 -3
- data/spec/paperclip/attachment_spec.rb +39 -8
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +44 -21
- data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +6 -3
- data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +7 -1
- data/spec/paperclip/io_adapters/file_adapter_spec.rb +2 -2
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +6 -1
- data/spec/paperclip/io_adapters/identity_adapter_spec.rb +1 -1
- data/spec/paperclip/io_adapters/registry_spec.rb +2 -2
- data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +1 -1
- data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +3 -3
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +6 -1
- data/spec/paperclip/storage/fog_spec.rb +16 -0
- data/spec/paperclip/storage/s3_live_spec.rb +12 -10
- data/spec/paperclip/storage/s3_spec.rb +85 -4
- data/spec/paperclip/tempfile_spec.rb +35 -0
- data/spec/paperclip/thumbnail_spec.rb +35 -32
- data/spec/spec_helper.rb +3 -1
- data/spec/support/assertions.rb +5 -1
- data/spec/support/conditional_filter_helper.rb +5 -0
- metadata +31 -135
- data/gemfiles/4.2.awsv2.1.gemfile +0 -17
- data/gemfiles/4.2.awsv2.gemfile +0 -20
- data/gemfiles/5.0.awsv2.0.gemfile +0 -17
- data/gemfiles/5.0.awsv2.gemfile +0 -20
@@ -1,6 +1,6 @@
|
|
1
1
|
When /^I attach the file "([^"]*)" to "([^"]*)" on S3$/ do |file_path, field|
|
2
2
|
definition = Paperclip::AttachmentRegistry.definitions_for(User)[field.downcase.to_sym]
|
3
|
-
path = "https://paperclip.s3
|
3
|
+
path = "https://paperclip.s3.us-west-2.amazonaws.com#{definition[:path]}"
|
4
4
|
path.gsub!(':filename', File.basename(file_path))
|
5
5
|
path.gsub!(/:([^\/\.]+)/) do |match|
|
6
6
|
"([^\/\.]+)"
|
data/features/support/env.rb
CHANGED
data/features/support/paths.rb
CHANGED
@@ -17,7 +17,7 @@ module NavigationHelpers
|
|
17
17
|
page_name =~ /the (.*) page/
|
18
18
|
path_components = $1.split(/\s+/)
|
19
19
|
self.send(path_components.push('path').join('_').to_sym)
|
20
|
-
rescue Object
|
20
|
+
rescue Object
|
21
21
|
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
22
22
|
"Now, go and add a mapping in #{__FILE__}"
|
23
23
|
end
|
data/features/support/rails.rb
CHANGED
@@ -35,29 +35,5 @@ module RailsCommandHelpers
|
|
35
35
|
def framework_major_version
|
36
36
|
framework_version.split(".").first.to_i
|
37
37
|
end
|
38
|
-
|
39
|
-
def using_protected_attributes?
|
40
|
-
framework_major_version < 4
|
41
|
-
end
|
42
|
-
|
43
|
-
def new_application_command
|
44
|
-
"rails new"
|
45
|
-
end
|
46
|
-
|
47
|
-
def generator_command
|
48
|
-
if framework_major_version >= 4
|
49
|
-
"rails generate"
|
50
|
-
else
|
51
|
-
"script/rails generate"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def runner_command
|
56
|
-
if framework_major_version >= 4
|
57
|
-
"rails runner"
|
58
|
-
else
|
59
|
-
"script/rails runner"
|
60
|
-
end
|
61
|
-
end
|
62
38
|
end
|
63
39
|
World(RailsCommandHelpers)
|
@@ -5,13 +5,13 @@ source "https://rubygems.org"
|
|
5
5
|
gem "sqlite3", "~> 1.3.8", :platforms => :ruby
|
6
6
|
gem "pry"
|
7
7
|
gem "rails", "~> 4.2.0"
|
8
|
-
gem "aws-sdk", "~> 2.0.0"
|
9
8
|
|
10
9
|
group :development, :test do
|
11
10
|
gem "activerecord-import"
|
12
11
|
gem "mime-types"
|
13
12
|
gem "builder"
|
14
13
|
gem "rubocop", :require => false
|
14
|
+
gem "rspec"
|
15
15
|
end
|
16
16
|
|
17
17
|
gemspec :path => "../"
|
@@ -4,14 +4,14 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
gem "sqlite3", "~> 1.3.8", :platforms => :ruby
|
6
6
|
gem "pry"
|
7
|
-
gem "rails", "5.0.0"
|
8
|
-
gem "aws-sdk", "~> 2.1.0"
|
7
|
+
gem "rails", "~> 5.0.0"
|
9
8
|
|
10
9
|
group :development, :test do
|
11
10
|
gem "activerecord-import"
|
12
11
|
gem "mime-types"
|
13
12
|
gem "builder"
|
14
13
|
gem "rubocop", :require => false
|
14
|
+
gem "rspec"
|
15
15
|
end
|
16
16
|
|
17
17
|
gemspec :path => "../"
|
data/lib/paperclip.rb
CHANGED
@@ -90,15 +90,14 @@ module Paperclip
|
|
90
90
|
# image's orientation. Defaults to true.
|
91
91
|
def self.options
|
92
92
|
@options ||= {
|
93
|
-
:
|
94
|
-
:
|
95
|
-
:
|
96
|
-
:
|
97
|
-
:
|
98
|
-
:
|
99
|
-
:
|
100
|
-
:
|
101
|
-
:read_timeout => nil
|
93
|
+
command_path: nil,
|
94
|
+
content_type_mappings: {},
|
95
|
+
log: true,
|
96
|
+
log_command: true,
|
97
|
+
read_timeout: nil,
|
98
|
+
swallow_stderr: true,
|
99
|
+
use_exif_orientation: true,
|
100
|
+
whiny: true,
|
102
101
|
}
|
103
102
|
end
|
104
103
|
|
@@ -120,7 +119,7 @@ module Paperclip
|
|
120
119
|
# called on it, the attachment will *not* be deleted until +save+ is called. See the
|
121
120
|
# Paperclip::Attachment documentation for more specifics. There are a number of options
|
122
121
|
# you can set to change the behavior of a Paperclip attachment:
|
123
|
-
# * +url+: The full URL of where the attachment is
|
122
|
+
# * +url+: The full URL of where the attachment is publicly accessible. This can just
|
124
123
|
# as easily point to a directory served directly through Apache as it can to an action
|
125
124
|
# that can control permissions. You can specify the full domain and path, but usually
|
126
125
|
# just an absolute path is sufficient. The leading slash *must* be included manually for
|
@@ -129,6 +128,9 @@ module Paperclip
|
|
129
128
|
# Paperclip::Attachment#interpolate for more information on variable interpolaton.
|
130
129
|
# :url => "/:class/:attachment/:id/:style_:filename"
|
131
130
|
# :url => "http://some.other.host/stuff/:class/:id_:extension"
|
131
|
+
# Note: When using the +s3+ storage option, the +url+ option expects
|
132
|
+
# particular values. See the Paperclip::Storage::S3#url documentation for
|
133
|
+
# specifics.
|
132
134
|
# * +default_url+: The URL that will be returned if there is no attachment assigned.
|
133
135
|
# This field is interpolated just as the url is. The default value is
|
134
136
|
# "/:attachment/:style/missing.png"
|
data/lib/paperclip/attachment.rb
CHANGED
@@ -33,6 +33,7 @@ module Paperclip
|
|
33
33
|
:use_timestamp => true,
|
34
34
|
:whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails],
|
35
35
|
:validate_media_type => true,
|
36
|
+
:adapter_options => { hash_digest: Digest::MD5 },
|
36
37
|
:check_validity_before_processing => true
|
37
38
|
}
|
38
39
|
end
|
@@ -97,7 +98,8 @@ module Paperclip
|
|
97
98
|
# attachment:
|
98
99
|
# new_user.avatar = old_user.avatar
|
99
100
|
def assign(uploaded_file)
|
100
|
-
@file = Paperclip.io_adapters.for(uploaded_file
|
101
|
+
@file = Paperclip.io_adapters.for(uploaded_file,
|
102
|
+
@options[:adapter_options])
|
101
103
|
ensure_required_accessors!
|
102
104
|
ensure_required_validations!
|
103
105
|
|
@@ -238,7 +240,8 @@ module Paperclip
|
|
238
240
|
# the instance's errors and returns false, cancelling the save.
|
239
241
|
def save
|
240
242
|
flush_deletes unless @options[:keep_old_files]
|
241
|
-
|
243
|
+
process = only_process
|
244
|
+
if process.any? && !process.include?(:original)
|
242
245
|
@queued_for_write.except!(:original)
|
243
246
|
end
|
244
247
|
flush_writes
|
@@ -523,15 +526,18 @@ module Paperclip
|
|
523
526
|
begin
|
524
527
|
raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
|
525
528
|
intermediate_files = []
|
529
|
+
original = @queued_for_write[:original]
|
526
530
|
|
527
|
-
@queued_for_write[name] = style.processors.
|
531
|
+
@queued_for_write[name] = style.processors.
|
532
|
+
reduce(original) do |file, processor|
|
528
533
|
file = Paperclip.processor(processor).make(file, style.processor_options, self)
|
529
534
|
intermediate_files << file unless file == @queued_for_write[:original]
|
530
535
|
file
|
531
536
|
end
|
532
537
|
|
533
538
|
unadapted_file = @queued_for_write[name]
|
534
|
-
@queued_for_write[name] = Paperclip.io_adapters.
|
539
|
+
@queued_for_write[name] = Paperclip.io_adapters.
|
540
|
+
for(@queued_for_write[name], @options[:adapter_options])
|
535
541
|
unadapted_file.close if unadapted_file.respond_to?(:close)
|
536
542
|
@queued_for_write[name]
|
537
543
|
rescue Paperclip::Errors::NotIdentifiedByImageMagickError => e
|
@@ -8,8 +8,20 @@ module Paperclip
|
|
8
8
|
delegate :binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :readbyte, :rewind, :unlink, :to => :@tempfile
|
9
9
|
alias :length :size
|
10
10
|
|
11
|
+
def initialize(target, options = {})
|
12
|
+
@target = target
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
11
16
|
def fingerprint
|
12
|
-
@fingerprint ||=
|
17
|
+
@fingerprint ||= begin
|
18
|
+
digest = @options.fetch(:hash_digest).new
|
19
|
+
File.open(path, "rb") do |f|
|
20
|
+
buf = ""
|
21
|
+
digest.update(buf) while f.read(16384, buf)
|
22
|
+
end
|
23
|
+
digest.hexdigest
|
24
|
+
end
|
13
25
|
end
|
14
26
|
|
15
27
|
def read(length = nil, buffer = nil)
|
@@ -40,8 +52,18 @@ module Paperclip
|
|
40
52
|
end
|
41
53
|
|
42
54
|
def copy_to_tempfile(src)
|
43
|
-
|
55
|
+
link_or_copy_file(src.path, destination.path)
|
44
56
|
destination
|
45
57
|
end
|
58
|
+
|
59
|
+
def link_or_copy_file(src, dest)
|
60
|
+
Paperclip.log("Trying to link #{src} to #{dest}")
|
61
|
+
FileUtils.ln(src, dest, force: true) # overwrite existing
|
62
|
+
@destination.close
|
63
|
+
@destination.open.binmode
|
64
|
+
rescue Errno::EXDEV, Errno::EPERM, Errno::ENOENT => e
|
65
|
+
Paperclip.log("Link failed with #{e.message}; copying link #{src} to #{dest}")
|
66
|
+
FileUtils.cp(src, dest)
|
67
|
+
end
|
46
68
|
end
|
47
69
|
end
|
@@ -1,6 +1,13 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class AttachmentAdapter < AbstractAdapter
|
3
|
-
def
|
3
|
+
def self.register
|
4
|
+
Paperclip.io_adapters.register self do |target|
|
5
|
+
Paperclip::Attachment === target || Paperclip::Style === target
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(target, options = {})
|
10
|
+
super
|
4
11
|
@target, @style = case target
|
5
12
|
when Paperclip::Attachment
|
6
13
|
[target, :original]
|
@@ -22,7 +29,7 @@ module Paperclip
|
|
22
29
|
|
23
30
|
def copy_to_tempfile(source)
|
24
31
|
if source.staged?
|
25
|
-
|
32
|
+
link_or_copy_file(source.staged_path(@style), destination.path)
|
26
33
|
else
|
27
34
|
source.copy_to_local_file(@style, destination.path)
|
28
35
|
end
|
@@ -31,6 +38,4 @@ module Paperclip
|
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
34
|
-
Paperclip.
|
35
|
-
Paperclip::Attachment === target || Paperclip::Style === target
|
36
|
-
end
|
41
|
+
Paperclip::AttachmentAdapter.register
|
@@ -1,22 +1,22 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class DataUriAdapter < StringioAdapter
|
3
|
+
def self.register
|
4
|
+
Paperclip.io_adapters.register self do |target|
|
5
|
+
String === target && target =~ REGEXP
|
6
|
+
end
|
7
|
+
end
|
3
8
|
|
4
9
|
REGEXP = /\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)/m
|
5
10
|
|
6
|
-
def initialize(target_uri)
|
7
|
-
super(extract_target(target_uri))
|
11
|
+
def initialize(target_uri, options = {})
|
12
|
+
super(extract_target(target_uri), options)
|
8
13
|
end
|
9
14
|
|
10
15
|
private
|
11
16
|
|
12
17
|
def extract_target(uri)
|
13
18
|
data_uri_parts = uri.match(REGEXP) || []
|
14
|
-
StringIO.new(Base64.decode64(data_uri_parts[2] ||
|
19
|
+
StringIO.new(Base64.decode64(data_uri_parts[2] || ""))
|
15
20
|
end
|
16
|
-
|
17
21
|
end
|
18
22
|
end
|
19
|
-
|
20
|
-
Paperclip.io_adapters.register Paperclip::DataUriAdapter do |target|
|
21
|
-
String === target && target =~ Paperclip::DataUriAdapter::REGEXP
|
22
|
-
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class EmptyStringAdapter < AbstractAdapter
|
3
|
-
def
|
3
|
+
def self.register
|
4
|
+
Paperclip.io_adapters.register self do |target|
|
5
|
+
target.is_a?(String) && target.empty?
|
6
|
+
end
|
4
7
|
end
|
5
8
|
|
6
9
|
def nil?
|
@@ -13,6 +16,4 @@ module Paperclip
|
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
16
|
-
Paperclip.
|
17
|
-
target.is_a?(String) && target.empty?
|
18
|
-
end
|
19
|
+
Paperclip::EmptyStringAdapter.register
|
@@ -1,14 +1,22 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class FileAdapter < AbstractAdapter
|
3
|
-
def
|
4
|
-
|
3
|
+
def self.register
|
4
|
+
Paperclip.io_adapters.register self do |target|
|
5
|
+
File === target || ::Tempfile === target
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(target, options = {})
|
10
|
+
super
|
5
11
|
cache_current_values
|
6
12
|
end
|
7
13
|
|
8
14
|
private
|
9
15
|
|
10
16
|
def cache_current_values
|
11
|
-
|
17
|
+
if @target.respond_to?(:original_filename)
|
18
|
+
self.original_filename = @target.original_filename
|
19
|
+
end
|
12
20
|
self.original_filename ||= File.basename(@target.path)
|
13
21
|
@tempfile = copy_to_tempfile(@target)
|
14
22
|
@content_type = ContentTypeDetector.new(@target.path).detect
|
@@ -17,6 +25,4 @@ module Paperclip
|
|
17
25
|
end
|
18
26
|
end
|
19
27
|
|
20
|
-
Paperclip.
|
21
|
-
File === target || Tempfile === target
|
22
|
-
end
|
28
|
+
Paperclip::FileAdapter.register
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class HttpUrlProxyAdapter < UriAdapter
|
3
|
+
def self.register
|
4
|
+
Paperclip.io_adapters.register self do |target|
|
5
|
+
String === target && target =~ REGEXP
|
6
|
+
end
|
7
|
+
end
|
3
8
|
|
4
9
|
REGEXP = /\Ahttps?:\/\//
|
5
10
|
|
6
|
-
def initialize(target)
|
7
|
-
super(URI(URI.escape(target)))
|
11
|
+
def initialize(target, options = {})
|
12
|
+
super(URI(URI.escape(target)), options)
|
8
13
|
end
|
9
|
-
|
10
14
|
end
|
11
15
|
end
|
12
|
-
|
13
|
-
Paperclip.io_adapters.register Paperclip::HttpUrlProxyAdapter do |target|
|
14
|
-
String === target && target =~ Paperclip::HttpUrlProxyAdapter::REGEXP
|
15
|
-
end
|
@@ -1,12 +1,18 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class IdentityAdapter < AbstractAdapter
|
3
|
-
def
|
4
|
-
|
3
|
+
def self.register
|
4
|
+
Paperclip.io_adapters.register Paperclip::IdentityAdapter.new do |target|
|
5
|
+
Paperclip.io_adapters.registered?(target)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
5
10
|
end
|
6
|
-
end
|
7
|
-
end
|
8
11
|
|
9
|
-
|
10
|
-
|
12
|
+
def new(target, _)
|
13
|
+
target
|
14
|
+
end
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
18
|
+
Paperclip::IdentityAdapter.register
|
@@ -1,8 +1,13 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class NilAdapter < AbstractAdapter
|
3
|
-
def
|
3
|
+
def self.register
|
4
|
+
Paperclip.io_adapters.register self do |target|
|
5
|
+
target.nil? || ((Paperclip::Attachment === target) && !target.present?)
|
6
|
+
end
|
4
7
|
end
|
5
8
|
|
9
|
+
def initialize(_target, _options = {}); end
|
10
|
+
|
6
11
|
def original_filename
|
7
12
|
""
|
8
13
|
end
|
@@ -19,7 +24,7 @@ module Paperclip
|
|
19
24
|
true
|
20
25
|
end
|
21
26
|
|
22
|
-
def read(*
|
27
|
+
def read(*_args)
|
23
28
|
nil
|
24
29
|
end
|
25
30
|
|
@@ -29,6 +34,4 @@ module Paperclip
|
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
|
-
Paperclip.
|
33
|
-
target.nil? || ( (Paperclip::Attachment === target) && !target.present? )
|
34
|
-
end
|
37
|
+
Paperclip::NilAdapter.register
|
@@ -12,6 +12,10 @@ module Paperclip
|
|
12
12
|
@registered_handlers << [block, handler_class]
|
13
13
|
end
|
14
14
|
|
15
|
+
def unregister(handler_class)
|
16
|
+
@registered_handlers.reject! { |_, klass| klass == handler_class }
|
17
|
+
end
|
18
|
+
|
15
19
|
def handler_for(target)
|
16
20
|
@registered_handlers.each do |tester, handler|
|
17
21
|
return handler if tester.call(target)
|
@@ -25,8 +29,8 @@ module Paperclip
|
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
28
|
-
def for(target)
|
29
|
-
handler_for(target).new(target)
|
32
|
+
def for(target, options = {})
|
33
|
+
handler_for(target).new(target, options)
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -1,7 +1,13 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class StringioAdapter < AbstractAdapter
|
3
|
-
def
|
4
|
-
|
3
|
+
def self.register
|
4
|
+
Paperclip.io_adapters.register self do |target|
|
5
|
+
StringIO === target
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(target, options = {})
|
10
|
+
super
|
5
11
|
cache_current_values
|
6
12
|
end
|
7
13
|
|
@@ -24,10 +30,7 @@ module Paperclip
|
|
24
30
|
destination.rewind
|
25
31
|
destination
|
26
32
|
end
|
27
|
-
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
31
|
-
Paperclip.
|
32
|
-
StringIO === target
|
33
|
-
end
|
36
|
+
Paperclip::StringioAdapter.register
|