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.
Files changed (44) hide show
  1. checksums.yaml +8 -8
  2. data/.rubocop.yml +56 -0
  3. data/.travis.yml +3 -1
  4. data/README.markdown +23 -10
  5. data/bin/image_optim +25 -15
  6. data/image_optim.gemspec +5 -2
  7. data/lib/image_optim.rb +47 -37
  8. data/lib/image_optim/bin_resolver.rb +17 -12
  9. data/lib/image_optim/bin_resolver/comparable_condition.rb +23 -7
  10. data/lib/image_optim/bin_resolver/simple_version.rb +2 -0
  11. data/lib/image_optim/config.rb +21 -13
  12. data/lib/image_optim/handler.rb +18 -12
  13. data/lib/image_optim/hash_helpers.rb +23 -13
  14. data/lib/image_optim/image_meta.rb +1 -0
  15. data/lib/image_optim/image_path.rb +14 -13
  16. data/lib/image_optim/option_definition.rb +11 -9
  17. data/lib/image_optim/option_helpers.rb +1 -2
  18. data/lib/image_optim/railtie.rb +18 -15
  19. data/lib/image_optim/runner.rb +67 -61
  20. data/lib/image_optim/space.rb +29 -0
  21. data/lib/image_optim/true_false_nil.rb +9 -1
  22. data/lib/image_optim/worker.rb +40 -16
  23. data/lib/image_optim/worker/advpng.rb +8 -1
  24. data/lib/image_optim/worker/gifsicle.rb +13 -1
  25. data/lib/image_optim/worker/jhead.rb +5 -0
  26. data/lib/image_optim/worker/jpegoptim.rb +17 -4
  27. data/lib/image_optim/worker/jpegtran.rb +9 -1
  28. data/lib/image_optim/worker/optipng.rb +13 -2
  29. data/lib/image_optim/worker/pngcrush.rb +14 -5
  30. data/lib/image_optim/worker/pngout.rb +10 -2
  31. data/lib/image_optim/worker/svgo.rb +1 -0
  32. data/script/update_worker_options_in_readme +42 -27
  33. data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +13 -13
  34. data/spec/image_optim/bin_resolver/simple_version_spec.rb +4 -4
  35. data/spec/image_optim/bin_resolver_spec.rb +65 -37
  36. data/spec/image_optim/config_spec.rb +121 -110
  37. data/spec/image_optim/handler_spec.rb +29 -18
  38. data/spec/image_optim/hash_helpers_spec.rb +29 -27
  39. data/spec/image_optim/image_path_spec.rb +17 -17
  40. data/spec/image_optim/space_spec.rb +24 -0
  41. data/spec/image_optim/worker_spec.rb +18 -0
  42. data/spec/image_optim_spec.rb +134 -74
  43. metadata +27 -7
  44. 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
- YTMzMWQ1ZjgyYzJkMmM5NzJmZmU2MDc2YTVhMmUxYTU0MTY2MWY1NA==
4
+ MDYzNDg2MTY5NjdlNGIyY2U3NzY0Mzc5MTc1YzA3N2I3MGRmNDQ0Yw==
5
5
  data.tar.gz: !binary |-
6
- NGYzYjFlY2IyOTBjNjI1OTRkNWY2Y2EyMDg2NzYwMWRhODRkNDBhZQ==
6
+ OTcyM2IxMWJjOTA4NDYyYTVkMjcwNTI4MDI5NzE0OTVmYjI5MzE0NQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NDU2NmJlMzZkYjE5YjRiMjNlN2EzNGVmNjJjMmZjNjkyMGRhZDMzYjM3ZmJl
10
- YWNlY2ZmZWU0YTY3YmJhZThiMmI1NjU5YTM1OTA4Yzk4YWQyOWNjNjVjMThk
11
- M2ZhNmQ0MTI3ZDkwMjc4OGU5YzJiMTkxYzM0ZThlN2Q1ZTQ1NWE=
9
+ N2ViYzljYTU3OGVkMzQ5OTNmNmYyM2RmZjdmZDZmN2E4MDA0MmJmNGI5MjFk
10
+ Yzc1NDFjYWM0NjgxZTU0ZWQwMjc0MmVjNGM1MjEyYmQ3MzhmNTlmYWNkZjU3
11
+ Mzc1Mjc2Y2M0NWNkZGM3OTFlN2ZjZDNiM2EwOGRmNjY0MmIxM2M=
12
12
  data.tar.gz: !binary |-
