kt-paperclip 6.2.1 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  4. data/.github/ISSUE_TEMPLATE/custom.md +10 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/.hound.yml +364 -357
  7. data/.rubocop.yml +2 -0
  8. data/.travis.yml +12 -14
  9. data/Appraisals +6 -0
  10. data/Gemfile +3 -3
  11. data/NEWS +28 -0
  12. data/README.md +41 -19
  13. data/features/step_definitions/attachment_steps.rb +11 -1
  14. data/gemfiles/4.2.gemfile +1 -1
  15. data/gemfiles/5.0.gemfile +1 -1
  16. data/gemfiles/5.1.gemfile +1 -1
  17. data/gemfiles/5.2.gemfile +1 -1
  18. data/gemfiles/6.0.gemfile +1 -1
  19. data/gemfiles/6.1.gemfile +21 -0
  20. data/lib/paperclip.rb +3 -3
  21. data/lib/paperclip/content_type_detector.rb +4 -4
  22. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +2 -2
  23. data/lib/paperclip/io_adapters/uri_adapter.rb +13 -3
  24. data/lib/paperclip/schema.rb +2 -2
  25. data/lib/paperclip/storage/s3.rb +2 -2
  26. data/lib/paperclip/url_generator.rb +8 -1
  27. data/lib/paperclip/validators.rb +4 -4
  28. data/lib/paperclip/validators/attachment_content_type_validator.rb +9 -2
  29. data/lib/paperclip/validators/attachment_file_name_validator.rb +9 -2
  30. data/lib/paperclip/validators/attachment_presence_validator.rb +1 -1
  31. data/lib/paperclip/validators/attachment_size_validator.rb +18 -2
  32. data/lib/paperclip/version.rb +1 -1
  33. data/paperclip.gemspec +4 -4
  34. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +1 -1
  35. data/spec/paperclip/io_adapters/file_adapter_spec.rb +1 -1
  36. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +20 -15
  37. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +13 -3
  38. data/spec/paperclip/storage/s3_spec.rb +54 -3
  39. data/spec/paperclip/url_generator_spec.rb +10 -0
  40. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +88 -0
  41. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +90 -0
  42. data/spec/paperclip/validators/attachment_size_validator_spec.rb +90 -0
  43. data/spec/support/fixtures/aws_s3.yml +13 -0
  44. data/spec/support/model_reconstruction.rb +1 -1
  45. metadata +17 -12
  46. data/.github/issue_template.md +0 -3
@@ -26,7 +26,7 @@ module Paperclip
26
26
  attachment_names.each do |attachment_name|
27
27
  COLUMNS.each_pair do |column_name, column_type|
28
28
  column_options = options.merge(options[column_name.to_sym] || {})
29
- add_column(table_name, "#{attachment_name}_#{column_name}", column_type, column_options)
29
+ add_column(table_name, "#{attachment_name}_#{column_name}", column_type, **column_options)
30
30
  end
31
31
  end
32
32
  end
@@ -55,7 +55,7 @@ module Paperclip
55
55
  attachment_names.each do |attachment_name|
56
56
  COLUMNS.each_pair do |column_name, column_type|
57
57
  column_options = options.merge(options[column_name.to_sym] || {})
58
- column("#{attachment_name}_#{column_name}", column_type, column_options)
58
+ column("#{attachment_name}_#{column_name}", column_type, **column_options)
59
59
  end
60
60
  end
61
61
  end
@@ -427,9 +427,9 @@ module Paperclip
427
427
  def find_credentials(creds)
428
428
  case creds
429
429
  when File
430
- YAML::safe_load(ERB.new(File.read(creds.path)).result)
430
+ YAML::safe_load(ERB.new(File.read(creds.path)).result, [], [], true)
431
431
  when String, Pathname
432
- YAML::safe_load(ERB.new(File.read(creds)).result)
432
+ YAML::safe_load(ERB.new(File.read(creds)).result, [], [], true)
433
433
  when Hash
434
434
  creds
435
435
  when NilClass
@@ -3,6 +3,13 @@ require "active_support/core_ext/module/delegation"
3
3
 
4
4
  module Paperclip
5
5
  class UrlGenerator
6
+ class << self
7
+ def encoder
8
+ @encoder ||= URI::RFC2396_Parser.new
9
+ end
10
+ delegate :escape, :unescape, to: :encoder
11
+ end
12
+
6
13
  def initialize(attachment)
7
14
  @attachment = attachment
8
15
  end
@@ -65,7 +72,7 @@ module Paperclip
65
72
  if url.respond_to?(:escape)
