image_optim 0.31.2 → 0.31.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7197f37a1a5cd0a61248ccd4ef6b08f2c05ff8f084866da48f76b038bac4e4ec
4
- data.tar.gz: 7be50ee48142fd8775022855624a7ea642436b11d8ca260f322742a7814d513a
3
+ metadata.gz: 28ea4eea777b5a6eac029c628c28509b70d65f5784ef16466c26b758d189d88b
4
+ data.tar.gz: b4cdec32a6fde2d92b7c3382b14fb90c094e523e46c79fd8fc24d21737348167
5
5
  SHA512:
6
- metadata.gz: 02dbbb2520b6250fc2a5262c2c0524a42519e3a969683efe95b384f0e0cc5b4247638e74e5766ce016ccc462f3646f3ab7e7bf363299b1f6e79894b59af69df2
7
- data.tar.gz: 6673410e57ba7e01f0f146678f2b74e8bbdcd97ccccb320679f24acb860ea46be229b2184383ee59e5a7bac6a4977f506e113a68bf53f975c1b296e9e28310d1
6
+ metadata.gz: ad0b465ae7de080c2af44efaaeea7a7b17b8bc343faa0f8034105b027362abfc952bf72fead0d9c30f368a94cb74a1a7d2e19d49b07c626f49bfb59b99f4de69
7
+ data.tar.gz: 8d7070adae2ff71fe71df2ffaf25addb42acdddca2a427bcfad3f91cd3138d0fccfc2b3284d2cf146dd8f513be9c88a4b9c2f378d6564bcfdf629f5f6ee492cf
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
@@ -10,47 +10,69 @@ jobs:
10
10
  strategy:
11
11
  matrix:
12
12
  ruby:
13
- - '2.0'
14
- - '2.1'
15
- - '2.2'
16
- - '2.3'
17
- - '2.4'
18
- - '2.5'
19
- - '2.6'
13
+ - '1.9.3'
20
14
  - '2.7'
21
15
  - '3.0'
22
16
  - '3.1'
23
- - jruby-9.2
24
- - jruby-9.3
17
+ - '3.2'
18
+ - '3.3'
25
19
  - jruby-9.4
26
20
  fail-fast: false
27
21
  steps:
28
- - uses: actions/checkout@v2
22
+ - uses: actions/checkout@v4
29
23
  - uses: ruby/setup-ruby@v1
30
24
  with:
31
25
  ruby-version: "${{ matrix.ruby }}"
32
26
  bundler-cache: true
33
- - run: sudo npm install -g svgo
34
- - run: curl -L "https://github.com/shssoichiro/oxipng/releases/download/v4.0.3/oxipng-4.0.3-x86_64-unknown-linux-musl.tar.gz" | tar -xz -C /usr/local/bin --strip-components 1 --wildcards '*oxipng'
27
+ - run: npm install -g svgo
35
28
  - run: bundle exec image_optim --info
36
29
  - run: bundle exec rspec
30
+ containers:
31
+ runs-on: ubuntu-latest
32
+ container: ${{ matrix.container }}
33
+ strategy:
34
+ matrix:
35
+ container:
36
+ - debian:buster
37
+ - debian:bullseye
38
+ - debian:bookworm
39
+ # - alpine
40
+ fail-fast: false
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ - run: |
44
+ if command -v apt-get &> /dev/null; then
45
+ apt-get update
46
+ apt-get -y install make gcc git curl imagemagick ruby ruby-dev rubygems
47
+ fi
48
+ - run: |
49
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
50
+ . ~/.nvm/nvm.sh
51
+ nvm install 20
52
+ echo "PATH=$PATH" >> $GITHUB_ENV
53
+ - run: npm install -g svgo
54
+ - run: gem install bundler || gem install bundler --version '< 2.4'
55
+ - run: bundle install
56
+ - run: bundle exec bin/image_optim --info
57
+ - run: bundle exec rspec
37
58
  windows:
38
59
  runs-on: windows-latest
39
60
  strategy:
40
61
  matrix:
41
62
  ruby:
42
- - '2.6'
43
63
  - '2.7'
44
64
  - '3.0'
45
65
  - '3.1'
66
+ - '3.2'
67
+ - '3.3'
46
68
  fail-fast: false
47
69
  steps:
48
- - uses: actions/checkout@v2
70
+ - uses: actions/checkout@v4
49
71
  - uses: ruby/setup-ruby@v1
50
72
  with:
51
73
  ruby-version: "${{ matrix.ruby }}"
52
74
  bundler-cache: true