13
- OWEzOTM2ZmY5ODIwMmUzNjg3NmEzYjFmZmJmODQ5Nzc2MTA1MmE0YTcxMzdl
14
- MTY5YWI5Nzc4MDYwMTFlODZjOWI4NDM5MzFjZjA4ZTY1NzRlY2VkYzAyN2Mx
15
- ZTZiNzUyNzQ4ODNiODZjM2RlYjAxZmUzYWVlNjcxNzEzMGMzY2Y=
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: bundle exec rspec
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
+ [![Gem Version](https://img.shields.io/gem/v/image_optim.svg)](https://rubygems.org/gems/image_optim)
2
+ [![Build Status](https://img.shields.io/travis/toy/image_optim/master.svg)](https://travis-ci.org/toy/image_optim)
3
+ [![Code Climate](https://img.shields.io/codeclimate/github/toy/image_optim.svg)](https://codeclimate.com/github/toy/image_optim)
4
+ [![Dependency Status](https://img.shields.io/gemnasium/toy/image_optim.svg)](https://gemnasium.com/toy/image_optim)
5
+ [![Inch CI](http://inch-ci.org/github/toy/image_optim.svg?branch=master)](http://inch-ci.org/github/toy/image_optim)
6
+ [![Gittip](https://img.shields.io/gittip/toy.svg)](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
- [![Gem Version](https://badge.fury.io/rb/image_optim.png)](http://badge.fury.io/rb/image_optim)
19
- [![Build Status](https://travis-ci.org/toy/image_optim.png?branch=master)](https://travis-ci.org/toy/image_optim)
20
- [![Code Climate](https://codeclimate.com/github/toy/image_optim.png)](https://codeclimate.com/github/toy/image_optim)
21
- [![Dependency Status](https://gemnasium.com/toy/image_optim.png)](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-1.4.0.tar.gz
99
- tar zxf jpegoptim-1.4.0.tar.gz
100
- cd jpegoptim-1.4.0
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/1.7.70/pngcrush-1.7.73.tar.gz
109
- tar zxf pngcrush-1.7.73.tar.gz
110
- cd pngcrush-1.7.73
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
- op.accept(ImageOptim::TrueFalseNil, OptionParser.top.atype[TrueClass][0].merge('nil' => nil)){ |arg, val| val }
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} (in current working directory)
19
+ | #{ImageOptim::Config::LOCAL_CONFIG_PATH}
20
20
  |
21
21
  TEXT
22
22
 
23
- op.on('-r', '-R', '--recursive', 'Recurively scan directories for images') do |recursive|
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 (defaults to number of processors)') do |threads|
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
- raise "Unknown type #{type}"
69
+ fail "Unknown type #{type}"
68
70
  end
69
71
 
70
- description = "#{option_definition.description.gsub(' - ', ' - ')} (defaults to #{default})"
71
- description = description.scan(/(.*?.{1,60})(?:\s|\z)/).flatten.join("\n ").split("\n")
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, *description) do |value|
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 args == %w[-v]
102
- args = %w[--version]
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
- $stderr.puts ImageOptim.full_version if options[:verbose]
107
- ImageOptim::Runner.run!(args, options) or exit 1
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.to_s}\n\n#{option_parser.help}"
119
+ abort "#{e}\n\n#{option_parser.help}"
110
120
  rescue => e
111
121
  if options[:verbose]
112
- abort "#{e.to_s}\n#{e.backtrace.join("\n")}"
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.13.3'
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
- def verbose?
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 nil determines number of processors, false disables parallel processing)
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 makes it 0)
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
- Worker.klasses.each do |klass|
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 optimization failed
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
- if workers = workers_for_image(original)
77
- handler = Handler.new(original)
78
- workers.each do |worker|
79
- handler.process do |src, dst|
80
- worker.optimize(src, dst)
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 optimization failed
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
- if result = optimize_image(original)
94
- result.replace(original)
95
- ImagePath::Optimized.new(original, result.original_size)
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 yield results
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 yield results
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 array of yield results
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 XXX_BIN where XXX is upper case bin name
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
- if bin = resolve?(name) && Bin.new(name, version(name))
35
- $stderr << "Resolved #{bin}\n" if @image_optim.verbose?
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
- raise BinNotFoundError, "`#{name}` not found"
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
- unless @bins.include?(name)
57
- @lock.synchronize do
58
- unless @bins.include?(name)
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
- date_str = capture_output("#{name} 2>&1")[/[A-Z][a-z]{2} (?: |\d)\d \d{4}/]
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
- raise BadBinVersion, "`#{bin}` (#{c}) is known to produce broken pngs"
110
+ fail BadBinVersion, "`#{bin}` (#{c}) is known to produce broken pngs"
106
111
  end
107
112
  when :advpng
108
113
  case bin.version