kt-paperclip-vips 1.2.2 → 1.2.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4aa0400c5db1aca28cf71692d749f5693bccfa4ac56d5cbd6599d10060423c3
4
- data.tar.gz: 99c3a302cae0af2f37c24918b6bfbfffca917cde6640fa18e013e6dd934bfc6e
3
+ metadata.gz: 5866bf3ce6a2d903bea4a6e68d37aa39c1ac780552d438691245b6768773730e
4
+ data.tar.gz: 9742d4563d322772ec01b0ff52ffeb95872387af8d6fa7aeb8fa95279d441c19
5
5
  SHA512:
6
- metadata.gz: aa8aee51bc040cd4a3bcb5ee2d2a90dffde49d602db8def2b42a50a284998cb310d49f2eabf7e109b16c5a8c2444f8a37728074372d581e1d6987ec06a2bc3c1
7
- data.tar.gz: 7a538b91a744206fe7dfa52eb80c8cb9d21085d2bb4fb23ed0f84498d70459149a36f2665a8f8fc27c033a638adc9007c9cd46dc360174508998d1ecc241c7f5
6
+ metadata.gz: 5ff964be47f1259e59d506a867b46deaf5c100d2a072623399ff75198294342e9818859118cd1b6de0be42bbe7b544d127ed6825a0c4866ba16c792536b6d393
7
+ data.tar.gz: 0c24ca2b9bdc655f62d90784386b5216beff0a7e0a077b5b8d310d58dc357e4cb7b61e521e5198ed577e0d158466e08bb5f97f3235cb77ba42c2e22bd445ff8f
data/.gitignore CHANGED
@@ -13,3 +13,4 @@
13
13
  *.gem
14
14
  mise.toml
15
15
  Gemfile.lock
16
+ .DS_Store
data/.rubocop.yml ADDED
@@ -0,0 +1,135 @@
1
+ plugins:
2
+ - rubocop-performance
3
+ - rubocop-rake
4
+ - rubocop-rspec
5
+
6
+ AllCops:
7
+ TargetRubyVersion: 3.2
8
+ DisabledByDefault: false
9
+ DisplayCopNames: true
10
+ NewCops: enable
11
+ Exclude:
12
+ - "**/bin/*"
13
+ - "**/config.ru"
14
+ - "**/db/**/*"
15
+ - "**/schema.rb"
16
+ - "**/node_modules/**/*"
17
+ - "**/vendor/**/*"
18
+ - "**/vendor/**/*.txt"
19
+ - "**/test/**/*"
20
+ - "**/tmp/**/*"
21
+
22
+ # Dim down rubocop's annoyance level below:
23
+
24
+ Layout/ArgumentAlignment:
25
+ Enabled: true
26
+ EnforcedStyle: with_fixed_indentation
27
+
28
+ Layout/CaseIndentation:
29
+ Enabled: true
30
+ EnforcedStyle: end
31
+
32
+ Layout/EndAlignment:
33
+ Enabled: true
34
+ EnforcedStyleAlignWith: variable
35
+ AutoCorrect: true
36
+
37
+ Layout/LineLength:
38
+ Exclude:
39
+ - app/views/**/*.rb
40
+ - app/components/**/*.rb
41
+ - lib/generators/**/*.rb
42
+ Max: 125
43
+
44
+ Layout/SpaceInsideBlockBraces:
45
+ Enabled: true
46
+ EnforcedStyleForEmptyBraces: space
47
+
48
+ Layout/SpaceInsideArrayLiteralBrackets:
49
+ EnforcedStyle: no_space
50
+
51
+ Layout/SpaceInsideHashLiteralBraces:
52
+ Enabled: true
53
+ EnforcedStyle: space
54
+ EnforcedStyleForEmptyBraces: no_space
55
+
56
+ Lint/MissingSuper:
57
+ Enabled: false
58
+
59
+ Metrics:
60
+ Enabled: false
61
+
62
+ Naming/FileName:
63
+ Exclude:
64
+ - lib/paperclip-vips.rb
65
+
66
+ RSpec/ExampleLength:
67
+ Enabled: false
68
+
69
+ RSpec/IndexedLet:
70
+ Enabled: false
71
+
72
+ RSpec/AlignLeftLetBrace:
73
+ Enabled: true
74
+
75
+ RSpec/MultipleExpectations:
76
+ Enabled: false
77
+
78
+ RSpec/MultipleMemoizedHelpers:
79
+ Max: 10
80
+
81
+ RSpec/NestedGroups:
82
+ Max: 4
83
+
84
+ RSpec/PendingWithoutReason:
85
+ Enabled: false
86
+
87
+ RSpec/PredicateMatcher:
88
+ Enabled: false
89
+
90
+ RSpec/SpecFilePathFormat:
91
+ Enabled: false
92
+
93
+ Style/ClassAndModuleChildren:
94
+ EnforcedStyle: nested
95
+ Exclude:
96
+ - app/events/**/*.rb
97
+ # - app/views/**/*.rb
98
+
99
+ Style/CommentedKeyword:
100
+ Enabled: false
101
+
102
+ Style/Documentation:
103
+ Enabled: false
104
+
105
+ Style/EmptyMethod:
106
+ Enabled: false
107
+
108
+ Style/FormatStringToken:
109
+ EnforcedStyle: template
110
+
111
+ Style/FrozenStringLiteralComment:
112
+ Enabled: true
113
+ EnforcedStyle: never
114
+
115
+ Style/HashSyntax:
116
+ Enabled: true
117
+ EnforcedShorthandSyntax: always
118
+
119
+ Style/PercentLiteralDelimiters:
120
+ PreferredDelimiters:
121
+ default: "()"
122
+ "%i": "[]"
123
+ "%I": "[]"
124
+ "%r": "{}"
125
+ "%w": "[]"
126
+ "%W": "[]"
127
+
128
+ Style/StringLiterals:
129
+ EnforcedStyle: double_quotes
130
+
131
+ Style/TrailingCommaInHashLiteral:
132
+ EnforcedStyleForMultiline: comma
133
+
134
+ Style/TrailingCommaInArrayLiteral:
135
+ EnforcedStyleForMultiline: comma
data/Gemfile CHANGED
@@ -4,7 +4,11 @@ source "https://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  gem "bundler"
7
+ gem "byebug"
8
+ gem "gem-release"
7
9
  gem "rake"
