paperclip 4.2.2 → 5.2.1
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/.codeclimate.yml +17 -0
- data/.hound.yml +1055 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +17 -15
- data/Appraisals +4 -16
- data/CONTRIBUTING.md +19 -8
- data/Gemfile +5 -9
- data/LICENSE +1 -1
- data/NEWS +148 -31
- data/README.md +327 -191
- data/RELEASING.md +17 -0
- data/Rakefile +2 -2
- data/UPGRADING +12 -9
- data/features/basic_integration.feature +10 -6
- data/features/migration.feature +0 -24
- data/features/step_definitions/attachment_steps.rb +33 -27
- data/features/step_definitions/html_steps.rb +2 -2
- data/features/step_definitions/rails_steps.rb +39 -38
- data/features/step_definitions/s3_steps.rb +2 -2
- data/features/step_definitions/web_steps.rb +1 -103
- data/features/support/env.rb +1 -0
- data/features/support/file_helpers.rb +2 -2
- data/features/support/paths.rb +1 -1
- data/features/support/rails.rb +0 -24
- data/gemfiles/4.2.gemfile +6 -8
- data/gemfiles/5.0.gemfile +17 -0
- data/lib/paperclip/attachment.rb +32 -20
- 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 +19 -14
- data/lib/paperclip/io_adapters/abstract_adapter.rb +26 -3
- 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 +10 -6
- data/lib/paperclip/io_adapters/uri_adapter.rb +41 -19
- 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/processor.rb +5 -4
- data/lib/paperclip/rails_environment.rb +25 -0
- data/lib/paperclip/schema.rb +3 -9
- data/lib/paperclip/storage/filesystem.rb +13 -2
- data/lib/paperclip/storage/fog.rb +30 -18
- data/lib/paperclip/storage/s3.rb +92 -65
- data/lib/paperclip/thumbnail.rb +16 -7
- data/lib/paperclip/url_generator.rb +16 -13
- data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
- data/lib/paperclip/validators.rb +1 -1
- data/lib/paperclip/version.rb +3 -1
- data/lib/paperclip.rb +25 -12
- data/lib/tasks/paperclip.rake +33 -3
- 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 +130 -39
- 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 +47 -23
- 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 +6 -3
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +26 -6
- 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 +5 -1
- data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +5 -5
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +77 -7
- 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 +58 -3
- data/spec/paperclip/storage/s3_live_spec.rb +20 -14
- data/spec/paperclip/storage/s3_spec.rb +398 -213
- data/spec/paperclip/tempfile_factory_spec.rb +4 -0
- data/spec/paperclip/tempfile_spec.rb +35 -0
- data/spec/paperclip/thumbnail_spec.rb +51 -32
- data/spec/paperclip/url_generator_spec.rb +55 -44
- data/spec/paperclip/validators/attachment_size_validator_spec.rb +26 -20
- data/spec/paperclip/validators_spec.rb +5 -5
- data/spec/spec_helper.rb +8 -1
- data/spec/support/assertions.rb +12 -1
- data/spec/support/conditional_filter_helper.rb +5 -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/mock_attachment.rb +2 -0
- data/spec/support/mock_url_generator_builder.rb +2 -2
- data/spec/support/model_reconstruction.rb +9 -1
- data/spec/support/reporting.rb +11 -0
- metadata +109 -162
- 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/gemfiles/4.1.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
data/gemfiles/4.2.gemfile
CHANGED
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
|
|
5
|
-
gem "sqlite3", "1.3.8", :platforms => :ruby
|
|
6
|
-
gem "jruby-openssl", :platforms => :jruby
|
|
7
|
-
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
|
|
8
|
-
gem "rubysl", :platforms => :rbx
|
|
9
|
-
gem "racc", :platforms => :rbx
|
|
5
|
+
gem "sqlite3", "~> 1.3.8", :platforms => :ruby
|
|
10
6
|
gem "pry"
|
|
11
|
-
gem "rails", "~> 4.2.0
|
|
12
|
-
gem "paperclip", :path => "../"
|
|
7
|
+
gem "rails", "~> 4.2.0"
|
|
13
8
|
|
|
14
9
|
group :development, :test do
|
|
15
|
-
gem "
|
|
10
|
+
gem "activerecord-import"
|
|
11
|
+
gem "mime-types"
|
|
16
12
|
gem "builder"
|
|
13
|
+
gem "rubocop", :require => false
|
|
14
|
+
gem "rspec"
|
|
17
15
|
end
|
|
18
16
|
|
|
19
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 => "../"
|
data/lib/paperclip/attachment.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
require 'uri'
|
|
3
3
|
require 'paperclip/url_generator'
|
|
4
4
|
require 'active_support/deprecation'
|
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
|
5
6
|
|
|
6
7
|
module Paperclip
|
|
7
8
|
# The Attachment class manages the files for a given attachment. It saves
|
|
@@ -32,6 +33,7 @@ module Paperclip
|
|
|
32
33
|
:use_timestamp => true,
|
|
33
34
|
:whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails],
|
|
34
35
|
:validate_media_type => true,
|
|
36
|
+
:adapter_options => { hash_digest: Digest::MD5 },
|
|
35
37
|
:check_validity_before_processing => true
|
|
36
38
|
}
|
|
37
39
|
end
|
|
@@ -49,7 +51,8 @@ module Paperclip
|
|
|
49
51
|
# +url+ - a relative URL of the attachment. This is interpolated using +interpolator+
|
|
50
52
|
# +path+ - where on the filesystem to store the attachment. This is interpolated using +interpolator+
|
|
51
53
|
# +styles+ - a hash of options for processing the attachment. See +has_attached_file+ for the details
|
|
52
|
-
# +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)
|
|
53
56
|
# +default_url+ - a URL for the missing image
|
|
54
57
|
# +default_style+ - the style to use when an argument is not specified e.g. #url, #path
|
|
55
58
|
# +storage+ - the storage mechanism. Defaults to :filesystem
|
|
@@ -68,7 +71,8 @@ module Paperclip
|
|
|
68
71
|
# +url_generator+ - the object used to generate URLs, using the interpolator. Defaults to Paperclip::UrlGenerator
|
|
69
72
|
# +escape_url+ - Perform URI escaping to URLs. Defaults to true
|
|
70
73
|
def initialize(name, instance, options = {})
|
|
71
|
-
@name = name
|
|
74
|
+
@name = name.to_sym
|
|
75
|
+
@name_string = name.to_s
|
|
72
76
|
@instance = instance
|
|
73
77
|
|
|
74
78
|
options = self.class.default_options.deep_merge(options)
|
|
@@ -80,7 +84,7 @@ module Paperclip
|
|
|
80
84
|
@errors = {}
|
|
81
85
|
@dirty = false
|
|
82
86
|
@interpolator = options[:interpolator]
|
|
83
|
-
@url_generator = options[:url_generator].new(self
|
|
87
|
+
@url_generator = options[:url_generator].new(self)
|
|
84
88
|
@source_file_options = options[:source_file_options]
|
|
85
89
|
@whiny = options[:whiny]
|
|
86
90
|
|
|
@@ -94,7 +98,8 @@ module Paperclip
|
|
|
94
98
|
# attachment:
|
|
95
99
|
# new_user.avatar = old_user.avatar
|
|
96
100
|
def assign(uploaded_file)
|
|
97
|
-
@file = Paperclip.io_adapters.for(uploaded_file
|
|
101
|
+
@file = Paperclip.io_adapters.for(uploaded_file,
|
|
102
|
+
@options[:adapter_options])
|
|
98
103
|
ensure_required_accessors!
|
|
99
104
|
ensure_required_validations!
|
|
100
105
|
|
|
@@ -235,6 +240,10 @@ module Paperclip
|
|
|
235
240
|
# the instance's errors and returns false, cancelling the save.
|
|
236
241
|
def save
|
|
237
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
|
|
238
247
|
flush_writes
|
|
239
248
|
@dirty = false
|
|
240
249
|
true
|
|
@@ -321,7 +330,7 @@ module Paperclip
|
|
|
321
330
|
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@options[:hash_digest]).new, @options[:hash_secret], data)
|
|
322
331
|
end
|
|
323
332
|
|
|
324
|
-
# This method really shouldn't be called that often.
|
|
333
|
+
# This method really shouldn't be called that often. Its expected use is
|
|
325
334
|
# in the paperclip:refresh rake task and that's it. It will regenerate all
|
|
326
335
|
# thumbnails forcefully, by reobtaining the original file and going through
|
|
327
336
|
# the post-process again.
|
|
@@ -346,7 +355,7 @@ module Paperclip
|
|
|
346
355
|
|
|
347
356
|
# Returns true if a file has been assigned.
|
|
348
357
|
def file?
|
|
349
|
-
|
|
358
|
+
original_filename.present?
|
|
350
359
|
end
|
|
351
360
|
|
|
352
361
|
alias :present? :file?
|
|
@@ -365,7 +374,7 @@ module Paperclip
|
|
|
365
374
|
# instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's
|
|
366
375
|
# "avatar_file_name" field (assuming the attachment is called avatar).
|
|
367
376
|
def instance_write(attr, value)
|
|
368
|
-
setter = :"#{
|
|
377
|
+
setter = :"#{@name_string}_#{attr}="
|
|
369
378
|
if instance.respond_to?(setter)
|
|
370
379
|
instance.send(setter, value)
|
|
371
380
|
end
|
|
@@ -374,7 +383,7 @@ module Paperclip
|
|
|
374
383
|
# Reads the attachment-specific attribute on the instance. See instance_write
|
|
375
384
|
# for more details.
|
|
376
385
|
def instance_read(attr)
|
|
377
|
-
getter = :"#{
|
|
386
|
+
getter = :"#{@name_string}_#{attr}"
|
|
378
387
|
if instance.respond_to?(getter)
|
|
379
388
|
instance.send(getter)
|
|
380
389
|
end
|
|
@@ -402,8 +411,8 @@ module Paperclip
|
|
|
402
411
|
|
|
403
412
|
def ensure_required_accessors! #:nodoc:
|
|
404
413
|
%w(file_name).each do |field|
|
|
405
|
-
unless @instance.respond_to?("#{
|
|
406
|
-
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}'")
|
|
407
416
|
end
|
|
408
417
|
end
|
|
409
418
|
end
|
|
@@ -425,7 +434,7 @@ module Paperclip
|
|
|
425
434
|
def assign_attributes
|
|
426
435
|
@queued_for_write[:original] = @file
|
|
427
436
|
assign_file_information
|
|
428
|
-
assign_fingerprint
|
|
437
|
+
assign_fingerprint { @file.fingerprint }
|
|
429
438
|
assign_timestamps
|
|
430
439
|
end
|
|
431
440
|
|
|
@@ -435,9 +444,9 @@ module Paperclip
|
|
|
435
444
|
instance_write(:file_size, @file.size)
|
|
436
445
|
end
|
|
437
446
|
|
|
438
|
-
def assign_fingerprint
|
|
447
|
+
def assign_fingerprint
|
|
439
448
|
if instance_respond_to?(:fingerprint)
|
|
440
|
-
instance_write(:fingerprint,
|
|
449
|
+
instance_write(:fingerprint, yield)
|
|
441
450
|
end
|
|
442
451
|
end
|
|
443
452
|
|
|
@@ -463,7 +472,7 @@ module Paperclip
|
|
|
463
472
|
|
|
464
473
|
def reset_file_if_original_reprocessed
|
|
465
474
|
instance_write(:file_size, @queued_for_write[:original].size)
|
|
466
|
-
assign_fingerprint
|
|
475
|
+
assign_fingerprint { @queued_for_write[:original].fingerprint }
|
|
467
476
|
reset_updater
|
|
468
477
|
end
|
|
469
478
|
|
|
@@ -499,7 +508,7 @@ module Paperclip
|
|
|
499
508
|
|
|
500
509
|
instance.run_paperclip_callbacks(:post_process) do
|
|
501
510
|
instance.run_paperclip_callbacks(:"#{name}_post_process") do
|
|
502
|
-
|
|
511
|
+
if !@options[:check_validity_before_processing] || !instance.errors.any?
|
|
503
512
|
post_process_styles(*style_args)
|
|
504
513
|
end
|
|
505
514
|
end
|
|
@@ -517,18 +526,21 @@ module Paperclip
|
|
|
517
526
|
begin
|
|
518
527
|
raise RuntimeError.new("Style #{name} has no processors defined.") if style.processors.blank?
|
|
519
528
|
intermediate_files = []
|
|
529
|
+
original = @queued_for_write[:original]
|
|
520
530
|
|
|
521
|
-
@queued_for_write[name] = style.processors.
|
|
531
|
+
@queued_for_write[name] = style.processors.
|
|
532
|
+
reduce(original) do |file, processor|
|
|
522
533
|
file = Paperclip.processor(processor).make(file, style.processor_options, self)
|
|
523
|
-
intermediate_files << file
|
|
534
|
+
intermediate_files << file unless file == @queued_for_write[:original]
|
|
524
535
|
file
|
|
525
536
|
end
|
|
526
537
|
|
|
527
538
|
unadapted_file = @queued_for_write[name]
|
|
528
|
-
@queued_for_write[name] = Paperclip.io_adapters.
|
|
539
|
+
@queued_for_write[name] = Paperclip.io_adapters.
|
|
540
|
+
for(@queued_for_write[name], @options[:adapter_options])
|
|
529
541
|
unadapted_file.close if unadapted_file.respond_to?(:close)
|
|
530
542
|
@queued_for_write[name]
|
|
531
|
-
rescue Paperclip::
|
|
543
|
+
rescue Paperclip::Errors::NotIdentifiedByImageMagickError => e
|
|
532
544
|
log("An error was received while processing: #{e.inspect}")
|
|
533
545
|
(@errors[:processing] ||= []) << e.message if @options[:whiny]
|
|
534
546
|
ensure
|
|
@@ -585,7 +597,7 @@ module Paperclip
|
|
|
585
597
|
|
|
586
598
|
# You can either specifiy :restricted_characters or you can define your own
|
|
587
599
|
# :filename_cleaner object. This object needs to respond to #call and takes
|
|
588
|
-
# the filename that will be cleaned. It should return the cleaned
|
|
600
|
+
# the filename that will be cleaned. It should return the cleaned filename.
|
|
589
601
|
def filename_cleaner
|
|
590
602
|
@options[:filename_cleaner] || FilenameCleaner.new(@options[:restricted_characters])
|
|
591
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(*[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)
|
|
@@ -22,11 +22,13 @@ module Paperclip
|
|
|
22
22
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
30
32
|
end
|
|
31
33
|
end
|
|
32
34
|
end
|
|
@@ -2,7 +2,7 @@ module Paperclip
|
|
|
2
2
|
class ContentTypeDetector
|
|
3
3
|
# The content-type detection strategy is as follows:
|
|
4
4
|
#
|
|
5
|
-
# 1. Blank/Empty files: If there's no
|
|
5
|
+
# 1. Blank/Empty files: If there's no filepath or the file is empty,
|
|
6
6
|
# provide a sensible default (application/octet-stream or inode/x-empty)
|
|
7
7
|
#
|
|
8
8
|
# 2. Calculated match: Return the first result that is found by both the
|
|
@@ -20,8 +20,8 @@ module Paperclip
|
|
|
20
20
|
EMPTY_TYPE = "inode/x-empty"
|
|
21
21
|
SENSIBLE_DEFAULT = "application/octet-stream"
|
|
22
22
|
|
|
23
|
-
def initialize(
|
|
24
|
-
@
|
|
23
|
+
def initialize(filepath)
|
|
24
|
+
@filepath = filepath
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# Returns a String describing the file's content type
|
|
@@ -33,32 +33,48 @@ module Paperclip
|
|
|
33
33
|
elsif calculated_type_matches.any?
|
|
34
34
|
calculated_type_matches.first
|
|
35
35
|
else
|
|
36
|
-
|
|
36
|
+
type_from_file_contents || SENSIBLE_DEFAULT
|
|
37
37
|
end.to_s
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
private
|
|
41
41
|
|
|
42
|
+
def blank_name?
|
|
43
|
+
@filepath.nil? || @filepath.empty?
|
|
44
|
+
end
|
|
45
|
+
|
|
42
46
|
def empty_file?
|
|
43
|
-
File.exist?(@
|
|
47
|
+
File.exist?(@filepath) && File.size(@filepath) == 0
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
alias :empty? :empty_file?
|
|
47
51
|
|
|
48
|
-
def
|
|
49
|
-
|
|
52
|
+
def calculated_type_matches
|
|
53
|
+
possible_types.select do |content_type|
|
|
54
|
+
content_type == type_from_file_contents
|
|
55
|
+
end
|
|
50
56
|
end
|
|
51
57
|
|
|
52
58
|
def possible_types
|
|
53
|
-
MIME::Types.type_for(@
|
|
59
|
+
MIME::Types.type_for(@filepath).collect(&:content_type)
|
|
54
60
|
end
|
|
55
61
|
|
|
56
|
-
def
|
|
57
|
-
|
|
62
|
+
def type_from_file_contents
|
|
63
|
+
type_from_mime_magic || type_from_file_command
|
|
64
|
+
rescue Errno::ENOENT => e
|
|
65
|
+
Paperclip.log("Error while determining content type: #{e}")
|
|
66
|
+
SENSIBLE_DEFAULT
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def type_from_mime_magic
|
|
70
|
+
@type_from_mime_magic ||= File.open(@filepath) do |file|
|
|
71
|
+
MimeMagic.by_magic(file).try(:type)
|
|
72
|
+
end
|
|
58
73
|
end
|
|
59
74
|
|
|
60
75
|
def type_from_file_command
|
|
61
|
-
@type_from_file_command ||=
|
|
76
|
+
@type_from_file_command ||=
|
|
77
|
+
FileCommandContentTypeDetector.new(@filepath).detect
|
|
62
78
|
end
|
|
63
79
|
end
|
|
64
80
|
end
|
data/lib/paperclip/errors.rb
CHANGED
|
@@ -19,7 +19,9 @@ module Paperclip
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# Will be thrown when ImageMagic cannot determine the uploaded file's
|
|
22
|
-
# metadata, usually this would mean the file is not an image.
|
|
22
|
+
# metadata, usually this would mean the file is not an image. If you are
|
|
23
|
+
# consistently receiving this error on PDFs make sure that you have
|
|
24
|
+
# installed Ghostscript.
|
|
23
25
|
class NotIdentifiedByImageMagickError < Paperclip::Error
|
|
24
26
|
end
|
|
25
27
|
|
|
@@ -13,20 +13,18 @@ module Paperclip
|
|
|
13
13
|
private
|
|
14
14
|
|
|
15
15
|
def type_from_file_command
|
|
16
|
+
# On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
|
|
16
17
|
type = begin
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
end
|
|
18
|
+
Paperclip.run("file", "-b --mime :file", file: @filename)
|
|
19
|
+
rescue Cocaine::CommandLineError => e
|
|
20
|
+
Paperclip.log("Error while determining content type: #{e}")
|
|
21
|
+
SENSIBLE_DEFAULT
|
|
22
|
+
end
|
|
23
23
|
|
|
24
24
|
if type.nil? || type.match(/\(.*?\)/)
|
|
25
25
|
type = SENSIBLE_DEFAULT
|
|
26
26
|
end
|
|
27
27
|
type.split(/[:;\s]+/)[0]
|
|
28
28
|
end
|
|
29
|
-
|
|
30
29
|
end
|
|
31
30
|
end
|
|
32
|
-
|
data/lib/paperclip/glue.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Paperclip
|
|
|
8
8
|
base.extend ClassMethods
|
|
9
9
|
base.send :include, Callbacks
|
|
10
10
|
base.send :include, Validators
|
|
11
|
-
base.send :include, Schema if defined? ActiveRecord
|
|
11
|
+
base.send :include, Schema if defined? ActiveRecord::Base
|
|
12
12
|
|
|
13
13
|
locale_path = Dir.glob(File.dirname(__FILE__) + "/locales/*.{rb,yml}")
|
|
14
14
|
I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
|
|
@@ -79,7 +79,8 @@ module Paperclip
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def add_required_validations
|
|
82
|
-
|
|
82
|
+
options = Paperclip::Attachment.default_options.deep_merge(@options)
|
|
83
|
+
if options[:validate_media_type] != false
|
|
83
84
|
name = @name
|
|
84
85
|
@klass.validates_media_type_spoof_detection name,
|
|
85
86
|
:if => ->(instance){ instance.send(name).dirty? }
|
|
@@ -90,7 +91,13 @@ module Paperclip
|
|
|
90
91
|
name = @name
|
|
91
92
|
@klass.send(:after_save) { send(name).send(:save) }
|
|
92
93
|
@klass.send(:before_destroy) { send(name).send(:queue_all_for_delete) }
|
|
93
|
-
@klass.
|
|
94
|
+
if @klass.respond_to?(:after_commit)
|
|
95
|
+
@klass.send(:after_commit, on: :destroy) do
|
|
96
|
+
send(name).send(:flush_deletes)
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
@klass.send(:after_destroy) { send(name).send(:flush_deletes) }
|
|
100
|
+
end
|
|
94
101
|
end
|
|
95
102
|
|
|
96
103
|
def add_paperclip_callbacks
|
data/lib/paperclip/helpers.rb
CHANGED
|
@@ -8,23 +8,27 @@ module Paperclip
|
|
|
8
8
|
Paperclip::Interpolations[key] = block
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# The run method takes the name of a binary to run, the arguments
|
|
12
|
-
# and some options
|
|
11
|
+
# The run method takes the name of a binary to run, the arguments
|
|
12
|
+
# to that binary, the values to interpolate and some local options.
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
# on the filesystem. Colon-separated, just like $PATH.
|
|
14
|
+
# :cmd -> The name of a binary to run.
|
|
16
15
|
#
|
|
17
|
-
#
|
|
18
|
-
# of the binary. Defaults to [0].
|
|
16
|
+
# :arguments -> The command line arguments to that binary.
|
|
19
17
|
#
|
|
20
|
-
#
|
|
21
|
-
# This will only log if logging in general is set to true as well.
|
|
18
|
+
# :interpolation_values -> Values to be interpolated into the arguments.
|
|
22
19
|
#
|
|
23
|
-
#
|
|
20
|
+
# :local_options -> The options to be used by Cocain::CommandLine.
|
|
21
|
+
# These could be: runner
|
|
22
|
+
# logger
|
|
23
|
+
# swallow_stderr
|
|
24
|
+
# expected_outcodes
|
|
25
|
+
# environment
|
|
26
|
+
# runner_options
|
|
24
27
|
#
|
|
25
28
|
def run(cmd, arguments = "", interpolation_values = {}, local_options = {})
|
|
26
29
|
command_path = options[:command_path]
|
|
27
|
-
|
|
30
|
+
cocaine_path_array = Cocaine::CommandLine.path.try(:split, Cocaine::OS.path_separator)
|
|
31
|
+
Cocaine::CommandLine.path = [cocaine_path_array, command_path].flatten.compact.uniq
|
|
28
32
|
if logging? && (options[:log_command] || local_options[:log_command])
|
|
29
33
|
local_options = local_options.merge(:logger => logger)
|
|
30
34
|
end
|
|
@@ -2,15 +2,16 @@ module Paperclip
|
|
|
2
2
|
module Interpolations
|
|
3
3
|
class PluralCache
|
|
4
4
|
def initialize
|
|
5
|
-
@
|
|
5
|
+
@symbol_cache = {}.compare_by_identity
|
|
6
|
+
@klass_cache = {}.compare_by_identity
|
|
6
7
|
end
|
|
7
8
|
|
|
8
|
-
def
|
|
9
|
-
@
|
|
9
|
+
def pluralize_symbol(symbol)
|
|
10
|
+
@symbol_cache[symbol] ||= symbol.to_s.downcase.pluralize
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
def
|
|
13
|
-
@
|
|
13
|
+
def underscore_and_pluralize_class(klass)
|
|
14
|
+
@klass_cache[klass] ||= klass.name.underscore.pluralize
|
|
14
15
|
end
|
|
15
16
|
end
|
|
16
17
|
end
|
|
@@ -10,6 +10,7 @@ module Paperclip
|
|
|
10
10
|
# and is not intended for normal use.
|
|
11
11
|
def self.[]= name, block
|
|
12
12
|
define_method(name, &block)
|
|
13
|
+
@interpolators_cache = nil
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
# Hash access of interpolations. Included only for compatibility,
|
|
@@ -20,7 +21,7 @@ module Paperclip
|
|
|
20
21
|
|
|
21
22
|
# Returns a sorted list of all interpolations.
|
|
22
23
|
def self.all
|
|
23
|
-
self.instance_methods(false).sort
|
|
24
|
+
self.instance_methods(false).sort!
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
# Perform the actual interpolation. Takes the pattern to interpolate
|
|
@@ -29,11 +30,15 @@ module Paperclip
|
|
|
29
30
|
# an interpolation pattern for Paperclip to use.
|
|
30
31
|
def self.interpolate pattern, *args
|
|
31
32
|
pattern = args.first.instance.send(pattern) if pattern.kind_of? Symbol
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
end
|
|
33
|
+
result = pattern.dup
|
|
34
|
+
interpolators_cache.each do |method, token|
|
|
35
|
+
result.gsub!(token) { send(method, *args) } if result.include?(token)
|
|
36
36
|
end
|
|
37
|
+
result
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.interpolators_cache
|
|
41
|
+
@interpolators_cache ||= all.reverse!.map! { |method| [method, ":#{method}"] }
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
def self.plural_cache
|
|
@@ -42,7 +47,7 @@ module Paperclip
|
|
|
42
47
|
|
|
43
48
|
# Returns the filename, the same way as ":basename.:extension" would.
|
|
44
49
|
def filename attachment, style_name
|
|
45
|
-
[ basename(attachment, style_name), extension(attachment, style_name) ].
|
|
50
|
+
[ basename(attachment, style_name), extension(attachment, style_name) ].delete_if(&:empty?).join(".".freeze)
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
# Returns the interpolated URL. Will raise an error if the url itself
|
|
@@ -85,12 +90,12 @@ module Paperclip
|
|
|
85
90
|
# all class names. Calling #class will return the expected class.
|
|
86
91
|
def class attachment = nil, style_name = nil
|
|
87
92
|
return super() if attachment.nil? && style_name.nil?
|
|
88
|
-
plural_cache.
|
|
93
|
+
plural_cache.underscore_and_pluralize_class(attachment.instance.class)
|
|
89
94
|
end
|
|
90
95
|
|
|
91
96
|
# Returns the basename of the file. e.g. "file" for "file.jpg"
|
|
92
97
|
def basename attachment, style_name
|
|
93
|
-
|
|
98
|
+
File.basename(attachment.original_filename, ".*".freeze)
|
|
94
99
|
end
|
|
95
100
|
|
|
96
101
|
# Returns the extension of the file. e.g. "jpg" for "file.jpg"
|
|
@@ -98,7 +103,7 @@ module Paperclip
|
|
|
98
103
|
# of the actual extension.
|
|
99
104
|
def extension attachment, style_name
|
|
100
105
|
((style = attachment.styles[style_name.to_s.to_sym]) && style[:format]) ||
|
|
101
|
-
File.extname(attachment.original_filename).
|
|
106
|
+
File.extname(attachment.original_filename).sub(/\A\.+/, "".freeze)
|
|
102
107
|
end
|
|
103
108
|
|
|
104
109
|
# Returns the dot+extension of the file. e.g. ".jpg" for "file.jpg"
|
|
@@ -106,7 +111,7 @@ module Paperclip
|
|
|
106
111
|
# of the actual extension. If the extension is empty, no dot is added.
|
|
107
112
|
def dotextension attachment, style_name
|
|
108
113
|
ext = extension(attachment, style_name)
|
|
109
|
-
ext.empty? ?
|
|
114
|
+
ext.empty? ? ext : ".#{ext}"
|
|
110
115
|
end
|
|
111
116
|
|
|
112
117
|
# Returns an extension based on the content type. e.g. "jpeg" for
|
|
@@ -136,7 +141,7 @@ module Paperclip
|
|
|
136
141
|
# It's possible, though unlikely, that the mime type is not in the
|
|
137
142
|
# database, so just use the part after the '/' in the mime type as the
|
|
138
143
|
# extension.
|
|
139
|
-
%r{/([^/]*)\
|
|
144
|
+
%r{/([^/]*)\z}.match(attachment.content_type)[1]
|
|
140
145
|
end
|
|
141
146
|
end
|
|
142
147
|
|
|
@@ -170,9 +175,9 @@ module Paperclip
|
|
|
170
175
|
def id_partition attachment, style_name
|
|
171
176
|
case id = attachment.instance.id
|
|
172
177
|
when Integer
|
|
173
|
-
("%09d" % id).scan(/\d{3}/).join("/")
|
|
178
|
+
("%09d".freeze % id).scan(/\d{3}/).join("/".freeze)
|
|
174
179
|
when String
|
|
175
|
-
|
|
180
|
+
id.scan(/.{3}/).first(3).join("/".freeze)
|
|
176
181
|
else
|
|
177
182
|
nil
|
|
178
183
|
end
|
|
@@ -181,7 +186,7 @@ module Paperclip
|
|
|
181
186
|
# Returns the pluralized form of the attachment name. e.g.
|
|
182
187
|
# "avatars" for an attachment of :avatar
|
|
183
188
|
def attachment attachment, style_name
|
|
184
|
-
plural_cache.
|
|
189
|
+
plural_cache.pluralize_symbol(attachment.name)
|
|
185
190
|
end
|
|
186
191
|
|
|
187
192
|
# Returns the style, or the default style if nil is supplied.
|
|
@@ -5,10 +5,23 @@ module Paperclip
|
|
|
5
5
|
OS_RESTRICTED_CHARACTERS = %r{[/:]}
|
|
6
6
|
|
|
7
7
|
attr_reader :content_type, :original_filename, :size
|
|
8
|
-
delegate :binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :rewind, :unlink, :to => :@tempfile
|
|
8
|
+
delegate :binmode, :binmode?, :close, :close!, :closed?, :eof?, :path, :readbyte, :rewind, :unlink, :to => :@tempfile
|
|
9
|
+
alias :length :size
|
|
10
|
+
|
|
11
|
+
def initialize(target, options = {})
|
|
12
|
+
@target = target
|
|
13
|
+
@options = options
|
|
14
|
+
end
|
|
9
15
|
|
|
10
16
|
def fingerprint
|
|
11
|
-
@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
|
|
12
25
|
end
|
|
13
26
|
|
|
14
27
|
def read(length = nil, buffer = nil)
|
|
@@ -39,8 +52,18 @@ module Paperclip
|
|
|
39
52
|
end
|
|
40
53
|
|
|
41
54
|
def copy_to_tempfile(src)
|
|
42
|
-
|
|
55
|
+
link_or_copy_file(src.path, destination.path)
|
|
43
56
|
destination
|
|
44
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, Errno::EEXIST => e
|
|
65
|
+
Paperclip.log("Link failed with #{e.message}; copying link #{src} to #{dest}")
|
|
66
|
+
FileUtils.cp(src, dest)
|
|
67
|
+
end
|
|
45
68
|
end
|
|
46
69
|
end
|