66
73
  url.escape
67
74
  else
68
- URI.escape(url).gsub(escape_regex) { |m| "%#{m.ord.to_s(16).upcase}" }
75
+ self.class.escape(url).gsub(escape_regex) { |m| "%#{m.ord.to_s(16).upcase}" }
69
76
  end
70
77
  end
71
78
 
@@ -20,10 +20,10 @@ module Paperclip
20
20
  ::Paperclip::REQUIRED_VALIDATORS = [AttachmentFileNameValidator, AttachmentContentTypeValidator, AttachmentFileTypeIgnoranceValidator].freeze
21
21
 
22
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
23
+ # This method is a shortcut to the validator classes that are in
24
+ # "Attachment...Validator" format. It is almost the same as the
25
+ # +validates+ method that ships with Rails, but is customized for
26
+ # use with attachment validators. This is helpful when you're using
27
27
  # multiple attachment validators on a single attachment.
28
28
  #
29
29
  # Example of using the validator:
@@ -3,6 +3,9 @@ module Paperclip
3
3
  class AttachmentContentTypeValidator < ActiveModel::EachValidator
4
4
  def initialize(options)
5
5
  options[:allow_nil] = true unless options.key?(:allow_nil)
6
+ unless options.key?(:add_validation_errors_to)
7
+ options[:add_validation_errors_to] = Paperclip.options[:add_validation_errors_to]
8
+ end
6
9
  super
7
10
  end
8
11
 
@@ -20,10 +23,14 @@ module Paperclip
20
23
  validate_whitelist(record, attribute, value)
21
24
  validate_blacklist(record, attribute, value)
22
25
 
23
- if record.errors.include? attribute
26
+ if record.errors.include?(attribute) &&
27
+ [:both, :base].include?(options[:add_validation_errors_to])
28
+
24
29
  record.errors[attribute].each do |error|
25
30
  record.errors.add base_attribute, error
26
31
  end
32
+
33
+ record.errors.delete(attribute) if options[:add_validation_errors_to] == :base
27
34
  end
28
35
  end
29
36
 
@@ -40,7 +47,7 @@ module Paperclip
40
47
  end
41
48
 
42
49
  def mark_invalid(record, attribute, types)
43
- record.errors.add attribute, :invalid, options.merge(types: types.join(", "))
50
+ record.errors.add attribute, :invalid, **options.merge(types: types.join(", "))
44
51
  end
45
52
 
46
53
  def allowed_types
@@ -3,6 +3,9 @@ module Paperclip
3
3
  class AttachmentFileNameValidator < ActiveModel::EachValidator
4
4
  def initialize(options)
5
5
  options[:allow_nil] = true unless options.key?(:allow_nil)
6
+ unless options.key?(:add_validation_errors_to)
7
+ options[:add_validation_errors_to] = Paperclip.options[:add_validation_errors_to]
8
+ end
6
9
  super
7
10
  end
8
11
 
@@ -20,10 +23,14 @@ module Paperclip
20
23
  validate_whitelist(record, attribute, value)
21
24
  validate_blacklist(record, attribute, value)
22
25
 
23
- if record.errors.include? attribute
26
+ if record.errors.include?(attribute) &&
27
+ [:both, :base].include?(options[:add_validation_errors_to])
28
+
24
29
  record.errors[attribute].each do |error|
25
30
  record.errors.add base_attribute, error
26
31
  end
32
+
33
+ record.errors.delete(attribute) if options[:add_validation_errors_to] == :base
27
34
  end
28
35
  end
29
36
 
@@ -36,7 +43,7 @@ module Paperclip
36
43
  end
37
44
 
38
45
  def mark_invalid(record, attribute, patterns)
39
- record.errors.add attribute, :invalid, options.merge(names: patterns.join(", "))
46
+ record.errors.add attribute, :invalid, **options.merge(names: patterns.join(", "))
40
47
  end
41
48
 
42
49
  def allowed
@@ -4,7 +4,7 @@ module Paperclip
4
4
  module Validators
5
5
  class AttachmentPresenceValidator < ActiveModel::EachValidator
6
6
  def validate_each(record, attribute, _value)
7
- record.errors.add(attribute, :blank, options) if record.send("#{attribute}_file_name").blank?
7
+ record.errors.add(attribute, :blank, **options) if record.send("#{attribute}_file_name").blank?
8
8
  end
9
9
 
10
10
  def self.helper_method_name
@@ -17,6 +17,18 @@ module Paperclip
17
17
  def validate_each(record, attr_name, value)
18
18
  base_attr_name = attr_name