53
- - uses: actions/cache@v2
75
+ - uses: actions/cache@v4
54
76
  with:
55
77
  path: "$HOME/bin"
56
78
  key: ${{ runner.os }}
@@ -65,18 +87,26 @@ jobs:
65
87
  - run: npm install -g svgo
66
88
  - run: bundle exec image_optim --info
67
89
  - run: bundle exec rspec
90
+ update_worker_options_in_readme:
91
+ runs-on: ubuntu-latest
92
+ steps:
93
+ - uses: actions/checkout@v4
94
+ - uses: ruby/setup-ruby@v1
95
+ with:
96
+ ruby-version: '3'
97
+ bundler-cache: true
98
+ - run: script/update_worker_options_in_readme -n
68
99
  coverage:
69
100
  runs-on: ubuntu-latest
70
101
  env:
71
102
  CC_TEST_REPORTER_ID: b433c6540d220a2da0663670c9b260806bafdb3a43c6f22b2e81bfb1f87b12fe
72
103
  steps:
73
- - uses: actions/checkout@v2
104
+ - uses: actions/checkout@v4
74
105
  - uses: ruby/setup-ruby@v1
75
106
  with:
76
- ruby-version: '3.1'
107
+ ruby-version: '3'
77
108
  bundler-cache: true
78
- - run: sudo npm install -g svgo
79
- - run: curl -L "https://github.com/shssoichiro/oxipng/releases/download/v4.0.3/oxipng-4.0.3-x86_64-unknown-linux-musl.tar.gz" | tar -xz -C /usr/local/bin --strip-components 1 --wildcards '*oxipng'
80
- - uses: paambaati/codeclimate-action@v2.7.5
109
+ - run: npm install -g svgo
110
+ - uses: paambaati/codeclimate-action@v9
81
111
  with:
82
112
  coverageCommand: bundle exec rspec
@@ -8,9 +8,9 @@ jobs:
8
8
  rubocop:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
- - uses: actions/checkout@v2
11
+ - uses: actions/checkout@v4
12
12
  - uses: ruby/setup-ruby@v1
13
13
  with:
14
- ruby-version: '3.1'
14
+ ruby-version: '3'
15
15
  bundler-cache: true
16
16
  - run: bundle exec rubocop
data/.rubocop.yml CHANGED
@@ -88,6 +88,9 @@ Style/AccessorGrouping:
88
88
  Style/Alias:
89
89
  EnforcedStyle: prefer_alias_method
90
90
 
91
+ Style/ArgumentsForwarding:
92
+ Enabled: false
93
+
91
94
  Style/DoubleNegation:
92
95
  Enabled: false
93
96
 
data/CHANGELOG.markdown CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## unreleased
4
4
 
