image_optim 0.22.1 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
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