paperclip 3.5.4 → 5.3.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/.gitignore +0 -6
- data/.hound.yml +1055 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +17 -20
- data/Appraisals +4 -16
- data/CONTRIBUTING.md +29 -13
- data/Gemfile +11 -3
- data/LICENSE +1 -3
- data/NEWS +241 -49
- data/README.md +471 -166
- data/RELEASING.md +17 -0
- data/Rakefile +6 -8
- data/UPGRADING +12 -9
- data/features/basic_integration.feature +27 -8
- data/features/migration.feature +0 -24
- data/features/step_definitions/attachment_steps.rb +36 -28
- data/features/step_definitions/html_steps.rb +2 -2
- data/features/step_definitions/rails_steps.rb +68 -37
- data/features/step_definitions/s3_steps.rb +2 -2
- data/features/step_definitions/web_steps.rb +1 -103
- data/features/support/env.rb +3 -2
- data/features/support/file_helpers.rb +2 -2
- data/features/support/fixtures/gemfile.txt +1 -1
- data/features/support/paths.rb +1 -1
- data/features/support/rails.rb +2 -25
- data/gemfiles/4.2.gemfile +17 -0
- data/gemfiles/5.0.gemfile +17 -0
- data/lib/generators/paperclip/paperclip_generator.rb +9 -3
- data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +2 -2
- data/lib/paperclip/attachment.rb +151 -46
- data/lib/paperclip/attachment_registry.rb +3 -2
- data/lib/paperclip/callbacks.rb +13 -1
- data/lib/paperclip/content_type_detector.rb +26 -22
- data/lib/paperclip/errors.rb +8 -1
- data/lib/paperclip/file_command_content_type_detector.rb +6 -8
- data/lib/paperclip/geometry_detector_factory.rb +5 -3
- data/lib/paperclip/geometry_parser_factory.rb +1 -1
- data/lib/paperclip/glue.rb +1 -1
- data/lib/paperclip/has_attached_file.rb +17 -1
- data/lib/paperclip/helpers.rb +15 -11
- data/lib/paperclip/interpolations/plural_cache.rb +6 -5
- data/lib/paperclip/interpolations.rb +26 -13
- data/lib/paperclip/io_adapters/abstract_adapter.rb +32 -4
- data/lib/paperclip/io_adapters/attachment_adapter.rb +13 -8
- data/lib/paperclip/io_adapters/data_uri_adapter.rb +11 -16
- 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 +15 -16
- data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +10 -6
- data/lib/paperclip/io_adapters/uri_adapter.rb +41 -19
- data/lib/paperclip/locales/en.yml +1 -0
- data/lib/paperclip/matchers/have_attached_file_matcher.rb +2 -1
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +2 -1
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +2 -1
- data/lib/paperclip/media_type_spoof_detector.rb +90 -0
- data/lib/paperclip/processor.rb +5 -41
- data/lib/paperclip/processor_helpers.rb +50 -0
- data/lib/paperclip/rails_environment.rb +25 -0
- data/lib/paperclip/schema.rb +9 -7
- data/lib/paperclip/storage/filesystem.rb +14 -3
- data/lib/paperclip/storage/fog.rb +37 -19
- data/lib/paperclip/storage/s3.rb +129 -69
- data/lib/paperclip/style.rb +8 -2
- data/lib/paperclip/tempfile_factory.rb +5 -1
- data/lib/paperclip/thumbnail.rb +30 -18
- data/lib/paperclip/url_generator.rb +26 -14
- data/lib/paperclip/validators/attachment_content_type_validator.rb +4 -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 +4 -0
- data/lib/paperclip/validators/attachment_size_validator.rb +5 -3
- data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
- data/lib/paperclip/validators.rb +11 -4
- data/lib/paperclip/version.rb +3 -1
- data/lib/paperclip.rb +30 -11
- data/lib/tasks/paperclip.rake +34 -5
- data/paperclip.gemspec +21 -16
- data/shoulda_macros/paperclip.rb +0 -1
- data/spec/paperclip/attachment_definitions_spec.rb +13 -0
- data/{test/attachment_processing_test.rb → spec/paperclip/attachment_processing_spec.rb} +17 -20
- data/spec/paperclip/attachment_registry_spec.rb +158 -0
- data/{test/attachment_test.rb → spec/paperclip/attachment_spec.rb} +502 -407
- data/{test/content_type_detector_test.rb → spec/paperclip/content_type_detector_spec.rb} +17 -20
- data/spec/paperclip/file_command_content_type_detector_spec.rb +40 -0
- data/spec/paperclip/filename_cleaner_spec.rb +14 -0
- data/spec/paperclip/geometry_detector_spec.rb +39 -0
- data/{test/geometry_parser_test.rb → spec/paperclip/geometry_parser_spec.rb} +27 -27
- data/{test/geometry_test.rb → spec/paperclip/geometry_spec.rb} +50 -52
- data/spec/paperclip/glue_spec.rb +44 -0
- data/spec/paperclip/has_attached_file_spec.rb +158 -0
- data/{test/integration_test.rb → spec/paperclip/integration_spec.rb} +137 -128
- data/{test/interpolations_test.rb → spec/paperclip/interpolations_spec.rb} +70 -46
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +132 -0
- data/{test/io_adapters/attachment_adapter_test.rb → spec/paperclip/io_adapters/attachment_adapter_spec.rb} +33 -32
- data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +89 -0
- data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
- data/{test/io_adapters/file_adapter_test.rb → spec/paperclip/io_adapters/file_adapter_spec.rb} +38 -42
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +121 -0
- data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
- data/{test/io_adapters/nil_adapter_test.rb → spec/paperclip/io_adapters/nil_adapter_spec.rb} +7 -7
- data/{test/io_adapters/registry_test.rb → spec/paperclip/io_adapters/registry_spec.rb} +12 -9
- data/{test/io_adapters/stringio_adapter_test.rb → spec/paperclip/io_adapters/stringio_adapter_spec.rb} +21 -18
- data/{test/io_adapters/uploaded_file_adapter_test.rb → spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb} +46 -46
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +172 -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 +94 -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/{test/processor_test.rb → spec/paperclip/processor_spec.rb} +7 -7
- data/spec/paperclip/rails_environment_spec.rb +33 -0
- data/{test/rake_test.rb → spec/paperclip/rake_spec.rb} +15 -15
- data/spec/paperclip/schema_spec.rb +248 -0
- data/{test/storage/filesystem_test.rb → spec/paperclip/storage/filesystem_spec.rb} +18 -18
- data/spec/paperclip/storage/fog_spec.rb +566 -0
- data/spec/paperclip/storage/s3_live_spec.rb +188 -0
- data/spec/paperclip/storage/s3_spec.rb +1693 -0
- data/spec/paperclip/style_spec.rb +255 -0
- data/spec/paperclip/tempfile_factory_spec.rb +33 -0
- data/spec/paperclip/tempfile_spec.rb +35 -0
- data/{test/thumbnail_test.rb → spec/paperclip/thumbnail_spec.rb} +153 -134
- data/spec/paperclip/url_generator_spec.rb +222 -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/{test/validators/attachment_presence_validator_test.rb → spec/paperclip/validators/attachment_presence_validator_spec.rb} +20 -20
- data/{test/validators/attachment_size_validator_test.rb → spec/paperclip/validators/attachment_size_validator_spec.rb} +77 -64
- 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 +47 -0
- data/spec/support/assertions.rb +82 -0
- data/spec/support/conditional_filter_helper.rb +5 -0
- data/spec/support/fake_model.rb +25 -0
- data/spec/support/fake_rails.rb +12 -0
- data/spec/support/fixtures/empty.html +1 -0
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/fixtures/spaced file.jpg +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/{test → spec}/support/mock_attachment.rb +2 -0
- data/{test → spec}/support/mock_url_generator_builder.rb +2 -2
- 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 +348 -225
- data/RUNNING_TESTS.md +0 -4
- data/cucumber/paperclip_steps.rb +0 -6
- data/gemfiles/3.0.gemfile +0 -11
- data/gemfiles/3.1.gemfile +0 -11
- data/gemfiles/3.2.gemfile +0 -11
- data/gemfiles/4.0.gemfile +0 -11
- data/test/attachment_definitions_test.rb +0 -12
- data/test/attachment_registry_test.rb +0 -88
- data/test/file_command_content_type_detector_test.rb +0 -27
- data/test/filename_cleaner_test.rb +0 -14
- data/test/generator_test.rb +0 -84
- data/test/geometry_detector_test.rb +0 -24
- data/test/has_attached_file_test.rb +0 -125
- data/test/helper.rb +0 -232
- data/test/io_adapters/abstract_adapter_test.rb +0 -58
- data/test/io_adapters/data_uri_adapter_test.rb +0 -74
- data/test/io_adapters/empty_string_adapter_test.rb +0 -18
- data/test/io_adapters/http_url_proxy_adapter_test.rb +0 -102
- data/test/io_adapters/identity_adapter_test.rb +0 -8
- data/test/io_adapters/uri_adapter_test.rb +0 -102
- data/test/matchers/have_attached_file_matcher_test.rb +0 -24
- data/test/matchers/validate_attachment_content_type_matcher_test.rb +0 -110
- data/test/matchers/validate_attachment_presence_matcher_test.rb +0 -69
- data/test/matchers/validate_attachment_size_matcher_test.rb +0 -86
- data/test/meta_class_test.rb +0 -32
- data/test/paperclip_missing_attachment_styles_test.rb +0 -90
- data/test/paperclip_test.rb +0 -217
- data/test/plural_cache_test.rb +0 -36
- data/test/schema_test.rb +0 -200
- data/test/storage/fog_test.rb +0 -473
- data/test/storage/s3_live_test.rb +0 -179
- data/test/storage/s3_test.rb +0 -1356
- data/test/style_test.rb +0 -213
- data/test/support/mock_model.rb +0 -2
- data/test/tempfile_factory_test.rb +0 -17
- data/test/url_generator_test.rb +0 -187
- data/test/validators/attachment_content_type_validator_test.rb +0 -324
- data/test/validators_test.rb +0 -61
- /data/{test → spec}/database.yml +0 -0
- /data/{test → spec/support}/fixtures/12k.png +0 -0
- /data/{test → spec/support}/fixtures/50x50.png +0 -0
- /data/{test → spec/support}/fixtures/5k.png +0 -0
- /data/{test → spec/support}/fixtures/animated +0 -0
- /data/{test → spec/support}/fixtures/animated.gif +0 -0
- /data/{test → spec/support}/fixtures/animated.unknown +0 -0
- /data/{test → spec/support}/fixtures/bad.png +0 -0
- /data/{test → spec/support}/fixtures/fog.yml +0 -0
- /data/{test → spec/support}/fixtures/rotated.jpg +0 -0
- /data/{test → spec/support}/fixtures/s3.yml +0 -0
- /data/{test → spec/support}/fixtures/spaced file.png +0 -0
- /data/{test → spec/support}/fixtures/text.txt +0 -0
- /data/{test → spec/support}/fixtures/twopage.pdf +0 -0
- /data/{test → spec/support}/fixtures/uppercase.PNG +0 -0
- /data/{test → spec}/support/mock_interpolator.rb +0 -0
@@ -103,107 +103,5 @@ When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field|
|
|
103
103
|
end
|
104
104
|
|
105
105
|
Then /^(?:|I )should see "([^"]*)"$/ do |text|
|
106
|
-
|
107
|
-
page.should have_content(text)
|
108
|
-
else
|
109
|
-
assert page.has_content?(text)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
Then /^(?:|I )should see \/([^\/]*)\/$/ do |regexp|
|
114
|
-
regexp = Regexp.new(regexp)
|
115
|
-
|
116
|
-
if page.respond_to? :should
|
117
|
-
page.should have_xpath('//*', :text => regexp)
|
118
|
-
else
|
119
|
-
assert page.has_xpath?('//*', :text => regexp)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
Then /^(?:|I )should not see "([^"]*)"$/ do |text|
|
124
|
-
if page.respond_to? :should
|
125
|
-
page.should have_no_content(text)
|
126
|
-
else
|
127
|
-
assert page.has_no_content?(text)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
Then /^(?:|I )should not see \/([^\/]*)\/$/ do |regexp|
|
132
|
-
regexp = Regexp.new(regexp)
|
133
|
-
|
134
|
-
if page.respond_to? :should
|
135
|
-
page.should have_no_xpath('//*', :text => regexp)
|
136
|
-
else
|
137
|
-
assert page.has_no_xpath?('//*', :text => regexp)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
Then /^the "([^"]*)" field(?: within (.*))? should contain "([^"]*)"$/ do |field, parent, value|
|
142
|
-
with_scope(parent) do
|
143
|
-
field = find_field(field)
|
144
|
-
if field.value.respond_to? :should
|
145
|
-
field.value.should =~ /#{value}/
|
146
|
-
else
|
147
|
-
assert_match(/#{value}/, field.value)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
Then /^the "([^"]*)" field(?: within (.*))? should not contain "([^"]*)"$/ do |field, parent, value|
|
153
|
-
with_scope(parent) do
|
154
|
-
field = find_field(field)
|
155
|
-
if field.value.respond_to? :should_not
|
156
|
-
field.value.should_not =~ /#{value}/
|
157
|
-
else
|
158
|
-
assert_no_match(/#{value}/, field.value)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
Then /^the "([^"]*)" checkbox(?: within (.*))? should be checked$/ do |label, parent|
|
164
|
-
with_scope(parent) do
|
165
|
-
field_checked = find_field(label)['checked']
|
166
|
-
if field_checked.respond_to? :should
|
167
|
-
field_checked.should be_true
|
168
|
-
else
|
169
|
-
assert field_checked
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
Then /^the "([^"]*)" checkbox(?: within (.*))? should not be checked$/ do |label, parent|
|
175
|
-
with_scope(parent) do
|
176
|
-
field_checked = find_field(label)['checked']
|
177
|
-
if field_checked.respond_to? :should
|
178
|
-
field_checked.should be_false
|
179
|
-
else
|
180
|
-
assert !field_checked
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
Then /^(?:|I )should be on (.+)$/ do |page_name|
|
186
|
-
current_path = URI.parse(current_url).path
|
187
|
-
if current_path.respond_to? :should
|
188
|
-
current_path.should == path_to(page_name)
|
189
|
-
else
|
190
|
-
assert_equal path_to(page_name), current_path
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
|
195
|
-
query = URI.parse(current_url).query
|
196
|
-
actual_params = query ? CGI.parse(query) : {}
|
197
|
-
expected_params = {}
|
198
|
-
expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
|
199
|
-
|
200
|
-
if actual_params.respond_to? :should
|
201
|
-
actual_params.should == expected_params
|
202
|
-
else
|
203
|
-
assert_equal expected_params, actual_params
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
Then /^show me the page$/ do
|
208
|
-
save_and_open_page
|
106
|
+
expect(page).to have_content(text)
|
209
107
|
end
|
data/features/support/env.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'aruba/cucumber'
|
2
2
|
require 'capybara/cucumber'
|
3
|
-
require '
|
3
|
+
require 'rspec/matchers'
|
4
4
|
|
5
5
|
$CUCUMBER=1
|
6
6
|
|
7
|
-
World(
|
7
|
+
World(RSpec::Matchers)
|
8
8
|
|
9
9
|
Before do
|
10
|
+
aruba.config.command_launcher = ENV.fetch("DEBUG", nil) ? :debug : :spawn
|
10
11
|
@aruba_timeout_seconds = 120
|
11
12
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module FileHelpers
|
2
2
|
def append_to(path, contents)
|
3
|
-
|
3
|
+
cd(".") do
|
4
4
|
File.open(path, "a") do |file|
|
5
5
|
file.puts
|
6
6
|
file.puts contents
|
@@ -13,7 +13,7 @@ module FileHelpers
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def comment_out_gem_in_gemfile(gemname)
|
16
|
-
|
16
|
+
cd(".") do
|
17
17
|
gemfile = File.read("Gemfile")
|
18
18
|
gemfile.sub!(/^(\s*)(gem\s*['"]#{gemname})/, "\\1# \\2")
|
19
19
|
File.open("Gemfile", 'w'){ |file| file.write(gemfile) }
|
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
@@ -6,7 +6,8 @@ ORIGINAL_BUNDLE_VARS = Hash[ENV.select{ |key,value| BUNDLE_ENV_VARS.include?(key
|
|
6
6
|
ENV['RAILS_ENV'] = 'test'
|
7
7
|
|
8
8
|
Before do
|
9
|
-
|
9
|
+
gemfile = ENV['BUNDLE_GEMFILE'].to_s
|
10
|
+
ENV['BUNDLE_GEMFILE'] = File.join(Dir.pwd, gemfile) unless gemfile.start_with?(Dir.pwd)
|
10
11
|
@framework_version = nil
|
11
12
|
end
|
12
13
|
|
@@ -34,29 +35,5 @@ module RailsCommandHelpers
|
|
34
35
|
def framework_major_version
|
35
36
|
framework_version.split(".").first.to_i
|
36
37
|
end
|
37
|
-
|
38
|
-
def using_protected_attributes?
|
39
|
-
framework_major_version < 4
|
40
|
-
end
|
41
|
-
|
42
|
-
def new_application_command
|
43
|
-
"rails new"
|
44
|
-
end
|
45
|
-
|
46
|
-
def generator_command
|
47
|
-
if framework_major_version >= 4
|
48
|
-
"rails generate"
|
49
|
-
else
|
50
|
-
"script/rails generate"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def runner_command
|
55
|
-
if framework_major_version >= 4
|
56
|
-
"rails runner"
|
57
|
-
else
|
58
|
-
"script/rails runner"
|
59
|
-
end
|
60
|
-
end
|
61
38
|
end
|
62
39
|
World(RailsCommandHelpers)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "sqlite3", "~> 1.3.8", :platforms => :ruby
|
6
|
+
gem "pry"
|
7
|
+
gem "rails", "~> 4.2.0"
|
8
|
+
|
9
|
+
group :development, :test do
|
10
|
+
gem "activerecord-import"
|
11
|
+
gem "mime-types"
|
12
|
+
gem "builder"
|
13
|
+
gem "rubocop", :require => false
|
14
|
+
gem "rspec"
|
15
|
+
end
|
16
|
+
|
17
|
+
gemspec :path => "../"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "sqlite3", "~> 1.3.8", :platforms => :ruby
|
6
|
+
gem "pry"
|
7
|
+
gem "rails", "~> 5.0.0"
|
8
|
+
|
9
|
+
group :development, :test do
|
10
|
+
gem "activerecord-import"
|
11
|
+
gem "mime-types"
|
12
|
+
gem "builder"
|
13
|
+
gem "rubocop", :require => false
|
14
|
+
gem "rspec"
|
15
|
+
end
|
16
|
+
|
17
|
+
gemspec :path => "../"
|
@@ -13,11 +13,11 @@ class PaperclipGenerator < ActiveRecord::Generators::Base
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def generate_migration
|
16
|
-
migration_template
|
16
|
+
migration_template("paperclip_migration.rb.erb",
|
17
|
+
"db/migrate/#{migration_file_name}",
|
18
|
+
migration_version: migration_version)
|
17
19
|
end
|
18
20
|
|
19
|
-
protected
|
20
|
-
|
21
21
|
def migration_name
|
22
22
|
"add_attachment_#{attachment_names.join("_")}_to_#{name.underscore.pluralize}"
|
23
23
|
end
|
@@ -29,4 +29,10 @@ class PaperclipGenerator < ActiveRecord::Generators::Base
|
|
29
29
|
def migration_class_name
|
30
30
|
migration_name.camelize
|
31
31
|
end
|
32
|
+
|
33
|
+
def migration_version
|
34
|
+
if Rails.version.start_with? "5"
|
35
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
36
|
+
end
|
37
|
+
end
|
32
38
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class <%= migration_class_name %> < ActiveRecord::Migration
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
|
2
2
|
def self.up
|
3
3
|
change_table :<%= table_name %> do |t|
|
4
4
|
<% attachment_names.each do |attachment| -%>
|
@@ -9,7 +9,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration
|
|
9
9
|
|
10
10
|
def self.down
|
11
11
|
<% attachment_names.each do |attachment| -%>
|
12
|
-
|
12
|
+
remove_attachment :<%= table_name %>, :<%= attachment %>
|
13
13
|
<% end -%>
|
14
14
|
end
|
15
15
|
end
|
data/lib/paperclip/attachment.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'uri'
|
3
3
|
require 'paperclip/url_generator'
|
4
|
+
require 'active_support/deprecation'
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
4
6
|
|
5
7
|
module Paperclip
|
6
8
|
# The Attachment class manages the files for a given attachment. It saves
|
@@ -30,12 +32,14 @@ module Paperclip
|
|
30
32
|
:use_default_time_zone => true,
|
31
33
|
:use_timestamp => true,
|
32
34
|
:whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails],
|
35
|
+
:validate_media_type => true,
|
36
|
+
:adapter_options => { hash_digest: Digest::MD5 },
|
33
37
|
:check_validity_before_processing => true
|
34
38
|
}
|
35
39
|
end
|
36
40
|
|
37
41
|
attr_reader :name, :instance, :default_style, :convert_options, :queued_for_write, :whiny,
|
38
|
-
:options, :interpolator, :source_file_options
|
42
|
+
:options, :interpolator, :source_file_options
|
39
43
|
attr_accessor :post_processing
|
40
44
|
|
41
45
|
# Creates an Attachment object. +name+ is the name of the attachment,
|
@@ -47,7 +51,8 @@ module Paperclip
|
|
47
51
|
# +url+ - a relative URL of the attachment. This is interpolated using +interpolator+
|
48
52
|
# +path+ - where on the filesystem to store the attachment. This is interpolated using +interpolator+
|
49
53
|
# +styles+ - a hash of options for processing the attachment. See +has_attached_file+ for the details
|
50
|
-
# +only_process+ - style args to be run through the post-processor. This defaults to the empty list
|
54
|
+
# +only_process+ - style args to be run through the post-processor. This defaults to the empty list (which is
|
55
|
+
# a special case that indicates all styles should be processed)
|
51
56
|
# +default_url+ - a URL for the missing image
|
52
57
|
# +default_style+ - the style to use when an argument is not specified e.g. #url, #path
|
53
58
|
# +storage+ - the storage mechanism. Defaults to :filesystem
|
@@ -66,7 +71,8 @@ module Paperclip
|
|
66
71
|
# +url_generator+ - the object used to generate URLs, using the interpolator. Defaults to Paperclip::UrlGenerator
|
67
72
|
# +escape_url+ - Perform URI escaping to URLs. Defaults to true
|
68
73
|
def initialize(name, instance, options = {})
|
69
|
-
@name = name
|
74
|
+
@name = name.to_sym
|
75
|
+
@name_string = name.to_s
|
70
76
|
@instance = instance
|
71
77
|
|
72
78
|
options = self.class.default_options.deep_merge(options)
|
@@ -78,7 +84,7 @@ module Paperclip
|
|
78
84
|
@errors = {}
|
79
85
|
@dirty = false
|
80
86
|
@interpolator = options[:interpolator]
|
81
|
-
@url_generator = options[:url_generator].new(self
|
87
|
+
@url_generator = options[:url_generator].new(self)
|
82
88
|
@source_file_options = options[:source_file_options]
|
83
89
|
@whiny = options[:whiny]
|
84
90
|
|
@@ -86,36 +92,30 @@ module Paperclip
|
|
86
92
|
end
|
87
93
|
|
88
94
|
# What gets called when you call instance.attachment = File. It clears
|
89
|
-
# errors, assigns attributes, and processes the file. It
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
95
|
+
# errors, assigns attributes, and processes the file. It also queues up the
|
96
|
+
# previous file for deletion, to be flushed away on #save of its host. In
|
97
|
+
# addition to form uploads, you can also assign another Paperclip
|
98
|
+
# attachment:
|
93
99
|
# new_user.avatar = old_user.avatar
|
94
|
-
def assign
|
100
|
+
def assign(uploaded_file)
|
101
|
+
@file = Paperclip.io_adapters.for(uploaded_file,
|
102
|
+
@options[:adapter_options])
|
95
103
|
ensure_required_accessors!
|
96
|
-
|
104
|
+
ensure_required_validations!
|
97
105
|
|
98
|
-
|
99
|
-
|
100
|
-
return nil if file.nil?
|
106
|
+
if @file.assignment?
|
107
|
+
clear(*only_process)
|
101
108
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
post_process(*only_process) if post_processing
|
113
|
-
|
114
|
-
# Reset the file size if the original file was reprocessed.
|
115
|
-
instance_write(:file_size, @queued_for_write[:original].size)
|
116
|
-
instance_write(:fingerprint, @queued_for_write[:original].fingerprint) if instance_respond_to?(:fingerprint)
|
117
|
-
updater = :"#{name}_file_name_will_change!"
|
118
|
-
instance.send updater if instance.respond_to? updater
|
109
|
+
if @file.nil?
|
110
|
+
nil
|
111
|
+
else
|
112
|
+
assign_attributes
|
113
|
+
post_process_file
|
114
|
+
reset_file_if_original_reprocessed
|
115
|
+
end
|
116
|
+
else
|
117
|
+
nil
|
118
|
+
end
|
119
119
|
end
|
120
120
|
|
121
121
|
# Returns the public URL of the attachment with a given style. This does
|
@@ -140,9 +140,8 @@ module Paperclip
|
|
140
140
|
#
|
141
141
|
# +#new(Paperclip::Attachment, options_hash)+
|
142
142
|
# +#for(style_name, options_hash)+
|
143
|
-
def url(style_name = default_style, options = {})
|
144
|
-
default_options = {:timestamp => @options[:use_timestamp], :escape => @options[:escape_url]}
|
145
143
|
|
144
|
+
def url(style_name = default_style, options = {})
|
146
145
|
if options == true || options == false # Backwards compatibility.
|
147
146
|
@url_generator.for(style_name, default_options.merge(:timestamp => options))
|
148
147
|
else
|
@@ -150,6 +149,13 @@ module Paperclip
|
|
150
149
|
end
|
151
150
|
end
|
152
151
|
|
152
|
+
def default_options
|
153
|
+
{
|
154
|
+
:timestamp => @options[:use_timestamp],
|
155
|
+
:escape => @options[:escape_url]
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
153
159
|
# Alias to +url+ that allows using the expiring_url method provided by the cloud
|
154
160
|
# storage implementations, but keep using filesystem storage for development and
|
155
161
|
# testing.
|
@@ -166,6 +172,18 @@ module Paperclip
|
|
166
172
|
path.respond_to?(:unescape) ? path.unescape : path
|
167
173
|
end
|
168
174
|
|
175
|
+
# :nodoc:
|
176
|
+
def staged_path(style_name = default_style)
|
177
|
+
if staged?
|
178
|
+
@queued_for_write[style_name].path
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# :nodoc:
|
183
|
+
def staged?
|
184
|
+
! @queued_for_write.empty?
|
185
|
+
end
|
186
|
+
|
169
187
|
# Alias to +url+
|
170
188
|
def to_s style_name = default_style
|
171
189
|
url(style_name)
|
@@ -222,6 +240,10 @@ module Paperclip
|
|
222
240
|
# the instance's errors and returns false, cancelling the save.
|
223
241
|
def save
|
224
242
|
flush_deletes unless @options[:keep_old_files]
|
243
|
+
process = only_process
|
244
|
+
if process.any? && !process.include?(:original)
|
245
|
+
@queued_for_write.except!(:original)
|
246
|
+
end
|
225
247
|
flush_writes
|
226
248
|
@dirty = false
|
227
249
|
true
|
@@ -308,7 +330,7 @@ module Paperclip
|
|
308
330
|
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@options[:hash_digest]).new, @options[:hash_secret], data)
|
309
331
|
end
|
310
332
|
|
311
|
-
# This method really shouldn't be called that often.
|
333
|
+
# This method really shouldn't be called that often. Its expected use is
|
312
334
|
# in the paperclip:refresh rake task and that's it. It will regenerate all
|
313
335
|
# thumbnails forcefully, by reobtaining the original file and going through
|
314
336
|
# the post-process again.
|
@@ -333,7 +355,7 @@ module Paperclip
|
|
333
355
|
|
334
356
|
# Returns true if a file has been assigned.
|
335
357
|
def file?
|
336
|
-
|
358
|
+
original_filename.present?
|
337
359
|
end
|
338
360
|
|
339
361
|
alias :present? :file?
|
@@ -352,7 +374,7 @@ module Paperclip
|
|
352
374
|
# instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's
|
353
375
|
# "avatar_file_name" field (assuming the attachment is called avatar).
|
354
376
|
def instance_write(attr, value)
|
355
|
-
setter = :"#{
|
377
|
+
setter = :"#{@name_string}_#{attr}="
|
356
378
|
if instance.respond_to?(setter)
|
357
379
|
instance.send(setter, value)
|
358
380
|
end
|
@@ -361,7 +383,7 @@ module Paperclip
|
|
361
383
|
# Reads the attachment-specific attribute on the instance. See instance_write
|
362
384
|
# for more details.
|
363
385
|
def instance_read(attr)
|
364
|
-
getter = :"#{
|
386
|
+
getter = :"#{@name_string}_#{attr}"
|
365
387
|
if instance.respond_to?(getter)
|
366
388
|
instance.send(getter)
|
367
389
|
end
|
@@ -373,10 +395,24 @@ module Paperclip
|
|
373
395
|
@options[:path].respond_to?(:call) ? @options[:path].call(self) : @options[:path]
|
374
396
|
end
|
375
397
|
|
398
|
+
def active_validator_classes
|
399
|
+
@instance.class.validators.map(&:class)
|
400
|
+
end
|
401
|
+
|
402
|
+
def missing_required_validator?
|
403
|
+
(active_validator_classes.flat_map(&:ancestors) & Paperclip::REQUIRED_VALIDATORS).empty?
|
404
|
+
end
|
405
|
+
|
406
|
+
def ensure_required_validations!
|
407
|
+
if missing_required_validator?
|
408
|
+
raise Paperclip::Errors::MissingRequiredValidatorError
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
376
412
|
def ensure_required_accessors! #:nodoc:
|
377
413
|
%w(file_name).each do |field|
|
378
|
-
unless @instance.respond_to?("#{
|
379
|
-
raise Paperclip::Error.new("#{@instance.class} model missing required attr_accessor for '#{
|
414
|
+
unless @instance.respond_to?("#{@name_string}_#{field}") && @instance.respond_to?("#{@name_string}_#{field}=")
|
415
|
+
raise Paperclip::Error.new("#{@instance.class} model missing required attr_accessor for '#{@name_string}_#{field}'")
|
380
416
|
end
|
381
417
|
end
|
382
418
|
end
|
@@ -395,6 +431,61 @@ module Paperclip
|
|
395
431
|
self.extend(storage_module)
|
396
432
|
end
|
397
433
|
|
434
|
+
def assign_attributes
|
435
|
+
@queued_for_write[:original] = @file
|
436
|
+
assign_file_information
|
437
|
+
assign_fingerprint { @file.fingerprint }
|
438
|
+
assign_timestamps
|
439
|
+
end
|
440
|
+
|
441
|
+
def assign_file_information
|
442
|
+
instance_write(:file_name, cleanup_filename(@file.original_filename))
|
443
|
+
instance_write(:content_type, @file.content_type.to_s.strip)
|
444
|
+
instance_write(:file_size, @file.size)
|
445
|
+
end
|
446
|
+
|
447
|
+
def assign_fingerprint
|
448
|
+
if instance_respond_to?(:fingerprint)
|
449
|
+
instance_write(:fingerprint, yield)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
def assign_timestamps
|
454
|
+
if has_enabled_but_unset_created_at?
|
455
|
+
instance_write(:created_at, Time.now)
|
456
|
+
end
|
457
|
+
|
458
|
+
instance_write(:updated_at, Time.now)
|
459
|
+
end
|
460
|
+
|
461
|
+
def post_process_file
|
462
|
+
dirty!
|
463
|
+
|
464
|
+
if post_processing
|
465
|
+
post_process(*only_process)
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
def dirty!
|
470
|
+
@dirty = true
|
471
|
+
end
|
472
|
+
|
473
|
+
def reset_file_if_original_reprocessed
|
474
|
+
instance_write(:file_size, @queued_for_write[:original].size)
|
475
|
+
assign_fingerprint { @queued_for_write[:original].fingerprint }
|
476
|
+
reset_updater
|
477
|
+
end
|
478
|
+
|
479
|
+
def reset_updater
|
480
|
+
if instance.respond_to?(updater)
|
481
|
+
instance.send(updater)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
def updater
|
486
|
+
:"#{name}_file_name_will_change!"
|
487
|
+
end
|
488
|
+
|
398
489
|
def extra_options_for(style) #:nodoc:
|
399
490
|
process_options(:convert_options, style)
|
400
491
|
end
|
@@ -417,7 +508,7 @@ module Paperclip
|
|
417
508
|
|
418
509
|
instance.run_paperclip_callbacks(:post_process) do
|
419
510
|
instance.run_paperclip_callbacks(:"#{name}_post_process") do
|
420
|
-
|
511
|
+
if !@options[:check_validity_before_processing] || !instance.errors.any?
|
421
512
|
post_process_styles(*style_args)
|
422
513
|
end
|
423
514
|
end
|
@@ -434,16 +525,26 @@ module Paperclip
|
|
434
525
|
def post_process_style(name, style) #:nodoc:
|
435
526
|
begin
|
436
527
|
raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
|
437
|
-
|
438
|
-
|
528
|
+
intermediate_files = []
|
529
|
+
original = @queued_for_write[:original]
|
530
|
+
|
531
|
+
@queued_for_write[name] = style.processors.
|
532
|
+
reduce(original) do |file, processor|
|
533
|
+
file = Paperclip.processor(processor).make(file, style.processor_options, self)
|
534
|
+
intermediate_files << file unless file == @queued_for_write[:original]
|
535
|
+
file
|
439
536
|
end
|
537
|
+
|
440
538
|
unadapted_file = @queued_for_write[name]
|
441
|
-
@queued_for_write[name] = Paperclip.io_adapters.
|
539
|
+
@queued_for_write[name] = Paperclip.io_adapters.
|
540
|
+
for(@queued_for_write[name], @options[:adapter_options])
|
442
541
|
unadapted_file.close if unadapted_file.respond_to?(:close)
|
443
542
|
@queued_for_write[name]
|
444
|
-
rescue Paperclip::
|
543
|
+
rescue Paperclip::Errors::NotIdentifiedByImageMagickError => e
|
445
544
|
log("An error was received while processing: #{e.inspect}")
|
446
545
|
(@errors[:processing] ||= []) << e.message if @options[:whiny]
|
546
|
+
ensure
|
547
|
+
unlink_files(intermediate_files)
|
447
548
|
end
|
448
549
|
end
|
449
550
|
|
@@ -482,9 +583,13 @@ module Paperclip
|
|
482
583
|
end
|
483
584
|
end
|
484
585
|
|
485
|
-
# called by storage after the writes are flushed and before @
|
586
|
+
# called by storage after the writes are flushed and before @queued_for_write is cleared
|
486
587
|
def after_flush_writes
|
487
|
-
@queued_for_write.
|
588
|
+
unlink_files(@queued_for_write.values)
|
589
|
+
end
|
590
|
+
|
591
|
+
def unlink_files(files)
|
592
|
+
Array(files).each do |file|
|
488
593
|
file.close unless file.closed?
|
489
594
|
file.unlink if file.respond_to?(:unlink) && file.path.present? && File.exist?(file.path)
|
490
595
|
end
|
@@ -492,7 +597,7 @@ module Paperclip
|
|
492
597
|
|
493
598
|
# You can either specifiy :restricted_characters or you can define your own
|
494
599
|
# :filename_cleaner object. This object needs to respond to #call and takes
|
495
|
-
# the filename that will be cleaned. It should return the cleaned
|
600
|
+
# the filename that will be cleaned. It should return the cleaned filename.
|
496
601
|
def filename_cleaner
|
497
602
|
@options[:filename_cleaner] || FilenameCleaner.new(@options[:restricted_characters])
|
498
603
|
end
|
@@ -51,8 +51,9 @@ module Paperclip
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def definitions_for(klass)
|
54
|
-
klass.ancestors.
|
55
|
-
|
54
|
+
parent_classes = klass.ancestors.reverse
|
55
|
+
parent_classes.each_with_object({}) do |ancestor, inherited_definitions|
|
56
|
+
inherited_definitions.deep_merge! @attachments[ancestor]
|
56
57
|
end
|
57
58
|
end
|
58
59
|
end
|
data/lib/paperclip/callbacks.rb
CHANGED
@@ -7,7 +7,7 @@ module Paperclip
|
|
7
7
|
|
8
8
|
module Defining
|
9
9
|
def define_paperclip_callbacks(*callbacks)
|
10
|
-
define_callbacks
|
10
|
+
define_callbacks(*[callbacks, { terminator: hasta_la_vista_baby }].flatten)
|
11
11
|
callbacks.each do |callback|
|
12
12
|
eval <<-end_callbacks
|
13
13
|
def before_#{callback}(*args, &blk)
|
@@ -19,6 +19,18 @@ module Paperclip
|
|
19
19
|
end_callbacks
|
20
20
|
end
|
21
21
|
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def hasta_la_vista_baby
|
26
|
+
lambda do |_, result|
|
27
|
+
if result.respond_to?(:call)
|
28
|
+
result.call == false
|
29
|
+
else
|
30
|
+
result == false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
22
34
|
end
|
23
35
|
|
24
36
|
module Running
|