image_optim 0.26.4 → 0.29.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.
- checksums.yaml +4 -4
- data/.appveyor.yml +2 -0
- data/.pre-commit-hooks.yaml +9 -0
- data/.rubocop.yml +31 -12
- data/.travis.yml +17 -15
- data/CHANGELOG.markdown +26 -0
- data/CONTRIBUTING.markdown +4 -1
- data/Gemfile +0 -2
- data/LICENSE.txt +1 -1
- data/README.markdown +8 -3
- data/image_optim.gemspec +6 -5
- data/lib/image_optim.rb +0 -6
- data/lib/image_optim/bin_resolver/bin.rb +3 -1
- data/lib/image_optim/bin_resolver/comparable_condition.rb +1 -0
- data/lib/image_optim/cache_path.rb +19 -2
- data/lib/image_optim/config.rb +4 -6
- data/lib/image_optim/optimized_path.rb +1 -1
- data/lib/image_optim/path.rb +29 -6
- data/lib/image_optim/runner/option_parser.rb +2 -0
- data/lib/image_optim/worker.rb +6 -15
- data/lib/image_optim/worker/class_methods.rb +2 -0
- data/lib/image_optim/worker/jpegoptim.rb +6 -4
- data/lib/image_optim/worker/jpegrecompress.rb +15 -0
- data/lib/image_optim/worker/optipng.rb +2 -2
- data/lib/image_optim/worker/pngcrush.rb +2 -2
- data/lib/image_optim/worker/pngquant.rb +1 -0
- data/script/update_worker_options_in_readme +1 -1
- data/script/worker_analysis +16 -15
- data/spec/image_optim/cache_path_spec.rb +69 -26
- data/spec/image_optim/cache_spec.rb +3 -1
- data/spec/image_optim/path_spec.rb +63 -24
- data/spec/image_optim/worker/jpegrecompress_spec.rb +32 -0
- data/spec/image_optim_spec.rb +5 -4
- data/spec/images/quant/generate +2 -2
- metadata +29 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9035f1f635ab728b4622dc5e99d2f5e9f96eab86ea35038c4037587fd3ca90d7
|
|
4
|
+
data.tar.gz: d24f6305c1461483d9a67a9338c4e9385ef9282070aecdc53cf0b79e7bffb5ed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3501154596158ee61209215d7c3a4df1b82d7384d8b54248a16992e5897701eb1e1c10bca9bbbe92312f728e403544dfa6f0dd3c3dfc33918026497550f72ef4
|
|
7
|
+
data.tar.gz: 72ef8a7dacc6a7cc8850d996a716a5df2098cbe47e91f58a051e95664a83494346c0daa03f61384f4bd0c6674a65e4c383ac931863ef31ffed2cc6a8d57ef4a3
|
data/.appveyor.yml
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -2,6 +2,7 @@ AllCops:
|
|
|
2
2
|
Exclude:
|
|
3
3
|
- '*.gemspec'
|
|
4
4
|
- 'vendor/**/*'
|
|
5
|
+
NewCops: enable
|
|
5
6
|
|
|
6
7
|
Bundler/OrderedGems:
|
|
7
8
|
Enabled: false
|
|
@@ -9,6 +10,9 @@ Bundler/OrderedGems:
|
|
|
9
10
|
Layout/AccessModifierIndentation:
|
|
10
11
|
EnforcedStyle: outdent
|
|
11
12
|
|
|
13
|
+
Layout/AssignmentIndentation:
|
|
14
|
+
Enabled: false
|
|
15
|
+
|
|
12
16
|
Layout/CaseIndentation:
|
|
13
17
|
EnforcedStyle: end
|
|
14
18
|
|
|
@@ -18,18 +22,18 @@ Layout/DotPosition:
|
|
|
18
22
|
Layout/EndAlignment:
|
|
19
23
|
EnforcedStyleAlignWith: variable
|
|
20
24
|
|
|
21
|
-
Layout/
|
|
25
|
+
Layout/FirstArrayElementIndentation:
|
|
22
26
|
EnforcedStyle: consistent
|
|
23
27
|
|
|
24
|
-
Layout/
|
|
25
|
-
Enabled: false
|
|
26
|
-
|
|
27
|
-
Layout/IndentFirstHashElement:
|
|
28
|
+
Layout/FirstHashElementIndentation:
|
|
28
29
|
EnforcedStyle: consistent
|
|
29
30
|
|
|
30
|
-
Layout/
|
|
31
|
+
Layout/HeredocIndentation:
|
|
31
32
|
Enabled: false
|
|
32
33
|
|
|
34
|
+
Layout/LineLength:
|
|
35
|
+
Max: 120
|
|
36
|
+
|
|
33
37
|
Layout/RescueEnsureAlignment:
|
|
34
38
|
Enabled: false
|
|
35
39
|
|
|
@@ -47,10 +51,10 @@ Lint/AmbiguousBlockAssociation:
|
|
|
47
51
|
Lint/NestedPercentLiteral:
|
|
48
52
|
Enabled: false
|
|
49
53
|
|
|
50
|
-
Lint/
|
|
54
|
+
Lint/RedundantRequireStatement:
|
|
51
55
|
Enabled: false
|
|
52
56
|
|
|
53
|
-
Lint/
|
|
57
|
+
Lint/RedundantSplatExpansion:
|
|
54
58
|
Enabled: false
|
|
55
59
|
|
|
56
60
|
Metrics/AbcSize:
|
|
@@ -68,19 +72,19 @@ Metrics/ClassLength:
|
|
|
68
72
|
Metrics/CyclomaticComplexity:
|
|
69
73
|
Max: 11
|
|
70
74
|
|
|
71
|
-
Metrics/LineLength:
|
|
72
|
-
Max: 120
|
|
73
|
-
|
|
74
75
|
Metrics/MethodLength:
|
|
75
76
|
Max: 25
|
|
76
77
|
|
|
77
78
|
Metrics/PerceivedComplexity:
|
|
78
|
-
Max:
|
|
79
|
+
Max: 10
|
|
79
80
|
|
|
80
81
|
Security/MarshalLoad:
|
|
81
82
|
Exclude:
|
|
82
83
|
- 'script/worker_analysis'
|
|
83
84
|
|
|
85
|
+
Style/AccessorGrouping:
|
|
86
|
+
Enabled: false
|
|
87
|
+
|
|
84
88
|
Style/Alias:
|
|
85
89
|
EnforcedStyle: prefer_alias_method
|
|
86
90
|
|
|
@@ -99,15 +103,30 @@ Style/ExpandPathArguments:
|
|
|
99
103
|
Style/FormatStringToken:
|
|
100
104
|
Enabled: false
|
|
101
105
|
|
|
106
|
+
Style/HashConversion:
|
|
107
|
+
Enabled: false
|
|
108
|
+
|
|
109
|
+
Style/HashEachMethods:
|
|
110
|
+
Enabled: true
|
|
111
|
+
|
|
102
112
|
Style/HashSyntax:
|
|
103
113
|
EnforcedStyle: hash_rockets
|
|
104
114
|
|
|
115
|
+
Style/HashTransformKeys:
|
|
116
|
+
Enabled: false
|
|
117
|
+
|
|
118
|
+
Style/HashTransformValues:
|
|
119
|
+
Enabled: false
|
|
120
|
+
|
|
105
121
|
Style/IfUnlessModifier:
|
|
106
122
|
Enabled: false
|
|
107
123
|
|
|
108
124
|
Style/NumericPredicate:
|
|
109
125
|
EnforcedStyle: comparison
|
|
110
126
|
|
|
127
|
+
Style/OptionalBooleanParameter:
|
|
128
|
+
Enabled: false
|
|
129
|
+
|
|
111
130
|
Style/ParallelAssignment:
|
|
112
131
|
Enabled: false
|
|
113
132
|
|
data/.travis.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
dist: xenial
|
|
2
2
|
language: ruby
|
|
3
3
|
cache:
|
|
4
4
|
bundler: true
|
|
@@ -6,38 +6,40 @@ cache:
|
|
|
6
6
|
- $(npm root)
|
|
7
7
|
- ~/bin
|
|
8
8
|
rvm:
|
|
9
|
-
- '1.8.7-p371'
|
|
10
9
|
- '1.9.3-p551'
|
|
11
10
|
- '2.0.0-p648'
|
|
12
11
|
- '2.1.10'
|
|
13
12
|
- '2.2.10'
|
|
14
13
|
- '2.3.8'
|
|
15
|
-
- '2.4.
|
|
16
|
-
- '2.5.
|
|
17
|
-
- '2.6.
|
|
18
|
-
- '
|
|
14
|
+
- '2.4.10'
|
|
15
|
+
- '2.5.9'
|
|
16
|
+
- '2.6.7'
|
|
17
|
+
- '2.7.3'
|
|
18
|
+
- '3.0.1'
|
|
19
|
+
- 'jruby-9.2.14.0'
|
|
19
20
|
script:
|
|
20
21
|
- bundle exec image_optim --info
|
|
21
22
|
- bundle exec rspec
|
|
22
23
|
before_install:
|
|
24
|
+
- 'echo "gem: --no-ri --no-rdoc --no-document" > ~/.gemrc'
|
|
23
25
|
- gem install rubygems-update || gem install rubygems-update --version '< 3'
|
|
24
|
-
-
|
|
26
|
+
- update_rubygems
|
|
25
27
|
- gem install bundler || gem install bundler --version '< 2'
|
|
26
28
|
- nvm install stable
|
|
27
|
-
- mkdir -p ~/bin
|
|
28
29
|
- command -v svgo || npm install -g svgo
|
|
29
|
-
-
|
|
30
|
+
- mkdir -p ~/bin
|
|
31
|
+
- command -v pngout || curl -L "https://www.jonof.id.au/files/kenutils/pngout-20200115-linux.tar.gz" | tar -xz -C ~/bin --strip-components 2 --wildcards '*/amd64/pngout'
|
|
30
32
|
matrix:
|
|
31
33
|
include:
|
|
32
|
-
- env: CODECLIMATE
|
|
33
|
-
rvm: '2.4.
|
|
34
|
+
- env: CODECLIMATE=1
|
|
35
|
+
rvm: '2.4.10'
|
|
34
36
|
after_success: bundle exec codeclimate-test-reporter
|
|
35
|
-
- env: RUBOCOP
|
|
36
|
-
rvm: '2.4.
|
|
37
|
+
- env: RUBOCOP=1
|
|
38
|
+
rvm: '2.4.10'
|
|
37
39
|
script: bundle exec rubocop
|
|
38
40
|
before_install: gem update --system && gem install bundler
|
|
39
|
-
- env: CHECK_RUBIES
|
|
40
|
-
rvm: '2.4.
|
|
41
|
+
- env: CHECK_RUBIES=1
|
|
42
|
+
rvm: '2.4.10'
|
|
41
43
|
script: bundle exec travis_check_rubies
|
|
42
44
|
before_install: gem update --system && gem install bundler
|
|
43
45
|
addons:
|
data/CHANGELOG.markdown
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
## unreleased
|
|
4
4
|
|
|
5
|
+
## v0.29.0 (2021-04-28)
|
|
6
|
+
|
|
7
|
+
* Require at least ruby 1.9.3 [@toy](https://github.com/toy)
|
|
8
|
+
* Add support for use as [pre-commit](https://pre-commit.com/) hook [#192](https://github.com/toy/image_optim/pull/192) [@proinsias](https://github.com/proinsias)
|
|
9
|
+
* Fix `Path#copy_metadata` by rescuing also `Errno::EACCES` as it is done in `fileutils` [#187](https://github.com/toy/image_optim/issues/187) [@toy](https://github.com/toy)
|
|
10
|
+
* More precise regular expression for capturing svgo version [@toy](https://github.com/toy)
|
|
11
|
+
|
|
12
|
+
## v0.28.0 (2020-12-18)
|
|
13
|
+
|
|
14
|
+
* Fix and update list of markers in jpegoptim worker: allow to pass `com` instead of incorrect `comments` and add missing `xmp` and `none` [#188](https://github.com/toy/image_optim/issues/188) [@toy](https://github.com/toy)
|
|
15
|
+
* Add `--skip-if-larger` flag to pngquant worker. The pngquant worker already does this, but this will make it fail faster. [#125](https://github.com/toy/image_optim/pull/125) [#181](https://github.com/toy/image_optim/pull/181) [@iggant](https://github.com/iggant) [@oblakeerickson](https://github.com/oblakeerickson)
|
|
16
|
+
* Add `method` option for jpegrecompress, default to `ssim` [#102](https://github.com/toy/image_optim/issues/102) [#103](https://github.com/toy/image_optim/pull/103) [#182](https://github.com/toy/image_optim/pull/182) [@ramiroaraujo](https://github.com/ramiroaraujo) [@oblakeerickson](https://github.com/oblakeerickson)
|
|
17
|
+
|
|
18
|
+
## v0.27.1 (2020-09-30)
|
|
19
|
+
|
|
20
|
+
* Fixed atomic replacement for case when equal `File::Stat#dev` doesn't mean that file can be linked [#180](https://github.com/toy/image_optim/issues/180) [@toy](https://github.com/toy)
|
|
21
|
+
|
|
22
|
+
## v0.27.0 (2020-08-27)
|
|
23
|
+
|
|
24
|
+
* Use `.tmp` as the extension for temporary file if it needs to be created for atomic replacement [#178](https://github.com/toy/image_optim/issues/178) [@toy](https://github.com/toy)
|
|
25
|
+
* Don't create a temporary file in destination directory for atomic replacement if temporary directory is on same device as destination [#178](https://github.com/toy/image_optim/issues/178) [@toy](https://github.com/toy)
|
|
26
|
+
|
|
27
|
+
## v0.26.5 (2019-07-14)
|
|
28
|
+
|
|
29
|
+
* Remove deprecated `rubyforge_project` attribute from gemspec [rubygems/rubygems#2436](https://github.com/rubygems/rubygems/pull/2436) [@toy](https://github.com/toy)
|
|
30
|
+
|
|
5
31
|
## v0.26.4 (2019-05-23)
|
|
6
32
|
|
|
7
33
|
* Enable frozen string literals [@toy](https://github.com/toy)
|
data/CONTRIBUTING.markdown
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
* Create topic/feature branch: `git checkout -b awesome-changes`
|
|
4
4
|
* Commit…
|
|
5
|
-
* Add entry at the top of [ChangeLog](CHANGELOG.markdown)
|
|
5
|
+
* Add an entry at the top (after ## unreleased) of [ChangeLog](CHANGELOG.markdown), include:
|
|
6
|
+
* Issues (`[#123](https://github.com/toy/image_optim/issues/123)`)
|
|
7
|
+
* Pull requests (`[#123](https://github.com/toy/image_optim/pull/123)`)
|
|
8
|
+
* Authors (`[@octocat](https://github.com/octocat)`)
|
|
6
9
|
* Run tests: `bundle exec rspec`
|
|
7
10
|
* Check code style: `bundle exec rubocop`
|
|
8
11
|
* Rebase on master and squash commits to logical units
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.markdown
CHANGED
|
@@ -60,7 +60,7 @@ With version:
|
|
|
60
60
|
|
|
61
61
|
<!---<update-version>-->
|
|
62
62
|
```ruby
|
|
63
|
-
gem 'image_optim', '~> 0.
|
|
63
|
+
gem 'image_optim', '~> 0.29'
|
|
64
64
|
```
|
|
65
65
|
<!---</update-version>-->
|
|
66
66
|
|
|
@@ -277,6 +277,10 @@ optipng:
|
|
|
277
277
|
level: 5
|
|
278
278
|
```
|
|
279
279
|
|
|
280
|
+
### Temporary directory
|
|
281
|
+
|
|
282
|
+
`image_optim` uses standard ruby library for creating temporary files. Temporary directory can be changed using one of `TMPDIR`, `TMP` or `TEMP` environment variables.
|
|
283
|
+
|
|
280
284
|
## Options
|
|
281
285
|
|
|
282
286
|
* `:nice` — Nice level, priority of all used tools with higher value meaning lower priority, in range `-20..19`, negative values can be set only if run by root user *(defaults to `10`)*
|
|
@@ -306,12 +310,13 @@ Worker has no options
|
|
|
306
310
|
|
|
307
311
|
### jpegoptim:
|
|
308
312
|
* `:allow_lossy` — Allow limiting maximum quality *(defaults to `false`)*
|
|
309
|
-
* `:strip` — List of
|
|
313
|
+
* `:strip` — List of markers to strip: `:com`, `:exif`, `:iptc`, `:icc`, `:xmp`, `:none` or `:all` *(defaults to `:all`)*
|
|
310
314
|
* `:max_quality` — Maximum image quality factor `0`..`100`, ignored in default/lossless mode *(defaults to `100`)*
|
|
311
315
|
|
|
312
316
|
### jpegrecompress:
|
|
313
317
|
* `:allow_lossy` — Allow worker, it is always lossy *(defaults to `false`)*
|
|
314
318
|
* `:quality` — JPEG quality preset: `0` - low, `1` - medium, `2` - high, `3` - veryhigh *(defaults to `3`)*
|
|
319
|
+
* `:method` — Comparison Metric: `mpe` - Mean pixel error, `ssim` - Structural similarity, `ms-ssim` - Multi-scale structural similarity (slow!), `smallfry` - Linear-weighted BBCQ-like (may be patented) *(defaults to ssim)*
|
|
315
320
|
|
|
316
321
|
### jpegtran:
|
|
317
322
|
* `:copy_chunks` — Copy all chunks *(defaults to `false`)*
|
|
@@ -357,4 +362,4 @@ In separate file [CHANGELOG.markdown](CHANGELOG.markdown).
|
|
|
357
362
|
|
|
358
363
|
## Copyright
|
|
359
364
|
|
|
360
|
-
Copyright (c) 2012-
|
|
365
|
+
Copyright (c) 2012-2021 Ivan Kuchin. See [LICENSE.txt](LICENSE.txt) for details.
|
data/image_optim.gemspec
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = 'image_optim'
|
|
5
|
-
s.version = '0.
|
|
5
|
+
s.version = '0.29.0'
|
|
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, pngcrush, pngout, pngquant, svgo)}
|
|
7
|
-
s.homepage = "
|
|
7
|
+
s.homepage = "https://github.com/toy/#{s.name}"
|
|
8
8
|
s.authors = ['Ivan Kuchin']
|
|
9
9
|
s.license = 'MIT'
|
|
10
10
|
|
|
11
|
-
s.
|
|
11
|
+
s.required_ruby_version = '>= 1.9.3'
|
|
12
12
|
|
|
13
13
|
s.metadata = {
|
|
14
14
|
'bug_tracker_uri' => "https://github.com/toy/#{s.name}/issues",
|
|
@@ -35,7 +35,8 @@ EOF
|
|
|
35
35
|
|
|
36
36
|
s.add_development_dependency 'image_optim_pack', '~> 0.2', '>= 0.2.2'
|
|
37
37
|
s.add_development_dependency 'rspec', '~> 3.0'
|
|
38
|
-
if RUBY_VERSION >= '2.
|
|
39
|
-
s.add_development_dependency 'rubocop', '~> 0
|
|
38
|
+
if RUBY_VERSION >= '2.4' && !Gem.win_platform? && !defined?(JRUBY_VERSION)
|
|
39
|
+
s.add_development_dependency 'rubocop', '~> 1.0'
|
|
40
|
+
s.add_development_dependency 'rubocop-rspec', '~> 2.0'
|
|
40
41
|
end
|
|
41
42
|
end
|
data/lib/image_optim.rb
CHANGED
|
@@ -188,12 +188,6 @@ class ImageOptim
|
|
|
188
188
|
optimize_image_method?(method) || super
|
|
189
189
|
end
|
|
190
190
|
|
|
191
|
-
if RUBY_VERSION < '1.9'
|
|
192
|
-
def respond_to?(method, include_private = false)
|
|
193
|
-
optimize_image_method?(method) || super
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
191
|
# Version of image_optim gem spec loaded
|
|
198
192
|
def version
|
|
199
193
|
Gem.loaded_specs['image_optim'].version.to_s
|
|
@@ -14,9 +14,11 @@ class ImageOptim
|
|
|
14
14
|
# Holds bin name and path, gets version
|
|
15
15
|
class Bin
|
|
16
16
|
class UnknownVersion < Error; end
|
|
17
|
+
|
|
17
18
|
class BadVersion < Error; end
|
|
18
19
|
|
|
19
20
|
attr_reader :name, :path, :version
|
|
21
|
+
|
|
20
22
|
def initialize(name, path)
|
|
21
23
|
@name = name.to_sym
|
|
22
24
|
@path = path.to_s
|
|
@@ -110,7 +112,7 @@ class ImageOptim
|
|
|
110
112
|
when :gifsicle, :jpegoptim, :optipng
|
|
111
113
|
capture("#{escaped_path} --version 2> #{Path::NULL}")[/\d+(\.\d+)+/]
|
|
112
114
|
when :svgo, :pngquant
|
|
113
|
-
capture("#{escaped_path} --version 2>&1")[/\d+(\.\d+)+/]
|
|
115
|
+
capture("#{escaped_path} --version 2>&1")[/\A\d+(\.\d+)+/]
|
|
114
116
|
when :jhead, :'jpeg-recompress'
|
|
115
117
|
capture("#{escaped_path} -V 2> #{Path::NULL}")[/\d+(\.\d+)+/]
|
|
116
118
|
when :jpegtran
|
|
@@ -7,8 +7,25 @@ class ImageOptim
|
|
|
7
7
|
class CachePath < Path
|
|
8
8
|
# Atomic replace dst with self
|
|
9
9
|
def replace(dst)
|
|
10
|
-
dst = self.class.
|
|
11
|
-
|
|
10
|
+
dst = self.class.convert(dst)
|
|
11
|
+
tmpdir = [dirname, Path.new(Dir.tmpdir)].find do |dir|
|
|
12
|
+
dir.same_dev?(dst.dirname)
|
|
13
|
+
end
|
|
14
|
+
if tmpdir
|
|
15
|
+
begin
|
|
16
|
+
replace_using_tmp_file(dst, tmpdir)
|
|
17
|
+
rescue Errno::EXDEV
|
|
18
|
+
replace_using_tmp_file(dst, dst.dirname)
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
replace_using_tmp_file(dst, dst.dirname)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def replace_using_tmp_file(dst, tmpdir)
|
|
28
|
+
dst.temp_path_with_tmp_ext(tmpdir) do |temp|
|
|
12
29
|
copy(temp)
|
|
13
30
|
dst.copy_metadata(temp)
|
|
14
31
|
temp.rename(dst.to_s)
|
data/lib/image_optim/config.rb
CHANGED
|
@@ -15,9 +15,7 @@ class ImageOptim
|
|
|
15
15
|
|
|
16
16
|
# Global config path at `$XDG_CONFIG_HOME/image_optim.yml` (by default
|
|
17
17
|
# `~/.config/image_optim.yml`)
|
|
18
|
-
GLOBAL_PATH =
|
|
19
|
-
File.join(ENV['XDG_CONFIG_HOME'] || '~/.config', 'image_optim.yml')
|
|
20
|
-
end
|
|
18
|
+
GLOBAL_PATH = File.join(ENV['XDG_CONFIG_HOME'] || '~/.config', 'image_optim.yml')
|
|
21
19
|
|
|
22
20
|
# Local config path at `./.image_optim.yml`
|
|
23
21
|
LOCAL_PATH = './.image_optim.yml'
|
|
@@ -197,10 +195,10 @@ class ImageOptim
|
|
|
197
195
|
when /darwin9/
|
|
198
196
|
Cmd.capture 'hwprefs cpu_count'
|
|
199
197
|
when /darwin/
|
|
200
|
-
if (Cmd.capture 'which hwprefs')
|
|
201
|
-
Cmd.capture 'hwprefs thread_count'
|
|
202
|
-
else
|
|
198
|
+
if (Cmd.capture 'which hwprefs') == ''
|
|
203
199
|
Cmd.capture 'sysctl -n hw.ncpu'
|
|
200
|
+
else
|
|
201
|
+
Cmd.capture 'hwprefs thread_count'
|
|
204
202
|
end
|
|
205
203
|
when /linux/
|
|
206
204
|
Cmd.capture 'grep -c processor /proc/cpuinfo'
|
|
@@ -7,7 +7,7 @@ class ImageOptim
|
|
|
7
7
|
class OptimizedPath < DelegateClass(Path)
|
|
8
8
|
def initialize(path, original_or_size = nil)
|
|
9
9
|
path = Path.convert(path)
|
|
10
|
-
|
|
10
|
+
super(path)
|
|
11
11
|
if original_or_size.is_a?(Integer)
|
|
12
12
|
@original = path
|
|
13
13
|
@original_size = original_or_size
|
data/lib/image_optim/path.rb
CHANGED
|
@@ -41,7 +41,7 @@ class ImageOptim
|
|
|
41
41
|
dst.utime(stat.atime, stat.mtime) if time
|
|
42
42
|
begin
|
|
43
43
|
dst.chown(stat.uid, stat.gid)
|
|
44
|
-
rescue Errno::EPERM
|
|
44
|
+
rescue Errno::EPERM, Errno::EACCES
|
|
45
45
|
dst.chmod(stat.mode & 0o1777)
|
|
46
46
|
else
|
|
47
47
|
dst.chmod(stat.mode)
|
|
@@ -50,11 +50,16 @@ class ImageOptim
|
|
|
50
50
|
|
|
51
51
|
# Atomic replace dst with self
|
|
52
52
|
def replace(dst)
|
|
53
|
-
dst = self.class.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
dst = self.class.convert(dst)
|
|
54
|
+
if same_dev?(dst.dirname)
|
|
55
|
+
dst.copy_metadata(self)
|
|
56
|
+
begin
|
|
57
|
+
rename(dst.to_s)
|
|
58
|
+
rescue Errno::EXDEV
|
|
59
|
+
replace_using_tmp_file(dst)
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
replace_using_tmp_file(dst)
|
|
58
63
|
end
|
|
59
64
|
end
|
|
60
65
|
|
|
@@ -68,5 +73,23 @@ class ImageOptim
|
|
|
68
73
|
def self.convert(path)
|
|
69
74
|
path.is_a?(self) ? path : new(path)
|
|
70
75
|
end
|
|
76
|
+
|
|
77
|
+
protected
|
|
78
|
+
|
|
79
|
+
def same_dev?(other)
|
|
80
|
+
stat.dev == other.stat.dev
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def replace_using_tmp_file(dst)
|
|
84
|
+
dst.temp_path_with_tmp_ext(dst.dirname) do |temp|
|
|
85
|
+
move(temp)
|
|
86
|
+
dst.copy_metadata(temp)
|
|
87
|
+
temp.rename(dst.to_s)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def temp_path_with_tmp_ext(*args, &block)
|
|
92
|
+
self.class.temp_file_path([basename.to_s, '.tmp'], *args, &block)
|
|
93
|
+
end
|
|
71
94
|
end
|
|
72
95
|
end
|
data/lib/image_optim/worker.rb
CHANGED
|
@@ -142,21 +142,12 @@ class ImageOptim
|
|
|
142
142
|
# Run command defining environment, setting nice level, removing output and
|
|
143
143
|
# reraising signal exception
|
|
144
144
|
def run_command(cmd_args)
|
|
145
|
-
args =
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
].join(' ')
|
|
152
|
-
else
|
|
153
|
-
[
|
|
154
|
-
{'PATH' => @image_optim.env_path},
|
|
155
|
-
%W[nice -n #{@image_optim.nice}],
|
|
156
|
-
cmd_args,
|
|
157
|
-
{:out => Path::NULL, :err => Path::NULL},
|
|
158
|
-
].flatten
|
|
159
|
-
end
|
|
145
|
+
args = [
|
|
146
|
+
{'PATH' => @image_optim.env_path},
|
|
147
|
+
*%W[nice -n #{@image_optim.nice}],
|
|
148
|
+
*cmd_args,
|
|
149
|
+
{:out => Path::NULL, :err => Path::NULL},
|
|
150
|
+
]
|
|
160
151
|
Cmd.run(*args)
|
|
161
152
|
end
|
|
162
153
|
end
|
|
@@ -18,6 +18,7 @@ class ImageOptim
|
|
|
18
18
|
|
|
19
19
|
# Remember all classes inheriting from this one
|
|
20
20
|
def inherited(base)
|
|
21
|
+
super
|
|
21
22
|
@klasses << base
|
|
22
23
|
end
|
|
23
24
|
|
|
@@ -36,6 +37,7 @@ class ImageOptim
|
|
|
36
37
|
|
|
37
38
|
def option(name, default, type, description = nil, &proc)
|
|
38
39
|
attr_reader name
|
|
40
|
+
|
|
39
41
|
OptionDefinition.new(name, default, type, description, &proc).
|
|
40
42
|
tap{ |option_definition| option_definitions << option_definition }
|
|
41
43
|
end
|
|
@@ -11,14 +11,16 @@ class ImageOptim
|
|
|
11
11
|
option(:allow_lossy, false, 'Allow limiting maximum quality'){ |v| !!v }
|
|
12
12
|
|
|
13
13
|
STRIP_OPTION =
|
|
14
|
-
option(:strip, :all, Array, 'List of
|
|
15
|
-
'`:
|
|
14
|
+
option(:strip, :all, Array, 'List of markers to strip: '\
|
|
15
|
+
'`:com`, '\
|
|
16
16
|
'`:exif`, '\
|
|
17
17
|
'`:iptc`, '\
|
|
18
|
-
'`:icc
|
|
18
|
+
'`:icc`, '\
|
|
19
|
+
'`:xmp`, '\
|
|
20
|
+
'`:none` or '\
|
|
19
21
|
'`:all`') do |v|
|
|
20
22
|
values = Array(v).map(&:to_s)
|
|
21
|
-
known_values = %w[
|
|
23
|
+
known_values = %w[com exif iptc icc xmp none all]
|
|
22
24
|
unknown_values = values - known_values
|
|
23
25
|
unless unknown_values.empty?
|
|
24
26
|
warn "Unknown markers for jpegoptim: #{unknown_values.join(', ')}"
|
|
@@ -26,6 +26,20 @@ class ImageOptim
|
|
|
26
26
|
OptionHelpers.limit_with_range(v.to_i, 0...QUALITY_NAMES.length)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
METHOD_OPTION =
|
|
30
|
+
option(:method, 'ssim', 'Comparison Metric: '\
|
|
31
|
+
'`mpe` - Mean pixel error, '\
|
|
32
|
+
'`ssim` - Structural similarity, '\
|
|
33
|
+
'`ms-ssim` - Multi-scale structural similarity (slow!), '\
|
|
34
|
+
'`smallfry` - Linear-weighted BBCQ-like (may be patented)') do |v, opt_def|
|
|
35
|
+
if %w[mpe ssim ms-ssim smallfry].include? v
|
|
36
|
+
v
|
|
37
|
+
else
|
|
38
|
+
warn "Unknown method for jpegrecompress: #{v}"
|
|
39
|
+
opt_def.default
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
29
43
|
def used_bins
|
|
30
44
|
[:'jpeg-recompress']
|
|
31
45
|
end
|
|
@@ -38,6 +52,7 @@ class ImageOptim
|
|
|
38
52
|
def optimize(src, dst)
|
|
39
53
|
args = %W[
|
|
40
54
|
--quality #{QUALITY_NAMES[quality]}
|
|
55
|
+
--method #{method}
|
|
41
56
|
--no-copy
|
|
42
57
|
#{src}
|
|
43
58
|
#{dst}
|
|
@@ -39,8 +39,8 @@ class ImageOptim
|
|
|
39
39
|
#{dst}
|
|
40
40
|
]
|
|
41
41
|
args.unshift "-i#{interlace ? 1 : 0}" unless interlace.nil?
|
|
42
|
-
if resolve_bin!(:optipng).version >= '0.7'
|
|
43
|
-
args.unshift '-strip', 'all'
|
|
42
|
+
if strip && resolve_bin!(:optipng).version >= '0.7'
|
|
43
|
+
args.unshift '-strip', 'all'
|
|
44
44
|
end
|
|
45
45
|
execute(:optipng, *args) && optimized?(src, dst)
|
|
46
46
|
end
|
|
@@ -39,8 +39,8 @@ class ImageOptim
|
|
|
39
39
|
end
|
|
40
40
|
flags.push '-fix' if fix
|
|
41
41
|
flags.push '-brute' if brute
|
|
42
|
-
if resolve_bin!(:pngcrush).version >= '1.7.38'
|
|
43
|
-
flags.push '-blacken'
|
|
42
|
+
if blacken && resolve_bin!(:pngcrush).version >= '1.7.38'
|
|
43
|
+
flags.push '-blacken'
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
args = flags + %W[
|
|
@@ -18,7 +18,7 @@ def write_worker_options(io, klass)
|
|
|
18
18
|
klass.option_definitions.each do |option_definition|
|
|
19
19
|
line = "* `:#{option_definition.name}` — #{option_definition.description}"
|
|
20
20
|
unless line['(defaults']
|
|
21
|
-
line
|
|
21
|
+
line += " *(defaults to #{option_definition.default_description})*"
|
|
22
22
|
end
|
|
23
23
|
io.puts line
|
|
24
24
|
end
|
data/script/worker_analysis
CHANGED
|
@@ -137,13 +137,14 @@ class Analyser
|
|
|
137
137
|
# Delegate to worker with short id
|
|
138
138
|
class WorkerVariant < DelegateClass(ImageOptim::Worker)
|
|
139
139
|
attr_reader :name, :id, :cons_id, :required
|
|
140
|
+
|
|
140
141
|
def initialize(klass, image_optim, options)
|
|
141
142
|
@required = options.delete(:required)
|
|
142
143
|
@run_order = options.delete(:run_order)
|
|
143
144
|
allow_consecutive_on = Array(options.delete(:allow_consecutive_on))
|
|
144
145
|
@image_optim = image_optim
|
|
145
146
|
@name = klass.bin_sym.to_s + options_string(options)
|
|
146
|
-
|
|
147
|
+
super(klass.new(image_optim, options))
|
|
147
148
|
@id = klass.bin_sym.to_s + options_string(self.options)
|
|
148
149
|
@cons_id = [klass, allow_consecutive_on.map{ |key| [key, send(key)] }]
|
|
149
150
|
end
|
|
@@ -357,20 +358,18 @@ class Analyser
|
|
|
357
358
|
end
|
|
358
359
|
|
|
359
360
|
def flatten_animation(image)
|
|
360
|
-
run_cache[:flatten][image.digest] ||=
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
image
|
|
373
|
-
end
|
|
361
|
+
run_cache[:flatten][image.digest] ||= if image.image_format == :gif
|
|
362
|
+
flattened = image.temp_path
|
|
363
|
+
Cmd.run(*%W[
|
|
364
|
+
convert
|
|
365
|
+
#{image.image_format}:#{image}
|
|
366
|
+
-coalesce
|
|
367
|
+
-append
|
|
368
|
+
#{image.image_format}:#{flattened}
|
|
369
|
+
]) || fail("failed flattening of #{image}")
|
|
370
|
+
flattened
|
|
371
|
+
else
|
|
372
|
+
image
|
|
374
373
|
end
|
|
375
374
|
end
|
|
376
375
|
|
|
@@ -452,6 +451,7 @@ class Analyser
|
|
|
452
451
|
attr_reader :name
|
|
453
452
|
attr_reader :success_count
|
|
454
453
|
attr_reader :time, :avg_time
|
|
454
|
+
|
|
455
455
|
def initialize(name, steps)
|
|
456
456
|
@name = name
|
|
457
457
|
@success_count = steps.count(&:success)
|
|
@@ -465,6 +465,7 @@ class Analyser
|
|
|
465
465
|
end
|
|
466
466
|
|
|
467
467
|
attr_reader :name, :results, :ids2names
|
|
468
|
+
|
|
468
469
|
def initialize(name, results, ids2names)
|
|
469
470
|
@name = name.to_s
|
|
470
471
|
@results = results
|
|
@@ -8,52 +8,95 @@ describe ImageOptim::CachePath do
|
|
|
8
8
|
include CapabilityCheckHelpers
|
|
9
9
|
|
|
10
10
|
before do
|
|
11
|
+
stub_const('Path', ImageOptim::Path)
|
|
11
12
|
stub_const('CachePath', ImageOptim::CachePath)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
describe '#replace' do
|
|
15
|
-
let(:
|
|
16
|
-
let(:
|
|
16
|
+
let(:src_dir){ Path.temp_dir }
|
|
17
|
+
let(:src){ CachePath.temp_file_path(nil, src_dir) }
|
|
18
|
+
let(:dst){ Path.temp_file_path }
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
shared_examples 'replaces file' do
|
|
21
|
+
it 'moves data to destination' do
|
|
22
|
+
src.write('src')
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
src.replace(dst)
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
expect(dst.read).to eq('src')
|
|
27
|
+
end
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
it 'does not remove original file' do
|
|
30
|
+
src.replace(dst)
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
expect(src).to exist
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'preserves attributes of destination file' do
|
|
36
|
+
skip 'full file modes are not support' unless any_file_modes_allowed?
|
|
37
|
+
mode = 0o666
|
|
38
|
+
|
|
39
|
+
dst.chmod(mode)
|
|
40
|
+
|
|
41
|
+
src.replace(dst)
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
got = dst.stat.mode & 0o777
|
|
44
|
+
expect(got).to eq(mode), format('expected %04o, got %04o', mode, got)
|
|
45
|
+
end
|
|
35
46
|
|
|
36
|
-
|
|
47
|
+
it 'does not preserve mtime of destination file' do
|
|
48
|
+
time = src.mtime
|
|
37
49
|
|
|
38
|
-
|
|
50
|
+
dst.utime(time - 1000, time - 1000)
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
src.replace(dst)
|
|
53
|
+
|
|
54
|
+
expect(dst.mtime).to be >= time
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'changes inode of destination' do
|
|
58
|
+
skip 'inodes are not supported' unless inodes_supported?
|
|
59
|
+
expect{ src.replace(dst) }.to change{ dst.stat.ino }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'is using temporary file with .tmp extension' do
|
|
63
|
+
expect(src).to receive(:copy).with(having_attributes(:extname => '.tmp')).at_least(:once)
|
|
64
|
+
|
|
65
|
+
src.replace(dst)
|
|
66
|
+
end
|
|
42
67
|
end
|
|
43
68
|
|
|
44
|
-
|
|
45
|
-
|
|
69
|
+
context 'when src and dst are on same device' do
|
|
70
|
+
before do
|
|
71
|
+
allow_any_instance_of(File::Stat).to receive(:dev).and_return(0)
|
|
72
|
+
end
|
|
46
73
|
|
|
47
|
-
|
|
74
|
+
include_examples 'replaces file'
|
|
75
|
+
end
|
|
48
76
|
|
|
49
|
-
|
|
77
|
+
context 'when src and dst are on different devices' do
|
|
78
|
+
before do
|
|
79
|
+
allow_any_instance_of(File::Stat).to receive(:dev, &:__id__)
|
|
80
|
+
end
|
|
50
81
|
|
|
51
|
-
|
|
82
|
+
include_examples 'replaces file'
|
|
52
83
|
end
|
|
53
84
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
context 'when src and dst are on same device, but rename causes Errno::EXDEV' do
|
|
86
|
+
before do
|
|
87
|
+
allow_any_instance_of(File::Stat).to receive(:dev).and_return(0)
|
|
88
|
+
allow(described_class).to receive(:temp_file_path).and_call_original
|
|
89
|
+
expect(described_class).to receive(:temp_file_path).
|
|
90
|
+
with([dst.basename.to_s, '.tmp'], src.dirname).
|
|
91
|
+
and_wrap_original do |m, *args, &block|
|
|
92
|
+
m.call(*args) do |tmp|
|
|
93
|
+
expect(tmp).to receive(:rename).with(dst.to_s).and_raise(Errno::EXDEV)
|
|
94
|
+
block.call(tmp)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
include_examples 'replaces file'
|
|
57
100
|
end
|
|
58
101
|
end
|
|
59
102
|
end
|
|
@@ -64,6 +64,7 @@ describe ImageOptim::Cache do
|
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
# rubocop:disable Style/RedundantFetchBlock
|
|
67
68
|
shared_examples 'an enabled cache' do
|
|
68
69
|
context 'when cached file does not exist' do
|
|
69
70
|
describe :fetch do
|
|
@@ -101,7 +102,7 @@ describe ImageOptim::Cache do
|
|
|
101
102
|
expect(FileUtils).not_to receive(:mv)
|
|
102
103
|
expect(File).not_to receive(:rename)
|
|
103
104
|
|
|
104
|
-
expect(cache.fetch(original){}).to eq(cached)
|
|
105
|
+
expect(cache.fetch(original){ nil }).to eq(cached)
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
it 'returns nil when file is already optimized' do
|
|
@@ -116,6 +117,7 @@ describe ImageOptim::Cache do
|
|
|
116
117
|
end
|
|
117
118
|
end
|
|
118
119
|
end
|
|
120
|
+
# rubocop:enable Style/RedundantFetchBlock
|
|
119
121
|
|
|
120
122
|
context 'when cache is enabled (without worker digests)' do
|
|
121
123
|
let(:image_optim) do
|
|
@@ -61,45 +61,84 @@ describe ImageOptim::Path do
|
|
|
61
61
|
let(:src){ Path.temp_file_path }
|
|
62
62
|
let(:dst){ Path.temp_file_path }
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
shared_examples 'replaces file' do
|
|
65
|
+
it 'moves data to destination' do
|
|
66
|
+
src.write('src')
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
src.replace(dst)
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
expect(dst.read).to eq('src')
|
|
71
|
+
end
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
it 'removes original file' do
|
|
74
|
+
src.replace(dst)
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
expect(src).to_not exist
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'preserves attributes of destination file' do
|
|
80
|
+
skip 'full file modes are not support' unless any_file_modes_allowed?
|
|
81
|
+
mode = 0o666
|
|
82
|
+
|
|
83
|
+
dst.chmod(mode)
|
|
77
84
|
|
|
78
|
-
|
|
79
|
-
skip 'full file modes are not support' unless any_file_modes_allowed?
|
|
80
|
-
mode = 0o666
|
|
85
|
+
src.replace(dst)
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
got = dst.stat.mode & 0o777
|
|
88
|
+
expect(got).to eq(mode), format('expected %04o, got %04o', mode, got)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'does not preserve mtime of destination file' do
|
|
92
|
+
time = src.mtime
|
|
93
|
+
|
|
94
|
+
dst.utime(time - 1000, time - 1000)
|
|
83
95
|
|
|
84
|
-
|
|
96
|
+
src.replace(dst)
|
|
85
97
|
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
expect(dst.mtime).to be >= time
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'changes inode of destination' do
|
|
102
|
+
skip 'inodes are not supported' unless inodes_supported?
|
|
103
|
+
expect{ src.replace(dst) }.to change{ dst.stat.ino }
|
|
104
|
+
end
|
|
88
105
|
end
|
|
89
106
|
|
|
90
|
-
|
|
91
|
-
|
|
107
|
+
context 'when src and dst are on same device' do
|
|
108
|
+
before do
|
|
109
|
+
allow_any_instance_of(File::Stat).to receive(:dev).and_return(0)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
include_examples 'replaces file'
|
|
113
|
+
end
|
|
92
114
|
|
|
93
|
-
|
|
115
|
+
context 'when src and dst are on different devices' do
|
|
116
|
+
before do
|
|
117
|
+
allow_any_instance_of(File::Stat).to receive(:dev, &:__id__)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
include_examples 'replaces file'
|
|
94
121
|
|
|
95
|
-
|
|
122
|
+
it 'is using temporary file with .tmp extension' do
|
|
123
|
+
expect(src).to receive(:move).with(having_attributes(:extname => '.tmp'))
|
|
96
124
|
|
|
97
|
-
|
|
125
|
+
src.replace(dst)
|
|
126
|
+
end
|
|
98
127
|
end
|
|
99
128
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
129
|
+
context 'when src and dst are on same device, but rename causes Errno::EXDEV' do
|
|
130
|
+
before do
|
|
131
|
+
allow_any_instance_of(File::Stat).to receive(:dev).and_return(0)
|
|
132
|
+
expect(src).to receive(:rename).with(dst.to_s).once.and_raise(Errno::EXDEV)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
include_examples 'replaces file'
|
|
136
|
+
|
|
137
|
+
it 'is using temporary file with .tmp extension' do
|
|
138
|
+
expect(src).to receive(:move).with(having_attributes(:extname => '.tmp'))
|
|
139
|
+
|
|
140
|
+
src.replace(dst)
|
|
141
|
+
end
|
|
103
142
|
end
|
|
104
143
|
end
|
|
105
144
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'image_optim/worker/jpegrecompress'
|
|
5
|
+
|
|
6
|
+
describe ImageOptim::Worker::Jpegrecompress do
|
|
7
|
+
describe 'method value' do
|
|
8
|
+
let(:subject){ described_class.new(ImageOptim.new, method).method }
|
|
9
|
+
|
|
10
|
+
context 'default' do
|
|
11
|
+
let(:method){ {} }
|
|
12
|
+
|
|
13
|
+
it{ is_expected.to eq('ssim') }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
context 'uses default when invalid' do
|
|
17
|
+
let(:method){ {:method => 'invalid'} }
|
|
18
|
+
|
|
19
|
+
it 'warns and keeps default' do
|
|
20
|
+
expect_any_instance_of(described_class).
|
|
21
|
+
to receive(:warn).with('Unknown method for jpegrecompress: invalid')
|
|
22
|
+
is_expected.to eq('ssim')
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context 'can use a valid option' do
|
|
27
|
+
let(:method){ {:method => 'smallfry'} }
|
|
28
|
+
|
|
29
|
+
it{ is_expected.to eq('smallfry') }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/spec/image_optim_spec.rb
CHANGED
|
@@ -23,10 +23,11 @@ describe ImageOptim do
|
|
|
23
23
|
stub_const('Cmd', ImageOptim::Cmd)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
isolated_options_base =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
isolated_options_base = Hash[
|
|
27
|
+
ImageOptim::Worker.klasses.map do |klass|
|
|
28
|
+
[klass.bin_sym, false]
|
|
29
|
+
end
|
|
30
|
+
].merge(:skip_missing_workers => false)
|
|
30
31
|
|
|
31
32
|
ImageOptim::Worker.klasses.each do |worker_klass|
|
|
32
33
|
describe "#{worker_klass.bin_sym} worker" do
|
data/spec/images/quant/generate
CHANGED
|
@@ -17,8 +17,8 @@ palettes.each do |palette|
|
|
|
17
17
|
rgb:-
|
|
18
18
|
PNG24:#{palette}.png
|
|
19
19
|
].shelljoin, 'w') do |f|
|
|
20
|
-
(side
|
|
21
|
-
color = i * palette / (side
|
|
20
|
+
(side**2).times do |i|
|
|
21
|
+
color = i * palette / (side**2) * 0x10000 / palette
|
|
22
22
|
f << [color / 0x100, color % 0x100, 0].pack('C*')
|
|
23
23
|
end
|
|
24
24
|
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.
|
|
4
|
+
version: 0.29.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ivan Kuchin
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-04-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: fspath
|
|
@@ -138,16 +138,30 @@ dependencies:
|
|
|
138
138
|
requirements:
|
|
139
139
|
- - "~>"
|
|
140
140
|
- !ruby/object:Gem::Version
|
|
141
|
-
version: '0
|
|
141
|
+
version: '1.0'
|
|
142
142
|
type: :development
|
|
143
143
|
prerelease: false
|
|
144
144
|
version_requirements: !ruby/object:Gem::Requirement
|
|
145
145
|
requirements:
|
|
146
146
|
- - "~>"
|
|
147
147
|
- !ruby/object:Gem::Version
|
|
148
|
-
version: '0
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
version: '1.0'
|
|
149
|
+
- !ruby/object:Gem::Dependency
|
|
150
|
+
name: rubocop-rspec
|
|
151
|
+
requirement: !ruby/object:Gem::Requirement
|
|
152
|
+
requirements:
|
|
153
|
+
- - "~>"
|
|
154
|
+
- !ruby/object:Gem::Version
|
|
155
|
+
version: '2.0'
|
|
156
|
+
type: :development
|
|
157
|
+
prerelease: false
|
|
158
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
159
|
+
requirements:
|
|
160
|
+
- - "~>"
|
|
161
|
+
- !ruby/object:Gem::Version
|
|
162
|
+
version: '2.0'
|
|
163
|
+
description:
|
|
164
|
+
email:
|
|
151
165
|
executables:
|
|
152
166
|
- image_optim
|
|
153
167
|
extensions: []
|
|
@@ -155,6 +169,7 @@ extra_rdoc_files: []
|
|
|
155
169
|
files:
|
|
156
170
|
- ".appveyor.yml"
|
|
157
171
|
- ".gitignore"
|
|
172
|
+
- ".pre-commit-hooks.yaml"
|
|
158
173
|
- ".rubocop.yml"
|
|
159
174
|
- ".travis.yml"
|
|
160
175
|
- CHANGELOG.markdown
|
|
@@ -224,6 +239,7 @@ files:
|
|
|
224
239
|
- spec/image_optim/runner/glob_helpers_spec.rb
|
|
225
240
|
- spec/image_optim/runner/option_parser_spec.rb
|
|
226
241
|
- spec/image_optim/space_spec.rb
|
|
242
|
+
- spec/image_optim/worker/jpegrecompress_spec.rb
|
|
227
243
|
- spec/image_optim/worker/optipng_spec.rb
|
|
228
244
|
- spec/image_optim/worker/pngquant_spec.rb
|
|
229
245
|
- spec/image_optim/worker_spec.rb
|
|
@@ -257,13 +273,13 @@ files:
|
|
|
257
273
|
- spec/spec_helper.rb
|
|
258
274
|
- vendor/jpegrescan
|
|
259
275
|
- vendor/jpegrescan.bat
|
|
260
|
-
homepage:
|
|
276
|
+
homepage: https://github.com/toy/image_optim
|
|
261
277
|
licenses:
|
|
262
278
|
- MIT
|
|
263
279
|
metadata:
|
|
264
280
|
bug_tracker_uri: https://github.com/toy/image_optim/issues
|
|
265
281
|
changelog_uri: https://github.com/toy/image_optim/blob/master/CHANGELOG.markdown
|
|
266
|
-
documentation_uri: https://www.rubydoc.info/gems/image_optim/0.
|
|
282
|
+
documentation_uri: https://www.rubydoc.info/gems/image_optim/0.29.0
|
|
267
283
|
source_code_uri: https://github.com/toy/image_optim
|
|
268
284
|
post_install_message: |
|
|
269
285
|
Rails image assets optimization is extracted into image_optim_rails gem
|
|
@@ -275,15 +291,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
275
291
|
requirements:
|
|
276
292
|
- - ">="
|
|
277
293
|
- !ruby/object:Gem::Version
|
|
278
|
-
version:
|
|
294
|
+
version: 1.9.3
|
|
279
295
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
280
296
|
requirements:
|
|
281
297
|
- - ">="
|
|
282
298
|
- !ruby/object:Gem::Version
|
|
283
299
|
version: '0'
|
|
284
300
|
requirements: []
|
|
285
|
-
rubygems_version: 3.
|
|
286
|
-
signing_key:
|
|
301
|
+
rubygems_version: 3.1.5
|
|
302
|
+
signing_key:
|
|
287
303
|
specification_version: 4
|
|
288
304
|
summary: Command line tool and ruby interface to optimize (lossless compress, optionally
|
|
289
305
|
lossy) jpeg, png, gif and svg images using external utilities (advpng, gifsicle,
|
|
@@ -307,6 +323,7 @@ test_files:
|
|
|
307
323
|
- spec/image_optim/runner/glob_helpers_spec.rb
|
|
308
324
|
- spec/image_optim/runner/option_parser_spec.rb
|
|
309
325
|
- spec/image_optim/space_spec.rb
|
|
326
|
+
- spec/image_optim/worker/jpegrecompress_spec.rb
|
|
310
327
|
- spec/image_optim/worker/optipng_spec.rb
|
|
311
328
|
- spec/image_optim/worker/pngquant_spec.rb
|
|
312
329
|
- spec/image_optim/worker_spec.rb
|