kt-paperclip 7.3.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -56
  3. data/lib/generators/paperclip/paperclip_generator.rb +3 -1
  4. data/lib/kt-paperclip.rb +2 -0
  5. data/lib/paperclip/attachment.rb +2 -0
  6. data/lib/paperclip/attachment_registry.rb +2 -0
  7. data/lib/paperclip/callbacks.rb +7 -4
  8. data/lib/paperclip/commands/imagemagick/geometry_parser.rb +63 -0
  9. data/lib/paperclip/commands/imagemagick/version_detector.rb +41 -0
  10. data/lib/paperclip/commands/imagemagick.rb +44 -0
  11. data/lib/paperclip/commands/runner.rb +38 -0
  12. data/lib/paperclip/commands/unix_file.rb +28 -0
  13. data/lib/paperclip/content_type_detector.rb +31 -32
  14. data/lib/paperclip/errors.rb +2 -0
  15. data/lib/paperclip/file_command_content_type_detector.rb +11 -12
  16. data/lib/paperclip/filename_cleaner.rb +2 -0
  17. data/lib/paperclip/geometry.rb +22 -7
  18. data/lib/paperclip/geometry_detector_factory.rb +11 -31
  19. data/lib/paperclip/geometry_parser_factory.rb +13 -17
  20. data/lib/paperclip/glue.rb +2 -0
  21. data/lib/paperclip/has_attached_file.rb +2 -0
  22. data/lib/paperclip/helpers.rb +5 -25
  23. data/lib/paperclip/interpolations/plural_cache.rb +2 -0
  24. data/lib/paperclip/interpolations.rb +2 -0
  25. data/lib/paperclip/io_adapters/abstract_adapter.rb +2 -0
  26. data/lib/paperclip/io_adapters/attachment_adapter.rb +2 -0
  27. data/lib/paperclip/io_adapters/data_uri_adapter.rb +2 -0
  28. data/lib/paperclip/io_adapters/empty_string_adapter.rb +2 -0
  29. data/lib/paperclip/io_adapters/file_adapter.rb +2 -0
  30. data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +2 -0
  31. data/lib/paperclip/io_adapters/identity_adapter.rb +2 -0
  32. data/lib/paperclip/io_adapters/nil_adapter.rb +2 -0
  33. data/lib/paperclip/io_adapters/registry.rb +2 -0
  34. data/lib/paperclip/io_adapters/stringio_adapter.rb +2 -0
  35. data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +2 -0
  36. data/lib/paperclip/io_adapters/uri_adapter.rb +5 -13
  37. data/lib/paperclip/logger.rb +2 -0
  38. data/lib/paperclip/matchers/have_attached_file_matcher.rb +2 -0
  39. data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +5 -3
  40. data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +2 -0
  41. data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +2 -0
  42. data/lib/paperclip/matchers.rb +2 -0
  43. data/lib/paperclip/media_type_spoof_detector.rb +3 -10
  44. data/lib/paperclip/missing_attachment_styles.rb +2 -0
  45. data/lib/paperclip/processor.rb +6 -19
  46. data/lib/paperclip/processor_helpers.rb +2 -0
  47. data/lib/paperclip/rails_environment.rb +2 -0
  48. data/lib/paperclip/railtie.rb +2 -0
  49. data/lib/paperclip/schema.rb +6 -4
  50. data/lib/paperclip/storage/filesystem.rb +2 -0
  51. data/lib/paperclip/storage/fog.rb +4 -3
  52. data/lib/paperclip/storage/s3.rb +3 -3
  53. data/lib/paperclip/storage.rb +2 -0
  54. data/lib/paperclip/style.rb +2 -0
  55. data/lib/paperclip/tempfile.rb +2 -0
  56. data/lib/paperclip/tempfile_factory.rb +2 -0
  57. data/lib/paperclip/thumbnail.rb +9 -10
  58. data/lib/paperclip/url_generator.rb +2 -0
  59. data/lib/paperclip/validators/attachment_content_type_validator.rb +2 -0
  60. data/lib/paperclip/validators/attachment_file_name_validator.rb +2 -0
  61. data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +2 -0
  62. data/lib/paperclip/validators/attachment_presence_validator.rb +2 -0
  63. data/lib/paperclip/validators/attachment_size_validator.rb +2 -0
  64. data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +2 -0
  65. data/lib/paperclip/validators.rb +2 -0
  66. data/lib/paperclip/version.rb +3 -1
  67. data/lib/paperclip.rb +25 -4
  68. data/lib/tasks/paperclip.rake +9 -4
  69. data/shoulda_macros/paperclip.rb +2 -0
  70. metadata +18 -538
  71. data/.codeclimate.yml +0 -17
  72. data/.github/FUNDING.yml +0 -3
  73. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  74. data/.github/ISSUE_TEMPLATE/custom.md +0 -10
  75. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  76. data/.gitignore +0 -19
  77. data/.hound.yml +0 -3
  78. data/.rubocop.yml +0 -1061
  79. data/.travis.yml +0 -45
  80. data/Appraisals +0 -30
  81. data/CONTRIBUTING.md +0 -85
  82. data/Gemfile +0 -19
  83. data/OLD_NEWS +0 -556
  84. data/RELEASING.md +0 -17
  85. data/Rakefile +0 -52
  86. data/UPGRADING +0 -17
  87. data/features/basic_integration.feature +0 -85
  88. data/features/migration.feature +0 -29
  89. data/features/rake_tasks.feature +0 -62
  90. data/features/step_definitions/attachment_steps.rb +0 -120
  91. data/features/step_definitions/html_steps.rb +0 -15
  92. data/features/step_definitions/rails_steps.rb +0 -257
  93. data/features/step_definitions/s3_steps.rb +0 -14
  94. data/features/step_definitions/web_steps.rb +0 -106
  95. data/features/support/env.rb +0 -12
  96. data/features/support/fakeweb.rb +0 -11
  97. data/features/support/file_helpers.rb +0 -34
  98. data/features/support/fixtures/boot_config.txt +0 -15
  99. data/features/support/fixtures/gemfile.txt +0 -5
  100. data/features/support/fixtures/preinitializer.txt +0 -20
  101. data/features/support/paths.rb +0 -28
  102. data/features/support/rails.rb +0 -39
  103. data/features/support/selectors.rb +0 -19
  104. data/gemfiles/4.2.gemfile +0 -20
  105. data/gemfiles/5.0.gemfile +0 -20
  106. data/gemfiles/5.1.gemfile +0 -20
  107. data/gemfiles/5.2.gemfile +0 -20
  108. data/gemfiles/6.0.gemfile +0 -20
  109. data/gemfiles/6.1.gemfile +0 -21
  110. data/gemfiles/7.0.gemfile +0 -21
  111. data/paperclip.gemspec +0 -50
  112. data/spec/database.yml +0 -4
  113. data/spec/paperclip/attachment_definitions_spec.rb +0 -13
  114. data/spec/paperclip/attachment_processing_spec.rb +0 -79
  115. data/spec/paperclip/attachment_registry_spec.rb +0 -158
  116. data/spec/paperclip/attachment_spec.rb +0 -1616
  117. data/spec/paperclip/content_type_detector_spec.rb +0 -54
  118. data/spec/paperclip/file_command_content_type_detector_spec.rb +0 -40
  119. data/spec/paperclip/filename_cleaner_spec.rb +0 -13
  120. data/spec/paperclip/geometry_detector_spec.rb +0 -47
  121. data/spec/paperclip/geometry_parser_spec.rb +0 -73
  122. data/spec/paperclip/geometry_spec.rb +0 -267
  123. data/spec/paperclip/glue_spec.rb +0 -63
  124. data/spec/paperclip/has_attached_file_spec.rb +0 -78
  125. data/spec/paperclip/integration_spec.rb +0 -702
  126. data/spec/paperclip/interpolations_spec.rb +0 -270
  127. data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +0 -160
  128. data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +0 -167
  129. data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +0 -88
  130. data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +0 -17
  131. data/spec/paperclip/io_adapters/file_adapter_spec.rb +0 -131
  132. data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +0 -142
  133. data/spec/paperclip/io_adapters/identity_adapter_spec.rb +0 -8
  134. data/spec/paperclip/io_adapters/nil_adapter_spec.rb +0 -25
  135. data/spec/paperclip/io_adapters/registry_spec.rb +0 -35
  136. data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +0 -64
  137. data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +0 -146
  138. data/spec/paperclip/io_adapters/uri_adapter_spec.rb +0 -231
  139. data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +0 -19
  140. data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +0 -108
  141. data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +0 -69
  142. data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +0 -88
  143. data/spec/paperclip/media_type_spoof_detector_spec.rb +0 -126
  144. data/spec/paperclip/meta_class_spec.rb +0 -30
  145. data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +0 -88
  146. data/spec/paperclip/paperclip_spec.rb +0 -196
  147. data/spec/paperclip/plural_cache_spec.rb +0 -37
  148. data/spec/paperclip/processor_helpers_spec.rb +0 -57
  149. data/spec/paperclip/processor_spec.rb +0 -26
  150. data/spec/paperclip/rails_environment_spec.rb +0 -30
  151. data/spec/paperclip/rake_spec.rb +0 -103
  152. data/spec/paperclip/schema_spec.rb +0 -252
  153. data/spec/paperclip/storage/filesystem_spec.rb +0 -102
  154. data/spec/paperclip/storage/fog_spec.rb +0 -606
  155. data/spec/paperclip/storage/s3_live_spec.rb +0 -188
  156. data/spec/paperclip/storage/s3_spec.rb +0 -1961
  157. data/spec/paperclip/style_spec.rb +0 -251
  158. data/spec/paperclip/tempfile_factory_spec.rb +0 -33
  159. data/spec/paperclip/tempfile_spec.rb +0 -35
  160. data/spec/paperclip/thumbnail_spec.rb +0 -504
  161. data/spec/paperclip/url_generator_spec.rb +0 -231
  162. data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +0 -410
  163. data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +0 -249
  164. data/spec/paperclip/validators/attachment_presence_validator_spec.rb +0 -85
  165. data/spec/paperclip/validators/attachment_size_validator_spec.rb +0 -325
  166. data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +0 -48
  167. data/spec/paperclip/validators_spec.rb +0 -179
  168. data/spec/spec_helper.rb +0 -46
  169. data/spec/support/assertions.rb +0 -84
  170. data/spec/support/fake_model.rb +0 -24
  171. data/spec/support/fake_rails.rb +0 -12
  172. data/spec/support/fixtures/12k.png +0 -0
  173. data/spec/support/fixtures/50x50.png +0 -0
  174. data/spec/support/fixtures/5k.png +0 -0
  175. data/spec/support/fixtures/animated +0 -0
  176. data/spec/support/fixtures/animated.gif +0 -0
  177. data/spec/support/fixtures/animated.unknown +0 -0
  178. data/spec/support/fixtures/aws_s3.yml +0 -13
  179. data/spec/support/fixtures/bad.png +0 -1
  180. data/spec/support/fixtures/empty.html +0 -1
  181. data/spec/support/fixtures/empty.xlsx +0 -0
  182. data/spec/support/fixtures/fog.yml +0 -8
  183. data/spec/support/fixtures/rotated.jpg +0 -0
  184. data/spec/support/fixtures/s3.yml +0 -8
  185. data/spec/support/fixtures/sample.xlsm +0 -0
  186. data/spec/support/fixtures/spaced file.jpg +0 -0
  187. data/spec/support/fixtures/spaced file.png +0 -0
  188. data/spec/support/fixtures/text.txt +0 -1
  189. data/spec/support/fixtures/twopage.pdf +0 -0
  190. data/spec/support/fixtures/uppercase.PNG +0 -0
  191. data/spec/support/matchers/accept.rb +0 -5
  192. data/spec/support/matchers/exist.rb +0 -5
  193. data/spec/support/matchers/have_column.rb +0 -23
  194. data/spec/support/mock_attachment.rb +0 -24
  195. data/spec/support/mock_interpolator.rb +0 -24
  196. data/spec/support/mock_url_generator_builder.rb +0 -26
  197. data/spec/support/model_reconstruction.rb +0 -72
  198. data/spec/support/reporting.rb +0 -11
  199. data/spec/support/test_data.rb +0 -13
  200. data/spec/support/version_helper.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 213cd04cf7f631853227e9c3fc6d6a38733f3c278907497e1f8e3c2e7a98bac8
