paperclip 4.2.2 → 5.0.0
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 +4 -4
- data/.hound.yml +1066 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +18 -15
- data/Appraisals +20 -12
- data/CONTRIBUTING.md +19 -8
- data/Gemfile +4 -9
- data/LICENSE +1 -1
- data/NEWS +101 -31
- data/README.md +243 -159
- data/RELEASING.md +17 -0
- data/Rakefile +1 -1
- data/UPGRADING +12 -9
- data/features/basic_integration.feature +8 -4
- data/features/migration.feature +0 -24
- data/features/step_definitions/attachment_steps.rb +27 -21
- data/features/step_definitions/html_steps.rb +2 -2
- data/features/step_definitions/rails_steps.rb +11 -17
- data/features/step_definitions/s3_steps.rb +2 -2
- data/features/step_definitions/web_steps.rb +1 -103
- data/features/support/file_helpers.rb +2 -2
- data/gemfiles/4.2.awsv2.0.gemfile +17 -0
- data/gemfiles/4.2.awsv2.1.gemfile +17 -0
- data/gemfiles/{4.1.gemfile → 4.2.awsv2.gemfile} +4 -3
- data/gemfiles/5.0.awsv2.0.gemfile +17 -0
- data/gemfiles/5.0.awsv2.1.gemfile +17 -0
- data/gemfiles/{4.2.gemfile → 5.0.awsv2.gemfile} +4 -3
- data/lib/paperclip/attachment.rb +19 -16
- data/lib/paperclip/attachment_registry.rb +3 -2
- data/lib/paperclip/callbacks.rb +8 -6
- data/lib/paperclip/content_type_detector.rb +27 -11
- data/lib/paperclip/errors.rb +3 -1
- data/lib/paperclip/file_command_content_type_detector.rb +6 -8
- data/lib/paperclip/geometry_parser_factory.rb +1 -1
- data/lib/paperclip/glue.rb +1 -1
- data/lib/paperclip/has_attached_file.rb +9 -2
- data/lib/paperclip/helpers.rb +14 -10
- data/lib/paperclip/interpolations/plural_cache.rb +6 -5
- data/lib/paperclip/interpolations.rb +18 -13
- data/lib/paperclip/io_adapters/abstract_adapter.rb +1 -0
- data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +1 -1
- data/lib/paperclip/io_adapters/uri_adapter.rb +3 -1
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
- data/lib/paperclip/media_type_spoof_detector.rb +2 -2
- data/lib/paperclip/rails_environment.rb +25 -0
- data/lib/paperclip/schema.rb +3 -9
- data/lib/paperclip/storage/fog.rb +21 -12
- data/lib/paperclip/storage/s3.rb +51 -50
- data/lib/paperclip/thumbnail.rb +2 -3
- data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
- data/lib/paperclip/version.rb +3 -1
- data/lib/paperclip.rb +15 -4
- data/lib/tasks/paperclip.rake +17 -1
- data/paperclip.gemspec +18 -15
- data/spec/paperclip/attachment_definitions_spec.rb +1 -1
- data/spec/paperclip/attachment_processing_spec.rb +2 -4
- data/spec/paperclip/attachment_registry_spec.rb +84 -13
- data/spec/paperclip/attachment_spec.rb +91 -31
- data/spec/paperclip/content_type_detector_spec.rb +8 -1
- data/spec/paperclip/file_command_content_type_detector_spec.rb +0 -1
- data/spec/paperclip/geometry_spec.rb +1 -1
- data/spec/paperclip/glue_spec.rb +44 -0
- data/spec/paperclip/has_attached_file_spec.rb +24 -8
- data/spec/paperclip/integration_spec.rb +4 -3
- data/spec/paperclip/interpolations_spec.rb +16 -13
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +2 -1
- data/spec/paperclip/io_adapters/file_adapter_spec.rb +4 -1
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +12 -0
- data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +4 -0
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +27 -0
- data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +10 -0
- data/spec/paperclip/media_type_spoof_detector_spec.rb +34 -11
- data/spec/paperclip/paperclip_spec.rb +4 -29
- data/spec/paperclip/plural_cache_spec.rb +17 -16
- data/spec/paperclip/rails_environment_spec.rb +33 -0
- data/spec/paperclip/storage/fog_spec.rb +42 -3
- data/spec/paperclip/storage/s3_live_spec.rb +8 -4
- data/spec/paperclip/storage/s3_spec.rb +255 -180
- data/spec/paperclip/tempfile_factory_spec.rb +4 -0
- data/spec/paperclip/thumbnail_spec.rb +16 -0
- data/spec/paperclip/url_generator_spec.rb +1 -1
- data/spec/paperclip/validators/attachment_size_validator_spec.rb +26 -20
- data/spec/paperclip/validators_spec.rb +3 -3
- data/spec/spec_helper.rb +6 -1
- data/spec/support/assertions.rb +7 -0
- data/spec/support/fake_model.rb +4 -0
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/matchers/have_column.rb +11 -2
- data/spec/support/model_reconstruction.rb +9 -1
- data/spec/support/reporting.rb +11 -0
- metadata +105 -54
- data/RUNNING_TESTS.md +0 -4
- data/cucumber/paperclip_steps.rb +0 -6
- data/gemfiles/3.2.gemfile +0 -19
- data/gemfiles/4.0.gemfile +0 -19
- data/lib/paperclip/locales/de.yml +0 -18
- data/lib/paperclip/locales/es.yml +0 -18
- data/lib/paperclip/locales/ja.yml +0 -18
- data/lib/paperclip/locales/pt-BR.yml +0 -18
- data/lib/paperclip/locales/zh-CN.yml +0 -18
- data/lib/paperclip/locales/zh-HK.yml +0 -18
- data/lib/paperclip/locales/zh-TW.yml +0 -18
- data/spec/support/mock_model.rb +0 -2
- data/spec/support/rails_helpers.rb +0 -7
|
@@ -73,10 +73,13 @@ describe Paperclip::FileAdapter do
|
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
context "file with content type derived from file
|
|
76
|
+
context "file with content type derived from file contents on *nix" do
|
|
77
77
|
before do
|
|
78
78
|
MIME::Types.stubs(:type_for).returns([])
|
|
79
79
|
Paperclip.stubs(:run).returns("application/vnd.ms-office\n")
|
|
80
|
+
Paperclip::ContentTypeDetector.any_instance
|
|
81
|
+
.stubs(:type_from_mime_magic).returns("application/vnd.ms-office")
|
|
82
|
+
|
|
80
83
|
@subject = Paperclip.io_adapters.for(@file)
|
|
81
84
|
end
|
|
82
85
|
|
|
@@ -98,4 +98,16 @@ describe Paperclip::HttpUrlProxyAdapter do
|
|
|
98
98
|
end
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
context "a url with special characters in the filename" do
|
|
102
|
+
it "returns a encoded filename" do
|
|
103
|
+
Paperclip::HttpUrlProxyAdapter.any_instance.stubs(:download_content).
|
|
104
|
+
returns(StringIO.new("x"))
|
|
105
|
+
url = "https://github.com/thoughtbot/paperclip-öäü字´½♥زÈ.png"
|
|
106
|
+
subject = Paperclip.io_adapters.for(url)
|
|
107
|
+
filename = "paperclip-%C3%B6%C3%A4%C3%BC%E5%AD%97%C2%B4%C2%BD%E2%99%A5"\
|
|
108
|
+
"%C3%98%C2%B2%C3%88.png"
|
|
109
|
+
|
|
110
|
+
assert_equal filename, subject.original_filename
|
|
111
|
+
end
|
|
112
|
+
end
|
|
101
113
|
end
|
|
@@ -20,6 +20,10 @@ describe Paperclip::StringioAdapter do
|
|
|
20
20
|
assert_equal 6, @subject.size
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
it "returns the length of the data" do
|
|
24
|
+
assert_equal 6, @subject.length
|
|
25
|
+
end
|
|
26
|
+
|
|
23
27
|
it "generates an MD5 hash of the contents" do
|
|
24
28
|
assert_equal Digest::MD5.hexdigest(@contents), @subject.fingerprint
|
|
25
29
|
end
|
|
@@ -99,4 +99,31 @@ describe Paperclip::UriAdapter do
|
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
+
describe "#download_content" do
|
|
103
|
+
before do
|
|
104
|
+
Paperclip::UriAdapter.any_instance.stubs(:open).returns(StringIO.new("xxx"))
|
|
105
|
+
@uri = URI.parse("https://github.com/thoughtbot/paper:clip.jpg")
|
|
106
|
+
@subject = Paperclip.io_adapters.for(@uri)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
after do
|
|
110
|
+
@subject.send(:download_content)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
context "with default read_timeout" do
|
|
114
|
+
it "calls open without options" do
|
|
115
|
+
@subject.expects(:open).with(@uri, {}).at_least_once
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
context "with custom read_timeout" do
|
|
120
|
+
before do
|
|
121
|
+
Paperclip.options[:read_timeout] = 120
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "calls open with read_timeout option" do
|
|
125
|
+
@subject.expects(:open).with(@uri, read_timeout: 120).at_least_once
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
102
129
|
end
|
|
@@ -17,22 +17,26 @@ describe Paperclip::Shoulda::Matchers::ValidateAttachmentContentTypeMatcher do
|
|
|
17
17
|
|
|
18
18
|
it "rejects a class with no validation" do
|
|
19
19
|
expect(matcher).to_not accept(Dummy)
|
|
20
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
it 'rejects a class when the validation fails' do
|
|
23
24
|
Dummy.validates_attachment_content_type :avatar, content_type: %r{audio/.*}
|
|
24
25
|
expect(matcher).to_not accept(Dummy)
|
|
26
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
it "accepts a class with a matching validation" do
|
|
28
30
|
Dummy.validates_attachment_content_type :avatar, content_type: %r{image/.*}
|
|
29
31
|
expect(matcher).to accept(Dummy)
|
|
32
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
it "accepts a class with other validations but matching types" do
|
|
33
36
|
Dummy.validates_presence_of :title
|
|
34
37
|
Dummy.validates_attachment_content_type :avatar, content_type: %r{image/.*}
|
|
35
38
|
expect(matcher).to accept(Dummy)
|
|
39
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
it "accepts a class that matches and a matcher that only specifies 'allowing'" do
|
|
@@ -40,6 +44,7 @@ describe Paperclip::Shoulda::Matchers::ValidateAttachmentContentTypeMatcher do
|
|
|
40
44
|
matcher = plain_matcher.allowing(%w(image/png image/jpeg))
|
|
41
45
|
|
|
42
46
|
expect(matcher).to accept(Dummy)
|
|
47
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
43
48
|
end
|
|
44
49
|
|
|
45
50
|
it "rejects a class that does not match and a matcher that only specifies 'allowing'" do
|
|
@@ -47,6 +52,7 @@ describe Paperclip::Shoulda::Matchers::ValidateAttachmentContentTypeMatcher do
|
|
|
47
52
|
matcher = plain_matcher.allowing(%w(image/png image/jpeg))
|
|
48
53
|
|
|
49
54
|
expect(matcher).to_not accept(Dummy)
|
|
55
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
50
56
|
end
|
|
51
57
|
|
|
52
58
|
it "accepts a class that matches and a matcher that only specifies 'rejecting'" do
|
|
@@ -54,6 +60,7 @@ describe Paperclip::Shoulda::Matchers::ValidateAttachmentContentTypeMatcher do
|
|
|
54
60
|
matcher = plain_matcher.rejecting(%w(audio/mp3 application/octet-stream))
|
|
55
61
|
|
|
56
62
|
expect(matcher).to accept(Dummy)
|
|
63
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
57
64
|
end
|
|
58
65
|
|
|
59
66
|
it "rejects a class that does not match and a matcher that only specifies 'rejecting'" do
|
|
@@ -61,6 +68,7 @@ describe Paperclip::Shoulda::Matchers::ValidateAttachmentContentTypeMatcher do
|
|
|
61
68
|
matcher = plain_matcher.rejecting(%w(audio/mp3 application/octet-stream))
|
|
62
69
|
|
|
63
70
|
expect(matcher).to_not accept(Dummy)
|
|
71
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
64
72
|
end
|
|
65
73
|
|
|
66
74
|
context "using an :if to control the validation" do
|
|
@@ -75,12 +83,14 @@ describe Paperclip::Shoulda::Matchers::ValidateAttachmentContentTypeMatcher do
|
|
|
75
83
|
dummy = Dummy.new
|
|
76
84
|
dummy.go = true
|
|
77
85
|
expect(matcher).to accept(dummy)
|
|
86
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
78
87
|
end
|
|
79
88
|
|
|
80
89
|
it "does not run the validation if the control is false" do
|
|
81
90
|
dummy = Dummy.new
|
|
82
91
|
dummy.go = false
|
|
83
92
|
expect(matcher).to_not accept(dummy)
|
|
93
|
+
expect { matcher.failure_message }.to_not raise_error
|
|
84
94
|
end
|
|
85
95
|
end
|
|
86
96
|
|
|
@@ -3,32 +3,32 @@ require 'spec_helper'
|
|
|
3
3
|
describe Paperclip::MediaTypeSpoofDetector do
|
|
4
4
|
it 'rejects a file that is named .html and identifies as PNG' do
|
|
5
5
|
file = File.open(fixture_file("5k.png"))
|
|
6
|
-
assert Paperclip::MediaTypeSpoofDetector.using(file, "5k.html").spoofed?
|
|
6
|
+
assert Paperclip::MediaTypeSpoofDetector.using(file, "5k.html", "image/png").spoofed?
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
it 'does not reject a file that is named .jpg and identifies as PNG' do
|
|
10
10
|
file = File.open(fixture_file("5k.png"))
|
|
11
|
-
assert ! Paperclip::MediaTypeSpoofDetector.using(file, "5k.jpg").spoofed?
|
|
11
|
+
assert ! Paperclip::MediaTypeSpoofDetector.using(file, "5k.jpg", "image/png").spoofed?
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
it 'does not reject a file that is named .html and identifies as HTML' do
|
|
15
15
|
file = File.open(fixture_file("empty.html"))
|
|
16
|
-
assert ! Paperclip::MediaTypeSpoofDetector.using(file, "empty.html").spoofed?
|
|
16
|
+
assert ! Paperclip::MediaTypeSpoofDetector.using(file, "empty.html", "text/html").spoofed?
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
it 'does not reject a file that does not have a name' do
|
|
20
20
|
file = File.open(fixture_file("empty.html"))
|
|
21
|
-
assert ! Paperclip::MediaTypeSpoofDetector.using(file, "").spoofed?
|
|
21
|
+
assert ! Paperclip::MediaTypeSpoofDetector.using(file, "", "text/html").spoofed?
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
it 'does not reject a file that does have an extension' do
|
|
25
25
|
file = File.open(fixture_file("empty.html"))
|
|
26
|
-
assert ! Paperclip::MediaTypeSpoofDetector.using(file, "data").spoofed?
|
|
26
|
+
assert ! Paperclip::MediaTypeSpoofDetector.using(file, "data", "text/html").spoofed?
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
it 'does not reject when the supplied file is an IOAdapter' do
|
|
30
30
|
adapter = Paperclip.io_adapters.for(File.new(fixture_file("5k.png")))
|
|
31
|
-
assert ! Paperclip::MediaTypeSpoofDetector.using(adapter, adapter.original_filename).spoofed?
|
|
31
|
+
assert ! Paperclip::MediaTypeSpoofDetector.using(adapter, adapter.original_filename, adapter.content_type).spoofed?
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
it 'does not reject when the extension => content_type is in :content_type_mappings' do
|
|
@@ -38,19 +38,42 @@ describe Paperclip::MediaTypeSpoofDetector do
|
|
|
38
38
|
file.puts "Certificate!"
|
|
39
39
|
file.close
|
|
40
40
|
adapter = Paperclip.io_adapters.for(File.new(file.path));
|
|
41
|
-
assert ! Paperclip::MediaTypeSpoofDetector.using(adapter, adapter.original_filename).spoofed?
|
|
41
|
+
assert ! Paperclip::MediaTypeSpoofDetector.using(adapter, adapter.original_filename, adapter.content_type).spoofed?
|
|
42
42
|
ensure
|
|
43
43
|
Paperclip.options[:content_type_mappings] = {}
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
file
|
|
49
|
-
|
|
47
|
+
context "file named .html and is as HTML, but we're told JPG" do
|
|
48
|
+
let(:file) { File.open(fixture_file("empty.html")) }
|
|
49
|
+
let(:spoofed?) { Paperclip::MediaTypeSpoofDetector.using(file, "empty.html", "image/jpg").spoofed? }
|
|
50
|
+
|
|
51
|
+
it "rejects the file" do
|
|
52
|
+
assert spoofed?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "logs info about the detected spoof" do
|
|
56
|
+
Paperclip.expects(:log).with('Content Type Spoof: Filename empty.html (image/jpg from Headers, ["text/html"] from Extension), content type discovered from file command: text/html. See documentation to allow this combination.')
|
|
57
|
+
spoofed?
|
|
58
|
+
end
|
|
50
59
|
end
|
|
51
60
|
|
|
52
|
-
it "does not reject
|
|
61
|
+
it "does not reject if content_type is empty but otherwise checks out" do
|
|
53
62
|
file = File.open(fixture_file("empty.html"))
|
|
54
63
|
assert ! Paperclip::MediaTypeSpoofDetector.using(file, "empty.html", "").spoofed?
|
|
55
64
|
end
|
|
65
|
+
|
|
66
|
+
it 'does allow array as :content_type_mappings' do
|
|
67
|
+
begin
|
|
68
|
+
Paperclip.options[:content_type_mappings] = {
|
|
69
|
+
html: ['binary', 'text/html']
|
|
70
|
+
}
|
|
71
|
+
file = File.open(fixture_file('empty.html'))
|
|
72
|
+
spoofed = Paperclip::MediaTypeSpoofDetector
|
|
73
|
+
.using(file, "empty.html", "text/html").spoofed?
|
|
74
|
+
assert !spoofed
|
|
75
|
+
ensure
|
|
76
|
+
Paperclip.options[:content_type_mappings] = {}
|
|
77
|
+
end
|
|
78
|
+
end
|
|
56
79
|
end
|
|
@@ -20,7 +20,7 @@ describe Paperclip do
|
|
|
20
20
|
it "saves Cocaine::CommandLine.path that set before" do
|
|
21
21
|
Cocaine::CommandLine.path = "/opt/my_app/bin"
|
|
22
22
|
Paperclip.run("convert", "stuff")
|
|
23
|
-
|
|
23
|
+
expect(Cocaine::CommandLine.path).to match("/opt/my_app/bin")
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
it "does not duplicate Cocaine::CommandLine.path on multiple runs" do
|
|
@@ -29,7 +29,9 @@ describe Paperclip do
|
|
|
29
29
|
Paperclip.options[:command_path] = "/opt/my_app/bin"
|
|
30
30
|
Paperclip.run("convert", "stuff")
|
|
31
31
|
Paperclip.run("convert", "more_stuff")
|
|
32
|
-
|
|
32
|
+
|
|
33
|
+
cmd_path = Paperclip.options[:command_path]
|
|
34
|
+
assert_equal 1, Cocaine::CommandLine.path.scan(cmd_path).count
|
|
33
35
|
end
|
|
34
36
|
end
|
|
35
37
|
|
|
@@ -123,33 +125,6 @@ describe Paperclip do
|
|
|
123
125
|
end
|
|
124
126
|
end
|
|
125
127
|
|
|
126
|
-
if using_protected_attributes?
|
|
127
|
-
context "that is attr_protected" do
|
|
128
|
-
before do
|
|
129
|
-
Dummy.class_eval do
|
|
130
|
-
attr_protected :avatar
|
|
131
|
-
end
|
|
132
|
-
@dummy = Dummy.new
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
it "does not assign the avatar on mass-set" do
|
|
136
|
-
@dummy.attributes = { other: "I'm set!",
|
|
137
|
-
avatar: @file }
|
|
138
|
-
|
|
139
|
-
assert_equal "I'm set!", @dummy.other
|
|
140
|
-
assert ! @dummy.avatar?
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
it "allows assigment on normal set" do
|
|
144
|
-
@dummy.other = "I'm set!"
|
|
145
|
-
@dummy.avatar = @file
|
|
146
|
-
|
|
147
|
-
assert_equal "I'm set!", @dummy.other
|
|
148
|
-
assert @dummy.avatar?
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
128
|
context "with a subclass" do
|
|
154
129
|
before do
|
|
155
130
|
class ::SubDummy < Dummy; end
|
|
@@ -3,34 +3,35 @@ require 'spec_helper'
|
|
|
3
3
|
describe 'Plural cache' do
|
|
4
4
|
it 'caches pluralizations' do
|
|
5
5
|
cache = Paperclip::Interpolations::PluralCache.new
|
|
6
|
-
|
|
6
|
+
symbol = :box
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
cache.pluralize(word)
|
|
8
|
+
first = cache.pluralize_symbol(symbol)
|
|
9
|
+
second = cache.pluralize_symbol(symbol)
|
|
10
|
+
expect(first).to equal(second)
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
it 'caches pluralizations and underscores' do
|
|
14
|
+
class BigBox ; end
|
|
15
15
|
cache = Paperclip::Interpolations::PluralCache.new
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
word.expects(:pluralize).returns(word).once
|
|
19
|
-
word.expects(:underscore).returns(word).once
|
|
16
|
+
klass = BigBox
|
|
20
17
|
|
|
21
|
-
cache.
|
|
22
|
-
cache.
|
|
18
|
+
first = cache.underscore_and_pluralize_class(klass)
|
|
19
|
+
second = cache.underscore_and_pluralize_class(klass)
|
|
20
|
+
expect(first).to equal(second)
|
|
23
21
|
end
|
|
24
22
|
|
|
25
23
|
it 'pluralizes words' do
|
|
26
24
|
cache = Paperclip::Interpolations::PluralCache.new
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
symbol = :box
|
|
26
|
+
|
|
27
|
+
expect(cache.pluralize_symbol(symbol)).to eq("boxes")
|
|
29
28
|
end
|
|
30
29
|
|
|
31
|
-
it 'pluralizes and underscore
|
|
30
|
+
it 'pluralizes and underscore class names' do
|
|
31
|
+
class BigBox ; end
|
|
32
32
|
cache = Paperclip::Interpolations::PluralCache.new
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
klass = BigBox
|
|
34
|
+
|
|
35
|
+
expect(cache.underscore_and_pluralize_class(klass)).to eq("big_boxes")
|
|
35
36
|
end
|
|
36
37
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Paperclip::RailsEnvironment do
|
|
4
|
+
|
|
5
|
+
it "returns nil when Rails isn't defined" do
|
|
6
|
+
resetting_rails_to(nil) do
|
|
7
|
+
expect(Paperclip::RailsEnvironment.get).to be_nil
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "returns nil when Rails.env isn't defined" do
|
|
12
|
+
resetting_rails_to({}) do
|
|
13
|
+
expect(Paperclip::RailsEnvironment.get).to be_nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "returns the value of Rails.env if it is set" do
|
|
18
|
+
resetting_rails_to(OpenStruct.new(env: "foo")) do
|
|
19
|
+
expect(Paperclip::RailsEnvironment.get).to eq "foo"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def resetting_rails_to(new_value)
|
|
24
|
+
begin
|
|
25
|
+
previous_rails = Object.send(:remove_const, "Rails")
|
|
26
|
+
Object.const_set("Rails", new_value) unless new_value.nil?
|
|
27
|
+
yield
|
|
28
|
+
ensure
|
|
29
|
+
Object.send(:remove_const, "Rails") if Object.const_defined?("Rails")
|
|
30
|
+
Object.const_set("Rails", previous_rails)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require 'fog'
|
|
2
|
+
require 'fog/aws'
|
|
3
|
+
require 'fog/local'
|
|
3
4
|
require 'timecop'
|
|
4
5
|
|
|
5
6
|
describe Paperclip::Storage::Fog do
|
|
@@ -182,6 +183,13 @@ describe Paperclip::Storage::Fog do
|
|
|
182
183
|
tempfile.close
|
|
183
184
|
end
|
|
184
185
|
|
|
186
|
+
it 'is able to be handled when missing while copying to a local file' do
|
|
187
|
+
tempfile = Tempfile.new("known_location")
|
|
188
|
+
tempfile.binmode
|
|
189
|
+
assert_equal false, @dummy.avatar.copy_to_local_file(:original, tempfile.path)
|
|
190
|
+
tempfile.close
|
|
191
|
+
end
|
|
192
|
+
|
|
185
193
|
it "passes the content type to the Fog::Storage::AWS::Files instance" do
|
|
186
194
|
Fog::Storage::AWS::Files.any_instance.expects(:create).with do |hash|
|
|
187
195
|
hash[:content_type]
|
|
@@ -320,6 +328,9 @@ describe Paperclip::Storage::Fog do
|
|
|
320
328
|
it "honors the scheme in public url" do
|
|
321
329
|
assert_match(/^http:\/\//, @dummy.avatar.url)
|
|
322
330
|
end
|
|
331
|
+
it "honors the scheme in expiring url" do
|
|
332
|
+
assert_match(/^http:\/\//, @dummy.avatar.expiring_url)
|
|
333
|
+
end
|
|
323
334
|
end
|
|
324
335
|
|
|
325
336
|
context "with scheme not set" do
|
|
@@ -334,15 +345,20 @@ describe Paperclip::Storage::Fog do
|
|
|
334
345
|
it "provides HTTPS public url" do
|
|
335
346
|
assert_match(/^https:\/\//, @dummy.avatar.url)
|
|
336
347
|
end
|
|
348
|
+
it "provides HTTPS expiring url" do
|
|
349
|
+
assert_match(/^https:\/\//, @dummy.avatar.expiring_url)
|
|
350
|
+
end
|
|
337
351
|
end
|
|
338
352
|
|
|
339
353
|
context "with a valid bucket name for a subdomain" do
|
|
354
|
+
before { @dummy.stubs(:new_record?).returns(false) }
|
|
355
|
+
|
|
340
356
|
it "provides an url in subdomain style" do
|
|
341
357
|
assert_match(/^https:\/\/papercliptests.s3.amazonaws.com\/avatars\/5k.png/, @dummy.avatar.url)
|
|
342
358
|
end
|
|
343
359
|
|
|
344
360
|
it "provides an url that expires in subdomain style" do
|
|
345
|
-
assert_match(/^
|
|
361
|
+
assert_match(/^https:\/\/papercliptests.s3.amazonaws.com\/avatars\/5k.png.+Expires=.+$/, @dummy.avatar.expiring_url)
|
|
346
362
|
end
|
|
347
363
|
end
|
|
348
364
|
|
|
@@ -390,7 +406,7 @@ describe Paperclip::Storage::Fog do
|
|
|
390
406
|
end
|
|
391
407
|
|
|
392
408
|
it "provides a url that expires in folder style" do
|
|
393
|
-
assert_match(/^
|
|
409
|
+
assert_match(/^https:\/\/s3.amazonaws.com\/this_is_invalid\/avatars\/5k.png.+Expires=.+$/, @dummy.avatar.expiring_url)
|
|
394
410
|
end
|
|
395
411
|
|
|
396
412
|
end
|
|
@@ -409,6 +425,9 @@ describe Paperclip::Storage::Fog do
|
|
|
409
425
|
assert @connection.directories.get(@dynamic_fog_directory).inspect
|
|
410
426
|
end
|
|
411
427
|
|
|
428
|
+
it "provides an url using dynamic bucket name" do
|
|
429
|
+
assert_match(/^https:\/\/dynamicpaperclip.s3.amazonaws.com\/avatars\/5k.png\?\d*$/, @dummy.avatar.url)
|
|
430
|
+
end
|
|
412
431
|
end
|
|
413
432
|
|
|
414
433
|
context "with a proc for the fog_host evaluating a model method" do
|
|
@@ -475,6 +494,25 @@ describe Paperclip::Storage::Fog do
|
|
|
475
494
|
assert_equal @dummy.avatar.fog_credentials, @dynamic_fog_credentials
|
|
476
495
|
end
|
|
477
496
|
end
|
|
497
|
+
|
|
498
|
+
context "with custom fog_options" do
|
|
499
|
+
before do
|
|
500
|
+
rebuild_model(
|
|
501
|
+
@options.merge(fog_options: { multipart_chunk_size: 104857600 }),
|
|
502
|
+
)
|
|
503
|
+
@dummy = Dummy.new
|
|
504
|
+
@dummy.avatar = @file
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
it "applies the options to the fog #create call" do
|
|
508
|
+
files = stub
|
|
509
|
+
@dummy.avatar.stubs(:directory).returns stub(files: files)
|
|
510
|
+
files.expects(:create).with(
|
|
511
|
+
has_entries(multipart_chunk_size: 104857600),
|
|
512
|
+
)
|
|
513
|
+
@dummy.save
|
|
514
|
+
end
|
|
515
|
+
end
|
|
478
516
|
end
|
|
479
517
|
|
|
480
518
|
end
|
|
@@ -492,6 +530,7 @@ describe Paperclip::Storage::Fog do
|
|
|
492
530
|
@file = File.new(fixture_file('5k.png'), 'rb')
|
|
493
531
|
@dummy = Dummy.new
|
|
494
532
|
@dummy.avatar = @file
|
|
533
|
+
@dummy.stubs(:new_record?).returns(false)
|
|
495
534
|
end
|
|
496
535
|
|
|
497
536
|
after do
|
|
@@ -8,6 +8,7 @@ unless ENV["S3_BUCKET"].blank?
|
|
|
8
8
|
storage: :s3,
|
|
9
9
|
bucket: ENV["S3_BUCKET"],
|
|
10
10
|
path: ":class/:attachment/:id/:style.:extension",
|
|
11
|
+
s3_region: ENV["S3_REGION"],
|
|
11
12
|
s3_credentials: {
|
|
12
13
|
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
|
|
13
14
|
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
|
|
@@ -45,6 +46,7 @@ unless ENV["S3_BUCKET"].blank?
|
|
|
45
46
|
storage: :s3,
|
|
46
47
|
bucket: ENV["S3_BUCKET"],
|
|
47
48
|
path: ":class/:attachment/:id/:style.:extension",
|
|
49
|
+
s3_region: ENV["S3_REGION"],
|
|
48
50
|
s3_credentials: {
|
|
49
51
|
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
|
|
50
52
|
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
|
|
@@ -64,6 +66,7 @@ unless ENV["S3_BUCKET"].blank?
|
|
|
64
66
|
storage: :s3,
|
|
65
67
|
bucket: ENV["S3_BUCKET"],
|
|
66
68
|
path: ":class/:attachment/:id/:style.:extension",
|
|
69
|
+
s3_region: ENV["S3_REGION"],
|
|
67
70
|
s3_credentials: {
|
|
68
71
|
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
|
|
69
72
|
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
|
|
@@ -105,6 +108,7 @@ unless ENV["S3_BUCKET"].blank?
|
|
|
105
108
|
rebuild_model styles: { thumb: "100x100", square: "32x32#" },
|
|
106
109
|
storage: :s3,
|
|
107
110
|
bucket: ENV["S3_BUCKET"],
|
|
111
|
+
s3_region: ENV["S3_REGION"],
|
|
108
112
|
s3_credentials: {
|
|
109
113
|
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
|
|
110
114
|
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
|
|
@@ -136,7 +140,7 @@ unless ENV["S3_BUCKET"].blank?
|
|
|
136
140
|
it "is destroyable" do
|
|
137
141
|
url = @dummy.avatar.url
|
|
138
142
|
@dummy.destroy
|
|
139
|
-
|
|
143
|
+
assert_forbidden_response url
|
|
140
144
|
end
|
|
141
145
|
end
|
|
142
146
|
|
|
@@ -146,12 +150,12 @@ unless ENV["S3_BUCKET"].blank?
|
|
|
146
150
|
storage: :s3,
|
|
147
151
|
bucket: ENV["S3_BUCKET"],
|
|
148
152
|
path: ":class/:attachment/:id/:style.:extension",
|
|
153
|
+
s3_region: ENV["S3_REGION"],
|
|
149
154
|
s3_credentials: {
|
|
150
155
|
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
|
|
151
156
|
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
|
|
152
157
|
},
|
|
153
|
-
s3_server_side_encryption:
|
|
154
|
-
|
|
158
|
+
s3_server_side_encryption: "AES256"
|
|
155
159
|
Dummy.delete_all
|
|
156
160
|
@dummy = Dummy.new
|
|
157
161
|
end
|
|
@@ -173,7 +177,7 @@ unless ENV["S3_BUCKET"].blank?
|
|
|
173
177
|
end
|
|
174
178
|
|
|
175
179
|
it "is encrypted on S3" do
|
|
176
|
-
assert @dummy.avatar.s3_object.server_side_encryption ==
|
|
180
|
+
assert @dummy.avatar.s3_object.server_side_encryption == "AES256"
|
|
177
181
|
end
|
|
178
182
|
end
|
|
179
183
|
end
|