8
10
  gem "rspec"
9
- gem "byebug"
10
- gem "gem-release"
11
+ gem "rubocop"
12
+ gem "rubocop-performance"
13
+ gem "rubocop-rake"
14
+ gem "rubocop-rspec"
data/README.md CHANGED
@@ -7,7 +7,7 @@ A custom paperclip processor that uses the [libvips image processing library](ht
7
7
  Add this line to your application's Gemfile:
8
8
 
9
9
  ```ruby
10
- gem 'paperclip-vips'
10
+ gem 'kt-paperclip-vips'
11
11
  ```
12
12
 
13
13
  And then execute:
@@ -16,7 +16,7 @@ And then execute:
16
16
 
17
17
  Or install it yourself as:
18
18
 
19
- $ gem install paperclip-vips
19
+ $ gem install kt-paperclip-vips
20
20
 
21
21
  Note: You will need to have libvips installed on your machine for this processor to work correctly.
22
22
 
data/Rakefile CHANGED
@@ -3,4 +3,4 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "paperclip/vips"
4
+ require 'bundler/setup'
5
+ require 'paperclip/vips'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "paperclip/vips"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
@@ -0,0 +1,151 @@
1
+ module Paperclip
2
+ class Vips < Processor
3
+ attr_accessor :auto_orient, :convert_options, :current_geometry, :format, :source_file_options,
4
+ :target_geometry, :whiny
5
+
6
+ def initialize(file, options = {}, attachment = nil)
7
+ super
8
+
9
+ geometry = options[:geometry].to_s
10
+ @should_crop = geometry[-1, 1] == "#"
11
+ @target_geometry = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
12
+ @current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
13
+ @whiny = options.fetch(:whiny, true)
14
+
15
+ @auto_orient = options.fetch(:auto_orient, true)
16
+ @convert_options = options[:convert_options]
17
+
18
+ @current_format = current_format(file).downcase
19
+ @format = options[:format] || @current_format
20
+
21
+ @basename = File.basename(@file.path, @current_format)
22
+ end
23
+
24
+ def make
25
+ source = @file
26
+ filename = [@basename, @format ? ".#{@format}" : ""].join
27
+ destination = TempfileFactory.new.generate(filename)
28
+
29
+ begin
30
+ if @target_geometry.present?
31
+ target_width = @target_geometry.width
32
+ target_height = @target_geometry.height
33
+ modifier = @target_geometry.modifier # e.g., ">", "#", etc.
34
+
35
+ # Use thumbnail for efficient resizing and cropping
36
+ crop = (modifier == "#")
37
+ thumbnail = ::Vips::Image.thumbnail(
38
+ source.path,
39
+ target_width,
40
+ height: crop ? target_height : nil,
41
+ size: (modifier == ">" ? :down : :both), # ">" means shrink only
42
+ crop: crop ? :centre : :none
43
+ )
44
+
45
+ # Additional cropping for exact "#" dimensions if needed
46
+ if crop && (thumbnail.width != target_width || thumbnail.height != target_height)
47
+ left = [0, (thumbnail.width - target_width) / 2].max
48
+ top = [0, (thumbnail.height - target_height) / 2].max
49
+ crop_width = [target_width, thumbnail.width - left].min
50
+ crop_height = [target_height, thumbnail.height - top].min
51
+ if crop_width.positive? && crop_height.positive?
52
+ thumbnail = thumbnail.crop(left, top, crop_width,
53
+ crop_height)
54
+ end
55
+ end
56
+ end
57
+
58
+ # Fallback to original image if no geometry
59
+ thumbnail = ::Vips::Image.new_from_file(source.path) unless defined?(thumbnail) && thumbnail
60
+
61
+ # Apply convert options
62
+ thumbnail = process_convert_options(thumbnail)
63
+
64
+ # Save the processed image
65
+ save_thumbnail(thumbnail, destination.path)
66
+ rescue StandardError => e
67
+ puts e.message, e.backtrace
68
+ if @whiny
69
+ message = "There was an error processing the thumbnail for #{@basename}:\n#{e.message}"
70
+ raise Paperclip::Error, message
71
+ end
72
+ end
73
+
74
+ destination # Return the Tempfile object
75
+ end
76
+
77
+ private
78
+
79
+ def crop
80
+ return @options[:crop] || :centre if @should_crop
81
+
82
+ nil
83
+ end
84
+
85
+ def current_format(file)
86
+ extension = File.extname(file.path)
87
+ return extension if extension.present?
88
+
89
+ extension = File.extname(file.original_filename)
90
+ return extension if extension.present?
91
+
92
+ ""
93
+ end
94
+
95
+ def width
96
+ @target_geometry&.width || @current_geometry.width
97
+ end
98
+
99
+ def height
100
+ @target_geometry&.height || @current_geometry.height
101
+ end
102
+
103
+ def process_convert_options(image)
104
+ if image && @convert_options.present?
105
+ # Handle string-based convert_options (e.g., "-quality 80 -strip")
106
+ quality = @convert_options[/quality (\d+)/, 1]&.to_i
107
+ strip = @convert_options.include?("-strip")
108
+
109
+ # Apply options via instance variables for save_thumbnail
110
+ @processed_quality = quality if quality
111
+ @processed_strip = strip if strip
112
+
113
+ # Handle JSON-based commands if present
114
+ commands = parsed_convert_commands(@convert_options)
115
+ commands.each do |cmd|
116
+ image = ::Vips::Operation.call(cmd[:cmd], [image, *cmd[:args]], cmd[:optional] || {})
117
+ end
118
+ end
119
+ image
120
+ end
121
+
122
+ def parsed_convert_commands(convert_options)
123
+ begin
124
+ commands = JSON.parse(convert_options, symbolize_names: true)
125
+ rescue StandardError
126
+ commands = []
127
+ end
128
+
129
+ commands.unshift({ cmd: "autorot" }) if @auto_orient && commands.none? { |cmd| cmd[:cmd] == "autorot" }
130
+
131
+ commands
132
+ end
133
+
134
+ def save_thumbnail(thumbnail, path)
135
+ quality = @processed_quality || 75
136
+ strip = @processed_strip || false
137
+
138
+ case @format.to_s.downcase
139
+ when ".jpeg", ".jpg", "jpeg", "jpg", "webp"
140
+ thumbnail.jpegsave(path, Q: quality, strip:)
141
+ when ".gif", "gif"
142
+ thumbnail.magicksave(path, strip:) # No quality for GIF
143
+ when ".png", "png"
144
+ compression = 9 - (quality / 11.1).floor # Map 0-100 to 9-0
145
+ thumbnail.pngsave(path, compression:, strip:)
146
+ else
147
+ thumbnail.write_to_file(path) # Fallback
148
+ end
149
+ end
150
+ end
151
+ end
@@ -1,3 +1,3 @@
1
1
  module PaperclipVips
2
- VERSION = "1.2.2"
2
+ VERSION = "1.2.5".freeze
3
3
  end
@@ -1,10 +1,10 @@
1
1
  require "ruby-vips"
2
2
  require "paperclip"
3
3
  require "paperclip-vips/version"
4
- require "paperclip-vips/paperclip/vips"
4
+ require "paperclip/vips"
5
5
 
6
6
  module PaperclipVips
7
7
  def self.root
8
- Gem::Specification.find_by_name('paperclip-vips').gem_dir
8
+ Gem::Specification.find_by_name("kt-paperclip-vips").gem_dir
9
9
  end
10
10
  end
@@ -1,5 +1,4 @@
1
-
2
- lib = File.expand_path("../lib", __FILE__)
1
+ lib = File.expand_path("lib", __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require "paperclip-vips/version"
5
4
 
@@ -9,19 +8,22 @@ Gem::Specification.new do |spec|
9
8
  spec.authors = ["Ken Greeff", "Mikael Henriksson"]
10
9
  spec.email = ["ken@kengreeff.com", "mikael@mhenrixon.com"]
11
10
 
12
- spec.summary = %q{Uses Ruby Vips to when creating thumbnails for faster generation.}
11
+ spec.summary = "Uses Ruby Vips to when creating thumbnails for faster generation."
13
12
  spec.homepage = "https://github.com/realhub/paperclip-vips"
14
13
  spec.license = "MIT"
15
14
 
15
+ spec.required_ruby_version = ">= 3.2.0"
16
+
16
17
  # Specify which files should be added to the gem when it is released.
17
18
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
19
20
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|Gemfile|mise.toml/bin)/}) }
20
21
  end