4
- data.tar.gz: 01aec044e7eff086cef50fab323730f617fd455e4ba70ffb99a58d099a4ffde5
3
+ metadata.gz: 57acb40c010d089179840fb06116bb36527035ce5e6ddaa25531d2d24067456f
4
+ data.tar.gz: f49b37dbd63550be17c59b47ce930981dae6f7d88461cf90721925051e579efa
5
5
  SHA512:
6
- metadata.gz: a7e922d52efbde590030651a7cbdca2087aa3c163d6cc498273e9ca57ce5c2b371b44601ccb9e825ee196f1197689438cb91cfca240b6361f259650ea63e5d03
7
- data.tar.gz: 412ef55d0817f23247e52859d1b39cb1358f55bfd8c8d67cb16df376b42cc2926611b938004d6cec95d7a211895702cb279721322c1ac16e235af954a6f1f40e
6
+ metadata.gz: f92b7453a259e25763375ba727dc262910ec647e54017182befa98e0330e6ac5890cd76ab27476a3be6725de35aeb8b85f73db463ad75007370d0076ed3220bb
7
+ data.tar.gz: dae535db1d37087c4505aaa0db691c2e300377755c9345ef412a28dfde1bf55f64f169d6f438d0285672db9bc8681629708c1c366e719cfcb2773d89f6ad5906
data/README.md CHANGED
@@ -93,73 +93,41 @@ Requirements
93
93
  Paperclip now requires Ruby version **>= 2.3** and Rails version **>= 4.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. To ensure
