image_optim 0.22.1 → 0.23.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 (50) hide show
  1. checksums.yaml +8 -8
  2. data/.appveyor.yml +95 -0
  3. data/.rubocop.yml +3 -0
  4. data/.travis.yml +27 -22
  5. data/CHANGELOG.markdown +10 -0
  6. data/CONTRIBUTING.markdown +2 -1
  7. data/Gemfile +1 -1
  8. data/README.markdown +10 -2
  9. data/image_optim.gemspec +4 -4
  10. data/lib/image_optim.rb +32 -16
  11. data/lib/image_optim/bin_resolver/bin.rb +11 -4
  12. data/lib/image_optim/cache.rb +71 -0
  13. data/lib/image_optim/cache_path.rb +16 -0
  14. data/lib/image_optim/config.rb +12 -2
  15. data/lib/image_optim/handler.rb +1 -1
  16. data/lib/image_optim/image_meta.rb +5 -10
  17. data/lib/image_optim/optimized_path.rb +25 -0
  18. data/lib/image_optim/path.rb +70 -0
  19. data/lib/image_optim/runner/option_parser.rb +13 -0
  20. data/lib/image_optim/worker.rb +5 -8
  21. data/lib/image_optim/worker/class_methods.rb +3 -1
  22. data/lib/image_optim/worker/jpegoptim.rb +3 -0
  23. data/lib/image_optim/worker/jpegrecompress.rb +3 -0
  24. data/lib/image_optim/worker/pngquant.rb +3 -0
  25. data/script/worker_analysis +10 -9
  26. data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +1 -1
  27. data/spec/image_optim/bin_resolver/simple_version_spec.rb +48 -40
  28. data/spec/image_optim/bin_resolver_spec.rb +190 -172
  29. data/spec/image_optim/cache_path_spec.rb +59 -0
  30. data/spec/image_optim/cache_spec.rb +159 -0
  31. data/spec/image_optim/cmd_spec.rb +11 -7
  32. data/spec/image_optim/config_spec.rb +92 -71
  33. data/spec/image_optim/handler_spec.rb +3 -6
  34. data/spec/image_optim/image_meta_spec.rb +61 -0
  35. data/spec/image_optim/optimized_path_spec.rb +58 -0
  36. data/spec/image_optim/option_helpers_spec.rb +25 -0
  37. data/spec/image_optim/path_spec.rb +105 -0
  38. data/spec/image_optim/railtie_spec.rb +6 -6
  39. data/spec/image_optim/runner/glob_helpers_spec.rb +2 -6
  40. data/spec/image_optim/runner/option_parser_spec.rb +3 -3
  41. data/spec/image_optim/space_spec.rb +16 -18
  42. data/spec/image_optim/worker/optipng_spec.rb +3 -3
  43. data/spec/image_optim/worker/pngquant_spec.rb +47 -7
  44. data/spec/image_optim/worker_spec.rb +114 -17
  45. data/spec/image_optim_spec.rb +58 -69
  46. data/spec/images/broken_jpeg +1 -0
  47. data/spec/spec_helper.rb +40 -10
  48. metadata +30 -8
  49. data/lib/image_optim/image_path.rb +0 -68
  50. data/spec/image_optim/image_path_spec.rb +0 -54
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MDRiNGJkYTQ0MDcxZGIyODc5YWJkMGZkNGVkYmQzNzRmYTk4N2Y4OQ==
4
+ MzcwOTc4MmMxMjI0YWM4YmQ2NzcyZTE0NTRkNjM4YjNlMmMwZGExMA==
5
5
  data.tar.gz: !binary |-