21
22
  spec.bindir = "exe"
22
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
24
  spec.require_paths = ["lib"]
24
25
 
25
- spec.add_runtime_dependency 'kt-paperclip', ">= 5.0.0"
26
- spec.add_runtime_dependency "ruby-vips", ">= 2.1"
26
+ spec.add_dependency "kt-paperclip", ">= 5.0.0"
27
+ spec.add_dependency "ruby-vips", ">= 2.1"
28
+ spec.metadata["rubygems_mfa_required"] = "true"
27
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kt-paperclip-vips
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken Greeff
8
8
  - Mikael Henriksson
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-02-20 00:00:00.000000000 Z
11
+ date: 2025-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kt-paperclip
@@ -47,6 +47,7 @@ extra_rdoc_files: []
47
47
  files:
48
48
  - ".gitignore"
49
49
  - ".rspec"
50
+ - ".rubocop.yml"
50
51
  - ".travis.yml"
51
52
  - CHANGELOG.md
52
53
  - CODE_OF_CONDUCT.md
@@ -57,13 +58,14 @@ files:
57
58
  - bin/console
58
59
  - bin/setup
59
60
  - lib/paperclip-vips.rb
60
- - lib/paperclip-vips/paperclip/vips.rb
61
61
  - lib/paperclip-vips/version.rb