19
19
  attr_name = "#{attr_name}_file_size".to_sym
20
+
21
+ error_attrs = []
22
+ case options[:add_validation_errors_to]
23
+ when :base
24
+ error_attrs << base_attr_name
25
+ when :attribute
26
+ error_attrs << attr_name
27
+ else
28
+ error_attrs << base_attr_name
29
+ error_attrs << attr_name
30
+ end
31
+
20
32
  value = record.send(:read_attribute_for_validation, attr_name)
21
33
 
22
34
  unless value.blank?
@@ -26,8 +38,8 @@ module Paperclip
26
38
 
27
39
  unless value.send(CHECKS[option], option_value)
28
40
  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(
41
+ error_attrs.each do |error_attr_name|
42
+ record.errors.add(error_attr_name, error_message_key, **filtered_options(value).merge(
31
43
  min: min_value_in_human_size(record),
32
44
  max: max_value_in_human_size(record),
33
45
  count: human_size(option_value)
@@ -56,6 +68,10 @@ module Paperclip
56
68
  options[:greater_than_or_equal_to] = range
57
69
  end
58
70
  end
71
+
72
+ unless options.key?(:add_validation_errors_to)
73
+ options[:add_validation_errors_to] = Paperclip.options[:add_validation_errors_to]
74
+ end
59
75
  end
60
76
 
61
77
  def extract_option_value(option, option_value)
@@ -1,3 +1,3 @@
1
1
  module Paperclip
2
- VERSION = "6.2.1" unless defined?(Paperclip::VERSION)
2
+ VERSION = "7.0.0" unless defined?(Paperclip::VERSION)
3
3
  end
data/paperclip.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.author = "Surendra Singhi"
9
9
  s.email = ["ssinghi@kreeti.com"]
10
- s.homepage = "https://github.com/kreeti/paperclip"
10
+ s.homepage = "https://github.com/kreeti/kt-paperclip"
11
11
  s.summary = "File attachments as attributes for ActiveRecord"
12
12
  s.description = "Easy upload management for ActiveRecord"
13
13
  s.license = "MIT"
@@ -20,12 +20,12 @@ Gem::Specification.new do |s|
20
20
  s.post_install_message = File.read("UPGRADING") if File.exist?("UPGRADING")
21
21
 
22
22
  s.requirements << "ImageMagick"
23
- s.required_ruby_version = ">= 2.1.0"
23
+ s.required_ruby_version = ">= 2.3.0"
24
24
 
25
25
  s.add_dependency("activemodel", ">= 4.2.0")
26
26
  s.add_dependency("activesupport", ">= 4.2.0")
27
27
  s.add_dependency("mime-types")
28
- s.add_dependency("mimemagic", "~> 0.3.0")
28
+ s.add_dependency("marcel", "~> 1.0.1")
29
29
  s.add_dependency("terrapin", "~> 0.6.0")
30
30
 
31
31
  s.add_development_dependency("activerecord", ">= 4.2.0")
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
34
34
  s.add_development_dependency("aws-sdk-s3")
35
35
  s.add_development_dependency("bundler")
36
36
  s.add_development_dependency("capybara")
37
- s.add_development_dependency("cucumber-expressions", "4.0.3") # TODO: investigate failures on 4.0.4
37
+ s.add_development_dependency("cucumber-expressions")
38
38
  s.add_development_dependency("cucumber-rails")
39
39
  s.add_development_dependency("fakeweb")
40
40
  s.add_development_dependency("fog-aws")
@@ -15,7 +15,7 @@ describe Paperclip::AbstractAdapter do
15
15
  before do
16
16
  allow(subject).to receive(:path).and_return("image.png")
17
17
  allow(Paperclip).to receive(:run).and_return("image/png\n")
18
- allow_any_instance_of(Paperclip::ContentTypeDetector).to receive(:type_from_mime_magic).and_return("image/png")
18
+ allow_any_instance_of(Paperclip::ContentTypeDetector).to receive(:type_from_marcel).and_return("image/png")
19
19
  end
20
20
 
21
21
  it "returns the content type without newline" do
@@ -78,7 +78,7 @@ describe Paperclip::FileAdapter do
78
78
  allow(MIME::Types).to receive(:type_for).and_return([])
79
79
  allow(Paperclip).to receive(:run).and_return("application/vnd.ms-office\n")
80
80
  allow_any_instance_of(Paperclip::ContentTypeDetector).
81
- to receive(:type_from_mime_magic).and_return("application/vnd.ms-office")
81
+ to receive(:type_from_marcel).and_return("application/vnd.ms-office")
82
82
 
83
83
  @subject = Paperclip.io_adapters.for(@file)
84
84
  end
@@ -16,54 +16,59 @@ describe Paperclip::HttpUrlProxyAdapter do
16
16
  context "a new instance" do
17
17
  before do
18
18
  @url = "http://thoughtbot.com/images/thoughtbot-logo.png"
19
- @subject = Paperclip.io_adapters.for(@url, hash_digest: Digest::MD5)
20
19
  end
21
20
 
21
+ subject { Paperclip.io_adapters.for(@url, hash_digest: Digest::MD5) }
22
+
22
23
  after do
23
- @subject.close
24
+ subject.close
24
25
  end
25
26
 
26
27
  it "returns a file name" do
27
- assert_equal "thoughtbot-logo.png", @subject.original_filename
28
+ expect(subject.original_filename).to(eq("thoughtbot-logo.png"))
28
29
  end
29
30
 
30
31
  it "closes open handle after reading" do
31
- assert_equal true, @open_return.closed?
32
+ expect { subject }.to(change { @open_return.closed? }.from(false).to(true))
32
33
  end
33
34
 
34
35
  it "returns a content type" do
35
- assert_equal "image/png", @subject.content_type
36
+ expect(subject.content_type).to(eq("image/png"))
36
37
  end
37
38
 
38
39
  it "returns the size of the data" do
39
- assert_equal @open_return.size, @subject.size
40
+ expect(subject.size).to(eq(@open_return.size))
40
41
  end
41
42
 
42
43
  it "generates an MD5 hash of the contents" do
43
- assert_equal Digest::MD5.hexdigest("xxx"), @subject.fingerprint
44
+ expect(subject.fingerprint).to(eq(Digest::MD5.hexdigest("xxx")))
44
45
  end
45
46
 
46
47
  it "generates correct fingerprint after read" do
47
- fingerprint = Digest::MD5.hexdigest(@subject.read)
48
- assert_equal fingerprint, @subject.fingerprint
48
+ fingerprint = Digest::MD5.hexdigest(subject.read)
49
+ expect(subject.fingerprint).to(eq(fingerprint))
49
50
  end
50
51
 
51
52
  it "generates same fingerprint" do
52
- assert_equal @subject.fingerprint, @subject.fingerprint
53
+ expect(subject.fingerprint).to(eq(subject.fingerprint))
53
54
  end
54
55
 
55
56
  it "returns the data contained in the StringIO" do
56
- assert_equal "xxx", @subject.read
57
+ expect(subject.read).to(eq("xxx"))
57
58
  end
58
59
 
59
60
  it "accepts a content_type" do
60
- @subject.content_type = "image/png"
61
- assert_equal "image/png", @subject.content_type
61
+ subject.content_type = "image/png"
62
+ expect(subject.content_type).to(eq("image/png"))
62
63
  end
63
64
 
64
65
  it "accepts an original_filename" do
65
- @subject.original_filename = "image.png"
66
- assert_equal "image.png", @subject.original_filename
66
+ subject.original_filename = "image.png"
67
+ expect(subject.original_filename).to(eq("image.png"))
68
+ end
69
+
70
+ it "doesn't emit deprecation warnings" do
71
+ expect { subject }.to_not(output(/URI\.(un)?escape is obsolete/).to_stderr)
67
72
  end
68
73
  end
69
74
 
@@ -193,9 +193,18 @@ describe Paperclip::UriAdapter do
193
193
 
194
194
  describe "#download_content" do
195
195
  before do
196
- allow_any_instance_of(Paperclip::UriAdapter).to receive(:open).and_return(@open_return)
196
+ allowed_mock =
197
+ if RUBY_VERSION < '2.5'
198
+ allow_any_instance_of(Paperclip::UriAdapter)
199
+ else
200
+ allow(URI)
201
+ end
202
+
203
+ allowed_mock.to receive(:open).and_return(@open_return)
204
+
197
205
  @uri = URI.parse("https://github.com/thoughtbot/paper:clip.jpg")
198
206
  @subject = Paperclip.io_adapters.for(@uri)
207
+ @uri_opener = RUBY_VERSION < '2.5' ? @subject : URI
199
208
  end
200
209
 
201
210
  after do
@@ -204,7 +213,7 @@ describe Paperclip::UriAdapter do
204
213
 
205
214
  context "with default read_timeout" do
206
215
  it "calls open without options" do
207
- expect(@subject).to receive(:open).with(@uri, {}).at_least(1).times
216
+ expect(@uri_opener).to receive(:open).with(@uri, {}).at_least(1).times
208
217
  end
209
218
  end
210
219
 
@@ -214,7 +223,8 @@ describe Paperclip::UriAdapter do
214
223
  end
215
224
 
216
225
  it "calls open with read_timeout option" do
217
- expect(@subject).to receive(:open).with(@uri, read_timeout: 120).at_least(1).times
226
+ expect(@uri_opener)
227
+ .to receive(:open).with(@uri, read_timeout: 120).at_least(1).times
218
228
  end
219
229
  end
220
230
  end
@@ -805,9 +805,7 @@ describe Paperclip::Storage::S3 do
805
805
  s3_region: "ap-northeast-1",
806
806
  s3_host_name: "s3-ap-northeast-1.amazonaws.com"
807
807
  },
808
- test: {
809
- s3_region: ""
810
- }
808
+ test: {}
811
809
  }
812
810
  @dummy = Dummy.new
813
811
  end
@@ -1439,6 +1437,32 @@ describe Paperclip::Storage::S3 do
1439
1437
  end
1440
1438
  end
1441
1439
 
1440
+ context "with S3 credentials supplied as Pathname and aliases being set" do
1441
+ before do
1442
+ ENV["S3_KEY"] = "pathname_key"
1443
+ ENV["S3_BUCKET"] = "pathname_bucket"
1444
+ ENV["S3_SECRET"] = "pathname_secret"
1445
+
1446
+ rails_env("test") do
1447
+ rebuild_model aws2_add_region.merge storage: :s3,
1448
+ s3_credentials: Pathname.new(fixture_file("aws_s3.yml"))
1449
+
1450
+ Dummy.delete_all
1451
+ @dummy = Dummy.new
1452
+ end
1453
+ end
1454
+
1455
+ it "parses the credentials" do
1456
+ assert_equal "pathname_bucket", @dummy.avatar.bucket_name
1457
+
1458
+ assert_equal "pathname_key",
1459
+ @dummy.avatar.s3_bucket.client.config.access_key_id
1460
+
1461
+ assert_equal "pathname_secret",
1462
+ @dummy.avatar.s3_bucket.client.config.secret_access_key
1463
+ end
1464
+ end
1465
+
1442
1466
  context "with S3 credentials in a YAML file" do
1443
1467
  before do
1444
1468
  ENV["S3_KEY"] = "env_key"
@@ -1466,6 +1490,33 @@ describe Paperclip::Storage::S3 do
1466
1490
  end
1467
1491
  end
1468
1492
 
1493
+ context "with S3 credentials in a YAML file and aliases being set" do
1494
+ before do
1495
+ ENV["S3_KEY"] = "env_key"
1496
+ ENV["S3_BUCKET"] = "env_bucket"
1497
+ ENV["S3_SECRET"] = "env_secret"
1498
+
1499
+ rails_env("test") do
1500
+ rebuild_model aws2_add_region.merge storage: :s3,
1501
+ s3_credentials: File.new(fixture_file("aws_s3.yml"))
1502
+
1503
+ Dummy.delete_all
1504
+
1505
+ @dummy = Dummy.new
1506
+ end
1507
+ end
1508
+
1509
+ it "runs the file through ERB" do
1510
+ assert_equal "env_bucket", @dummy.avatar.bucket_name
1511
+
1512
+ assert_equal "env_key",
1513
+ @dummy.avatar.s3_bucket.client.config.access_key_id
1514
+
1515
+ assert_equal "env_secret",
1516
+ @dummy.avatar.s3_bucket.client.config.secret_access_key
1517
+ end
1518
+ end
1519
+
1469
1520
  context "S3 Permissions" do
1470
1521
  context "defaults to :public_read" do
1471
1522
  before do
@@ -194,6 +194,16 @@ describe Paperclip::UrlGenerator do
194
194
  "expected the interpolator to be passed #{expected.inspect} but it wasn't"
195
195
  end
196
196
 
197
+ it "doesn't emit deprecation warnings" do
198
+ expected = "the expected result"
199
+ mock_interpolator = MockInterpolator.new(result: expected)
200
+ options = { interpolator: mock_interpolator }
201
+ mock_attachment = MockAttachment.new(options)
202
+ url_generator = Paperclip::UrlGenerator.new(mock_attachment)
203
+
204
+ expect { url_generator.for(:style_name, escape: true) }.to_not(output(/URI\.(un)?escape is obsolete/).to_stderr)
205
+ end
206
+
197
207
  describe "should be able to escape (, ), [, and ]." do
198
208
  def generate(expected, updated_at = nil)
199
209
  mock_interpolator = MockInterpolator.new(result: expected)