6
- NDMyODhiNzkyZGIxZmVmNDdmODY3MWFkOTE0NWFkMTM5ZWY0YWY5OQ==
6
+ ZmFmOWZhY2U5MTMyNjIwM2U3N2M0Y2U1NjVkNTJkNmNjYTk4NzQ0Yg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NzdkYmVmNDRmYmYwODFmZjAyOWJhODBlZGE3NWEwZmJhNjllNjE5ZmJjM2Y2
10
- ZTk1NDZiOWViNTZjMTI2MDE3YjgwNWQ3N2Y0Y2ZjNWQ0ODJjMGU1MjQ3NDkx
11
- NTZjZjA4NjQ4MGM4YzNhMDkyYTQ3OTI3ZGYwZGMzMGRjNWU5ODQ=
9
+ NDMyNWI3ODA2ZTg5NTFjN2Y3YTJjMzU5MjAzZGM4ZDg3NzU4Y2IzYjU3NGZl
10
+ NmJkN2JiMTg3YmMyNmJjZjFkMDRlOGVlYWIzYjg2MmVhYzMxYTY5YjE0NDc5
11
+ N2ZlMmU2ZTQxNTU4NGMyYzcxMjU3MDM2MTllZGMxZjhiYjc0Y2E=
12
12
  data.tar.gz: !binary |-
13
- MDMwNzg5MDM4NWRmNzM5ZjI0MDJmZTgwNzRiMjBlZDI1ZTNmOTMzZWUwNGE0
14
- ZTc0ZGIyNWVkMjExNTIyZGFkNTk1YWQ2MjU1NzU1ZjY3ZmZkNGUyNGVmZTk2
15
- ZGMyMDZjNGVlY2M1NmRmY2EyNWRjNTY5ZTBjNGRiMmI5YWYxOTE=
13
+ OWU2NjI5YjMyOWJmNmEzMGJjNGNmMjA0NmNlYWYyNzMxMDA0MTgyNDhjNmQz
14
+ NGU2NGY1Yzg4YzI3NTVkMjI4NTFiOGY1MzY0ZDlkYmI2MWFjNThmOTI3NGM0
15
+ M2ZmMjQ3OGIyZDc3YzY1NDI1MjU2N2U5ZDUyMDg3MDcwMzBkYTE=
@@ -0,0 +1,95 @@
1
+ version: '{build}-{branch}'
2
+ install:
3
+ - mkdir tmp\bin || exit 0
4
+ - set PATH=%cd%\tmp\bin;%PATH%
5
+ - ruby --version
6
+ - bundle package --all
7
+ - echo curl.exe -fsSL --max-time 15 --retry 5 -o %%%%1 %%%%2 > tmp\bin\download.bat
8
+
9
+ - ps: | # advpng
10
+ if (-not (test-path tmp/bin/advpng.exe)) {
11
+ download.bat tmp/advancecomp.zip https://github.com/amadvance/advancecomp/releases/download/v1.20/advancecomp-1.20-windows-x86.zip
12
+ 7z e tmp/advancecomp.zip -otmp/bin advpng.exe
13
+ }
14
+
15
+ - ps: | # gifsicle
16
+ if (-not (test-path tmp/bin/gifsicle.exe)) {
17
+ download.bat tmp/gifsicle.zip https://eternallybored.org/misc/gifsicle/releases/gifsicle-1.88-win64.zip
18
+ 7z e tmp/gifsicle.zip -otmp/bin */gifsicle.exe
19
+ }
20
+
21
+ - ps: | # jhead
22
+ if (-not (test-path tmp/bin/jhead.exe)) {
23
+ download.bat tmp/bin/jhead.exe http://www.sentex.net/~mwandel/jhead/jhead.exe
24
+ }
25
+
26
+ - ps: | # jpeg-recompress
27
+ if (-not (test-path tmp/bin/jpeg-recompress.exe)) {
28
+ download.bat tmp/jpeg-archive.zip https://ci.appveyor.com/api/buildjobs/8t8vga27t8vn1js2/artifacts/jpeg-archive.zip
29
+ 7z e tmp/jpeg-archive.zip -otmp/bin jpeg-recompress.exe
30
+ }
31
+
32
+ - ps: | # jpegoptim
33
+ if (-not (test-path tmp/bin/jpegoptim.exe)) {
34
+ download.bat tmp/jpegoptim.zip https://bitbucket.org/rorgoroth/jpegoptim-for-windows/downloads/jpegoptim-1.4.3-Win32.zip
35
+ 7z e tmp/jpegoptim.zip -otmp/bin */jpegoptim.exe
36
+ }
37
+
38
+ - ps: | # jpegtran
39
+ if (-not (test-path tmp/bin/jpegtran.exe)) {
40
+ download.bat tmp/bin/jpegtran.exe http://jpegclub.org/jpegtran.exe
41
+ }
42
+
43
+ - ps: | # optipng
44
+ if (-not (test-path tmp/bin/optipng.exe)) {
45
+ download.bat tmp/optipng.zip https://sourceforge.net/projects/optipng/files/OptiPNG/optipng-0.7.6/optipng-0.7.6-win32.zip/download
46
+ 7z e tmp/optipng.zip -otmp/bin */optipng.exe
47
+ }
48
+
49
+ - ps: | # pngcrush
50
+ if (-not (test-path tmp/bin/pngcrush.exe)) {
51
+ download.bat tmp/bin/pngcrush.exe https://sourceforge.net/projects/pmt/files/pngcrush-executables/1.8.1/pngcrush_1_8.1_w32.exe/download
52
+ }
53
+
54
+ - ps: | # pngout
55
+ if (-not (test-path tmp/bin/pngout.exe)) {
56
+ download.bat tmp/bin/pngout.exe http://advsys.net/ken/util/pngout.exe
57
+ }
58
+
59
+ - ps: | # pngquant
60
+ if (-not (test-path tmp/bin/pngquant.exe)) {
61
+ download.bat tmp/pngquant.zip https://pngquant.org/pngquant-windows.zip
62
+ 7z e tmp/pngquant.zip -otmp/bin */pngquant.exe
63
+ }
64
+
65
+ - ps: | # svgo
66
+ npm install -g svgo
67
+
68
+ - ps: | # ImageMagick
69
+ if ((-not (test-path tmp/bin/convert.exe)) -or (-not (test-path tmp/bin/compare.exe)) -or (-not (test-path tmp/bin/magic.xml))) {
70
+ choco install -y imagemagick.tool
71
+ cp C:\ProgramData\chocolatey\lib\imagemagick.tool\tools\convert.exe tmp\bin
72
+ cp C:\ProgramData\chocolatey\lib\imagemagick.tool\tools\compare.exe tmp\bin
73
+ cp C:\ProgramData\chocolatey\lib\imagemagick.tool\tools\magic.xml tmp\bin
74
+ }
75
+
76
+ - bundle exec image_optim --info --allow-lossy --no-pack
77
+ cache:
78
+ - tmp/bin
79
+ build: off
80
+ test_script:
81
+ - ps: |
82
+ $path = $env:Path
83
+ $rubypaths = ls -Path C:\Ruby*\bin
84
+ foreach ($rubypath in $rubypaths[0, -2, -1]) {
85
+ echo "################################################################################"
86
+ $env:Path = "$rubypath;" + $path
87
+ ruby --version
88
+ bundle install --local -j4
89
+ bundle exec rspec
90
+ if ($LASTEXITCODE -gt 0) {
91
+ exit 1
92
+ }
93
+ }
94
+ $env:Path = $path
95
+ - bundle exec image_optim --allow-lossy -r spec/images
@@ -36,6 +36,9 @@ Style/DotPosition:
36
36
  Style/DoubleNegation:
