image_optim 0.16.0 → 0.17.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 +9 -9
- data/.rubocop.yml +1 -1
- data/.travis.yml +1 -0
- data/CHANGELOG.markdown +8 -0
- data/README.markdown +3 -1
- data/bin/image_optim +25 -9
- data/image_optim.gemspec +1 -1
- data/lib/image_optim/bin_resolver.rb +44 -79
- data/lib/image_optim/bin_resolver/bin.rb +84 -0
- data/lib/image_optim/bin_resolver/error.rb +6 -0
- data/lib/image_optim/config.rb +14 -23
- data/lib/image_optim/runner.rb +7 -12
- data/lib/image_optim/worker.rb +1 -0
- data/spec/image_optim/bin_resolver_spec.rb +101 -74
- data/spec/image_optim/config_spec.rb +80 -46
- data/spec/image_optim/image_path_spec.rb +7 -7
- data/spec/image_optim_spec.rb +1 -7
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDA2ZGQwMGNiYTI1OTgxN2IyYWY1MmEyNDg0OTgwYzQ1YmY2ODk2MQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
7
|
-
|
6
|
+
ZTdmNTAzMDYwMzRmM2ZlZmM5YTg4MDk5ZTgzNWJjZTUxZjY4OTA2YQ==
|
7
|
+
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NjM2NTMyODYxNDU2ZjNiOWQxYTE3NmY0NjgyMDY3ZGRmYjEzZDQzMTUwNmYy
|
10
|
+
N2IyZTdhODAxYmI0YjRjMmM5YWY5MWFlNGI0MGU3M2FmZjMzYTc1ZWQzMzhl
|
11
|
+
OWNjNGFlNzEyY2NjNDY1ODhmZTU1Mzk5MWQ5OTM4ZDQ0MmM3MWU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
Y2NmZjlmODI1NDAyODk0NjAwYzRmYjUzNDU0ZTllMTNhYmM4NWRkYjQ3YmMz
|
14
|
+
NDFkNDc2ZTIwNTBhOWZlNmQyYTQ2YTc0NDViYmY3NDg4Yzc0ZGNlN2JjMDA2
|
15
|
+
MjRmOGNlNjdiNTE2YWQxMGE5Mjc5NDdkYTk1YTkzNWNlZDI0MjI=
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
## unreleased
|
4
4
|
|
5
|
+
## v0.17.0 (2014-10-04)
|
6
|
+
|
7
|
+
* Use pure ruby detection of bin path [@toy](https://github.com/toy)
|
8
|
+
* Fail if version of bin can't be detected [#39](https://github.com/toy/image_optim/issues/39) [@toy](https://github.com/toy)
|
9
|
+
* Check path in `XXX_BIN` to exist, be a file and be executable [@toy](https://github.com/toy)
|
10
|
+
* `image_optim --info` to perform initialization with verbose output without running optimizations [@toy](https://github.com/toy)
|
11
|
+
* Changeable config paths [@toy](https://github.com/toy)
|
12
|
+
|
5
13
|
## v0.16.0 (2014-09-12)
|
6
14
|
|
7
15
|
* Wrote this ChangeLog [#62](https://github.com/toy/image_optim/issues/62) [@toy](https://github.com/toy)
|
data/README.markdown
CHANGED
@@ -220,11 +220,13 @@ Image optimization can be time consuming, so depending on your deployment proces
|
|
220
220
|
|
221
221
|
## Configuration
|
222
222
|
|
223
|
-
Configuration in YAML format will be read and
|
223
|
+
Configuration in YAML format will be read and prepended to options from two paths:
|
224
224
|
|
225
225
|
* `$XDG_CONFIG_HOME/image_optim.yml` (by default `~/.config/image_optim.yml`)
|
226
226
|
* `.image_optim.yml` in current working directory
|
227
227
|
|
228
|
+
Paths can be changed using `:config_paths` option and `--config-paths` argument.
|
229
|
+
|
228
230
|
Example configuration:
|
229
231
|
|
230
232
|
```yaml
|
data/bin/image_optim
CHANGED
@@ -18,11 +18,18 @@ option_parser = OptionParser.new do |op|
|
|
18
18
|
| #{op.program_name} [options] image_path …
|
19
19
|
|
|
20
20
|
|Configuration will be read and prepanded to options from two paths:
|
21
|
-
| #{ImageOptim::Config::
|
22
|
-
| #{ImageOptim::Config::
|
21
|
+
| #{ImageOptim::Config::GLOBAL_PATH}
|
22
|
+
| #{ImageOptim::Config::LOCAL_PATH}
|
23
23
|
|
|
24
24
|
TEXT
|
25
25
|
|
26
|
+
op.on('--config-paths PATH1,PATH2', Array, 'Config paths to use instead of '\
|
27
|
+
'default ones') do |paths|
|
28
|
+
options[:config_paths] = paths
|
29
|
+
end
|
30
|
+
|
31
|
+
op.separator nil
|
32
|
+
|
26
33
|
op.on('-r', '-R', '--recursive', 'Recursively scan directories '\
|
27
34
|
'for images') do |recursive|
|
28
35
|
options[:recursive] = recursive
|
@@ -107,33 +114,42 @@ option_parser = OptionParser.new do |op|
|
|
107
114
|
op.separator nil
|
108
115
|
op.separator ' Common options:'
|
109
116
|
|
110
|
-
op.on('-v', '--verbose', 'Verbose output') do
|
111
|
-
options[:verbose] =
|
117
|
+
op.on('-v', '--verbose', 'Verbose output') do
|
118
|
+
options[:verbose] = true
|
112
119
|
end
|
113
120
|
|
114
|
-
op.on_tail('-h', '--help', 'Show
|
121
|
+
op.on_tail('-h', '--help', 'Show help and exit') do
|
115
122
|
puts op.help
|
116
123
|
exit
|
117
124
|
end
|
118
125
|
|
119
|
-
op.on_tail('--version', 'Show version') do
|
126
|
+
op.on_tail('--version', 'Show version and exit') do
|
120
127
|
puts ImageOptim.version
|
121
128
|
exit
|
122
129
|
end
|
130
|
+
|
131
|
+
op.on_tail('--info', 'Show environment info and exit') do
|
132
|
+
options[:verbose] = true
|
133
|
+
options[:only_info] = true
|
134
|
+
end
|
123
135
|
end
|
124
136
|
|
125
137
|
begin
|
126
138
|
args = ARGV.dup
|
127
139
|
|
128
|
-
# assume -v to be request to print version if it is the only argument
|
140
|
+
# assume -v to be a request to print version if it is the only argument
|
129
141
|
args = %w[--version] if args == %w[-v]
|
130
142
|
|
131
143
|
option_parser.parse!(args)
|
132
144
|
if options[:verbose]
|
133
145
|
$stderr.puts ImageOptim.full_version
|
134
146
|
end
|
135
|
-
|
136
|
-
|
147
|
+
|
148
|
+
only_info = options.delete(:only_info)
|
149
|
+
runner = ImageOptim::Runner.new(options)
|
150
|
+
unless only_info
|
151
|
+
abort 'specify paths to optimize' if args.empty?
|
152
|
+
abort unless runner.run!(args)
|
137
153
|
end
|
138
154
|
rescue OptionParser::ParseError => e
|
139
155
|
abort "#{e}\n\n#{option_parser.help}"
|
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.17.0'
|
6
6
|
s.summary = %q{Optimize (lossless compress) images (jpeg, png, gif, svg) using external utilities (advpng, gifsicle, jpegoptim, jpegtran, optipng, pngcrush, pngout, pngquant, svgo)}
|
7
7
|
s.homepage = "http://github.com/toy/#{s.name}"
|
8
8
|
s.authors = ['Ivan Kuchin']
|
@@ -1,59 +1,53 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'fspath'
|
3
|
-
require 'image_optim/bin_resolver/
|
4
|
-
require 'image_optim/bin_resolver/
|
3
|
+
require 'image_optim/bin_resolver/error'
|
4
|
+
require 'image_optim/bin_resolver/bin'
|
5
5
|
|
6
6
|
class ImageOptim
|
7
7
|
# Handles resolving binaries and checking versions
|
8
8
|
#
|
9
|
-
# If there is an environment variable XXX_BIN when
|
9
|
+
# If there is an environment variable XXX_BIN when resolving xxx, then a
|
10
10
|
# symlink to binary will be created in a temporary directory which will be
|
11
11
|
# added to PATH
|
12
12
|
class BinResolver
|
13
|
-
class Error < StandardError; end
|
14
13
|
class BinNotFound < Error; end
|
15
|
-
class BadBinVersion < Error; end
|
16
|
-
|
17
|
-
# Holds name and version of an executable
|
18
|
-
class Bin
|
19
|
-
attr_reader :name, :version
|
20
|
-
def initialize(name, version)
|
21
|
-
@name = name
|
22
|
-
@version = version && SimpleVersion.new(version)
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_s
|
26
|
-
"#{@name} #{@version || '-'}"
|
27
|
-
end
|
28
|
-
end
|
29
14
|
|
15
|
+
# Directory for symlinks to bins if XXX_BIN was used
|
30
16
|
attr_reader :dir
|
17
|
+
|
31
18
|
def initialize(image_optim)
|
32
19
|
@image_optim = image_optim
|
33
20
|
@bins = {}
|
34
21
|
@lock = Mutex.new
|
35
22
|
end
|
36
23
|
|
24
|
+
# Binary resolving: create symlink if there is XXX_BIN environment variable,
|
25
|
+
# build Bin with full path, check binary version
|
37
26
|
def resolve!(name)
|
38
27
|
name = name.to_sym
|
39
28
|
|
40
29
|
resolving(name) do
|
41
|
-
|
30
|
+
path = symlink_custom_bin!(name) || full_path(name)
|
31
|
+
bin = Bin.new(name, path) if path
|
32
|
+
|
42
33
|
if bin && @image_optim.verbose
|
43
34
|
$stderr << "Resolved #{bin}\n"
|
44
35
|
end
|
36
|
+
|
45
37
|
@bins[name] = bin
|
46
38
|
end
|
47
39
|
|
48
40
|
if @bins[name]
|
49
|
-
|
41
|
+
@bins[name].check!
|
50
42
|
else
|
51
43
|
fail BinNotFound, "`#{name}` not found"
|
52
44
|
end
|
53
45
|
end
|
54
46
|
|
47
|
+
# Path to vendor at root of image_optim
|
55
48
|
VENDOR_PATH = File.expand_path('../../../vendor', __FILE__)
|
56
49
|
|
50
|
+
# Prepand `dir` and append `VENDOR_PATH` to `PATH` from environment
|
57
51
|
def env_path
|
58
52
|
[dir, ENV['PATH'], VENDOR_PATH].compact.join(':')
|
59
53
|
end
|
@@ -73,6 +67,7 @@ class ImageOptim
|
|
73
67
|
|
74
68
|
private
|
75
69
|
|
70
|
+
# Double-checked locking
|
76
71
|
def resolving(name)
|
77
72
|
return if @bins.include?(name)
|
78
73
|
@lock.synchronize do
|
@@ -80,71 +75,41 @@ class ImageOptim
|
|
80
75
|
end
|
81
76
|
end
|
82
77
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
78
|
+
# Check path in XXX_BIN to exist, be a file and be executable and symlink to
|
79
|
+
# dir as name
|
80
|
+
def symlink_custom_bin!(name)
|
81
|
+
env_name = "#{name}_bin".upcase
|
82
|
+
path = ENV[env_name]
|
83
|
+
return unless path
|
84
|
+
path = File.expand_path(path)
|
85
|
+
desc = "`#{path}` specified in #{env_name}"
|
86
|
+
fail "#{desc} doesn\'t exist" unless File.exist?(path)
|
87
|
+
fail "#{desc} is not a file" unless File.file?(path)
|
88
|
+
fail "#{desc} is not executable" unless File.executable?(path)
|
89
|
+
if @image_optim.verbose
|
90
|
+
$stderr << "Custom path for #{name} specified in #{env_name}: #{path}\n"
|
91
91
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
def accessible?(name)
|
96
|
-
!!version(name)
|
97
|
-
end
|
98
|
-
|
99
|
-
def version(name)
|
100
|
-
case name.to_sym
|
101
|
-
when :advpng, :gifsicle, :jpegoptim, :optipng, :pngquant
|
102
|
-
capture_output("#{name} --version 2> /dev/null")[/\d+(\.\d+){1,}/]
|
103
|
-
when :svgo
|
104
|
-
capture_output("#{name} --version 2>&1")[/\d+(\.\d+){1,}/]
|
105
|
-
when :jhead
|
106
|
-
capture_output("#{name} -V 2> /dev/null")[/\d+(\.\d+){1,}/]
|
107
|
-
when :jpegtran
|
108
|
-
capture_output("#{name} -v - 2>&1")[/version (\d+\S*)/, 1]
|
109
|
-
when :pngcrush
|
110
|
-
capture_output("#{name} -version 2>&1")[/\d+(\.\d+){1,}/]
|
111
|
-
when :pngout
|
112
|
-
date_regexp = /[A-Z][a-z]{2} (?: |\d)\d \d{4}/
|
113
|
-
date_str = capture_output("#{name} 2>&1")[date_regexp]
|
114
|
-
Date.parse(date_str).strftime('%Y%m%d') if date_str
|
115
|
-
when :jpegrescan
|
116
|
-
# jpegrescan has no version so just check presence
|
117
|
-
capture_output("command -v #{name}")['jpegrescan']
|
118
|
-
else
|
119
|
-
fail "getting `#{name}` version is not defined"
|
92
|
+
unless @dir
|
93
|
+
@dir = FSPath.temp_dir
|
94
|
+
at_exit{ FileUtils.remove_entry_secure @dir }
|
120
95
|
end
|
96
|
+
symlink = @dir / name
|
97
|
+
symlink.make_symlink(path)
|
98
|
+
path
|
121
99
|
end
|
122
100
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
case bin.version
|
133
|
-
when c = is < '1.17'
|
134
|
-
warn "Note that `#{bin}` (#{c}) does not use zopfli"
|
135
|
-
end
|
136
|
-
when :pngquant
|
137
|
-
case bin.version
|
138
|
-
when c = is < '2.0'
|
139
|
-
fail BadBinVersion, "`#{bin}` (#{c}) is not supported"
|
140
|
-
when c = is < '2.1'
|
141
|
-
warn "Note that `#{bin}` (#{c}) may be lossy even with quality `100-`"
|
101
|
+
# Return full path to bin or null
|
102
|
+
# based on http://stackoverflow.com/a/5471032/96823
|
103
|
+
def full_path(name)
|
104
|
+
# PATHEXT is needed only for windows
|
105
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
106
|
+
ENV['PATH'].to_s.split(File::PATH_SEPARATOR).each do |dir|
|
107
|
+
exts.each do |ext|
|
108
|
+
path = File.expand_path("#{name}#{ext}", dir)
|
109
|
+
return path if File.file?(path) && File.executable?(path)
|
142
110
|
end
|
143
111
|
end
|
144
|
-
|
145
|
-
|
146
|
-
def capture_output(command)
|
147
|
-
`env PATH=#{env_path.shellescape} #{command}`
|
112
|
+
nil
|
148
113
|
end
|
149
114
|
end
|
150
115
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'image_optim/bin_resolver/error'
|
2
|
+
require 'image_optim/bin_resolver/simple_version'
|
3
|
+
require 'image_optim/bin_resolver/comparable_condition'
|
4
|
+
|
5
|
+
class ImageOptim
|
6
|
+
class BinResolver
|
7
|
+
# Holds bin name and path, gets version
|
8
|
+
class Bin
|
9
|
+
class BadVersion < Error; end
|
10
|
+
|
11
|
+
attr_reader :name, :path, :version
|
12
|
+
def initialize(name, path)
|
13
|
+
@name = name.to_sym
|
14
|
+
@path = path
|
15
|
+
@version = detect_version
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"#{name} #{version || '?'} at #{path}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Fail or warn if version is known to misbehave depending on severity
|
23
|
+
def check!
|
24
|
+
unless version
|
25
|
+
fail BadVersion, "didn't get version of #{name} at #{path}"
|
26
|
+
end
|
27
|
+
|
28
|
+
is = ComparableCondition.is
|
29
|
+
case name
|
30
|
+
when :pngcrush
|
31
|
+
case version
|
32
|
+
when c = is.between?('1.7.60', '1.7.65')
|
33
|
+
fail BadVersion, "#{self} (#{c}) is known to produce broken pngs"
|
34
|
+
end
|
35
|
+
when :advpng
|
36
|
+
case version
|
37
|
+
when c = is < '1.17'
|
38
|
+
warn "WARN: #{self} (#{c}) does not use zopfli"
|
39
|
+
end
|
40
|
+
when :pngquant
|
41
|
+
case version
|
42
|
+
when c = is < '2.0'
|
43
|
+
fail BadVersion, "#{self} (#{c}) is not supported"
|
44
|
+
when c = is < '2.1'
|
45
|
+
warn "WARN: #{self} (#{c}) may be lossy even with quality `100-`"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Wrap version_string with SimpleVersion
|
53
|
+
def detect_version
|
54
|
+
str = version_string
|
55
|
+
str && SimpleVersion.new(str)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Getting version of bin, will fail for an unknown name
|
59
|
+
def version_string
|
60
|
+
case name
|
61
|
+
when :advpng, :gifsicle, :jpegoptim, :optipng, :pngquant
|
62
|
+
`#{path.shellescape} --version 2> /dev/null`[/\d+(\.\d+){1,}/]
|
63
|
+
when :svgo
|
64
|
+
`#{path.shellescape} --version 2>&1`[/\d+(\.\d+){1,}/]
|
65
|
+
when :jhead
|
66
|
+
`#{path.shellescape} -V 2> /dev/null`[/\d+(\.\d+){1,}/]
|
67
|
+
when :jpegtran
|
68
|
+
`#{path.shellescape} -v - 2>&1`[/version (\d+\S*)/, 1]
|
69
|
+
when :pngcrush
|
70
|
+
`#{path.shellescape} -version 2>&1`[/\d+(\.\d+){1,}/]
|
71
|
+
when :pngout
|
72
|
+
date_regexp = /[A-Z][a-z]{2} (?: |\d)\d \d{4}/
|
73
|
+
date_str = `#{path.shellescape} 2>&1`[date_regexp]
|
74
|
+
Date.parse(date_str).strftime('%Y%m%d') if date_str
|
75
|
+
when :jpegrescan
|
76
|
+
# jpegrescan has no version so just check presence
|
77
|
+
path && '-'
|
78
|
+
else
|
79
|
+
fail "getting `#{name}` version is not defined"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/image_optim/config.rb
CHANGED
@@ -10,26 +10,15 @@ class ImageOptim
|
|
10
10
|
class Config
|
11
11
|
include OptionHelpers
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
GLOBAL_PATH = begin
|
14
|
+
File.join(ENV['XDG_CONFIG_HOME'] || '~/.config', 'image_optim.yml')
|
15
|
+
end
|
16
|
+
LOCAL_PATH = './.image_optim.yml'
|
16
17
|
|
17
18
|
class << self
|
18
|
-
# Read
|
19
|
-
#
|
20
|
-
def
|
21
|
-
read(GLOBAL_CONFIG_PATH)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Read config at LOCAL_CONFIG_PATH if it exists, warn if anything is
|
25
|
-
# wrong
|
26
|
-
def local
|
27
|
-
read(LOCAL_CONFIG_PATH)
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def read(path)
|
19
|
+
# Read options at path: expand path (warn on failure), return {} if file
|
20
|
+
# does not exist, read yaml, check if it is a Hash, deep symbolise keys
|
21
|
+
def read_options(path)
|
33
22
|
begin
|
34
23
|
full_path = File.expand_path(path)
|
35
24
|
rescue ArgumentError => e
|
@@ -49,11 +38,13 @@ class ImageOptim
|
|
49
38
|
end
|
50
39
|
|
51
40
|
def initialize(options)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
41
|
+
config_paths = options.delete(:config_paths) || [GLOBAL_PATH, LOCAL_PATH]
|
42
|
+
config_paths = Array(config_paths)
|
43
|
+
|
44
|
+
to_merge = config_paths.map{ |path| self.class.read_options(path) }
|
45
|
+
to_merge << HashHelpers.deep_symbolise_keys(options)
|
46
|
+
|
47
|
+
@options = to_merge.reduce do |memo, hash|
|
57
48
|
HashHelpers.deep_merge(memo, hash)
|
58
49
|
end
|
59
50
|
@used = Set.new
|
data/lib/image_optim/runner.rb
CHANGED
@@ -44,8 +44,7 @@ class ImageOptim
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
def initialize(
|
48
|
-
fail 'specify paths to optimize' if args.empty?
|
47
|
+
def initialize(options)
|
49
48
|
options = HashHelpers.deep_symbolise_keys(options)
|
50
49
|
@recursive = options.delete(:recursive)
|
51
50
|
@exclude_dir_globs, @exclude_file_globs = %w[dir file].map do |type|
|
@@ -53,14 +52,14 @@ class ImageOptim
|
|
53
52
|
GlobHelpers.expand_braces(glob)
|
54
53
|
end
|
55
54
|
@image_optim = ImageOptim.new(options)
|
56
|
-
@to_optimize = find_to_optimize(args)
|
57
55
|
end
|
58
56
|
|
59
|
-
def run!
|
60
|
-
|
57
|
+
def run!(args)
|
58
|
+
to_optimize = find_to_optimize(args)
|
59
|
+
unless to_optimize.empty?
|
61
60
|
results = Results.new
|
62
61
|
|
63
|
-
optimize_images
|
62
|
+
optimize_images!(to_optimize).each do |original, optimized|
|
64
63
|
results.add(original, optimized)
|
65
64
|
end
|
66
65
|
|
@@ -70,15 +69,11 @@ class ImageOptim
|
|
70
69
|
!@warnings
|
71
70
|
end
|
72
71
|
|
73
|
-
def self.run!(args, options)
|
74
|
-
new(args, options).run!
|
75
|
-
end
|
76
|
-
|
77
72
|
private
|
78
73
|
|
79
|
-
def optimize_images!(&block)
|
74
|
+
def optimize_images!(to_optimize, &block)
|
80
75
|
@image_optim.
|
81
|
-
optimize_images!(
|
76
|
+
optimize_images!(to_optimize.with_progress('optimizing'), &block)
|
82
77
|
end
|
83
78
|
|
84
79
|
def find_to_optimize(paths)
|
data/lib/image_optim/worker.rb
CHANGED
@@ -10,50 +10,85 @@ ensure
|
|
10
10
|
end
|
11
11
|
|
12
12
|
describe ImageOptim::BinResolver do
|
13
|
+
BinResolver = ImageOptim::BinResolver
|
14
|
+
Bin = BinResolver::Bin
|
15
|
+
SimpleVersion = BinResolver::SimpleVersion
|
16
|
+
|
13
17
|
let(:image_optim){ double(:image_optim, :verbose => false) }
|
14
|
-
let(:resolver){
|
18
|
+
let(:resolver){ BinResolver.new(image_optim) }
|
19
|
+
|
20
|
+
describe :full_path do
|
21
|
+
def full_path(name)
|
22
|
+
resolver.instance_eval{ full_path(name) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def command_v(name)
|
26
|
+
path = `sh -c 'command -v #{name}' 2> /dev/null`.strip
|
27
|
+
path unless path.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should find binary in path' do
|
31
|
+
with_env 'PATH', 'bin' do
|
32
|
+
expect(full_path('image_optim')).
|
33
|
+
to eq(File.expand_path('bin/image_optim'))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should return nil on failure' do
|
38
|
+
with_env 'PATH', 'lib' do
|
39
|
+
expect(full_path('image_optim')).to be_nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
%w[ls sh which bash image_optim should_not_exist].each do |name|
|
44
|
+
it "should return same path as `command -v` for #{name}" do
|
45
|
+
expect(full_path(name)).to eq(command_v(name))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
15
49
|
|
16
50
|
it 'should resolve bin in path' do
|
17
51
|
with_env 'LS_BIN', nil do
|
18
|
-
allow(resolver).to receive(:version).with(:ls).and_return('xxx')
|
19
|
-
expect(resolver).to receive(:accessible?).with(:ls).once.and_return(true)
|
20
52
|
expect(FSPath).not_to receive(:temp_dir)
|
53
|
+
expect(resolver).to receive(:full_path).with(:ls).and_return('/bin/ls')
|
54
|
+
bin = double
|
55
|
+
expect(Bin).to receive(:new).with(:ls, '/bin/ls').and_return(bin)
|
56
|
+
expect(bin).to receive(:check!).exactly(5).times
|
21
57
|
|
22
58
|
5.times do
|
23
59
|
resolver.resolve!(:ls)
|
24
60
|
end
|
25
61
|
expect(resolver.env_path).to eq([
|
26
62
|
ENV['PATH'],
|
27
|
-
|
63
|
+
BinResolver::VENDOR_PATH,
|
28
64
|
].join(':'))
|
29
65
|
end
|
30
66
|
end
|
31
67
|
|
32
|
-
it 'should
|
68
|
+
it 'should raise on failure to resolve bin' do
|
33
69
|
with_env 'LS_BIN', nil do
|
34
70
|
expect(FSPath).not_to receive(:temp_dir)
|
71
|
+
expect(resolver).to receive(:full_path).with(:ls).and_return(nil)
|
72
|
+
expect(Bin).not_to receive(:new)
|
35
73
|
|
36
74
|
5.times do
|
37
75
|
expect do
|
38
76
|
resolver.resolve!(:ls)
|
39
|
-
end.to raise_error
|
77
|
+
end.to raise_error BinResolver::BinNotFound
|
40
78
|
end
|
41
79
|
expect(resolver.env_path).to eq([
|
42
80
|
ENV['PATH'],
|
43
|
-
|
81
|
+
BinResolver::VENDOR_PATH,
|
44
82
|
].join(':'))
|
45
83
|
end
|
46
84
|
end
|
47
85
|
|
48
86
|
it 'should resolve bin specified in ENV' do
|
49
|
-
path = '
|
87
|
+
path = 'bin/image_optim'
|
50
88
|
with_env 'IMAGE_OPTIM_BIN', path do
|
51
89
|
tmpdir = double(:tmpdir, :to_str => 'tmpdir')
|
52
90
|
symlink = double(:symlink)
|
53
91
|
|
54
|
-
allow(resolver).to receive(:version).with(:image_optim).and_return('xxx')
|
55
|
-
expect(resolver).to receive(:accessible?).
|
56
|
-
with(:image_optim).once.and_return(true)
|
57
92
|
expect(FSPath).to receive(:temp_dir).
|
58
93
|
once.and_return(tmpdir)
|
59
94
|
expect(tmpdir).to receive(:/).
|
@@ -61,6 +96,12 @@ describe ImageOptim::BinResolver do
|
|
61
96
|
expect(symlink).to receive(:make_symlink).
|
62
97
|
with(File.expand_path(path)).once
|
63
98
|
|
99
|
+
expect(resolver).not_to receive(:full_path)
|
100
|
+
bin = double
|
101
|
+
expect(Bin).to receive(:new).
|
102
|
+
with(:image_optim, File.expand_path(path)).and_return(bin)
|
103
|
+
expect(bin).to receive(:check!).exactly(5).times
|
104
|
+
|
64
105
|
at_exit_blocks = []
|
65
106
|
expect(resolver).to receive(:at_exit).once do |&block|
|
66
107
|
at_exit_blocks.unshift(block)
|
@@ -72,7 +113,7 @@ describe ImageOptim::BinResolver do
|
|
72
113
|
expect(resolver.env_path).to eq([
|
73
114
|
tmpdir,
|
74
115
|
ENV['PATH'],
|
75
|
-
|
116
|
+
BinResolver::VENDOR_PATH,
|
76
117
|
].join(':'))
|
77
118
|
|
78
119
|
expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir)
|
@@ -80,85 +121,71 @@ describe ImageOptim::BinResolver do
|
|
80
121
|
end
|
81
122
|
end
|
82
123
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
124
|
+
{
|
125
|
+
'some/path/should_not_exist_bin' => 'doesn\'t exist',
|
126
|
+
'.' => 'is not a file',
|
127
|
+
__FILE__ => 'is not executable',
|
128
|
+
}.each do |path, error_message|
|
129
|
+
it "should raise when bin specified in ENV #{error_message}" do
|
130
|
+
with_env 'IMAGE_OPTIM_BIN', path do
|
131
|
+
expect(FSPath).not_to receive(:temp_dir)
|
132
|
+
expect(resolver).not_to receive(:at_exit)
|
133
|
+
|
134
|
+
5.times do
|
135
|
+
expect do
|
136
|
+
resolver.resolve!(:image_optim)
|
137
|
+
end.to raise_error RuntimeError, /#{Regexp.escape(error_message)}/
|
138
|
+
end
|
139
|
+
expect(resolver.env_path).to eq([
|
140
|
+
ENV['PATH'],
|
141
|
+
BinResolver::VENDOR_PATH,
|
142
|
+
].join(':'))
|
91
143
|
end
|
92
|
-
expect(resolver.env_path).to eq(ImageOptim::BinResolver::VENDOR_PATH)
|
93
144
|
end
|
94
145
|
end
|
95
146
|
|
96
|
-
it 'should
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
expect(resolver).to receive(:accessible?).
|
103
|
-
with(:should_not_exist).once.and_return(false)
|
104
|
-
expect(FSPath).to receive(:temp_dir).
|
105
|
-
once.and_return(tmpdir)
|
106
|
-
expect(tmpdir).to receive(:/).
|
107
|
-
with(:should_not_exist).once.and_return(symlink)
|
108
|
-
expect(symlink).to receive(:make_symlink).
|
109
|
-
with(File.expand_path(path)).once
|
110
|
-
|
111
|
-
at_exit_blocks = []
|
112
|
-
expect(resolver).to receive(:at_exit).once do |&block|
|
113
|
-
at_exit_blocks.unshift(block)
|
147
|
+
it 'should resolve bin only once, but check every time' do
|
148
|
+
with_env 'LS_BIN', nil do
|
149
|
+
expect(resolver).to receive(:full_path).once.with(:ls) do
|
150
|
+
sleep 0.1
|
151
|
+
'/bin/ls'
|
114
152
|
end
|
153
|
+
bin = double
|
154
|
+
expect(Bin).to receive(:new).once.with(:ls, '/bin/ls').and_return(bin)
|
115
155
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end.to raise_error ImageOptim::BinResolver::BinNotFound
|
120
|
-
end
|
121
|
-
expect(resolver.env_path).to eq([
|
122
|
-
tmpdir,
|
123
|
-
ENV['PATH'],
|
124
|
-
ImageOptim::BinResolver::VENDOR_PATH,
|
125
|
-
].join(':'))
|
126
|
-
|
127
|
-
expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir)
|
128
|
-
at_exit_blocks.each(&:call)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
it 'should resolve bin only once' do
|
133
|
-
with_env 'LS_BIN', nil do
|
134
|
-
allow(resolver).to receive(:version).with(:ls).and_return('xxx')
|
135
|
-
expect(resolver).to receive(:resolve?).once.with(:ls){ sleep 0.1; true }
|
156
|
+
check_count = 0
|
157
|
+
mutex = Mutex.new
|
158
|
+
allow(bin).to receive(:check!){ mutex.synchronize{ check_count += 1 } }
|
136
159
|
|
137
160
|
10.times.map do
|
138
161
|
Thread.new do
|
139
162
|
resolver.resolve!(:ls)
|
140
163
|
end
|
141
164
|
end.each(&:join)
|
165
|
+
|
166
|
+
expect(check_count).to eq(10)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'should raise if did not got bin version' do
|
171
|
+
bin = Bin.new(:pngcrush, '/bin/pngcrush')
|
172
|
+
allow(bin).to receive(:version).and_return(nil)
|
173
|
+
|
174
|
+
5.times do
|
175
|
+
expect do
|
176
|
+
bin.check!
|
177
|
+
end.to raise_error Bin::BadVersion
|
142
178
|
end
|
143
179
|
end
|
144
180
|
|
145
181
|
it 'should raise on detection of problematic version' do
|
146
|
-
|
147
|
-
|
148
|
-
with(:pngcrush).once.and_return(true)
|
149
|
-
expect(resolver).to receive(:version).
|
150
|
-
with(:pngcrush).once.and_return('1.7.60')
|
151
|
-
expect(FSPath).not_to receive(:temp_dir)
|
182
|
+
bin = Bin.new(:pngcrush, '/bin/pngcrush')
|
183
|
+
allow(bin).to receive(:version).and_return(SimpleVersion.new('1.7.60'))
|
152
184
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
end
|
158
|
-
expect(resolver.env_path).to eq([
|
159
|
-
ENV['PATH'],
|
160
|
-
ImageOptim::BinResolver::VENDOR_PATH,
|
161
|
-
].join(':'))
|
185
|
+
5.times do
|
186
|
+
expect do
|
187
|
+
bin.check!
|
188
|
+
end.to raise_error Bin::BadVersion
|
162
189
|
end
|
163
190
|
end
|
164
191
|
end
|
@@ -3,21 +3,20 @@ require 'rspec'
|
|
3
3
|
require 'image_optim/config'
|
4
4
|
|
5
5
|
describe ImageOptim::Config do
|
6
|
-
|
7
|
-
|
8
|
-
before do
|
9
|
-
allow(Config).to receive(:global).and_return({})
|
10
|
-
allow(Config).to receive(:local).and_return({})
|
11
|
-
end
|
6
|
+
IOConfig = ImageOptim::Config
|
12
7
|
|
13
8
|
describe 'assert_no_unused_options!' do
|
9
|
+
before do
|
10
|
+
allow(IOConfig).to receive(:read_options).and_return({})
|
11
|
+
end
|
12
|
+
|
14
13
|
it 'should not raise when no unused options' do
|
15
|
-
config =
|
14
|
+
config = IOConfig.new({})
|
16
15
|
config.assert_no_unused_options!
|
17
16
|
end
|
18
17
|
|
19
18
|
it 'should raise when there are unused options' do
|
20
|
-
config =
|
19
|
+
config = IOConfig.new(:unused => true)
|
21
20
|
expect do
|
22
21
|
config.assert_no_unused_options!
|
23
22
|
end.to raise_error(ImageOptim::ConfigurationError)
|
@@ -25,41 +24,53 @@ describe ImageOptim::Config do
|
|
25
24
|
end
|
26
25
|
|
27
26
|
describe 'nice' do
|
27
|
+
before do
|
28
|
+
allow(IOConfig).to receive(:read_options).and_return({})
|
29
|
+
end
|
30
|
+
|
28
31
|
it 'should be 10 by default' do
|
29
|
-
config =
|
32
|
+
config = IOConfig.new({})
|
30
33
|
expect(config.nice).to eq(10)
|
31
34
|
end
|
32
35
|
|
33
36
|
it 'should be 0 if disabled' do
|
34
|
-
config =
|
37
|
+
config = IOConfig.new(:nice => false)
|
35
38
|
expect(config.nice).to eq(0)
|
36
39
|
end
|
37
40
|
|
38
41
|
it 'should convert value to number' do
|
39
|
-
config =
|
42
|
+
config = IOConfig.new(:nice => '13')
|
40
43
|
expect(config.nice).to eq(13)
|
41
44
|
end
|
42
45
|
end
|
43
46
|
|
44
47
|
describe 'threads' do
|
48
|
+
before do
|
49
|
+
allow(IOConfig).to receive(:read_options).and_return({})
|
50
|
+
end
|
51
|
+
|
45
52
|
it 'should be processor_count by default' do
|
46
|
-
config =
|
53
|
+
config = IOConfig.new({})
|
47
54
|
allow(config).to receive(:processor_count).and_return(13)
|
48
55
|
expect(config.threads).to eq(13)
|
49
56
|
end
|
50
57
|
|
51
58
|
it 'should be 1 if disabled' do
|
52
|
-
config =
|
59
|
+
config = IOConfig.new(:threads => false)
|
53
60
|
expect(config.threads).to eq(1)
|
54
61
|
end
|
55
62
|
|
56
63
|
it 'should convert value to number' do
|
57
|
-
config =
|
64
|
+
config = IOConfig.new(:threads => '616')
|
58
65
|
expect(config.threads).to eq(616)
|
59
66
|
end
|
60
67
|
end
|
61
68
|
|
62
69
|
describe 'for_worker' do
|
70
|
+
before do
|
71
|
+
allow(IOConfig).to receive(:read_options).and_return({})
|
72
|
+
end
|
73
|
+
|
63
74
|
Abc = Class.new do
|
64
75
|
def self.bin_sym
|
65
76
|
:abc
|
@@ -71,80 +82,103 @@ describe ImageOptim::Config do
|
|
71
82
|
end
|
72
83
|
|
73
84
|
it 'should return empty hash by default' do
|
74
|
-
config =
|
85
|
+
config = IOConfig.new({})
|
75
86
|
expect(config.for_worker(Abc)).to eq({})
|
76
87
|
end
|
77
88
|
|
78
89
|
it 'should return passed hash' do
|
79
|
-
config =
|
90
|
+
config = IOConfig.new(:abc => {:option => true})
|
80
91
|
expect(config.for_worker(Abc)).to eq(:option => true)
|
81
92
|
end
|
82
93
|
|
83
94
|
it 'should return passed false' do
|
84
|
-
config =
|
95
|
+
config = IOConfig.new(:abc => false)
|
85
96
|
expect(config.for_worker(Abc)).to eq(false)
|
86
97
|
end
|
87
98
|
|
88
|
-
it 'should raise on unknown
|
89
|
-
config =
|
99
|
+
it 'should raise on unknown option' do
|
100
|
+
config = IOConfig.new(:abc => 13)
|
90
101
|
expect do
|
91
102
|
config.for_worker(Abc)
|
92
103
|
end.to raise_error(ImageOptim::ConfigurationError)
|
93
104
|
end
|
94
105
|
end
|
95
106
|
|
96
|
-
describe '
|
97
|
-
|
98
|
-
|
99
|
-
|
107
|
+
describe 'config' do
|
108
|
+
it 'should read options from default locations' do
|
109
|
+
expect(IOConfig).to receive(:read_options).
|
110
|
+
with(IOConfig::GLOBAL_PATH).and_return(:a => 1, :b => 2, :c => 3)
|
111
|
+
expect(IOConfig).to receive(:read_options).
|
112
|
+
with(IOConfig::LOCAL_PATH).and_return(:a => 10, :b => 20)
|
113
|
+
|
114
|
+
config = IOConfig.new(:a => 100)
|
115
|
+
expect(config.get!(:a)).to eq(100)
|
116
|
+
expect(config.get!(:b)).to eq(20)
|
117
|
+
expect(config.get!(:c)).to eq(3)
|
118
|
+
config.assert_no_unused_options!
|
100
119
|
end
|
101
120
|
|
102
|
-
|
103
|
-
|
104
|
-
expect(Config).to receive(:read).
|
105
|
-
with(Config::GLOBAL_CONFIG_PATH).and_return(:config => true)
|
121
|
+
it 'should not read options with empty config_paths' do
|
122
|
+
expect(IOConfig).not_to receive(:read_options)
|
106
123
|
|
107
|
-
|
108
|
-
|
124
|
+
config = IOConfig.new(:config_paths => [])
|
125
|
+
config.assert_no_unused_options!
|
109
126
|
end
|
110
127
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
128
|
+
it 'should read options from specified paths' do
|
129
|
+
expect(IOConfig).to receive(:read_options).
|
130
|
+
with('/etc/image_optim.yml').and_return(:a => 1, :b => 2, :c => 3)
|
131
|
+
expect(IOConfig).to receive(:read_options).
|
132
|
+
with('config/image_optim.yml').and_return(:a => 10, :b => 20)
|
115
133
|
|
116
|
-
|
117
|
-
|
134
|
+
config = IOConfig.new(:a => 100, :config_paths => %w[
|
135
|
+
/etc/image_optim.yml
|
136
|
+
config/image_optim.yml
|
137
|
+
])
|
138
|
+
expect(config.get!(:a)).to eq(100)
|
139
|
+
expect(config.get!(:b)).to eq(20)
|
140
|
+
expect(config.get!(:c)).to eq(3)
|
141
|
+
config.assert_no_unused_options!
|
118
142
|
end
|
119
143
|
|
120
|
-
|
144
|
+
it 'should convert config_paths to array' do
|
145
|
+
expect(IOConfig).to receive(:read_options).
|
146
|
+
with('config/image_optim.yml').and_return({})
|
147
|
+
|
148
|
+
config = IOConfig.new(:config_paths => 'config/image_optim.yml')
|
149
|
+
config.assert_no_unused_options!
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe 'class methods' do
|
154
|
+
describe 'read_options' do
|
121
155
|
let(:path){ double(:path) }
|
122
156
|
let(:full_path){ double(:full_path) }
|
123
157
|
|
124
158
|
it 'should warn if expand path fails' do
|
125
|
-
expect(
|
159
|
+
expect(IOConfig).to receive(:warn)
|
126
160
|
expect(File).to receive(:expand_path).
|
127
161
|
with(path).and_raise(ArgumentError)
|
128
162
|
expect(File).not_to receive(:file?)
|
129
163
|
|
130
|
-
expect(
|
164
|
+
expect(IOConfig.read_options(path)).to eq({})
|
131
165
|
end
|
132
166
|
|
133
167
|
it 'should return empty hash if path is not a file' do
|
134
|
-
expect(
|
168
|
+
expect(IOConfig).not_to receive(:warn)
|
135
169
|
expect(File).to receive(:expand_path).
|
136
170
|
with(path).and_return(full_path)
|
137
171
|
expect(File).to receive(:file?).
|
138
172
|
with(full_path).and_return(false)
|
139
173
|
|
140
|
-
expect(
|
174
|
+
expect(IOConfig.read_options(path)).to eq({})
|
141
175
|
end
|
142
176
|
|
143
177
|
it 'should return hash with deep symbolised keys from reader' do
|
144
178
|
stringified = {'config' => {'this' => true}}
|
145
179
|
symbolized = {:config => {:this => true}}
|
146
180
|
|
147
|
-
expect(
|
181
|
+
expect(IOConfig).not_to receive(:warn)
|
148
182
|
expect(File).to receive(:expand_path).
|
149
183
|
with(path).and_return(full_path)
|
150
184
|
expect(File).to receive(:file?).
|
@@ -152,11 +186,11 @@ describe ImageOptim::Config do
|
|
152
186
|
expect(YAML).to receive(:load_file).
|
153
187
|
with(full_path).and_return(stringified)
|
154
188
|
|
155
|
-
expect(
|
189
|
+
expect(IOConfig.read_options(path)).to eq(symbolized)
|
156
190
|
end
|
157
191
|
|
158
192
|
it 'should warn and return an empty hash if reader returns non hash' do
|
159
|
-
expect(
|
193
|
+
expect(IOConfig).to receive(:warn)
|
160
194
|
expect(File).to receive(:expand_path).
|
161
195
|
with(path).and_return(full_path)
|
162
196
|
expect(File).to receive(:file?).
|
@@ -164,11 +198,11 @@ describe ImageOptim::Config do
|
|
164
198
|
expect(YAML).to receive(:load_file).
|
165
199
|
with(full_path).and_return([:config])
|
166
200
|
|
167
|
-
expect(
|
201
|
+
expect(IOConfig.read_options(path)).to eq({})
|
168
202
|
end
|
169
203
|
|
170
204
|
it 'should warn and return an empty hash if reader raises exception' do
|
171
|
-
expect(
|
205
|
+
expect(IOConfig).to receive(:warn)
|
172
206
|
expect(File).to receive(:expand_path).
|
173
207
|
with(path).and_return(full_path)
|
174
208
|
expect(File).to receive(:file?).
|
@@ -176,7 +210,7 @@ describe ImageOptim::Config do
|
|
176
210
|
expect(YAML).to receive(:load_file).
|
177
211
|
with(full_path).and_raise
|
178
212
|
|
179
|
-
expect(
|
213
|
+
expect(IOConfig.read_options(path)).to eq({})
|
180
214
|
end
|
181
215
|
end
|
182
216
|
end
|
@@ -9,8 +9,8 @@ describe ImageOptim::ImagePath do
|
|
9
9
|
it 'should return ImagePath for string' do
|
10
10
|
path = 'a'
|
11
11
|
|
12
|
-
expect(ImagePath.convert(path)).to be_a(
|
13
|
-
expect(ImagePath.convert(path)).to eq(
|
12
|
+
expect(ImagePath.convert(path)).to be_a(ImagePath)
|
13
|
+
expect(ImagePath.convert(path)).to eq(ImagePath.new(path))
|
14
14
|
|
15
15
|
expect(ImagePath.convert(path)).not_to eq(path)
|
16
16
|
expect(ImagePath.convert(path)).not_to be(path)
|
@@ -19,18 +19,18 @@ describe ImageOptim::ImagePath do
|
|
19
19
|
it 'should return ImagePath for Pathname' do
|
20
20
|
pathname = Pathname.new('a')
|
21
21
|
|
22
|
-
expect(ImagePath.convert(pathname)).to be_a(
|
23
|
-
expect(ImagePath.convert(pathname)).to eq(
|
22
|
+
expect(ImagePath.convert(pathname)).to be_a(ImagePath)
|
23
|
+
expect(ImagePath.convert(pathname)).to eq(ImagePath.new(pathname))
|
24
24
|
|
25
25
|
expect(ImagePath.convert(pathname)).to eq(pathname)
|
26
26
|
expect(ImagePath.convert(pathname)).not_to be(pathname)
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'should return same instance for ImagePath' do
|
30
|
-
image_path =
|
30
|
+
image_path = ImagePath.new('a')
|
31
31
|
|
32
|
-
expect(ImagePath.convert(image_path)).to be_a(
|
33
|
-
expect(ImagePath.convert(image_path)).to eq(
|
32
|
+
expect(ImagePath.convert(image_path)).to be_a(ImagePath)
|
33
|
+
expect(ImagePath.convert(image_path)).to eq(ImagePath.new(image_path))
|
34
34
|
|
35
35
|
expect(ImagePath.convert(image_path)).to eq(image_path)
|
36
36
|
expect(ImagePath.convert(image_path)).to be(image_path)
|
data/spec/image_optim_spec.rb
CHANGED
@@ -76,17 +76,11 @@ describe ImageOptim do
|
|
76
76
|
end
|
77
77
|
|
78
78
|
describe 'worker' do
|
79
|
-
image_optim = ImageOptim.new
|
80
|
-
|
81
79
|
base_options = Hash[ImageOptim::Worker.klasses.map do |klass|
|
82
80
|
[klass.bin_sym, false]
|
83
81
|
end]
|
84
82
|
|
85
|
-
|
86
|
-
klass.new(image_optim, {}).image_formats.empty?
|
87
|
-
end
|
88
|
-
|
89
|
-
real_workers.each do |worker_klass|
|
83
|
+
ImageOptim::Worker.klasses.each do |worker_klass|
|
90
84
|
describe worker_klass.bin_sym do
|
91
85
|
it 'should optimize at least one test image' do
|
92
86
|
options = base_options.merge(worker_klass.bin_sym => true)
|
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.17.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Kuchin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fspath
|
@@ -145,7 +145,9 @@ files:
|
|
145
145
|
- image_optim.gemspec
|
146
146
|
- lib/image_optim.rb
|
147
147
|
- lib/image_optim/bin_resolver.rb
|
148
|
+
- lib/image_optim/bin_resolver/bin.rb
|
148
149
|
- lib/image_optim/bin_resolver/comparable_condition.rb
|
150
|
+
- lib/image_optim/bin_resolver/error.rb
|
149
151
|
- lib/image_optim/bin_resolver/simple_version.rb
|
150
152
|
- lib/image_optim/config.rb
|
151
153
|
- lib/image_optim/configuration_error.rb
|
@@ -227,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
227
229
|
version: '0'
|
228
230
|
requirements: []
|
229
231
|
rubyforge_project: image_optim
|
230
|
-
rubygems_version: 2.
|
232
|
+
rubygems_version: 2.4.1
|
231
233
|
signing_key:
|
232
234
|
specification_version: 4
|
233
235
|
summary: Optimize (lossless compress) images (jpeg, png, gif, svg) using external
|
@@ -267,4 +269,3 @@ test_files:
|
|
267
269
|
- spec/images/transparency1.png
|
268
270
|
- spec/images/transparency2.png
|
269
271
|
- spec/images/vergroessert.jpg
|
270
|
-
has_rdoc:
|