62
+ - lib/paperclip/vips.rb
62
63
  - paperclip-vips.gemspec
63
64
  homepage: https://github.com/realhub/paperclip-vips
64
65
  licenses:
65
66
  - MIT
66
- metadata: {}
67
+ metadata:
68
+ rubygems_mfa_required: 'true'
67
69
  rdoc_options: []
68
70
  require_paths:
69
71
  - lib
@@ -71,14 +73,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
73
  requirements:
72
74
  - - ">="
73
75
  - !ruby/object:Gem::Version
74
- version: '0'
76
+ version: 3.2.0
75
77
  required_rubygems_version: !ruby/object:Gem::Requirement
76
78
  requirements:
77
79
  - - ">="
78
80
  - !ruby/object:Gem::Version
79
81
  version: '0'
80
82
  requirements: []
81
- rubygems_version: 3.6.2
83
+ rubygems_version: 3.6.6
82
84
  specification_version: 4
83
85
  summary: Uses Ruby Vips to when creating thumbnails for faster generation.
84
86
  test_files: []
@@ -1,122 +0,0 @@
1
- module Paperclip
2
- class Vips < Processor
3
- attr_accessor :auto_orient, :convert_options, :current_geometry, :format, :source_file_options,
4
- :target_geometry, :whiny
5
-
6
- def initialize(file, options = {}, attachment = nil)
7
- super
8
-
9
- geometry = options[:geometry].to_s
10
- @should_crop = geometry[-1,1] == '#'
11
- @target_geometry = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
12
- @current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
13
- @whiny = options.fetch(:whiny, true)
14
-
15
- @auto_orient = options.fetch(:auto_orient, true)
16
- @convert_options = options[:convert_options]
17
-
18
- @current_format = current_format(file).downcase
19
- @format = options[:format] || @current_format
20
-
21
- @basename = File.basename(@file.path, @current_format)
22
- end
23
-
24
- def make
25
- source = @file
26
- filename = [@basename, @format ? ".#{@format}" : ""].join
27
- destination = TempfileFactory.new.generate(filename)
28
-
29
- begin
30
- thumbnail = ::Vips::Image.thumbnail(source.path, width, height: crop ? height : nil, crop: crop) if @target_geometry
31
- thumbnail = ::Vips::Image.new_from_file(source.path) if !defined?(thumbnail) || thumbnail.nil?
32
- thumbnail = process_convert_options(thumbnail)
33
- save_thumbnail(thumbnail, destination.path)
34
-
35
- rescue => e
36
- p e.message, e.backtrace
37
-
38
- if @whiny
39
- message = "There was an error processing the thumbnail for #{@basename}:\n" + e.message
40
- raise Paperclip::Error, message
41
- end
42
- end
43
-
44
- return destination
45
- end
46
-
47
- private
48
- def crop
49
- if @should_crop
50
- return @options[:crop] || :centre
51
- end
52
-
53
- nil
54
- end
55
-
56
- def current_format(file)
57
- extension = File.extname(file.path)
58
- return extension if extension.present?
59
-
60
- extension = File.extname(file.original_filename)
61
- return extension if extension.present?
62
-
63
- return ""
64
- end
65
-
66
- def width
67
- @target_geometry&.width || @current_geometry.width
68
- end
69
-
70
- def height
71
- @target_geometry&.height || @current_geometry.height
72
- end
73
-
74
- def process_convert_options(image)
75
- if image
76
- commands = parsed_convert_commands(@convert_options)
77
- commands.each do |cmd|
78
- image = ::Vips::Operation.call(cmd[:cmd], [image, *cmd[:args]], cmd[:optional] || {})
79
- end
80
- end
81
-
82
- return image
83
- end
84
-
85
- def parsed_convert_commands(convert_options)
86
- begin
87
- commands = JSON.parse(convert_options, symbolize_names: true)
88
- rescue
89
- commands = []
90
- end
91
-
92
- if @auto_orient && commands.none? { _1[:cmd] == "autorot" }
93
- commands.unshift({ cmd: "autorot" })
94
- end
95
-
96
- return commands
97
- end
98
-
99
- def save_thumbnail(thumbnail, path)
100
- case @current_format
101
- when ".jpeg", ".jpg"
102
- save_jpg(thumbnail, path)
103
- when ".gif"
104
- save_gif(thumbnail, path)
105
- when ".png"
106
- save_png(thumbnail, path)
107
- end
108
- end
109
-
110
- def save_jpg(thumbnail, path)
111
- thumbnail.jpegsave(path)
112
- end
113
-
114
- def save_gif(thumbnail, path)
115
- thumbnail.magicksave(path)
116
- end
117
-
118
- def save_png(thumbnail, path)
119
- thumbnail.pngsave(path)
120
- end
121
- end
122
- end