37
37
  Enabled: false
38
38
 
39
+ Style/EmptyCaseCondition:
40
+ Enabled: false
41
+
39
42
  Style/Encoding:
40
43
  EnforcedStyle: when_needed
41
44
 
@@ -13,25 +13,30 @@ rvm:
13
13
  - '2.2'
14
14
  - jruby-19mode
15
15
  - ree
16
- script:
17
- if [ -n "$RUBOCOP" ]; then
18
- bundle exec rubocop
19
- ; elif [ -n "$RAILS_VERSION" ]; then
20
- bundle exec image_optim --info
21
- && bundle exec rspec spec/image_optim/railtie_spec.rb
22
- ; else
23
- bundle exec image_optim --info
24
- && bundle exec rspec
25
- ; fi
26
- before_install:
27
- if [ -z "$RUBOCOP" ]; then
28
- command -v svgo || npm install svgo
29
- ;
30
- command -v pngout || (
31
- mkdir -p ~/bin
32
- && curl -L "http://static.jonof.id.au/dl/kenutils/pngout-20130221-linux.tar.gz" | tar -xz -C ~/bin --strip-components 2 --wildcards '*/x86_64/pngout'
33
- )
34
- ; fi
16
+ script: |
17
+ (
18
+ set -ex
19
+ if [ -n "$RUBOCOP" ]; then
20
+ bundle exec rubocop
21
+ elif [ -n "$RAILS_VERSION" ] && [ -z "$CODECLIMATE" ]; then
22
+ bundle exec image_optim --info
23
+ bundle exec rspec spec/image_optim/railtie_spec.rb
24
+ else
25
+ bundle exec image_optim --info
26
+ bundle exec rspec
27
+ fi
28
+ )
29
+ before_install: |
30
+ (
31
+ set -ex
32
+ if [ -z "$RUBOCOP" ]; then
33
+ command -v svgo || npm install svgo
34
+ command -v pngout || {
35
+ mkdir -p ~/bin
36
+ curl -L "http://static.jonof.id.au/dl/kenutils/pngout-20130221-linux.tar.gz" | tar -xz -C ~/bin --strip-components 2 --wildcards '*/x86_64/pngout'
37
+ }
38
+ fi
39
+ )
35
40
  matrix:
