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