image_optim 0.13.3 → 0.14.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 +8 -8
- data/.rubocop.yml +56 -0
- data/.travis.yml +3 -1
- data/README.markdown +23 -10
- data/bin/image_optim +25 -15
- data/image_optim.gemspec +5 -2
- data/lib/image_optim.rb +47 -37
- data/lib/image_optim/bin_resolver.rb +17 -12
- data/lib/image_optim/bin_resolver/comparable_condition.rb +23 -7
- data/lib/image_optim/bin_resolver/simple_version.rb +2 -0
- data/lib/image_optim/config.rb +21 -13
- data/lib/image_optim/handler.rb +18 -12
- data/lib/image_optim/hash_helpers.rb +23 -13
- data/lib/image_optim/image_meta.rb +1 -0
- data/lib/image_optim/image_path.rb +14 -13
- data/lib/image_optim/option_definition.rb +11 -9
- data/lib/image_optim/option_helpers.rb +1 -2
- data/lib/image_optim/railtie.rb +18 -15
- data/lib/image_optim/runner.rb +67 -61
- data/lib/image_optim/space.rb +29 -0
- data/lib/image_optim/true_false_nil.rb +9 -1
- data/lib/image_optim/worker.rb +40 -16
- data/lib/image_optim/worker/advpng.rb +8 -1
- data/lib/image_optim/worker/gifsicle.rb +13 -1
- data/lib/image_optim/worker/jhead.rb +5 -0
- data/lib/image_optim/worker/jpegoptim.rb +17 -4
- data/lib/image_optim/worker/jpegtran.rb +9 -1
- data/lib/image_optim/worker/optipng.rb +13 -2
- data/lib/image_optim/worker/pngcrush.rb +14 -5
- data/lib/image_optim/worker/pngout.rb +10 -2
- data/lib/image_optim/worker/svgo.rb +1 -0
- data/script/update_worker_options_in_readme +42 -27
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +13 -13
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +4 -4
- data/spec/image_optim/bin_resolver_spec.rb +65 -37
- data/spec/image_optim/config_spec.rb +121 -110
- data/spec/image_optim/handler_spec.rb +29 -18
- data/spec/image_optim/hash_helpers_spec.rb +29 -27
- data/spec/image_optim/image_path_spec.rb +17 -17
- data/spec/image_optim/space_spec.rb +24 -0
- data/spec/image_optim/worker_spec.rb +18 -0
- data/spec/image_optim_spec.rb +134 -74
- metadata +27 -7
- data/script/update_instructions_in_readme +0 -44
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MDYzNDg2MTY5NjdlNGIyY2U3NzY0Mzc5MTc1YzA3N2I3MGRmNDQ0Yw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OTcyM2IxMWJjOTA4NDYyYTVkMjcwNTI4MDI5NzE0OTVmYjI5MzE0NQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
N2ViYzljYTU3OGVkMzQ5OTNmNmYyM2RmZjdmZDZmN2E4MDA0MmJmNGI5MjFk
|
10
|
+
Yzc1NDFjYWM0NjgxZTU0ZWQwMjc0MmVjNGM1MjEyYmQ3MzhmNTlmYWNkZjU3
|
11
|
+
Mzc1Mjc2Y2M0NWNkZGM3OTFlN2ZjZDNiM2EwOGRmNjY0MmIxM2M=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
Y2M4Y2I4ZTNiYjZhYThmYThhNTQxYjQ4N2UxZDhhNmU4NjQ1MmI2NmY1YTdh
|
14
|
+
MGI3YTI1YjRiZmIyYmQ5NGE4MDI2NmVjNGY1YWNmNmRkMDM3MzZhMGY3ZjQ5
|
15
|
+
MjYwZmJhOWNkYTM3YWU3NTc4OTQwMThiZjhmY2RkY2Y5ZTc3ZmY=
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- '*.gemspec'
|
4
|
+
|
5
|
+
Lint/EndAlignment:
|
6
|
+
AlignWith: variable
|
7
|
+
|
8
|
+
Style/AccessModifierIndentation:
|
9
|
+
EnforcedStyle: outdent
|
10
|
+
|
11
|
+
Style/CaseIndentation:
|
12
|
+
IndentWhenRelativeTo: end
|
13
|
+
|
14
|
+
Style/ClassLength:
|
15
|
+
Max: 120
|
16
|
+
|
17
|
+
Style/CyclomaticComplexity:
|
18
|
+
Max: 8
|
19
|
+
|
20
|
+
Style/DotPosition:
|
21
|
+
EnforcedStyle: trailing
|
22
|
+
|
23
|
+
Style/DoubleNegation:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Style/Encoding:
|
27
|
+
EnforcedStyle: when_needed
|
28
|
+
|
29
|
+
Style/HashSyntax:
|
30
|
+
EnforcedStyle: hash_rockets
|
31
|
+
|
32
|
+
Style/IfUnlessModifier:
|
33
|
+
MaxLineLength: 40
|
34
|
+
|
35
|
+
Style/IndentHash:
|
36
|
+
EnforcedStyle: consistent
|
37
|
+
|
38
|
+
Style/MethodLength:
|
39
|
+
Max: 20
|
40
|
+
|
41
|
+
Style/PercentLiteralDelimiters:
|
42
|
+
PreferredDelimiters:
|
43
|
+
'%w': '[]'
|
44
|
+
'%W': '[]'
|
45
|
+
|
46
|
+
Style/Semicolon:
|
47
|
+
AllowAsExpressionSeparator: true
|
48
|
+
|
49
|
+
Style/SpaceBeforeBlockBraces:
|
50
|
+
EnforcedStyle: no_space
|
51
|
+
|
52
|
+
Style/SpaceInsideHashLiteralBraces:
|
53
|
+
EnforcedStyle: no_space
|
54
|
+
|
55
|
+
Style/TrailingComma:
|
56
|
+
EnforcedStyleForMultiline: comma
|
data/.travis.yml
CHANGED
@@ -11,7 +11,9 @@ rvm:
|
|
11
11
|
- jruby-19mode
|
12
12
|
- jruby-head
|
13
13
|
- ree
|
14
|
-
script:
|
14
|
+
script:
|
15
|
+
- bundle exec rspec
|
16
|
+
- '! bundle show rubocop || bundle exec rubocop' # run rubocop only if it is bundled
|
15
17
|
before_install:
|
16
18
|
- sudo apt-get update -qq
|
17
19
|
- sudo apt-get install -qq advancecomp gifsicle jhead jpegoptim libjpeg-progs optipng pngcrush
|
data/README.markdown
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
[](https://rubygems.org/gems/image_optim)
|
2
|
+
[](https://travis-ci.org/toy/image_optim)
|
3
|
+
[](https://codeclimate.com/github/toy/image_optim)
|
4
|
+
[](https://gemnasium.com/toy/image_optim)
|
5
|
+
[](http://inch-ci.org/github/toy/image_optim)
|
6
|
+
[](https://www.gittip.com/toy/)
|
7
|
+
|
1
8
|
# image_optim
|
2
9
|
|
3
10
|
Optimize (lossless compress) images (jpeg, png, gif, svg) using external utilities:
|
@@ -15,10 +22,7 @@ Optimize (lossless compress) images (jpeg, png, gif, svg) using external utiliti
|
|
15
22
|
|
16
23
|
Based on [ImageOptim.app](http://imageoptim.com/).
|
17
24
|
|
18
|
-
[
|
19
|
-
[](https://travis-ci.org/toy/image_optim)
|
20
|
-
[](https://codeclimate.com/github/toy/image_optim)
|
21
|
-
[](https://gemnasium.com/toy/image_optim)
|
25
|
+
Documentation for [latest version](http://rubydoc.info/gems/image_optim/frames) and [master](http://rubydoc.info/github/toy/image_optim/master/frames).
|
22
26
|
|
23
27
|
## Gem installation
|
24
28
|
|
@@ -29,16 +33,19 @@ gem install image_optim
|
|
29
33
|
### Bundler
|
30
34
|
|
31
35
|
Add to your `Gemfile`:
|
36
|
+
|
32
37
|
```ruby
|
33
38
|
gem 'image_optim'
|
34
39
|
```
|
35
40
|
|
36
41
|
With version:
|
42
|
+
|
37
43
|
```ruby
|
38
44
|
gem 'image_optim', '~> 0.11'
|
39
45
|
```
|
40
46
|
|
41
47
|
If you want to check latest changes:
|
48
|
+
|
42
49
|
```ruby
|
43
50
|
gem 'image_optim', :git => 'git://github.com/toy/image_optim.git'
|
44
51
|
```
|
@@ -93,21 +100,27 @@ You will also need to install `jpegoptim` and `pngcrush` from source:
|
|
93
100
|
|
94
101
|
#### jpegoptim
|
95
102
|
|
103
|
+
Replace `X.Y.Z` with latest version number from http://www.kokkonen.net/tjko/projects.html#jpegoptim.
|
104
|
+
|
96
105
|
```bash
|
106
|
+
JPEGOPTIM_VERSION=X.Y.Z
|
97
107
|
cd /tmp
|
98
|
-
curl -O http://www.kokkonen.net/tjko/src/jpegoptim
|
99
|
-
tar zxf jpegoptim
|
100
|
-
cd jpegoptim
|
108
|
+
curl -O http://www.kokkonen.net/tjko/src/jpegoptim-$JPEGOPTIM_VERSION.tar.gz
|
109
|
+
tar zxf jpegoptim-$JPEGOPTIM_VERSION.tar.gz
|
110
|
+
cd jpegoptim-$JPEGOPTIM_VERSION
|
101
111
|
./configure && make && make install
|
102
112
|
```
|
103
113
|
|
104
114
|
#### pngcrush
|
105
115
|
|
116
|
+
Replace `X.Y.Z` with latest version number from http://sourceforge.net/projects/pmt/files/pngcrush/.
|
117
|
+
|
106
118
|
```bash
|
119
|
+
PNGCRUSH_VERSION=X.Y.Z
|
107
120
|
cd /tmp
|
108
|
-
curl -O http://iweb.dl.sourceforge.net/project/pmt/pngcrush/
|
109
|
-
tar zxf pngcrush
|
110
|
-
cd pngcrush
|
121
|
+
curl -O http://iweb.dl.sourceforge.net/project/pmt/pngcrush/$PNGCRUSH_VERSION/pngcrush-$PNGCRUSH_VERSION.tar.gz
|
122
|
+
tar zxf pngcrush-$PNGCRUSH_VERSION.tar.gz
|
123
|
+
cd pngcrush-$PNGCRUSH_VERSION
|
111
124
|
make && cp -f pngcrush /usr/local/bin
|
112
125
|
```
|
113
126
|
|
data/bin/image_optim
CHANGED
@@ -6,7 +6,7 @@ require 'image_optim/runner'
|
|
6
6
|
options = {}
|
7
7
|
|
8
8
|
option_parser = OptionParser.new do |op|
|
9
|
-
|
9
|
+
ImageOptim::TrueFalseNil.add_to_option_parser(op)
|
10
10
|
|
11
11
|
op.banner = <<-TEXT.gsub(/^\s*\|/, '')
|
12
12
|
|#{ImageOptim.full_version}
|
@@ -16,17 +16,19 @@ option_parser = OptionParser.new do |op|
|
|
16
16
|
|
|
17
17
|
|Configuration will be read and prepanded to options from two paths:
|
18
18
|
| #{ImageOptim::Config::GLOBAL_CONFIG_PATH}
|
19
|
-
| #{ImageOptim::Config::LOCAL_CONFIG_PATH}
|
19
|
+
| #{ImageOptim::Config::LOCAL_CONFIG_PATH}
|
20
20
|
|
|
21
21
|
TEXT
|
22
22
|
|
23
|
-
op.on('-r', '-R', '--recursive', 'Recurively scan directories
|
23
|
+
op.on('-r', '-R', '--recursive', 'Recurively scan directories '\
|
24
|
+
'for images') do |recursive|
|
24
25
|
options[:recursive] = recursive
|
25
26
|
end
|
26
27
|
|
27
28
|
op.separator nil
|
28
29
|
|
29
|
-
op.on('--[no-]threads N', Integer, 'Number of threads or disable
|
30
|
+
op.on('--[no-]threads N', Integer, 'Number of threads or disable '\
|
31
|
+
'(defaults to number of processors)') do |threads|
|
30
32
|
options[:threads] = threads
|
31
33
|
end
|
32
34
|
|
@@ -64,13 +66,18 @@ option_parser = OptionParser.new do |op|
|
|
64
66
|
when Array >= type
|
65
67
|
[Array, 'a,b,c']
|
66
68
|
else
|
67
|
-
|
69
|
+
fail "Unknown type #{type}"
|
68
70
|
end
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
+
description_lines = %W[
|
73
|
+
#{option_definition.description.gsub(' - ', ' - ')}
|
74
|
+
(defaults to #{default})
|
75
|
+
].join(' ').
|
76
|
+
scan(/.*?.{1,60}(?:\s|\z)/).
|
77
|
+
join("\n ").
|
78
|
+
split("\n")
|
72
79
|
|
73
|
-
op.on("--#{bin}-#{name} #{marking}", type, *
|
80
|
+
op.on("--#{bin}-#{name} #{marking}", type, *description_lines) do |value|
|
74
81
|
options[bin] = {} unless options[bin].is_a?(Hash)
|
75
82
|
options[bin][option_definition.name.to_sym] = value
|
76
83
|
end
|
@@ -98,18 +105,21 @@ end
|
|
98
105
|
begin
|
99
106
|
args = ARGV.dup
|
100
107
|
|
101
|
-
if
|
102
|
-
|
103
|
-
end
|
108
|
+
# assume -v to be request to print version if it is the only argument
|
109
|
+
args = %w[--version] if args == %w[-v]
|
104
110
|
|
105
111
|
option_parser.parse!(args)
|
106
|
-
|
107
|
-
|
112
|
+
if options[:verbose]
|
113
|
+
$stderr.puts ImageOptim.full_version
|
114
|
+
end
|
115
|
+
unless ImageOptim::Runner.run!(args, options)
|
116
|
+
abort
|
117
|
+
end
|
108
118
|
rescue OptionParser::ParseError => e
|
109
|
-
abort "#{e
|
119
|
+
abort "#{e}\n\n#{option_parser.help}"
|
110
120
|
rescue => e
|
111
121
|
if options[:verbose]
|
112
|
-
abort "#{e
|
122
|
+
abort "#{e}\n#{e.backtrace.join("\n")}"
|
113
123
|
else
|
114
124
|
abort e.to_s
|
115
125
|
end
|
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.
|
5
|
+
s.version = '0.14.0'
|
6
6
|
s.summary = %q{Optimize (lossless compress) images (jpeg, png, gif, svg) using external utilities (advpng, gifsicle, jpegoptim, jpegtran, optipng, pngcrush, pngout, svgo)}
|
7
7
|
s.homepage = "http://github.com/toy/#{s.name}"
|
8
8
|
s.authors = ['Ivan Kuchin']
|
@@ -20,5 +20,8 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.add_dependency 'exifr', '~> 1.1.3'
|
21
21
|
s.add_dependency 'progress', '~> 3.0.0'
|
22
22
|
s.add_dependency 'in_threads', '~> 1.2.0'
|
23
|
-
s.add_development_dependency 'rspec'
|
23
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
24
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.2')
|
25
|
+
s.add_development_dependency 'rubocop', '~> 0.24.1'
|
26
|
+
end
|
24
27
|
end
|
data/lib/image_optim.rb
CHANGED
@@ -7,6 +7,7 @@ require 'image_optim/worker'
|
|
7
7
|
require 'in_threads'
|
8
8
|
require 'shellwords'
|
9
9
|
|
10
|
+
# Main interface
|
10
11
|
class ImageOptim
|
11
12
|
# Nice level
|
12
13
|
attr_reader :nice
|
@@ -15,9 +16,7 @@ class ImageOptim
|
|
15
16
|
attr_reader :threads
|
16
17
|
|
17
18
|
# Verbose output?
|
18
|
-
|
19
|
-
@verbose
|
20
|
-
end
|
19
|
+
attr_reader :verbose
|
21
20
|
|
22
21
|
# Initialize workers, specify options using worker underscored name:
|
23
22
|
#
|
@@ -29,11 +28,13 @@ class ImageOptim
|
|
29
28
|
#
|
30
29
|
# ImageOptim.new(:advpng => {:level => 3}, :optipng => {:level => 2})
|
31
30
|
#
|
32
|
-
# use :threads to set number of parallel optimizers to run (passing true or
|
31
|
+
# use :threads to set number of parallel optimizers to run (passing true or
|
32
|
+
# nil determines number of processors, false disables parallel processing)
|
33
33
|
#
|
34
34
|
# ImageOptim.new(:threads => 8)
|
35
35
|
#
|
36
|
-
# use :nice to specify optimizers nice level (true or nil makes it 10, false
|
36
|
+
# use :nice to specify optimizers nice level (true or nil makes it 10, false
|
37
|
+
# makes it 0)
|
37
38
|
#
|
38
39
|
# ImageOptim.new(:nice => 20)
|
39
40
|
def initialize(options = {})
|
@@ -42,7 +43,7 @@ class ImageOptim
|
|
42
43
|
@threads = config.threads
|
43
44
|
@verbose = config.verbose
|
44
45
|
|
45
|
-
if verbose
|
46
|
+
if verbose
|
46
47
|
$stderr << config
|
47
48
|
$stderr << "Nice level: #{nice}\n"
|
48
49
|
$stderr << "Using threads: #{threads}\n"
|
@@ -50,17 +51,9 @@ class ImageOptim
|
|
50
51
|
|
51
52
|
@bin_resolver = BinResolver.new(self)
|
52
53
|
|
53
|
-
@workers_by_format =
|
54
|
-
|
55
|
-
if worker_options = config.for_worker(klass)
|
56
|
-
worker = klass.new(self, worker_options)
|
57
|
-
worker.image_formats.each do |format|
|
58
|
-
@workers_by_format[format] ||= []
|
59
|
-
@workers_by_format[format] << worker
|
60
|
-
end
|
61
|
-
end
|
54
|
+
@workers_by_format = create_workers_by_format do |klass|
|
55
|
+
config.for_worker(klass)
|
62
56
|
end
|
63
|
-
@workers_by_format.values.each(&:sort!)
|
64
57
|
|
65
58
|
config.assert_no_unused_options!
|
66
59
|
end
|
@@ -70,30 +63,29 @@ class ImageOptim
|
|
70
63
|
@workers_by_format[ImagePath.convert(path).format]
|
71
64
|
end
|
72
65
|
|
73
|
-
# Optimize one file, return new path as OptimizedImagePath or nil if
|
66
|
+
# Optimize one file, return new path as OptimizedImagePath or nil if
|
67
|
+
# optimization failed
|
74
68
|
def optimize_image(original)
|
75
69
|
original = ImagePath.convert(original)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
83
|
-
handler.cleanup
|
84
|
-
if handler.result
|
85
|
-
ImagePath::Optimized.new(handler.result, original)
|
70
|
+
return unless (workers = workers_for_image(original))
|
71
|
+
handler = Handler.new(original)
|
72
|
+
workers.each do |worker|
|
73
|
+
handler.process do |src, dst|
|
74
|
+
worker.optimize(src, dst)
|
86
75
|
end
|
87
76
|
end
|
77
|
+
handler.cleanup
|
78
|
+
return unless handler.result
|
79
|
+
ImagePath::Optimized.new(handler.result, original)
|
88
80
|
end
|
89
81
|
|
90
|
-
# Optimize one file in place, return original as OptimizedImagePath or nil if
|
82
|
+
# Optimize one file in place, return original as OptimizedImagePath or nil if
|
83
|
+
# optimization failed
|
91
84
|
def optimize_image!(original)
|
92
85
|
original = ImagePath.convert(original)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
86
|
+
return unless (result = optimize_image(original))
|
87
|
+
result.replace(original)
|
88
|
+
ImagePath::Optimized.new(original, result.original_size)
|
97
89
|
end
|
98
90
|
|
99
91
|
# Optimize image data, return new data or nil if optimization failed
|
@@ -105,28 +97,31 @@ class ImageOptim
|
|
105
97
|
temp.write(original_data)
|
106
98
|
temp.close
|
107
99
|
|
108
|
-
if result = optimize_image(temp.path)
|
100
|
+
if (result = optimize_image(temp.path))
|
109
101
|
result.open('rb', &:read)
|
110
102
|
end
|
111
103
|
end
|
112
104
|
end
|
113
105
|
|
114
106
|
# Optimize multiple images
|
115
|
-
# if block given yields path and result for each image and returns array of
|
107
|
+
# if block given yields path and result for each image and returns array of
|
108
|
+
# yield results
|
116
109
|
# else return array of results
|
117
110
|
def optimize_images(paths, &block)
|
118
111
|
run_method_for(paths, :optimize_image, &block)
|
119
112
|
end
|
120
113
|
|
121
114
|
# Optimize multiple images in place
|
122
|
-
# if block given yields path and result for each image and returns array of
|
115
|
+
# if block given yields path and result for each image and returns array of
|
116
|
+
# yield results
|
123
117
|
# else return array of results
|
124
118
|
def optimize_images!(paths, &block)
|
125
119
|
run_method_for(paths, :optimize_image!, &block)
|
126
120
|
end
|
127
121
|
|
128
122
|
# Optimize multiple image datas
|
129
|
-
# if block given yields original and result for each image data and returns
|
123
|
+
# if block given yields original and result for each image data and returns
|
124
|
+
# array of yield results
|
130
125
|
# else return array of results
|
131
126
|
def optimize_images_data(datas, &block)
|
132
127
|
run_method_for(datas, :optimize_image_data, &block)
|
@@ -156,7 +151,8 @@ class ImageOptim
|
|
156
151
|
!!workers_for_image(path)
|
157
152
|
end
|
158
153
|
|
159
|
-
# Check existance of binary, create symlink if ENV contains path for key
|
154
|
+
# Check existance of binary, create symlink if ENV contains path for key
|
155
|
+
# XXX_BIN where XXX is upper case bin name
|
160
156
|
def resolve_bin!(bin)
|
161
157
|
@bin_resolver.resolve!(bin)
|
162
158
|
end
|
@@ -168,6 +164,20 @@ class ImageOptim
|
|
168
164
|
|
169
165
|
private
|
170
166
|
|
167
|
+
# Create hash with format mapped to list of workers sorted by run order
|
168
|
+
def create_workers_by_format(&options_proc)
|
169
|
+
by_format = {}
|
170
|
+
Worker.klasses.each do |klass|
|
171
|
+
next unless (options = options_proc[klass])
|
172
|
+
worker = klass.new(self, options)
|
173
|
+
worker.image_formats.each do |format|
|
174
|
+
by_format[format] ||= []
|
175
|
+
by_format[format] << worker
|
176
|
+
end
|
177
|
+
end
|
178
|
+
by_format.each{ |_format, workers| workers.sort! }
|
179
|
+
end
|
180
|
+
|
171
181
|
# Run method for each path and yield each path and result if block given
|
172
182
|
def run_method_for(paths, method_name, &block)
|
173
183
|
apply_threading(paths).map do |path|
|
@@ -7,7 +7,13 @@ class ImageOptim
|
|
7
7
|
class BinNotFoundError < StandardError; end
|
8
8
|
class BadBinVersion < StandardError; end
|
9
9
|
|
10
|
+
# Handles resolving binaries and checking versions
|
11
|
+
#
|
12
|
+
# If there is an environment variable XXX_BIN when resolbing xxx, then a
|
13
|
+
# symlink to binary will be created in a temporary directory which will be
|
14
|
+
# added to PATH
|
10
15
|
class BinResolver
|
16
|
+
# Holds name and version of an executable
|
11
17
|
class Bin
|
12
18
|
attr_reader :name, :version
|
13
19
|
def initialize(name, version)
|
@@ -31,8 +37,9 @@ class ImageOptim
|
|
31
37
|
name = name.to_sym
|
32
38
|
|
33
39
|
resolving(name) do
|
34
|
-
|
35
|
-
|
40
|
+
bin = Bin.new(name, version(name)) if resolve?(name)
|
41
|
+
if bin && @image_optim.verbose
|
42
|
+
$stderr << "Resolved #{bin}\n"
|
36
43
|
end
|
37
44
|
@bins[name] = bin
|
38
45
|
end
|
@@ -40,7 +47,7 @@ class ImageOptim
|
|
40
47
|
if @bins[name]
|
41
48
|
check!(@bins[name])
|
42
49
|
else
|
43
|
-
|
50
|
+
fail BinNotFoundError, "`#{name}` not found"
|
44
51
|
end
|
45
52
|
end
|
46
53
|
|
@@ -53,17 +60,14 @@ class ImageOptim
|
|
53
60
|
private
|
54
61
|
|
55
62
|
def resolving(name)
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
yield
|
60
|
-
end
|
61
|
-
end
|
63
|
+
return if @bins.include?(name)
|
64
|
+
@lock.synchronize do
|
65
|
+
yield unless @bins.include?(name)
|
62
66
|
end
|
63
67
|
end
|
64
68
|
|
65
69
|
def resolve?(name)
|
66
|
-
if path = ENV["#{name}_bin".upcase]
|
70
|
+
if (path = ENV["#{name}_bin".upcase])
|
67
71
|
unless @dir
|
68
72
|
@dir = FSPath.temp_dir
|
69
73
|
at_exit{ FileUtils.remove_entry_secure @dir }
|
@@ -91,7 +95,8 @@ class ImageOptim
|
|
91
95
|
when :pngcrush
|
92
96
|
capture_output("#{name} -version 2>&1")[/\d+(\.\d+){1,}/]
|
93
97
|
when :pngout
|
94
|
-
|
98
|
+
date_regexp = /[A-Z][a-z]{2} (?: |\d)\d \d{4}/
|
99
|
+
date_str = capture_output("#{name} 2>&1")[date_regexp]
|
95
100
|
Date.parse(date_str).strftime('%Y%m%d')
|
96
101
|
end
|
97
102
|
end
|
@@ -102,7 +107,7 @@ class ImageOptim
|
|
102
107
|
when :pngcrush
|
103
108
|
case bin.version
|
104
109
|
when c = is.between?('1.7.60', '1.7.65')
|
105
|
-
|
110
|
+
fail BadBinVersion, "`#{bin}` (#{c}) is known to produce broken pngs"
|
106
111
|
end
|
107
112
|
when :advpng
|
108
113
|
case bin.version
|