36
41
  fast_finish: true
37
42
  include:
@@ -40,9 +45,9 @@ matrix:
40
45
  - env: RAILS_VERSION='~> 3.2'
41
46
  rvm: default
42
47
  - env: RAILS_VERSION='~> 4.0' SPROCKETS_RAILS_VERSION='~> 2.0'
43
- rvm: default
44
- - env: RAILS_VERSION='~> 4.0'
45
- rvm: default
48
+ rvm: '2'
49
+ - env: RAILS_VERSION='~> 4.0' CODECLIMATE=1
50
+ rvm: '2'
46
51
  - env: RUBOCOP=true
47
52
  rvm: default
48
53
  allow_failures:
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## unreleased
4
4
 
5
+ ## v0.23.0 (2016-07-17)
6
+
7
+ * Added `cache_dir` and `cache_worker_digests` options to cache results [#83](https://github.com/toy/image_optim/issues/83) [@gpakosz](https://github.com/gpakosz)
8
+ * Should work on windows [#24](https://github.com/toy/image_optim/issues/24) [@toy](https://github.com/toy)
9
+ * Rename `ImageOptim::ImagePath` to `ImageOptim::Path` and its method `#format` to `#image_format` [@toy](https://github.com/toy)
10
+ * Ignore empty config files [#133](https://github.com/toy/image_optim/issues/133) [@toy](https://github.com/toy)
11
+ * Use `FileUtils.move` in `ImagePath#replace` to rename file instead of copying on same device, don't preserve mtime and atime [#134](https://github.com/toy/image_optim/issues/134) [@toy](https://github.com/toy)
12
+ * Make `:allow_lossy` an individual option for workers that can use it, so it will be in the list of worker options [#130](https://github.com/toy/image_optim/issues/130) [@toy](https://github.com/toy)
13
+ * Use first 8 characters of sha1 hex for jpegrescan version [#131](https://github.com/toy/image_optim/issues/131) [@toy](https://github.com/toy)
14
+
5
15
  ## v0.22.1 (2016-02-21)
6
16
 
7
17
  * Fix missing old (1.x) `pngquant` version as it was output to stderr [#123](https://github.com/toy/image_optim/issues/123) [@toy](https://github.com/toy)
@@ -2,9 +2,10 @@
2
2
 
3
3
  * Create topic/feature branch: `git checkout -b awesome-changes`
4
4
  * Commit…
5
- * Add entry to [ChangeLog](CHANGELOG.markdown)
5
+ * Add entry at the top of [ChangeLog](CHANGELOG.markdown)
6
6
  * Run tests: `bundle exec rspec`
7
7
  * Check code style: `bundle exec rubocop`
8
+ * Rebase on master and squash commits to logical units
8
9
  * Push your branch: `git push origin awesome-changes`
9
10
  * Create pull request
10
11
  * Check if [travis is happy](https://travis-ci.org/toy/image_optim/pull_requests)
data/Gemfile CHANGED
@@ -2,7 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9')
5
+ if ENV['CODECLIMATE']
6
6
  gem 'codeclimate-test-reporter', :group => :test, :require => nil
7
7
  end
8
8
 
@@ -1,9 +1,10 @@
1
1
  [![Gem Version](https://img.shields.io/gem/v/image_optim.svg?style=flat)](https://rubygems.org/gems/image_optim)
2
2
  [![Build Status](https://img.shields.io/travis/toy/image_optim/master.svg?style=flat)](https://travis-ci.org/toy/image_optim)
3
+ [![AppVeyor Status](https://img.shields.io/appveyor/ci/toy/image-optim/master.svg?style=flat&label=windows)](https://ci.appveyor.com/project/toy/image-optim)
3
4
  [![Code Climate](https://img.shields.io/codeclimate/github/toy/image_optim.svg?style=flat)](https://codeclimate.com/github/toy/image_optim)
4
5
  [![Code Climate Coverage](https://img.shields.io/codeclimate/coverage/github/toy/image_optim.svg?style=flat)](https://codeclimate.com/github/toy/image_optim)
5
6
  [![Dependency Status](https://img.shields.io/gemnasium/toy/image_optim.svg?style=flat)](https://gemnasium.com/toy/image_optim)
6
- [![Inch CI](http://inch-ci.org/github/toy/image_optim.svg?branch=master&style=flat)](http://inch-ci.org/github/toy/image_optim)
7
+ [![Inch CI](https://inch-ci.org/github/toy/image_optim.svg?branch=master&style=flat)](https://inch-ci.org/github/toy/image_optim)
7
8
 
8
9
  # image_optim
9
10
 
@@ -55,9 +56,11 @@ gem 'image_optim_pack'
55
56
 
56
57
  With version:
57
58
 
59
+ <!---<update-version>-->
58
60
  ```ruby
59
- gem 'image_optim', '~> 0.11'
61
+ gem 'image_optim', '~> 0.23'
60
62
  ```
63
+ <!---</update-version>-->
61
64
 
62
65
  If you want to check latest changes:
63
66
 
@@ -271,6 +274,8 @@ optipng:
271
274
  * `:pack` — Require image\_optim\_pack or disable it, by default image\_optim\_pack will be used if available, will turn on `:skip-missing-workers` unless explicitly disabled *(defaults to `nil`)*
272
275
  * `:skip_missing_workers` — Skip workers with missing or problematic binaries *(defaults to `false`)*
273
276
  * `:allow_lossy` — Allow lossy workers and optimizations *(defaults to `false`)*
277
+ * `:cache_dir` — Configure cache directory
278
+ * `:cache_worker_digests` - Also cache worker digests along with original file digest and worker options: updating workers invalidates cache
274
279
 
275
280
  Worker can be disabled by passing `false` instead of options hash or by setting option `:disable` to `true`.
276
281
 
@@ -289,10 +294,12 @@ Worker can be disabled by passing `false` instead of options hash or by setting
289
294
  Worker has no options
290
295
 
291
296
  ### jpegoptim:
297
+ * `:allow_lossy` — Allow limiting maximum quality *(defaults to `false`)*
292
298
  * `:strip` — List of extra markers to strip: `:comments`, `:exif`, `:iptc`, `:icc` or `:all` *(defaults to `:all`)*
293
299
  * `:max_quality` — Maximum image quality factor `0`..`100`, ignored in default/lossless mode *(defaults to `100`)*
294
300
 
295
301
  ### jpegrecompress:
302
+ * `:allow_lossy` — Allow worker, it is always lossy *(defaults to `false`)*
296
303
  * `:quality` — JPEG quality preset: `0` - low, `1` - medium, `2` - high, `3` - veryhigh *(defaults to `3`)*
297
304
 
298
305
  ### jpegtran:
@@ -316,6 +323,7 @@ Worker has no options
316
323
  * `:strategy` — Strategy: `0` - xtreme, `1` - intense, `2` - longest Match, `3` - huffman Only, `4` - uncompressed *(defaults to `0`)*
317
324
 
318
325
  ### pngquant:
326
+ * `:allow_lossy` — Allow quality option *(defaults to `false`)*
319
327
  * `:quality` — min..max - don't save below min, use less colors below max (both in range `0..100`; in yaml - `!ruby/range 0..100`), ignored in default/lossless mode *(defaults to `100..100`, `0..100` in lossy mode)*
320
328
  * `:speed` — speed/quality trade-off: `1` - slow, `3` - default, `11` - fast & rough *(defaults to `3`)*
321
329
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'image_optim'
5
- s.version = '0.22.1'
5
+ s.version = '0.23.0'
6
6
  s.summary = %q{Optimize (lossless compress, optionally lossy) images (jpeg, png, gif, svg) using external utilities (advpng, gifsicle, jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, pngcrush, pngout, pngquant, svgo)}
7
7
  s.homepage = "http://github.com/toy/#{s.name}"
8
8
  s.authors = ['Ivan Kuchin']
@@ -15,15 +15,15 @@ Gem::Specification.new do |s|
15
15
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
16
  s.require_paths = %w[lib]
17
17
 
18
- s.add_dependency 'fspath', '~> 2.1'
18
+ s.add_dependency 'fspath', '~> 3.0'
19
19
  s.add_dependency 'image_size', '~> 1.3'
20
20
  s.add_dependency 'exifr', '~> 1.2', '>= 1.2.2'
21
21
  s.add_dependency 'progress', '~> 3.0', '>= 3.0.1'
22
22
  s.add_dependency 'in_threads', '~> 1.3'
23
23
 
24
- s.add_development_dependency 'image_optim_pack', '~> 0.2'
24
+ s.add_development_dependency 'image_optim_pack', '~> 0.2', '>= 0.2.2'
25
25
  s.add_development_dependency 'rspec', '~> 3.0'
26
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3')
26
+ if RUBY_VERSION >= '1.9.3'
27
27
  s.add_development_dependency 'rubocop', '~> 0.37'
28
28
  end
29
29
  end
@@ -1,8 +1,10 @@
1
1
  require 'image_optim/bin_resolver'
2
+ require 'image_optim/cache'
2
3
  require 'image_optim/config'
3
4
  require 'image_optim/handler'
4
5
  require 'image_optim/image_meta'
5
- require 'image_optim/image_path'
6
+ require 'image_optim/optimized_path'
7
+ require 'image_optim/path'
6
8
  require 'image_optim/railtie' if defined?(Rails)
7
9
  require 'image_optim/worker'
8
10
  require 'in_threads'
@@ -37,6 +39,12 @@ class ImageOptim
37
39
  # Allow lossy workers and optimizations
38
40
  attr_reader :allow_lossy
39
41
 
42
+ # Cache directory
43
+ attr_reader :cache_dir
44
+
45
+ # Cache worker digests
46
+ attr_reader :cache_worker_digests
47
+
40
48
  # Initialize workers, specify options using worker underscored name:
41
49
  #
42
50
  # pass false to disable worker
@@ -67,6 +75,8 @@ class ImageOptim
67
75
  pack
68
76
  skip_missing_workers
69
77
  allow_lossy
78
+ cache_dir
79
+ cache_worker_digests
70
80
  ].each do |name|
71
81
  instance_variable_set(:"@#{name}", config.send(name))
72
82
  $stderr << "#{name}: #{send(name)}\n" if verbose
@@ -78,6 +88,8 @@ class ImageOptim
78
88
  config.for_worker(klass)
79
89
  end
80
90
 
91
+ @cache = Cache.new(self, @workers_by_format)
92
+
81
93
  log_workers_by_format if verbose
82
94
 
83
95
  config.assert_no_unused_options!
@@ -85,39 +97,43 @@ class ImageOptim
85
97
 
86
98
  # Get workers for image
87
99
  def workers_for_image(path)
88
- @workers_by_format[ImagePath.convert(path).format]
100
+ @workers_by_format[Path.convert(path).image_format]
89
101
  end
90
102
 
91
- # Optimize one file, return new path as OptimizedImagePath or nil if
103
+ # Optimize one file, return new path as OptimizedPath or nil if
92
104
  # optimization failed
93
105
  def optimize_image(original)
94
- original = ImagePath.convert(original)
106
+ original = Path.convert(original)
95
107
  return unless (workers = workers_for_image(original))
96
- result = Handler.for(original) do |handler|
97
- workers.each do |worker|
98
- handler.process do |src, dst|
99
- worker.optimize(src, dst)
108
+
109
+ optimized = @cache.fetch(original) do
110
+ Handler.for(original) do |handler|
111
+ workers.each do |worker|
112
+ handler.process do |src, dst|
113
+ worker.optimize(src, dst)
114
+ end
100
115
  end
101
116
  end
102
117
  end
103
- return unless result
104
- ImagePath::Optimized.new(result, original)
118
+
119
+ return unless optimized
120
+ OptimizedPath.new(optimized, original)
105
121
  end
106
122
 
107
- # Optimize one file in place, return original as OptimizedImagePath or nil if
123
+ # Optimize one file in place, return original as OptimizedPath or nil if
108
124
  # optimization failed
109
125
  def optimize_image!(original)
110
- original = ImagePath.convert(original)
126
+ original = Path.convert(original)
111
127
  return unless (result = optimize_image(original))
112
128
  result.replace(original)
113
- ImagePath::Optimized.new(original, result.original_size)
129
+ OptimizedPath.new(original, result.original_size)
114
130
  end
115
131
 
116
132
  # Optimize image data, return new data or nil if optimization failed
117
133
  def optimize_image_data(original_data)
118
- image_meta = ImageMeta.for_data(original_data)
119
- return unless image_meta && image_meta.format
120
- ImagePath.temp_file %W[image_optim .#{image_meta.format}] do |temp|
134
+ format = ImageMeta.format_for_data(original_data)
135
+ return unless format
136
+ Path.temp_file %W[image_optim .#{format}] do |temp|
121
137
  temp.binmode
122
138
  temp.write(original_data)
123
139
  temp.close
@@ -2,7 +2,9 @@ require 'image_optim/bin_resolver/error'
2
2
  require 'image_optim/bin_resolver/simple_version'
3
3
  require 'image_optim/bin_resolver/comparable_condition'
4
4
  require 'image_optim/cmd'
5
+ require 'image_optim/path'
5
6
  require 'shellwords'
7
+ require 'digest/sha1'
6
8
 
7
9
  class ImageOptim
8
10
  class BinResolver
@@ -18,6 +20,11 @@ class ImageOptim
18
20
  @version = detect_version
19
21
  end
20
22
 
23
+ def digest
24
+ return @digest if defined?(@digest)
25
+ @digest = File.exist?(@path) && Digest::SHA1.file(@path).hexdigest
26
+ end
27
+
21
28
  def to_s
22
29
  "#{name} #{version || '?'} at #{path}"
23
30
  end
@@ -73,11 +80,11 @@ class ImageOptim
73
80
  def version_string
74
81
  case name
75
82
  when :advpng, :gifsicle, :jpegoptim, :optipng
76
- capture("#{escaped_path} --version 2> /dev/null")[/\d+(\.\d+)+/]
83
+ capture("#{escaped_path} --version 2> #{Path::NULL}")[/\d+(\.\d+)+/]
77
84
  when :svgo, :pngquant
78
85
  capture("#{escaped_path} --version 2>&1")[/\d+(\.\d+)+/]
79
86
  when :jhead, :'jpeg-recompress'
80
- capture("#{escaped_path} -V 2> /dev/null")[/\d+(\.\d+)+/]
87
+ capture("#{escaped_path} -V 2> #{Path::NULL}")[/\d+(\.\d+)+/]
81
88
  when :jpegtran
82
89
  capture("#{escaped_path} -v - 2>&1")[/version (\d+\S*)/, 1]
83
90
  when :pngcrush
@@ -87,8 +94,8 @@ class ImageOptim
87
94
  date_str = capture("#{escaped_path} 2>&1")[date_regexp]
88
95
  Date.parse(date_str).strftime('%Y%m%d') if date_str
89
96
  when :jpegrescan
90
- # jpegrescan has no version so just check presence
91
- path && '-'
97
+ # jpegrescan has no version so use first 8 characters of sha1 hex
98
+ Digest::SHA1.file(path).hexdigest[0, 8] if path
92
99
  else
93
100
  fail "getting `#{name}` version is not defined"
94
101
  end