5
+ ## v0.31.4 (2024-11-19)
6
+
7
+ * Added `--svgo-allow-lossy` and `--svgo-precision` options to use svgo in lossy mode. This sets svgo `--precision`, which can result in substantially smaller svgs (see [#211](https://github.com/toy/image_optim/issues/211) for some experiments). Lower values are more lossy. 3 is the default, but many SVGs will work well even with 0 or 1. Like all worker specific lossy flags, this is also enabled with `--allow-lossy`. [#210](https://github.com/toy/image_optim/issues/210) [#211](https://github.com/toy/image_optim/issues/211) [@gurgeous](https://github.com/gurgeous)
8
+
9
+ ## v0.31.3 (2023-02-17)
10
+
11
+ * Support Psych4/Ruby 3.1 changes to use safe_yaml methods by default [#203](https://github.com/toy/image_optim/issues/203) [#204](https://github.com/toy/image_optim/pull/204) [@oscillot](https://github.com/oscillot) [@toy](https://github.com/toy)
12
+
5
13
  ## v0.31.2 (2022-11-27)
6
14
 
7
15
  * Support jruby 9.4 [@toy](https://github.com/toy)
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-2022 Ivan Kuchin
1
+ Copyright (c) 2012-2024 Ivan Kuchin
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.markdown CHANGED
@@ -1,7 +1,7 @@
1
1
  [![Gem Version](https://img.shields.io/gem/v/image_optim?logo=rubygems)](https://rubygems.org/gems/image_optim)
2
- [![Build Status](https://img.shields.io/github/workflow/status/toy/image_optim/check/master?logo=github)](https://github.com/toy/image_optim/actions/workflows/check.yml)
3
- [![Rubocop](https://img.shields.io/github/workflow/status/toy/image_optim/rubocop/master?label=rubocop&logo=rubocop)](https://github.com/toy/image_optim/actions/workflows/rubocop.yml)
4
- [![CodeQL](https://img.shields.io/github/workflow/status/toy/image_optim/codeql/master?label=codeql&logo=github)](https://github.com/toy/image_optim/actions/workflows/codeql.yml)
2
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/toy/image_optim/check.yml?logo=github)](https://github.com/toy/image_optim/actions/workflows/check.yml)
3
+ [![Rubocop](https://img.shields.io/github/actions/workflow/status/toy/image_optim/rubocop.yml?label=rubocop&logo=rubocop)](https://github.com/toy/image_optim/actions/workflows/rubocop.yml)
4
+ [![CodeQL](https://img.shields.io/github/actions/workflow/status/toy/image_optim/codeql.yml?label=codeql&logo=github)](https://github.com/toy/image_optim/actions/workflows/codeql.yml)
5
5
  [![Code Climate](https://img.shields.io/codeclimate/maintainability/toy/image_optim?logo=codeclimate)](https://codeclimate.com/github/toy/image_optim)
6
6
  [![Code Climate Coverage](https://img.shields.io/codeclimate/coverage/toy/image_optim?logo=codeclimate)](https://codeclimate.com/github/toy/image_optim)
7
7
  [![Depfu](https://img.shields.io/depfu/toy/image_optim)](https://depfu.com/github/toy/image_optim)
@@ -375,6 +375,8 @@ Worker has no options
375
375
  ### svgo:
376
376
  * `:disable_plugins` — List of plugins to disable *(defaults to `[]`)*
377
377
  * `:enable_plugins` — List of plugins to enable *(defaults to `[]`)*
378
+ * `:allow_lossy` — Allow precision option *(defaults to `false`)*
379
+ * `:precision` — Number of digits in the fractional part `0`..`20`, ignored in default/lossless mode *(defaults to `3`)*
378
380
 
379
381
  <!---</worker-options>-->
380
382
 
@@ -390,4 +392,4 @@ In separate file [CHANGELOG.markdown](CHANGELOG.markdown).
390
392
 
391
393
  ## Copyright
392
394
 
393
- Copyright (c) 2012-2022 Ivan Kuchin. See [LICENSE.txt](LICENSE.txt) for details.
395
+ Copyright (c) 2012-2024 Ivan Kuchin. See [LICENSE.txt](LICENSE.txt) for details.
data/image_optim.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'image_optim'
5
- s.version = '0.31.2'
5
+ s.version = '0.31.4'
6
6
  s.summary = %q{Command line tool and ruby interface to optimize (lossless compress, optionally lossy) jpeg, png, gif and svg images using external utilities (advpng, gifsicle, jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, oxipng, pngcrush, pngout, pngquant, svgo)}
7
7
  s.homepage = "https://github.com/toy/#{s.name}"
8
8
  s.authors = ['Ivan Kuchin']
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
15
15
  'changelog_uri' => "https://github.com/toy/#{s.name}/blob/master/CHANGELOG.markdown",
16
16
  'documentation_uri' => "https://www.rubydoc.info/gems/#{s.name}/#{s.version}",
17
17
  'source_code_uri' => "https://github.com/toy/#{s.name}",
18
- }
18
+ } if s.respond_to?(:metadata=)
19
19
 
20
20
  s.files = `git ls-files`.split("\n")
21
21
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -42,7 +42,7 @@ class ImageOptim
42
42
  if optimized
43
43
  tmp = FSPath.temp_file_path(digest, @cache_dir)
44
44
  FileUtils.mv(optimized, tmp)
45
- tmp.chmod(0o666 & ~File.umask)
45
+ tmp.chmod(~File.umask & 0o666)
46
46
  tmp.rename(cached)
47
47
  cached_path = CachePath.convert(cached)
48
48
 
@@ -33,7 +33,7 @@ class ImageOptim
33
33
  end
34
34
  return {} unless File.size?(full_path)
35
35
 
36
- config = YAML.load_file(full_path)
36
+ config = load_yaml_file(full_path)
37
37
  unless config.is_a?(Hash)
38
38
  fail "expected hash, got #{config.inspect}"
39
39
  end
@@ -43,6 +43,14 @@ class ImageOptim
43
43
  warn "exception when reading #{full_path}: #{e}"
44
44
  {}
45
45
  end
46
+
47
+ def load_yaml_file(path)
48
+ if YAML.respond_to?(:safe_load_file)
49
+ YAML.safe_load_file(path, permitted_classes: [Range])
50
+ else
51
+ YAML.load_file(path)
52
+ end
53
+ end
46
54
  end
47
55
 
48
56
  # Merge config from files with passed options
@@ -34,8 +34,8 @@ class ImageOptim
34
34
  def help
35
35
  text = super
36
36
 
37
- # reserve one column
38
- columns = terminal_columns - 1
37
+ # reserve one column and limit to 120
38
+ columns = [terminal_columns - 1, 120].min
39
39
  # 1 for distance between summary and description
40
40
  # 2 for additional indent
41
41
  wrapped_indent = summary_indent + (' ' * (summary_width + 1 + 2))
@@ -43,20 +43,7 @@ class ImageOptim
43
43
  # don't try to wrap if there is too little space for description
44
44
  return text if wrapped_width < 20
45
45
 
46
- wrapped = ''.dup
47
- text.split("\n").each do |line|
48
- if line.length <= columns
49
- wrapped << line << "\n"
50
- else
51
- indented = line =~ /^\s/
52
- wrapped << line.slice!(wrap_regex(columns)) << "\n"
53
- line.scan(wrap_regex(wrapped_width)) do |part|
54
- wrapped << wrapped_indent if indented
55
- wrapped << part << "\n"
56
- end
57
- end
58
- end
59
- wrapped
46
+ wrapped_text(text, wrapped_width, wrapped_indent, columns)
60
47
  end
61
48
 
62
49
  private
@@ -66,6 +53,27 @@ class ImageOptim
66
53
  stty_columns ? stty_columns.to_i : `tput cols`.to_i
67
54
  end
68
55
 
56
+ def wrapped_text(text, wrapped_width, wrapped_indent, columns)
57
+ wrapped = []
58
+ text.split("\n").each do |line|
59
+ if line.length <= columns
60
+ wrapped << line << "\n"
61
+ else
62
+ wrapped << line.slice!(wrap_regex(columns)).rstrip << "\n"
63
+ if line =~ /^\s/
64
+ line.scan(wrap_regex(wrapped_width)) do |part|
65
+ wrapped << wrapped_indent << part.rstrip << "\n"
66
+ end
67
+ else
68
+ line.scan(wrap_regex(columns)) do |part|
69
+ wrapped << part.rstrip << "\n"
70
+ end
71
+ end
72
+ end
73
+ end
74
+ wrapped.join(nil)
75
+ end
76
+
69
77
  def wrap_regex(width)
70
78
  /.*?.{1,#{width}}(?:\s|\z)/
71
79
  end
@@ -96,30 +104,25 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
96
104
  |
97
105
  TEXT
98
106
 
99
- op.on('--config-paths PATH1,PATH2', Array, 'Config paths to use instead of ' \
100
- 'default ones') do |paths|
107
+ op.on('--config-paths PATH1,PATH2', Array, 'Config paths to use instead of default ones') do |paths|
101
108
  options[:config_paths] = paths
102
109
  end
103
110
 
104
111
  op.separator nil
105
112
 
106
- op.on('-r', '-R', '--recursive', 'Recursively scan directories ' \
107
- 'for images') do |recursive|
113
+ op.on('-r', '-R', '--recursive', 'Recursively scan directories for images') do |recursive|
108
114
  options[:recursive] = recursive
109
115
  end
110
116
 
111
- op.on("--exclude-dir 'GLOB'", 'Glob for excluding directories ' \
112
- '(defaults to .*)') do |glob|
117
+ op.on("--exclude-dir 'GLOB'", 'Glob for excluding directories (defaults to .*)') do |glob|
113
118
  options[:exclude_dir_glob] = glob
114
119
  end
115
120
 
116
- op.on("--exclude-file 'GLOB'", 'Glob for excluding files ' \
117
- '(defaults to .*)') do |glob|
121
+ op.on("--exclude-file 'GLOB'", 'Glob for excluding files (defaults to .*)') do |glob|
118
122
  options[:exclude_file_glob] = glob
119
123
  end
120
124
 
121
- op.on("--exclude 'GLOB'", 'Set glob for excluding both directories and ' \
122
- 'files') do |glob|
125
+ op.on("--exclude 'GLOB'", 'Set glob for excluding both directories and files') do |glob|
123
126
  options[:exclude_file_glob] = options[:exclude_dir_glob] = glob
124
127
  end
125
128
 
@@ -129,41 +132,42 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
129
132
  options[:show_progress] = show_progress
130
133
  end
131
134
 
132
- op.on('--[no-]threads N', Integer, 'Number of threads or disable ' \
133
- '(defaults to number of processors)') do |threads|
135
+ op.on('--[no-]threads N', Integer, 'Number of threads or disable (defaults to number of processors)') do |threads|
134
136
  options[:threads] = threads
135
137
  end
136
138
 
137
- op.on('--[no-]nice N', Integer, 'Nice level, priority of all used tools ' \
138
- 'with higher value meaning lower priority, in range -20..19, negative ' \
139
- 'values can be set only if run by root user (defaults to 10)') do |nice|
139
+ op.on(
140
+ '--[no-]nice N',
141
+ Integer,
142
+ 'Nice level, priority of all used tools with higher value meaning lower priority, in range -20..19, negative ' \
143
+ 'values can be set only if run by root user (defaults to 10)'
144
+ ) do |nice|
140
145
  options[:nice] = nice
141
146
  end
142
147
 
143
- op.on('--[no-]pack', 'Require image_optim_pack or disable it, ' \
144
- 'by default image_optim_pack will be used if available, ' \
145
- 'will turn on skip-missing-workers unless explicitly disabled') do |pack|
148
+ op.on(
149
+ '--[no-]pack',
150
+ 'Require image_optim_pack or disable it, by default image_optim_pack will be used if available, will turn on ' \
151
+ 'skip-missing-workers unless explicitly disabled'
152
+ ) do |pack|
146
153
  options[:pack] = pack
147
154
  end
148
155
 
149
156
  op.separator nil
150
157
  op.separator ' Caching:'
151
158
 
152
- op.on('--cache-dir DIR', 'Cache optimized images ' \
153
- 'into the specified directory') do |cache_dir|
159
+ op.on('--cache-dir DIR', 'Cache optimized images into the specified directory') do |cache_dir|
154
160
  options[:cache_dir] = cache_dir
155
161
  end
156
162
 
157
- op.on('--cache-worker-digests', 'Cache worker digests ' \
158
- '(updating workers invalidates cache)') do |cache_worker_digests|
163
+ op.on('--cache-worker-digests', 'Cache worker digests (updating workers invalidates cache)') do |cache_worker_digests|
159
164
  options[:cache_worker_digests] = cache_worker_digests
160
165
  end
161
166
 
162
167
  op.separator nil
163
168
  op.separator ' Disabling workers:'
164
169
 
165
- op.on('--[no-]skip-missing-workers', 'Skip workers with missing or ' \
166
- 'problematic binaries') do |skip|
170
+ op.on('--[no-]skip-missing-workers', 'Skip workers with missing or problematic binaries') do |skip|
167
171
  options[:skip_missing_workers] = skip
168
172
  end
169
173
 
@@ -177,8 +181,7 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
177
181
  op.separator nil
178
182
  op.separator ' Worker options:'
179
183
 
180
- op.on('--allow-lossy', 'Allow lossy workers and ' \
181
- 'optimizations') do |allow_lossy|
184
+ op.on('--allow-lossy', 'Allow lossy workers and optimizations') do |allow_lossy|
182
185
  options[:allow_lossy] = allow_lossy
183
186
  end
184
187
 
@@ -202,13 +205,13 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
202
205
  type, marking = case
203
206
  when [TrueClass, FalseClass, ImageOptim::TrueFalseNil].include?(type)
204
207
  [type, 'B']
205
- when Integer >= type
208
+ when type <= Integer
206
209
  [Integer, 'N']
207
- when Array >= type
210
+ when type <= Array
208
211
  [Array, 'a,b,c']
209
- when String >= type
212
+ when type <= String
210
213
  [String, 'S']
211
- when ImageOptim::NonNegativeIntegerRange == type
214
+ when type == ImageOptim::NonNegativeIntegerRange
212
215
  [type, 'M-N']
213
216
  else
214
217
  fail "Unknown type #{type}"
@@ -229,9 +232,12 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
229
232
  op.separator nil
230
233
  op.separator ' Common options:'
231
234
 
232
- op.on_tail('-v', '--verbose', 'Verbose output (show global and worker ' \
233
- 'config, binary resolution log, information about each tool invocation, ' \
234
- 'backtrace of exception)') do
235
+ op.on_tail(
236
+ '-v',
237
+ '--verbose',
238
+ 'Verbose output (show global and worker config, binary resolution log, information about each tool invocation, ' \
239
+ 'backtrace of exception)'
240
+ ) do
235
241
  options[:verbose] = true
236
242
  end
237
243
 
@@ -38,7 +38,7 @@ class ImageOptim
38
38
  if size_a == size_b
39
39
  "------ #{Space::EMPTY_SPACE}"
40
40
  else
41
- percent = 100 - (100.0 * size_b / size_a)
41
+ percent = 100 - (size_b * 100.0 / size_a)
42
42
  space = Space.space(size_a - size_b)
43
43
  format('%5.2f%% %s', percent, space)
44
44
  end
@@ -6,7 +6,7 @@ class ImageOptim
6
6
  SIZE_SYMBOLS = %w[B K M G T P E].freeze
7
7
  BASE = 1024.0
8
8
  PRECISION = 1
9
- LENGTH = 4 + PRECISION + 1
9
+ LENGTH = PRECISION + 4 + 1
10
10
 
11
11
  EMPTY_SPACE = ' ' * LENGTH
12
12
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'image_optim/option_helpers'
3
4
  require 'image_optim/worker'
4
5
 
5
6
  class ImageOptim
@@ -16,6 +17,24 @@ class ImageOptim
16
17
  Array(v).map(&:to_s)
17
18
  end
18
19
 
20
+ ALLOW_LOSSY_OPTION =
21
+ option(:allow_lossy, false, 'Allow precision option'){ |v| !!v }
22
+
23
+ PRECISION_OPTION =
24
+ option(:precision, 3, 'Number of digits in the fractional part ' \
25
+ '`0`..`20`, ignored in default/lossless mode') \
26
+ do |v, opt_def|
27
+ if allow_lossy
28
+ OptionHelpers.limit_with_range(v.to_i, 0..20)
29
+ else
30
+ if v != opt_def.default
31
+ warn "#{self.class.bin_sym} #{opt_def.name} #{v} ignored " \
32
+ 'in default/lossless mode'
33
+ end
34
+ opt_def.default
35
+ end
36
+ end
37
+
19
38
  def optimize(src, dst, options = {})
20
39
  args = %W[
21
40
  --input #{src}
@@ -27,6 +46,7 @@ class ImageOptim
27
46
  enable_plugins.each do |plugin_name|
28
47
  args.unshift "--enable=#{plugin_name}"
29
48
  end
49
+ args.unshift "--precision=#{precision}" if allow_lossy
30
50
  execute(:svgo, args, options) && optimized?(src, dst)
31
51
  end
32
52
  end
@@ -51,8 +51,21 @@ def update_readme(text)
51
51
  end
52
52
 
53
53
  readme = File.read(README_FILE)
54
- if (readme = update_readme(readme))
55
- File.write(README_FILE, readme)
54
+ program_name = File.basename($PROGRAM_NAME, '.*')
55
+ case ARGV
56
+ when []
57
+ if (readme = update_readme(readme))
58
+ File.write(README_FILE, readme)
59
+ else
60
+ abort 'Did not update worker options'
61
+ end
62
+ when %w[-n]
63
+ updated = update_readme(readme)
64
+ unless updated == readme
65
+ puts "Run #{program_name} to update work options in readme"
66
+ IO.popen('diff -u README.markdown -', 'w'){ |f| f << updated }
67
+ exit 1
68
+ end
56
69
  else
57
- abort 'Did not update worker options'
70
+ abort "#{program_name} [-n] (-n to change exit code instead of updating)"
58
71
  end
@@ -0,0 +1,3 @@
1
+ range: !ruby/range 80..99
2
+ number: 3
3
+ string: foo
@@ -223,7 +223,7 @@ describe ImageOptim::Config do
223
223
  with(path).and_return(full_path)
224
224
  expect(File).to receive(:size?).
225
225
  with(full_path).and_return(true)
226
- expect(YAML).to receive(:load_file).
226
+ expect(IOConfig).to receive(:load_yaml_file).
227
227
  with(full_path).and_return(stringified)
228
228
 
229
229
  expect(IOConfig.read_options(path)).to eq(symbolized)
@@ -235,7 +235,7 @@ describe ImageOptim::Config do
235
235
  with(path).and_return(full_path)
236
236
  expect(File).to receive(:size?).
237
237
  with(full_path).and_return(true)
238
- expect(YAML).to receive(:load_file).
238
+ expect(IOConfig).to receive(:load_yaml_file).
239
239
  with(full_path).and_return([:config])
240
240
 
241
241
  expect(IOConfig.read_options(path)).to eq({})
@@ -247,10 +247,46 @@ describe ImageOptim::Config do
247
247
  with(path).and_return(full_path)
248
248
  expect(File).to receive(:size?).
249
249
  with(full_path).and_return(true)
250
- expect(YAML).to receive(:load_file).
250
+ expect(IOConfig).to receive(:load_yaml_file).
251
251
  with(full_path).and_raise
252
252
 
253
253
  expect(IOConfig.read_options(path)).to eq({})
254
254
  end
255
255
  end
256
+
257
+ describe '.load_yaml_file' do
258
+ describe 'selecting method' do
259
+ let(:path){ 'foo' }
260
+ let(:result){ 'bar' }
261
+ let(:yaml){ double }
262
+
263
+ before do
264
+ stub_const('YAML', yaml)
265
+ end
266
+
267
+ it 'uses YAML.safe_load_file if available' do
268
+ expect(yaml).to receive(:safe_load_file).
269
+ with(path, permitted_classes: [Range]).and_return(result)
270
+
271
+ expect(IOConfig.load_yaml_file(path)).to eq(result)
272
+ end
273
+
274
+ it 'uses YAML.load if safe_load_file is not available' do
275
+ expect(yaml).to receive(:load_file).
276
+ with(path).and_return(result)
277
+
278
+ expect(IOConfig.load_yaml_file(path)).to eq(result)
279
+ end
280
+ end
281
+
282
+ it 'handles yaml that includes `!ruby/range`' do
283
+ path = 'spec/files/config_with_range.yaml'
284
+
285
+ expect(IOConfig.load_yaml_file(path)).to eq({
286
+ 'range' => 80..99,
287
+ 'number' => 3,
288
+ 'string' => 'foo',
289
+ })
290
+ end
291
+ end
256
292
  end
@@ -101,7 +101,16 @@ describe ImageOptim::Runner::OptionParser do
101
101
  allow(parser).to receive(:terminal_columns).and_return(80)
102
102
 
103
103
  expect(parser.help.split("\n")).
104
- to all(satisfy{ |line| line.length <= 80 })
104
+ to all(satisfy{ |line| line.length < 80 })
105
+ end
106
+
107
+ it 'wraps texts even for too wide terminals' do
108
+ parser = OptionParser.new({})
109
+
110
+ allow(parser).to receive(:terminal_columns).and_return(1000)
111
+
112
+ expect(parser.help.split("\n")).
113
+ to all(satisfy{ |line| line.length <= 120 })
105
114
  end
106
115
  end
107
116
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'image_optim/worker/svgo'
5
+
6
+ describe ImageOptim::Worker::Svgo do
7
+ describe 'precision option' do
8
+ describe 'default' do
9
+ subject{ described_class::PRECISION_OPTION.default }
10
+
11
+ it{ is_expected.to eq(3) }
12
+ end
13
+
14
+ describe 'value' do
15
+ let(:subject){ described_class.new(ImageOptim.new, options).precision }
16
+
17
+ context 'when lossy not allowed' do
18
+ context 'by default' do
19
+ let(:options){ {} }
20
+
21
+ it{ is_expected.to eq(3) }
22
+ end
23
+
24
+ context 'when value is passed through options' do
25
+ let(:options){ {precision: 5} }
26
+
27
+ it 'warns and keeps default' do
28
+ expect_any_instance_of(described_class).
29
+ to receive(:warn).with(%r{ignored in default/lossless mode})
30
+ is_expected.to eq(3)
31
+ end
32
+ end
33
+ end
34
+
35
+ context 'when lossy allowed' do
36
+ context 'by default' do
37
+ let(:options){ {allow_lossy: true} }
38
+
39
+ it{ is_expected.to eq(3) }
40
+ end
41
+
42
+ context 'when value is passed through options' do
43
+ let(:options){ {allow_lossy: true, precision: 5} }
44
+
45
+ it 'sets the value without warning' do
46
+ expect_any_instance_of(described_class).not_to receive(:warn)
47
+ is_expected.to eq(5)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -121,9 +121,9 @@ describe ImageOptim do
121
121
  it 'sends timeout to every worker' do
122
122
  some_path = instance_of(ImageOptim::Path)
123
123
 
124
- expect(workers[0]).to receive(:optimize).with(some_path, some_path, timeout: timer)
125
- expect(workers[1]).to receive(:optimize).with(some_path, some_path, timeout: timer)
126
- expect(workers[2]).to receive(:optimize).with(some_path, some_path, timeout: timer)
124
+ expect(workers[0]).to receive(:optimize).with(some_path, some_path, {timeout: timer})
125
+ expect(workers[1]).to receive(:optimize).with(some_path, some_path, {timeout: timer})
126
+ expect(workers[2]).to receive(:optimize).with(some_path, some_path, {timeout: timer})
127
127
 
128
128
  image_optim.optimize_image(path)
129
129
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: image_optim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.2
4
+ version: 0.31.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Kuchin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-27 00:00:00.000000000 Z
11
+ date: 2024-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fspath
@@ -173,8 +173,8 @@ executables:
173
173
  extensions: []
174
174
  extra_rdoc_files: []
175
175
  files:
176
+ - ".github/dependabot.yml"
176
177
  - ".github/workflows/check.yml"
177
- - ".github/workflows/codeql.yml"
178
178
  - ".github/workflows/rubocop.yml"
179
179
  - ".gitignore"
180
180
  - ".pre-commit-hooks.yaml"
@@ -232,6 +232,7 @@ files:
232
232
  - script/template/worker_analysis.erb
233
233
  - script/update_worker_options_in_readme
234
234
  - script/worker_analysis
235
+ - spec/files/config_with_range.yaml
235
236
  - spec/image_optim/bin_resolver/comparable_condition_spec.rb
236
237
  - spec/image_optim/bin_resolver/simple_version_spec.rb
237
238
  - spec/image_optim/bin_resolver_spec.rb
@@ -255,6 +256,7 @@ files:
255
256
  - spec/image_optim/worker/optipng_spec.rb
256
257
  - spec/image_optim/worker/oxipng_spec.rb
257
258
  - spec/image_optim/worker/pngquant_spec.rb
259
+ - spec/image_optim/worker/svgo_spec.rb
258
260
  - spec/image_optim/worker_spec.rb
259
261
  - spec/image_optim_spec.rb
260
262
  - spec/images/broken_jpeg
@@ -292,7 +294,7 @@ licenses:
292
294
  metadata:
293
295
  bug_tracker_uri: https://github.com/toy/image_optim/issues
294
296
  changelog_uri: https://github.com/toy/image_optim/blob/master/CHANGELOG.markdown
295
- documentation_uri: https://www.rubydoc.info/gems/image_optim/0.31.2
297
+ documentation_uri: https://www.rubydoc.info/gems/image_optim/0.31.4
296
298
  source_code_uri: https://github.com/toy/image_optim
297
299
  post_install_message: |
298
300
  Rails image assets optimization is extracted into image_optim_rails gem
@@ -311,7 +313,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
311
313
  - !ruby/object:Gem::Version
312
314
  version: '0'
313
315
  requirements: []
314
- rubygems_version: 3.3.26
316
+ rubygems_version: 3.5.23
315
317
  signing_key:
316
318
  specification_version: 4
317
319
  summary: Command line tool and ruby interface to optimize (lossless compress, optionally
@@ -319,6 +321,7 @@ summary: Command line tool and ruby interface to optimize (lossless compress, op
319
321
  jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, oxipng, pngcrush,
320
322
  pngout, pngquant, svgo)
321
323
  test_files:
324
+ - spec/files/config_with_range.yaml
322
325
  - spec/image_optim/bin_resolver/comparable_condition_spec.rb
323
326
  - spec/image_optim/bin_resolver/simple_version_spec.rb
324
327
  - spec/image_optim/bin_resolver_spec.rb
@@ -342,6 +345,7 @@ test_files:
342
345
  - spec/image_optim/worker/optipng_spec.rb
343
346
  - spec/image_optim/worker/oxipng_spec.rb
344
347
  - spec/image_optim/worker/pngquant_spec.rb
348
+ - spec/image_optim/worker/svgo_spec.rb
345
349
  - spec/image_optim/worker_spec.rb
346
350
  - spec/image_optim_spec.rb
347
351
  - spec/images/broken_jpeg
@@ -1,30 +0,0 @@
1
- name: codeql
2
- on:
3
- push:
4
- pull_request:
5
- schedule:
6
- - cron: '43 20 * * 0'
7
-
8
- jobs:
9
- analyze:
10
- runs-on: ubuntu-latest
11
- permissions:
12
- actions: read
13
- contents: read
14
- security-events: write
15
-
16
- strategy:
17
- fail-fast: false
18
- matrix:
19
- language: [ 'ruby' ]
20
-
21
- steps:
22
- - uses: actions/checkout@v2
23
-
24
- - uses: github/codeql-action/init@v1
25
- with:
26
- languages: ${{ matrix.language }}
27
-
28
- - uses: github/codeql-action/autobuild@v1
29
-
30
- - uses: github/codeql-action/analyze@v1