99
- that it does, on your command line, run `which convert` (one of the ImageMagick
100
- utilities). This will give you the path where that utility is installed. For
101
- example, it might return `/usr/local/bin/convert`.
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
- Then, in your environment config file, let Paperclip know to look there by adding that
104
- directory to its path.
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[:command_path] = "/usr/local/bin/"
107
+ Paperclip.options[:imagemagick_version] = "7"
110
108
  ```
111
109
 
112
- If you're on Mac OS X, you'll want to run the following with [Homebrew](http://www.brew.sh):
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.
110
+ Please ensure ImageMagick is in your environment path, or alternatively set
111
+ the path manually in an initializer:
135
112
 
136
- To manually install, you should perform the following:
137
-
138
- > **Download & install `file` from [this URL](http://gnuwin32.sourceforge.net/packages/file.htm)**
139
-
140
- To test, you can use the image below:
141
- ![untitled](https://cloud.githubusercontent.com/assets/1104431/4524452/a1f8cce4-4d44-11e4-872e-17adb96f79c9.png)
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
- OR
117
+ ### Optional: Unix `file` Command
155
118
 
156
- **Environment**
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
- 1. Open `config/environments/development.rb`
159
- 2. Add the following line: `Paperclip.options[:command_path] = 'C:\Program Files (x86)\GnuWin32\bin'`
160
- 3. Restart your Rails server
124
+ ```ruby
125
+ Paperclip.options[:file_command_path] = "/usr/local/bin/"
126
+ ```
161
127
 
162
- Either of these methods will give your Rails setup access to the `file.exe` functionality, thus providing the ability to check the contents of a file (fixing the spoofing problem)
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
 
@@ -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}]" if Rails.version.first.to_i > 4
36
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
35
37
  end
36
38
  end
data/lib/kt-paperclip.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "paperclip"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "uri"
2
4
  require "paperclip/url_generator"
3
5
  require "active_support/deprecation"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "singleton"
2
4
 
3
5
  module Paperclip
@@ -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
- eval <<-end_callbacks
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
- def after_#{callback}(*args, &blk)
18
+
19
+ def self.after_#{callback}(*args, &blk)
17
20
  set_callback(:#{callback}, :after, *args, &blk)
18
21
  end
19
- end_callbacks
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
- SENSIBLE_DEFAULT
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 || SENSIBLE_DEFAULT
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
- type_from_marcel || type_from_file_command
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
- SENSIBLE_DEFAULT
71
+ @type_from_file_contents = nil
67
72
  end
68
73
 
69
74
  def type_from_marcel
70
- return @type_from_marcel if defined? @type_from_marcel
71
-
72
- @type_from_marcel = Marcel::MimeType.for Pathname.new(@filepath),
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
- @type_from_file_command ||=
82
- FileCommandContentTypeDetector.new(@filepath).detect
81
+ Commands::UnixFile.detect_content_type(@filepath)
83
82
  end
84
83
  end
85
84
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Paperclip
2
4
  # A base error class for Paperclip. Most of the error that will be thrown
3
5
  # from Paperclip will inherits from this class.
@@ -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
- type_from_file_command
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 type_from_file_command
16
- # On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Paperclip
2
4
  class FilenameCleaner
3
5
  def initialize(invalid_character_regex)
@@ -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
- GeometryDetector.new(file).make
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.new(string).make
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 = String.new
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
@@ -1,45 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Paperclip
4
+ # @deprecated Will be removed in Paperclip 8.0. Use +Paperclip::Commands::ImageMagick::GeometryParser+ instead.
2
5
  class GeometryDetector
6
+ # @deprecated Will be removed in Paperclip 8.0. Use +Paperclip::Commands::ImageMagick::GeometryParser+ instead.
3
7
  def initialize(file)
8
+ warn_deprecation
4
9
  @file = file
5
- raise_if_blank_file
10
+ raise Errors::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name") if @file.blank?
6
11
  end
7
12
 
13
+ # @deprecated Will be removed in Paperclip 8.0. Use +Paperclip::Commands::ImageMagick::GeometryParser+ instead.
8
14
  def make
9
- geometry = GeometryParser.new(geometry_string.strip).make
10
- geometry || raise(Errors::NotIdentifiedByImageMagickError.new("Could not identify image size"))
15
+ warn_deprecation
16
+ Paperclip::Commands::ImageMagick::GeometryParser.from_file(@file)
11
17
  end
12
18
 
13
19
  private
14
20
 
15
- def geometry_string
16
- orientation = Paperclip.options[:use_exif_orientation] ?
17
- "%[exif:orientation]" : "1"
18
- Paperclip.run(
19
- Paperclip.options[:is_windows] ? "magick identify" : "identify",
20
- "-format '%wx%h,#{orientation}' :file", {
21
- file: "#{path}[0]"
22
- },
23
- swallow_stderr: true
24
- )
25
- rescue Terrapin::ExitStatusError
26
- ""
27
- rescue Terrapin::CommandNotFoundError => e
28
- raise_because_imagemagick_missing
29
- end
30
-
31
- def path
32
- @file.respond_to?(:path) ? @file.path : @file
33
- end
34
-
35
- def raise_if_blank_file
36
- if path.blank?
37
- raise Errors::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name")
38
- end
39
- end
40
-
41
- def raise_because_imagemagick_missing
42
- raise Errors::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
21
+ def warn_deprecation
22
+ Paperclip.deprecator.warn("Paperclip::GeometryDetector has been replaced by Paperclip::Commands::ImageMagick::GeometryParser")
43
23
  end
44
24
  end
45
25
  end