kt-paperclip 7.3.0 → 8.0.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/README.md +40 -61
- data/lib/generators/paperclip/paperclip_generator.rb +3 -1
- data/lib/kt-paperclip.rb +2 -0
- data/lib/paperclip/attachment.rb +2 -0
- data/lib/paperclip/attachment_registry.rb +2 -0
- data/lib/paperclip/callbacks.rb +7 -4
- data/lib/paperclip/commands/imagemagick/geometry_parser.rb +63 -0
- data/lib/paperclip/commands/imagemagick/version_detector.rb +41 -0
- data/lib/paperclip/commands/imagemagick.rb +44 -0
- data/lib/paperclip/commands/runner.rb +38 -0
- data/lib/paperclip/commands/unix_file.rb +28 -0
- data/lib/paperclip/content_type_detector.rb +31 -32
- data/lib/paperclip/errors.rb +2 -0
- data/lib/paperclip/file_command_content_type_detector.rb +11 -12
- data/lib/paperclip/filename_cleaner.rb +2 -0
- data/lib/paperclip/geometry.rb +22 -7
- data/lib/paperclip/geometry_detector_factory.rb +11 -31
- data/lib/paperclip/geometry_parser_factory.rb +13 -17
- data/lib/paperclip/glue.rb +2 -0
- data/lib/paperclip/has_attached_file.rb +2 -0
- data/lib/paperclip/helpers.rb +5 -25
- data/lib/paperclip/interpolations/plural_cache.rb +2 -0
- data/lib/paperclip/interpolations.rb +2 -0
- data/lib/paperclip/io_adapters/abstract_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/attachment_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/data_uri_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/empty_string_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/file_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/identity_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/nil_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/registry.rb +2 -0
- data/lib/paperclip/io_adapters/stringio_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +2 -0
- data/lib/paperclip/io_adapters/uri_adapter.rb +5 -13
- data/lib/paperclip/logger.rb +2 -0
- data/lib/paperclip/matchers/have_attached_file_matcher.rb +2 -0
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +5 -3
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +2 -0
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +2 -0
- data/lib/paperclip/matchers.rb +2 -0
- data/lib/paperclip/media_type_spoof_detector.rb +42 -11
- data/lib/paperclip/missing_attachment_styles.rb +2 -0
- data/lib/paperclip/processor.rb +6 -19
- data/lib/paperclip/processor_helpers.rb +2 -0
- data/lib/paperclip/rails_environment.rb +2 -0
- data/lib/paperclip/railtie.rb +2 -0
- data/lib/paperclip/schema.rb +6 -4
- data/lib/paperclip/storage/filesystem.rb +2 -0
- data/lib/paperclip/storage/fog.rb +4 -3
- data/lib/paperclip/storage/s3.rb +3 -3
- data/lib/paperclip/storage.rb +2 -0
- data/lib/paperclip/style.rb +2 -0
- data/lib/paperclip/tempfile.rb +2 -0
- data/lib/paperclip/tempfile_factory.rb +2 -0
- data/lib/paperclip/thumbnail.rb +9 -10
- data/lib/paperclip/url_generator.rb +2 -0
- data/lib/paperclip/validators/attachment_content_type_validator.rb +2 -0
- data/lib/paperclip/validators/attachment_file_name_validator.rb +2 -0
- data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +2 -0
- data/lib/paperclip/validators/attachment_presence_validator.rb +2 -0
- data/lib/paperclip/validators/attachment_size_validator.rb +2 -0
- data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +2 -0
- data/lib/paperclip/validators.rb +2 -0
- data/lib/paperclip/version.rb +3 -1
- data/lib/paperclip.rb +25 -4
- data/lib/tasks/paperclip.rake +9 -4
- data/shoulda_macros/paperclip.rb +2 -0
- metadata +18 -538
- data/.codeclimate.yml +0 -17
- data/.github/FUNDING.yml +0 -3
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
- data/.github/ISSUE_TEMPLATE/custom.md +0 -10
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- data/.gitignore +0 -19
- data/.hound.yml +0 -3
- data/.rubocop.yml +0 -1061
- data/.travis.yml +0 -45
- data/Appraisals +0 -30
- data/CONTRIBUTING.md +0 -85
- data/Gemfile +0 -19
- data/OLD_NEWS +0 -556
- data/RELEASING.md +0 -17
- data/Rakefile +0 -52
- data/UPGRADING +0 -17
- data/features/basic_integration.feature +0 -85
- data/features/migration.feature +0 -29
- data/features/rake_tasks.feature +0 -62
- data/features/step_definitions/attachment_steps.rb +0 -120
- data/features/step_definitions/html_steps.rb +0 -15
- data/features/step_definitions/rails_steps.rb +0 -257
- data/features/step_definitions/s3_steps.rb +0 -14
- data/features/step_definitions/web_steps.rb +0 -106
- data/features/support/env.rb +0 -12
- data/features/support/fakeweb.rb +0 -11
- data/features/support/file_helpers.rb +0 -34
- data/features/support/fixtures/boot_config.txt +0 -15
- data/features/support/fixtures/gemfile.txt +0 -5
- data/features/support/fixtures/preinitializer.txt +0 -20
- data/features/support/paths.rb +0 -28
- data/features/support/rails.rb +0 -39
- data/features/support/selectors.rb +0 -19
- data/gemfiles/4.2.gemfile +0 -20
- data/gemfiles/5.0.gemfile +0 -20
- data/gemfiles/5.1.gemfile +0 -20
- data/gemfiles/5.2.gemfile +0 -20
- data/gemfiles/6.0.gemfile +0 -20
- data/gemfiles/6.1.gemfile +0 -21
- data/gemfiles/7.0.gemfile +0 -21
- data/paperclip.gemspec +0 -50
- data/spec/database.yml +0 -4
- data/spec/paperclip/attachment_definitions_spec.rb +0 -13
- data/spec/paperclip/attachment_processing_spec.rb +0 -79
- data/spec/paperclip/attachment_registry_spec.rb +0 -158
- data/spec/paperclip/attachment_spec.rb +0 -1616
- data/spec/paperclip/content_type_detector_spec.rb +0 -54
- data/spec/paperclip/file_command_content_type_detector_spec.rb +0 -40
- data/spec/paperclip/filename_cleaner_spec.rb +0 -13
- data/spec/paperclip/geometry_detector_spec.rb +0 -47
- data/spec/paperclip/geometry_parser_spec.rb +0 -73
- data/spec/paperclip/geometry_spec.rb +0 -267
- data/spec/paperclip/glue_spec.rb +0 -63
- data/spec/paperclip/has_attached_file_spec.rb +0 -78
- data/spec/paperclip/integration_spec.rb +0 -702
- data/spec/paperclip/interpolations_spec.rb +0 -270
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +0 -160
- data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +0 -167
- data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +0 -88
- data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +0 -17
- data/spec/paperclip/io_adapters/file_adapter_spec.rb +0 -131
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +0 -142
- data/spec/paperclip/io_adapters/identity_adapter_spec.rb +0 -8
- data/spec/paperclip/io_adapters/nil_adapter_spec.rb +0 -25
- data/spec/paperclip/io_adapters/registry_spec.rb +0 -35
- data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +0 -64
- data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +0 -146
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +0 -231
- data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +0 -19
- data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +0 -108
- data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +0 -69
- data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +0 -88
- data/spec/paperclip/media_type_spoof_detector_spec.rb +0 -126
- data/spec/paperclip/meta_class_spec.rb +0 -30
- data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +0 -88
- data/spec/paperclip/paperclip_spec.rb +0 -196
- data/spec/paperclip/plural_cache_spec.rb +0 -37
- data/spec/paperclip/processor_helpers_spec.rb +0 -57
- data/spec/paperclip/processor_spec.rb +0 -26
- data/spec/paperclip/rails_environment_spec.rb +0 -30
- data/spec/paperclip/rake_spec.rb +0 -103
- data/spec/paperclip/schema_spec.rb +0 -252
- data/spec/paperclip/storage/filesystem_spec.rb +0 -102
- data/spec/paperclip/storage/fog_spec.rb +0 -606
- data/spec/paperclip/storage/s3_live_spec.rb +0 -188
- data/spec/paperclip/storage/s3_spec.rb +0 -1961
- data/spec/paperclip/style_spec.rb +0 -251
- data/spec/paperclip/tempfile_factory_spec.rb +0 -33
- data/spec/paperclip/tempfile_spec.rb +0 -35
- data/spec/paperclip/thumbnail_spec.rb +0 -504
- data/spec/paperclip/url_generator_spec.rb +0 -231
- data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +0 -410
- data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +0 -249
- data/spec/paperclip/validators/attachment_presence_validator_spec.rb +0 -85
- data/spec/paperclip/validators/attachment_size_validator_spec.rb +0 -325
- data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +0 -48
- data/spec/paperclip/validators_spec.rb +0 -179
- data/spec/spec_helper.rb +0 -46
- data/spec/support/assertions.rb +0 -84
- data/spec/support/fake_model.rb +0 -24
- data/spec/support/fake_rails.rb +0 -12
- data/spec/support/fixtures/12k.png +0 -0
- data/spec/support/fixtures/50x50.png +0 -0
- data/spec/support/fixtures/5k.png +0 -0
- data/spec/support/fixtures/animated +0 -0
- data/spec/support/fixtures/animated.gif +0 -0
- data/spec/support/fixtures/animated.unknown +0 -0
- data/spec/support/fixtures/aws_s3.yml +0 -13
- data/spec/support/fixtures/bad.png +0 -1
- data/spec/support/fixtures/empty.html +0 -1
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/fixtures/fog.yml +0 -8
- data/spec/support/fixtures/rotated.jpg +0 -0
- data/spec/support/fixtures/s3.yml +0 -8
- data/spec/support/fixtures/sample.xlsm +0 -0
- data/spec/support/fixtures/spaced file.jpg +0 -0
- data/spec/support/fixtures/spaced file.png +0 -0
- data/spec/support/fixtures/text.txt +0 -1
- data/spec/support/fixtures/twopage.pdf +0 -0
- data/spec/support/fixtures/uppercase.PNG +0 -0
- data/spec/support/matchers/accept.rb +0 -5
- data/spec/support/matchers/exist.rb +0 -5
- data/spec/support/matchers/have_column.rb +0 -23
- data/spec/support/mock_attachment.rb +0 -24
- data/spec/support/mock_interpolator.rb +0 -24
- data/spec/support/mock_url_generator_builder.rb +0 -26
- data/spec/support/model_reconstruction.rb +0 -72
- data/spec/support/reporting.rb +0 -11
- data/spec/support/test_data.rb +0 -13
- data/spec/support/version_helper.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 71b12469bab65eb2861d1170c154ecf1a08f6a0591806096f3d49f598f4ec115
|
|
4
|
+
data.tar.gz: 12efe7db20633834994b3b1836cc697ff21dbf1bd37851346d1bd5e0961aa045
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6db416b89eaa01127e3b8eb7ac7615a19d840a236ea114be4814675d61765fa53f25e25b351b5d9438badf06ab3a20ec036565a418f47dbbcd97563d96e0f91f
|
|
7
|
+
data.tar.gz: 8aa390b628e9926d17c9de71e0dfe861fe2197762fff916064e1b61bd18fda5e3d2f6eda291d4e208e36ecb2fa3fc98ca366e5a28c479f7dde00b02736be2765
|
data/README.md
CHANGED
|
@@ -90,76 +90,44 @@ Requirements
|
|
|
90
90
|
|
|
91
91
|
### Ruby and Rails
|
|
92
92
|
|
|
93
|
-
Paperclip now requires Ruby version **>= 2.
|
|
93
|
+
Paperclip now requires Ruby version **>= 2.7** and Rails version **>= 5.2**
|
|
94
94
|
(only if you're going to use Paperclip with Ruby on Rails).
|
|
95
95
|
|
|
96
|
-
### Image Processor
|
|
96
|
+
### ImageMagick Image Processor
|
|
97
97
|
|
|
98
|
-
[ImageMagick](http://www.imagemagick.org) must be installed and Paperclip must have access to it.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
[ImageMagick](http://www.imagemagick.org) must be installed and Paperclip must have access to it.
|
|
99
|
+
Refer to ImageMagick's [installations instructions](https://imagemagick.org/download/).
|
|
100
|
+
ImageMagick 7 is the latest version. As of April 2026, some package managers such as `apt`
|
|
101
|
+
still install ImageMagick 6.
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
In development mode, you might add this line to `config/environments/development.rb)`:
|
|
103
|
+
Paperclip supports both ImageMagick versions 6 and 7, and will auto-detect your version.
|
|
104
|
+
You may enforce a specific version in an initializer such as `config/initializers/paperclip.rb`:
|
|
107
105
|
|
|
108
106
|
```ruby
|
|
109
|
-
Paperclip.options[:
|
|
107
|
+
Paperclip.options[:imagemagick_version] = "7"
|
|
110
108
|
```
|
|
111
109
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
brew install imagemagick
|
|
115
|
-
|
|
116
|
-
If you are dealing with pdf uploads or running the test suite, you'll also need
|
|
117
|
-
to install GhostScript. On Mac OS X, you can also install that using Homebrew:
|
|
118
|
-
|
|
119
|
-
brew install gs
|
|
120
|
-
|
|
121
|
-
If you are on Ubuntu (or any Debian base Linux distribution), you'll want to run
|
|
122
|
-
the following with apt-get:
|
|
123
|
-
|
|
124
|
-
sudo apt-get install imagemagick -y
|
|
125
|
-
|
|
126
|
-
### `file`
|
|
127
|
-
|
|
128
|
-
The Unix [`file` command](https://en.wikipedia.org/wiki/File_(command)) is required for content-type checking.
|
|
129
|
-
This utility isn't available in Windows, but comes bundled with Ruby [Devkit](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit),
|
|
130
|
-
so Windows users must make sure that the devkit is installed and added to the system `PATH`.
|
|
131
|
-
|
|
132
|
-
**Manual Installation**
|
|
133
|
-
|
|
134
|
-
If you're using Windows 7+ as a development environment, you may need to install the `file.exe` application manually. The `file spoofing` system in Paperclip 4+ relies on this; if you don't have it working, you'll receive `Validation failed: Upload file has an extension that does not match its contents.` errors.
|
|
135
|
-
|
|
136
|
-
To manually install, you should perform the following:
|
|
110
|
+
Please ensure ImageMagick is in your environment path, or alternatively set
|
|
111
|
+
the path manually in an initializer:
|
|
137
112
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-

|
|
142
|
-
|
|
143
|
-
Next, you need to integrate with your environment - preferably through the `PATH` variable, or by changing your `config/environments/development.rb` file
|
|
144
|
-
|
|
145
|
-
**PATH**
|
|
146
|
-
|
|
147
|
-
1. Click "Start"
|
|
148
|
-
2. On "Computer", right-click and select "Properties"
|
|
149
|
-
3. In Properties, select "Advanced System Settings"
|
|
150
|
-
4. Click the "Environment Variables" button
|
|
151
|
-
5. Locate the "PATH" var - at the end, add the path to your newly installed `file.exe` (typically `C:\Program Files (x86)\GnuWin32\bin`)
|
|
152
|
-
6. Restart any CMD shells you have open & see if it works
|
|
113
|
+
```ruby
|
|
114
|
+
Paperclip.options[:imagemagick_path] = "/usr/local/bin/"
|
|
115
|
+
```
|
|
153
116
|
|
|
154
|
-
|
|
117
|
+
### Optional: Unix `file` Command
|
|
155
118
|
|
|
156
|
-
|
|
119
|
+
Paperclip uses the [Marcel gem](https://github.com/rails/marcel) to detect file content types.
|
|
120
|
+
If Marcel cannot detect the type, the [Unix `file` command](https://en.wikipedia.org/wiki/File_(command))
|
|
121
|
+
is used as an optional fallback. To use it, ensure `file` is in your environment path,
|
|
122
|
+
or alternatively set its path manually in an initializer:
|
|
157
123
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
124
|
+
```ruby
|
|
125
|
+
Paperclip.options[:file_command_path] = "/usr/local/bin/"
|
|
126
|
+
```
|
|
161
127
|
|
|
162
|
-
|
|
128
|
+
Windows users should install the `file` utility via the Ruby Installer for Windows'
|
|
129
|
+
[DevKit](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit), or alternatively,
|
|
130
|
+
by installing [MSYS2](https://www.msys2.org/) which provides Unix utilities for Windows.
|
|
163
131
|
|
|
164
132
|
---
|
|
165
133
|
|
|
@@ -171,7 +139,7 @@ Paperclip is distributed as a gem, which is how it should be used in your app.
|
|
|
171
139
|
Include the gem in your Gemfile:
|
|
172
140
|
|
|
173
141
|
```ruby
|
|
174
|
-
gem "kt-paperclip", "~>
|
|
142
|
+
gem "kt-paperclip", "~> 8.0"
|
|
175
143
|
```
|
|
176
144
|
|
|
177
145
|
Or, if you want to get the latest, you can get master from the main paperclip repository:
|
|
@@ -407,8 +375,10 @@ validates_attachment :avatar,
|
|
|
407
375
|
content_type: ["image/jpeg", "image/gif", "image/png"]
|
|
408
376
|
```
|
|
409
377
|
|
|
410
|
-
`Paperclip::ContentTypeDetector`
|
|
411
|
-
|
|
378
|
+
`Paperclip::ContentTypeDetector` uses the file's extension as a hint when the contents alone are
|
|
379
|
+
ambiguous, so the stored `content_type` is not by itself proof of what a file contains. The check
|
|
380
|
+
that ignores the filename entirely is `validates_media_type_spoof_detection` (see Security
|
|
381
|
+
Validations below); use it alongside your `content_type` validation.
|
|
412
382
|
|
|
413
383
|
### Duplicate error messages
|
|
414
384
|
|
|
@@ -476,17 +446,26 @@ being uploaded as JPEGs, but will not prevent GIFs from being uploaded with a
|
|
|
476
446
|
`.jpg` extension. This validation will only add validation errors to the form. It
|
|
477
447
|
will not cause errors to be raised.
|
|
478
448
|
|
|
449
|
+
The type is detected from the file's contents only, using magic-number detection plus the Unix
|
|
450
|
+
`file` utility. The filename is never consulted. Where `file` is unavailable the check falls back
|
|
451
|
+
to magic-number detection alone, which cannot identify text-based formats; content it cannot
|
|
452
|
+
identify at all is accepted rather than rejected.
|
|
453
|
+
|
|
479
454
|
This can sometimes cause false validation errors in applications that use custom
|
|
480
455
|
file extensions. In these cases you may wish to add your custom extension to the
|
|
481
456
|
list of content type mappings by creating `config/initializers/paperclip.rb`:
|
|
482
457
|
|
|
483
458
|
```ruby
|
|
484
|
-
# Allow ".foo" as an extension for files
|
|
459
|
+
# Allow ".foo" as an extension for files whose contents are detected as "text/plain".
|
|
485
460
|
Paperclip.options[:content_type_mappings] = {
|
|
486
461
|
foo: %w(text/plain)
|
|
487
462
|
}
|
|
488
463
|
```
|
|
489
464
|
|
|
465
|
+
The value is the type the *contents* detect as (what `file -b --mime` reports), not the type you
|
|
466
|
+
would like the extension to mean. Text-based formats such as SVG and CSV are the usual reason to
|
|
467
|
+
need one.
|
|
468
|
+
|
|
490
469
|
---
|
|
491
470
|
|
|
492
471
|
Defaults
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "rails/generators/active_record"
|
|
2
4
|
|
|
3
5
|
class PaperclipGenerator < ActiveRecord::Generators::Base
|
|
@@ -31,6 +33,6 @@ class PaperclipGenerator < ActiveRecord::Generators::Base
|
|
|
31
33
|
end
|
|
32
34
|
|
|
33
35
|
def migration_version
|
|
34
|
-
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
|
36
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
|
35
37
|
end
|
|
36
38
|
end
|
data/lib/kt-paperclip.rb
CHANGED
data/lib/paperclip/attachment.rb
CHANGED
data/lib/paperclip/callbacks.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Paperclip
|
|
2
4
|
module Callbacks
|
|
3
5
|
def self.included(base)
|
|
@@ -9,14 +11,15 @@ module Paperclip
|
|
|
9
11
|
def define_paperclip_callbacks(*callbacks)
|
|
10
12
|
define_callbacks(*[callbacks, { terminator: hasta_la_vista_baby }].flatten)
|
|
11
13
|
callbacks.each do |callback|
|
|
12
|
-
|
|
13
|
-
def before_#{callback}(*args, &blk)
|
|
14
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
|
15
|
+
def self.before_#{callback}(*args, &blk)
|
|
14
16
|
set_callback(:#{callback}, :before, *args, &blk)
|
|
15
17
|
end
|
|
16
|
-
|
|
18
|
+
|
|
19
|
+
def self.after_#{callback}(*args, &blk)
|
|
17
20
|
set_callback(:#{callback}, :after, *args, &blk)
|
|
18
21
|
end
|
|
19
|
-
|
|
22
|
+
RUBY
|
|
20
23
|
end
|
|
21
24
|
end
|
|
22
25
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Paperclip
|
|
4
|
+
module Commands
|
|
5
|
+
module ImageMagick
|
|
6
|
+
module GeometryParser
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
FORMAT = /\b(\d*)x?(\d*)\b(?:,(\d?))?(@>|>@|[><#@%^!])?/i.freeze
|
|
10
|
+
|
|
11
|
+
# Runs `magick identify` and returns a Paperclip::Geometry
|
|
12
|
+
def detect(file)
|
|
13
|
+
parse(fetch(file))
|
|
14
|
+
end
|
|
15
|
+
alias_method :from_file, :detect
|
|
16
|
+
|
|
17
|
+
# Parses a "WxH,O" string into a Paperclip::Geometry
|
|
18
|
+
def parse(string)
|
|
19
|
+
return unless (match = string&.strip&.match(FORMAT))
|
|
20
|
+
|
|
21
|
+
Paperclip::Geometry.new(
|
|
22
|
+
width: match[1],
|
|
23
|
+
height: match[2],
|
|
24
|
+
orientation: match[3],
|
|
25
|
+
modifier: match[4]
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def fetch(file)
|
|
32
|
+
filepath = path(file)
|
|
33
|
+
raise_error("Cannot find the geometry of a file with a blank name") if filepath.blank?
|
|
34
|
+
|
|
35
|
+
str = Paperclip::Commands::ImageMagick.identify(
|
|
36
|
+
"-format '%wx%h,#{orientation}' :file",
|
|
37
|
+
{ file: "#{filepath}[0]" },
|
|
38
|
+
{ swallow_stderr: true }
|
|
39
|
+
).presence
|
|
40
|
+
|
|
41
|
+
raise_error unless str
|
|
42
|
+
|
|
43
|
+
str
|
|
44
|
+
rescue Terrapin::ExitStatusError
|
|
45
|
+
raise_error
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def path(file)
|
|
49
|
+
file.respond_to?(:path) ? file.path : file
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def orientation
|
|
53
|
+
Paperclip.options[:use_exif_orientation] ? "%[exif:orientation]" : "1"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def raise_error(message = nil)
|
|
57
|
+
message ||= "Could not identify image size"
|
|
58
|
+
raise Errors::NotIdentifiedByImageMagickError.new(message)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Paperclip
|
|
4
|
+
module Commands
|
|
5
|
+
module ImageMagick
|
|
6
|
+
module VersionDetector
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
# Returns 6, 7, or nil (not installed)
|
|
10
|
+
def detected_version
|
|
11
|
+
return @detected_version if defined?(@detected_version)
|
|
12
|
+
|
|
13
|
+
@detected_version = detect
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def detect
|
|
19
|
+
return 7 if valid?("magick")
|
|
20
|
+
return 6 if valid?("convert")
|
|
21
|
+
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def valid?(command)
|
|
26
|
+
run(command).to_s.include?("ImageMagick")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def run(command)
|
|
30
|
+
Paperclip::Commands::Runner.run(command, path, "-version", {}, swallow_stderr: true)
|
|
31
|
+
rescue Terrapin::CommandLineError
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def path
|
|
36
|
+
Paperclip::Commands::ImageMagick.imagemagick_path
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Paperclip
|
|
4
|
+
module Commands
|
|
5
|
+
module ImageMagick
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
def convert(arguments = nil, interpolation_values = {}, local_options = {})
|
|
9
|
+
run(convert_command, imagemagick_path, arguments, interpolation_values, local_options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def identify(arguments = nil, interpolation_values = {}, local_options = {})
|
|
13
|
+
run(identify_command, imagemagick_path, arguments, interpolation_values, local_options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def imagemagick_path
|
|
17
|
+
Paperclip.options[:imagemagick_path]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def run(command, path = nil, arguments = nil, interpolation_values = {}, local_options = {})
|
|
23
|
+
Runner.run(command, path || imagemagick_path, arguments, interpolation_values, local_options)
|
|
24
|
+
rescue Terrapin::CommandNotFoundError
|
|
25
|
+
raise Errors::CommandNotFoundError.new("Could not run the `#{command}` command. Please install ImageMagick.")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns true if ImageMagick 7 is in use (either detected or forced via config)
|
|
29
|
+
def magick_command?
|
|
30
|
+
version = Paperclip.options[:imagemagick_version]&.to_i ||
|
|
31
|
+
VersionDetector.detected_version
|
|
32
|
+
!version.nil? && version >= 7
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def convert_command
|
|
36
|
+
magick_command? ? "magick" : "convert"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def identify_command
|
|
40
|
+
magick_command? ? "magick identify" : "identify"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Paperclip
|
|
4
|
+
module Commands
|
|
5
|
+
module Runner
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
def run(command, path = nil, arguments = nil, interpolation_values = {}, local_options = {})
|
|
9
|
+
if Paperclip.logging? && (Paperclip.options[:log_command] || local_options[:log_command])
|
|
10
|
+
local_options = local_options.merge(logger: Paperclip.logger)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
binary = resolve_binary(command, path)
|
|
14
|
+
|
|
15
|
+
Terrapin::CommandLine.new(binary, arguments, local_options)
|
|
16
|
+
.run(interpolation_values)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def resolve_binary(command, path)
|
|
22
|
+
# @deprecated +Paperclip.options[:command_path]+ will be removed in Paperclip 8.0
|
|
23
|
+
path ||= Paperclip.options[:command_path]
|
|
24
|
+
return command unless path
|
|
25
|
+
|
|
26
|
+
# command may be multi-word (e.g. "magick identify")
|
|
27
|
+
words = command.split(" ", 2)
|
|
28
|
+
candidate = File.join(path, words[0])
|
|
29
|
+
if File.executable?(candidate)
|
|
30
|
+
words[0] = candidate
|
|
31
|
+
words.join(" ")
|
|
32
|
+
else
|
|
33
|
+
command
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Paperclip
|
|
4
|
+
module Commands
|
|
5
|
+
module UnixFile
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
# Returns a normalized content type value, otherwise nil.
|
|
9
|
+
def detect_content_type(file)
|
|
10
|
+
type = run_file_command(file)
|
|
11
|
+
return if type.blank? || type.match(/\(.*?\)/)
|
|
12
|
+
|
|
13
|
+
type.split(/[:;\s]+/)[0].strip
|
|
14
|
+
rescue Terrapin::CommandLineError => e
|
|
15
|
+
Paperclip.log("Error while determining content type: #{e}")
|
|
16
|
+
nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def run_file_command(file)
|
|
22
|
+
Runner.run("file", Paperclip.options[:file_command_path], "-b --mime :file", { file: file })
|
|
23
|
+
rescue Terrapin::CommandLineError
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -1,22 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Paperclip
|
|
4
|
+
# The content-type detection strategy is as follows:
|
|
5
|
+
#
|
|
6
|
+
# 1. Blank/Empty files: If there's no filepath or the file is empty,
|
|
7
|
+
# provide a sensible default (application/octet-stream or inode/x-empty)
|
|
8
|
+
#
|
|
9
|
+
# 2. Calculated match: Return the first result that is found by both the
|
|
10
|
+
# Marcel gem (or `file` command) and MIME::Types.
|
|
11
|
+
#
|
|
12
|
+
# 3. Standard types: Return the first standard (without an x- prefix) entry
|
|
13
|
+
# in MIME::Types
|
|
14
|
+
#
|
|
15
|
+
# 4. Experimental types: If there were no standard types in MIME::Types
|
|
16
|
+
# list, try to return the first experimental one
|
|
17
|
+
#
|
|
18
|
+
# 5. Raw `file` command: Just use the output of Marcel (or `file` command),
|
|
19
|
+
# cached from step 2.
|
|
20
|
+
#
|
|
21
|
+
# 6. If nothing found, return a sensible default.
|
|
2
22
|
class ContentTypeDetector
|
|
3
|
-
# The content-type detection strategy is as follows:
|
|
4
|
-
#
|
|
5
|
-
# 1. Blank/Empty files: If there's no filepath or the file is empty,
|
|
6
|
-
# provide a sensible default (application/octet-stream or inode/x-empty)
|
|
7
|
-
#
|
|
8
|
-
# 2. Calculated match: Return the first result that is found by both the
|
|
9
|
-
# `file` command and MIME::Types.
|
|
10
|
-
#
|
|
11
|
-
# 3. Standard types: Return the first standard (without an x- prefix) entry
|
|
12
|
-
# in MIME::Types
|
|
13
|
-
#
|
|
14
|
-
# 4. Experimental types: If there were no standard types in MIME::Types
|
|
15
|
-
# list, try to return the first experimental one
|
|
16
|
-
#
|
|
17
|
-
# 5. Raw `file` command: Just use the output of the `file` command raw, or
|
|
18
|
-
# a sensible default. This is cached from Step 2.
|
|
19
|
-
|
|
20
23
|
EMPTY_TYPE = "inode/x-empty"
|
|
21
24
|
SENSIBLE_DEFAULT = "application/octet-stream"
|
|
22
25
|
|
|
@@ -25,15 +28,15 @@ module Paperclip
|
|
|
25
28
|
end
|
|
26
29
|
|
|
27
30
|
# Returns a String describing the file's content type
|
|
28
|
-
def detect
|
|
31
|
+
def detect(default = SENSIBLE_DEFAULT)
|
|
29
32
|
if blank_name?
|
|
30
|
-
|
|
33
|
+
default
|
|
31
34
|
elsif empty_file?
|
|
32
35
|
EMPTY_TYPE
|
|
33
36
|
elsif calculated_type_matches.any?
|
|
34
37
|
calculated_type_matches.first
|
|
35
38
|
else
|
|
36
|
-
type_from_file_contents ||
|
|
39
|
+
type_from_file_contents || default
|
|
37
40
|
end.to_s
|
|
38
41
|
end
|
|
39
42
|
|
|
@@ -60,26 +63,22 @@ module Paperclip
|
|
|
60
63
|
end
|
|
61
64
|
|
|
62
65
|
def type_from_file_contents
|
|
63
|
-
|
|
66
|
+
return @type_from_file_contents if defined?(@type_from_file_contents)
|
|
67
|
+
|
|
68
|
+
@type_from_file_contents = type_from_marcel || type_from_file_command
|
|
64
69
|
rescue Errno::ENOENT => e
|
|
65
70
|
Paperclip.log("Error while determining content type: #{e}")
|
|
66
|
-
|
|
71
|
+
@type_from_file_contents = nil
|
|
67
72
|
end
|
|
68
73
|
|
|
69
74
|
def type_from_marcel
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
name: @filepath
|
|
74
|
-
# Marcel::MineType returns 'application/octet-stream' if it can't find
|
|
75
|
-
# a valid type.
|
|
76
|
-
@type_from_marcel = nil if @type_from_marcel == SENSIBLE_DEFAULT
|
|
77
|
-
@type_from_marcel
|
|
75
|
+
type = Marcel::MimeType.for(Pathname.new(@filepath), name: @filepath)
|
|
76
|
+
# Marcel::MineType returns 'application/octet-stream' if it can't find a valid type.
|
|
77
|
+
type == SENSIBLE_DEFAULT ? nil : type
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def type_from_file_command
|
|
81
|
-
@
|
|
82
|
-
FileCommandContentTypeDetector.new(@filepath).detect
|
|
81
|
+
Commands::UnixFile.detect_content_type(@filepath)
|
|
83
82
|
end
|
|
84
83
|
end
|
|
85
84
|
end
|
data/lib/paperclip/errors.rb
CHANGED
|
@@ -1,28 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Paperclip
|
|
4
|
+
# @deprecated Will be removed in Paperclip 8.0. Use +Paperclip::ContentTypeDetector+ instead.
|
|
2
5
|
class FileCommandContentTypeDetector
|
|
6
|
+
# @deprecated Will be removed in Paperclip 8.0.
|
|
3
7
|
SENSIBLE_DEFAULT = "application/octet-stream"
|
|
4
8
|
|
|
9
|
+
# @deprecated Will be removed in Paperclip 8.0. Use +Paperclip::ContentTypeDetector+ instead.
|
|
5
10
|
def initialize(filename)
|
|
11
|
+
warn_deprecation
|
|
6
12
|
@filename = filename
|
|
7
13
|
end
|
|
8
14
|
|
|
15
|
+
# @deprecated Will be removed in Paperclip 8.0. Use +Paperclip::ContentTypeDetector+ instead.
|
|
9
16
|
def detect
|
|
10
|
-
|
|
17
|
+
warn_deprecation
|
|
18
|
+
Paperclip::Commands::UnixFile.detect_content_type(@filename) || SENSIBLE_DEFAULT
|
|
11
19
|
end
|
|
12
20
|
|
|
13
21
|
private
|
|
14
22
|
|
|
15
|
-
def
|
|
16
|
-
|
|
17
|
-
type = begin
|
|
18
|
-
Paperclip.run("file", "-b --mime :file", file: @filename)
|
|
19
|
-
rescue Terrapin::CommandLineError => e
|
|
20
|
-
Paperclip.log("Error while determining content type: #{e}")
|
|
21
|
-
SENSIBLE_DEFAULT
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
type = SENSIBLE_DEFAULT if type.nil? || type.match(/\(.*?\)/)
|
|
25
|
-
type.split(/[:;\s]+/)[0]
|
|
23
|
+
def warn_deprecation
|
|
24
|
+
Paperclip.deprecator.warn("Paperclip::FileCommandContentTypeDetector has been replaced by Paperclip::ContentTypeDetector.")
|
|
26
25
|
end
|
|
27
26
|
end
|
|
28
27
|
end
|
data/lib/paperclip/geometry.rb
CHANGED
|
@@ -1,35 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Paperclip
|
|
2
4
|
# Defines the geometry of an image.
|
|
3
5
|
class Geometry
|
|
4
|
-
attr_accessor :height, :width, :modifier
|
|
6
|
+
attr_accessor :height, :width, :modifier, :orientation
|
|
5
7
|
|
|
6
8
|
EXIF_ROTATED_ORIENTATION_VALUES = [5, 6, 7, 8].freeze
|
|
7
9
|
|
|
8
10
|
# Gives a Geometry representing the given height and width
|
|
9
|
-
def initialize(width = nil, height = nil, modifier = nil)
|
|
11
|
+
def initialize(width = nil, height = nil, modifier = nil, orientation = nil)
|
|
10
12
|
if width.is_a?(Hash)
|
|
11
13
|
options = width
|
|
12
14
|
@height = options[:height].to_f
|
|
13
15
|
@width = options[:width].to_f
|
|
14
|
-
@modifier = options[:modifier]
|
|
16
|
+
@modifier = options[:modifier].presence
|
|
15
17
|
@orientation = options[:orientation].to_i
|
|
16
18
|
else
|
|
17
19
|
@height = height.to_f
|
|
18
20
|
@width = width.to_f
|
|
19
|
-
@modifier = modifier
|
|
21
|
+
@modifier = modifier.presence
|
|
22
|
+
@orientation = orientation.to_i
|
|
20
23
|
end
|
|
21
24
|
end
|
|
22
25
|
|
|
23
26
|
# Extracts the Geometry from a file (or path to a file)
|
|
27
|
+
#
|
|
28
|
+
# @deprecated Will be removed in Paperclip 8.0.
|
|
24
29
|
def self.from_file(file)
|
|
25
|
-
|
|
30
|
+
Commands::ImageMagick::GeometryParser.from_file(file)
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
# Extracts the Geometry from a "WxH,O" string
|
|
29
34
|
# Where W is the width, H is the height,
|
|
30
35
|
# and O is the EXIF orientation
|
|
36
|
+
#
|
|
37
|
+
# @deprecated Will be removed in Paperclip 8.0.
|
|
31
38
|
def self.parse(string)
|
|
32
|
-
GeometryParser.
|
|
39
|
+
Commands::ImageMagick::GeometryParser.parse(string)
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
# Swaps the height and width if necessary
|
|
@@ -72,13 +79,21 @@ module Paperclip
|
|
|
72
79
|
|
|
73
80
|
# Returns the width and height in a format suitable to be passed to Geometry.parse
|
|
74
81
|
def to_s
|
|
75
|
-
s =
|
|
82
|
+
s = +""
|
|
76
83
|
s << width.to_i.to_s if width > 0
|
|
77
84
|
s << "x#{height.to_i}" if height > 0
|
|
78
85
|
s << modifier.to_s
|
|
79
86
|
s
|
|
80
87
|
end
|
|
81
88
|
|
|
89
|
+
def ==(other)
|
|
90
|
+
other.is_a?(Geometry) &&
|
|
91
|
+
width == other.width &&
|
|
92
|
+
height == other.height &&
|
|
93
|
+
modifier == other.modifier &&
|
|
94
|
+
orientation == other.orientation
|
|
95
|
+
end
|
|
96
|
+
|
|
82
97
|
# Same as to_s
|
|
83
98
|
def inspect
|
|
84
99
|
to_s
|