image_optim 0.13.3 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/.rubocop.yml +56 -0
- data/.travis.yml +3 -1
- data/README.markdown +23 -10
- data/bin/image_optim +25 -15
- data/image_optim.gemspec +5 -2
- data/lib/image_optim.rb +47 -37
- data/lib/image_optim/bin_resolver.rb +17 -12
- data/lib/image_optim/bin_resolver/comparable_condition.rb +23 -7
- data/lib/image_optim/bin_resolver/simple_version.rb +2 -0
- data/lib/image_optim/config.rb +21 -13
- data/lib/image_optim/handler.rb +18 -12
- data/lib/image_optim/hash_helpers.rb +23 -13
- data/lib/image_optim/image_meta.rb +1 -0
- data/lib/image_optim/image_path.rb +14 -13
- data/lib/image_optim/option_definition.rb +11 -9
- data/lib/image_optim/option_helpers.rb +1 -2
- data/lib/image_optim/railtie.rb +18 -15
- data/lib/image_optim/runner.rb +67 -61
- data/lib/image_optim/space.rb +29 -0
- data/lib/image_optim/true_false_nil.rb +9 -1
- data/lib/image_optim/worker.rb +40 -16
- data/lib/image_optim/worker/advpng.rb +8 -1
- data/lib/image_optim/worker/gifsicle.rb +13 -1
- data/lib/image_optim/worker/jhead.rb +5 -0
- data/lib/image_optim/worker/jpegoptim.rb +17 -4
- data/lib/image_optim/worker/jpegtran.rb +9 -1
- data/lib/image_optim/worker/optipng.rb +13 -2
- data/lib/image_optim/worker/pngcrush.rb +14 -5
- data/lib/image_optim/worker/pngout.rb +10 -2
- data/lib/image_optim/worker/svgo.rb +1 -0
- data/script/update_worker_options_in_readme +42 -27
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +13 -13
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +4 -4
- data/spec/image_optim/bin_resolver_spec.rb +65 -37
- data/spec/image_optim/config_spec.rb +121 -110
- data/spec/image_optim/handler_spec.rb +29 -18
- data/spec/image_optim/hash_helpers_spec.rb +29 -27
- data/spec/image_optim/image_path_spec.rb +17 -17
- data/spec/image_optim/space_spec.rb +24 -0
- data/spec/image_optim/worker_spec.rb +18 -0
- data/spec/image_optim_spec.rb +134 -74
- metadata +27 -7
- data/script/update_instructions_in_readme +0 -44
@@ -1,6 +1,16 @@
|
|
1
1
|
class ImageOptim
|
2
2
|
class BinResolver
|
3
|
+
# Allows to externalize conditions for an instance of Comparable to use in
|
4
|
+
# case statemens
|
5
|
+
#
|
6
|
+
# is = ComparableCondition.is
|
7
|
+
# case rand(100)
|
8
|
+
# when is < 10 then # ...
|
9
|
+
# when is.between?(13, 23) then # ...
|
10
|
+
# when is >= 90 then # ...
|
11
|
+
# end
|
3
12
|
class ComparableCondition
|
13
|
+
# Helper class for creating conditions using ComparableCondition.is
|
4
14
|
class Builder
|
5
15
|
Comparable.instance_methods.each do |method|
|
6
16
|
define_method method do |*args|
|
@@ -15,22 +25,22 @@ class ImageOptim
|
|
15
25
|
|
16
26
|
attr_reader :method, :args
|
17
27
|
def initialize(method, *args)
|
18
|
-
@method = method.to_sym
|
19
|
-
@args = args
|
28
|
+
@method, @args = method.to_sym, args
|
20
29
|
|
21
30
|
case @method
|
22
31
|
when :between?
|
23
|
-
|
32
|
+
@args.length == 2 || argument_error!("`between?' expects 2 arguments")
|
24
33
|
when :<, :<=, :==, :>, :>=
|
25
|
-
|
34
|
+
@args.length == 1 || argument_error!("`#{method}' expects 1 argument")
|
26
35
|
else
|
27
|
-
|
36
|
+
argument_error! "Unknown method `#{method}'"
|
28
37
|
end
|
29
38
|
end
|
30
39
|
|
31
|
-
def ===(
|
32
|
-
|
40
|
+
def ===(other)
|
41
|
+
other.send(@method, *@args)
|
33
42
|
end
|
43
|
+
alias_method :match, :===
|
34
44
|
|
35
45
|
def to_s
|
36
46
|
if @method == :between?
|
@@ -39,6 +49,12 @@ class ImageOptim
|
|
39
49
|
"#{@method} #{@args.first}"
|
40
50
|
end
|
41
51
|
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def argument_error!(message)
|
56
|
+
fail ArgumentError, message
|
57
|
+
end
|
42
58
|
end
|
43
59
|
end
|
44
60
|
end
|
data/lib/image_optim/config.rb
CHANGED
@@ -6,17 +6,23 @@ require 'set'
|
|
6
6
|
require 'yaml'
|
7
7
|
|
8
8
|
class ImageOptim
|
9
|
+
# Read, merge and parse configuration
|
9
10
|
class Config
|
10
11
|
include OptionHelpers
|
11
12
|
|
12
|
-
|
13
|
-
|
13
|
+
CONFIG_HOME = File.expand_path(ENV['XDG_CONFIG_HOME'] || '~/.config')
|
14
|
+
GLOBAL_CONFIG_PATH = File.join(CONFIG_HOME, 'image_optim.yml')
|
15
|
+
LOCAL_CONFIG_PATH = './.image_optim.yml'
|
14
16
|
|
15
17
|
class << self
|
18
|
+
# Read config at GLOBAL_CONFIG_PATH if it exists, warn if anything is
|
19
|
+
# wrong
|
16
20
|
def global
|
17
21
|
File.file?(GLOBAL_CONFIG_PATH) ? read(GLOBAL_CONFIG_PATH) : {}
|
18
22
|
end
|
19
23
|
|
24
|
+
# Read config at LOCAL_CONFIG_PATH if it exists, warn if anything is
|
25
|
+
# wrong
|
20
26
|
def local
|
21
27
|
File.file?(LOCAL_CONFIG_PATH) ? read(LOCAL_CONFIG_PATH) : {}
|
22
28
|
end
|
@@ -26,7 +32,7 @@ class ImageOptim
|
|
26
32
|
def read(path)
|
27
33
|
config = YAML.load_file(path)
|
28
34
|
unless config.is_a?(Hash)
|
29
|
-
|
35
|
+
fail "excpected hash, got #{config.inspect}"
|
30
36
|
end
|
31
37
|
HashHelpers.deep_symbolise_keys(config)
|
32
38
|
rescue => e
|
@@ -40,7 +46,7 @@ class ImageOptim
|
|
40
46
|
Config.global,
|
41
47
|
Config.local,
|
42
48
|
HashHelpers.deep_symbolise_keys(options),
|
43
|
-
].
|
49
|
+
].reduce do |memo, hash|
|
44
50
|
HashHelpers.deep_merge(memo, hash)
|
45
51
|
end
|
46
52
|
@used = Set.new
|
@@ -53,10 +59,10 @@ class ImageOptim
|
|
53
59
|
end
|
54
60
|
|
55
61
|
def assert_no_unused_options!
|
56
|
-
unknown_options = @options.reject{ |key,
|
57
|
-
|
58
|
-
|
59
|
-
|
62
|
+
unknown_options = @options.reject{ |key, _value| @used.include?(key) }
|
63
|
+
return if unknown_options.empty?
|
64
|
+
fail ConfigurationError, "unknown options #{unknown_options.inspect} "\
|
65
|
+
"for #{self}"
|
60
66
|
end
|
61
67
|
|
62
68
|
def nice
|
@@ -100,7 +106,8 @@ class ImageOptim
|
|
100
106
|
when false
|
101
107
|
false
|
102
108
|
else
|
103
|
-
|
109
|
+
fail ConfigurationError, "Got #{worker_options.inspect} for "\
|
110
|
+
"#{klass.name} options"
|
104
111
|
end
|
105
112
|
end
|
106
113
|
|
@@ -110,7 +117,7 @@ class ImageOptim
|
|
110
117
|
|
111
118
|
private
|
112
119
|
|
113
|
-
# http://stackoverflow.com/
|
120
|
+
# http://stackoverflow.com/a/6420817
|
114
121
|
def processor_count
|
115
122
|
@processor_count ||= case host_os = RbConfig::CONFIG['host_os']
|
116
123
|
when /darwin9/
|
@@ -123,9 +130,10 @@ class ImageOptim
|
|
123
130
|
`sysctl -n hw.ncpu`
|
124
131
|
when /mswin|mingw/
|
125
132
|
require 'win32ole'
|
126
|
-
|
127
|
-
|
128
|
-
|
133
|
+
WIN32OLE.
|
134
|
+
connect('winmgmts://').
|
135
|
+
ExecQuery('select NumberOfLogicalProcessors from Win32_Processor').
|
136
|
+
to_enum.first.NumberOfLogicalProcessors
|
129
137
|
else
|
130
138
|
warn "Unknown architecture (#{host_os}) assuming one processor."
|
131
139
|
1
|
data/lib/image_optim/handler.rb
CHANGED
@@ -1,35 +1,41 @@
|
|
1
1
|
require 'image_optim/image_path'
|
2
2
|
|
3
3
|
class ImageOptim
|
4
|
+
# Handles processing of original to result using upto two temp files
|
4
5
|
class Handler
|
6
|
+
# Holds latest successful result
|
5
7
|
attr_reader :result
|
8
|
+
|
9
|
+
# original must respond to temp_path
|
6
10
|
def initialize(original)
|
7
|
-
|
11
|
+
unless original.respond_to?(:temp_path)
|
12
|
+
fail ArgumentError, 'original should respond to temp_path'
|
13
|
+
end
|
8
14
|
|
9
15
|
@original = original
|
10
16
|
@result = nil
|
11
17
|
end
|
12
18
|
|
19
|
+
# Yields two paths, one to latest successful result or original, second to
|
20
|
+
# temp path
|
13
21
|
def process
|
14
22
|
@src ||= @original
|
15
23
|
@dst ||= @original.temp_path
|
16
24
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
25
|
+
return unless yield @src, @dst
|
26
|
+
@result = @dst
|
27
|
+
if @src == @original
|
28
|
+
@src, @dst = @dst, nil
|
29
|
+
else
|
30
|
+
@src, @dst = @dst, @src
|
24
31
|
end
|
25
32
|
end
|
26
33
|
|
27
34
|
# Remove extra temp files
|
28
35
|
def cleanup
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
36
|
+
return unless @dst
|
37
|
+
@dst.unlink
|
38
|
+
@dst = nil
|
33
39
|
end
|
34
40
|
end
|
35
41
|
end
|
@@ -1,28 +1,22 @@
|
|
1
1
|
class ImageOptim
|
2
|
+
# Helper methods to manipulate Hash, mainly used in config
|
2
3
|
module HashHelpers
|
3
4
|
class << self
|
4
|
-
|
5
|
-
|
6
|
-
hash.each do |k, v|
|
7
|
-
new_hash[block.call(k)] = if v.is_a?(Hash)
|
8
|
-
deep_transform_keys(v, &block)
|
9
|
-
else
|
10
|
-
v
|
11
|
-
end
|
12
|
-
end
|
13
|
-
new_hash
|
14
|
-
end
|
15
|
-
|
5
|
+
# Returns a new hash with all keys of root and nested hashes converted to
|
6
|
+
# strings
|
16
7
|
def deep_stringify_keys(hash)
|
17
8
|
deep_transform_keys(hash, &:to_s)
|
18
9
|
end
|
19
10
|
|
11
|
+
# Returns a new hash with all keys of root and nested hashes converted to
|
12
|
+
# symbols
|
20
13
|
def deep_symbolise_keys(hash)
|
21
14
|
deep_transform_keys(hash, &:to_sym)
|
22
15
|
end
|
23
16
|
|
17
|
+
# Returns a new hash with recursive merge of all keys
|
24
18
|
def deep_merge(a, b)
|
25
|
-
a.merge(b) do |
|
19
|
+
a.merge(b) do |_k, v_a, v_b|
|
26
20
|
if v_a.is_a?(Hash) && v_b.is_a?(Hash)
|
27
21
|
deep_merge(v_a, v_b)
|
28
22
|
else
|
@@ -30,6 +24,22 @@ class ImageOptim
|
|
30
24
|
end
|
31
25
|
end
|
32
26
|
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Returns a new hash with all keys of root and nested hashes converted by
|
31
|
+
# provided block
|
32
|
+
def deep_transform_keys(hash, &block)
|
33
|
+
new_hash = {}
|
34
|
+
hash.each do |k, v|
|
35
|
+
new_hash[block.call(k)] = if v.is_a?(Hash)
|
36
|
+
deep_transform_keys(v, &block)
|
37
|
+
else
|
38
|
+
v
|
39
|
+
end
|
40
|
+
end
|
41
|
+
new_hash
|
42
|
+
end
|
33
43
|
end
|
34
44
|
end
|
35
45
|
end
|
@@ -2,23 +2,24 @@ require 'fspath'
|
|
2
2
|
require 'image_optim/image_meta'
|
3
3
|
|
4
4
|
class ImageOptim
|
5
|
+
# FSPath with additional helpful methods
|
5
6
|
class ImagePath < FSPath
|
7
|
+
# Holds optimized image with reference to original and its size
|
6
8
|
class Optimized < DelegateClass(self)
|
7
9
|
def initialize(path, original_or_size = nil)
|
8
10
|
path = ImagePath.convert(path)
|
9
11
|
__setobj__(path)
|
10
|
-
if original_or_size
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@original_size = @original.size
|
17
|
-
end
|
12
|
+
if original_or_size.is_a?(Integer)
|
13
|
+
@original = path
|
14
|
+
@original_size = original_or_size
|
15
|
+
elsif original_or_size
|
16
|
+
@original = ImagePath.convert(original_or_size)
|
17
|
+
@original_size = @original.size
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
# Original path, use original_size to get its size as original can be
|
21
|
+
# Original path, use original_size to get its size as original can be
|
22
|
+
# overwritten
|
22
23
|
attr_reader :original
|
23
24
|
|
24
25
|
# Stored size of original
|
@@ -49,12 +50,12 @@ class ImageOptim
|
|
49
50
|
|
50
51
|
# Get format using ImageSize
|
51
52
|
def format
|
52
|
-
|
53
|
-
|
54
|
-
end
|
53
|
+
image_meta = ImageMeta.for_path(self)
|
54
|
+
image_meta && image_meta.format
|
55
55
|
end
|
56
56
|
|
57
|
-
# Returns path if it is already an instance of this class otherwise new
|
57
|
+
# Returns path if it is already an instance of this class otherwise new
|
58
|
+
# instance
|
58
59
|
def self.convert(path)
|
59
60
|
path.is_a?(self) ? path : new(path)
|
60
61
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
-
class
|
1
|
+
class ImageOptim
|
2
|
+
# Hold information about an option
|
3
|
+
class OptionDefinition
|
4
|
+
attr_reader :name, :default, :type, :description, :proc
|
2
5
|
|
3
|
-
|
6
|
+
def initialize(name, default, type, description, &proc)
|
7
|
+
if type.is_a?(String)
|
8
|
+
type, description = default.class, type
|
9
|
+
end
|
4
10
|
|
5
|
-
|
6
|
-
|
7
|
-
type,
|
11
|
+
@name = name.to_sym
|
12
|
+
@description = description.to_s
|
13
|
+
@default, @type, @proc = default, type, proc
|
8
14
|
end
|
9
|
-
|
10
|
-
@name = name.to_sym
|
11
|
-
@description = description.to_s
|
12
|
-
@default, @type, @proc = default, type, proc
|
13
15
|
end
|
14
16
|
end
|
data/lib/image_optim/railtie.rb
CHANGED
@@ -1,28 +1,31 @@
|
|
1
1
|
require 'image_optim'
|
2
2
|
|
3
3
|
class ImageOptim
|
4
|
+
# Adds image_optim as preprocessor for gif, jpeg, png and svg images
|
4
5
|
class Railtie < Rails::Railtie
|
5
6
|
initializer 'image_optim.initializer' do |app|
|
6
|
-
if app.config.assets.compress != false && app.config.assets.image_optim != false && app.assets
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
app.config.assets.image_optim || {}
|
12
|
-
end
|
8
|
+
break if app.config.assets.compress == false
|
9
|
+
break if app.config.assets.image_optim == false
|
10
|
+
break unless app.assets
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
options = if app.config.assets.image_optim == true
|
13
|
+
{}
|
14
|
+
else
|
15
|
+
app.config.assets.image_optim || {}
|
16
|
+
end
|
19
17
|
|
20
|
-
|
21
|
-
app.assets.register_preprocessor 'image/jpeg', :image_optim, &processor
|
22
|
-
app.assets.register_preprocessor 'image/png', :image_optim, &processor
|
23
|
-
app.assets.register_preprocessor 'image/svg+xml', :image_optim, &processor
|
18
|
+
image_optim = ImageOptim.new(options)
|
24
19
|
|
20
|
+
processor = proc do |_context, data|
|
21
|
+
image_optim.optimize_image_data(data) || data
|
25
22
|
end
|
23
|
+
|
24
|
+
app.assets.register_preprocessor 'image/gif', :image_optim, &processor
|
25
|
+
app.assets.register_preprocessor 'image/jpeg', :image_optim, &processor
|
26
|
+
app.assets.register_preprocessor 'image/png', :image_optim, &processor
|
27
|
+
app.assets.register_preprocessor 'image/svg+xml', :image_optim, &processor
|
28
|
+
|
26
29
|
end
|
27
30
|
end
|
28
31
|
end
|
data/lib/image_optim/runner.rb
CHANGED
@@ -1,67 +1,69 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
require 'image_optim'
|
4
2
|
require 'image_optim/hash_helpers'
|
5
3
|
require 'image_optim/true_false_nil'
|
4
|
+
require 'image_optim/space'
|
6
5
|
require 'progress'
|
7
6
|
require 'optparse'
|
8
7
|
require 'find'
|
9
8
|
require 'yaml'
|
10
9
|
|
11
10
|
class ImageOptim
|
11
|
+
# Handling optimization using image_optim binary
|
12
12
|
class Runner
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
# Collect and output results of optimization
|
14
|
+
class Results
|
15
|
+
def initialize
|
16
|
+
@lines = []
|
17
|
+
@original_size_sum = 0
|
18
|
+
@optimized_size_sum = 0
|
19
|
+
end
|
17
20
|
|
18
|
-
|
21
|
+
def add(original, optimized)
|
22
|
+
original_size = optimized ? optimized.original_size : original.size
|
23
|
+
optimized_size = optimized ? optimized.size : original.size
|
24
|
+
@lines << "#{size_percent(original_size, optimized_size)} #{original}"
|
25
|
+
@original_size_sum += original_size
|
26
|
+
@optimized_size_sum += optimized_size
|
27
|
+
end
|
19
28
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
29
|
+
def print
|
30
|
+
puts @lines
|
31
|
+
puts "Total: #{size_percent(@original_size_sum, @optimized_size_sum)}"
|
32
|
+
end
|
25
33
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
34
|
+
private
|
35
|
+
|
36
|
+
def size_percent(size_a, size_b)
|
37
|
+
if size_a == size_b
|
38
|
+
"------ #{Space::EMPTY_SPACE}"
|
39
|
+
else
|
40
|
+
percent = 100 - 100.0 * size_b / size_a
|
41
|
+
space = Space.space(size_a - size_b)
|
42
|
+
format('%5.2f%% %s', percent, space)
|
36
43
|
end
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
40
47
|
def initialize(args, options)
|
41
|
-
|
48
|
+
fail 'specify paths to optimize' if args.empty?
|
42
49
|
options = HashHelpers.deep_symbolise_keys(options)
|
43
50
|
@recursive = options.delete(:recursive)
|
44
51
|
@image_optim = ImageOptim.new(options)
|
45
|
-
@
|
52
|
+
@to_optimize = find_to_optimize(args)
|
46
53
|
end
|
47
54
|
|
48
55
|
def run!
|
49
|
-
unless @
|
50
|
-
|
51
|
-
@image_optim.optimize_images!(@files.with_progress('optimizing')) do |original, optimized|
|
52
|
-
original_size = optimized ? optimized.original_size : original.size
|
53
|
-
optimized_size = optimized ? optimized.size : original.size
|
54
|
-
["#{size_percent(original_size, optimized_size)} #{original}", original_size, optimized_size]
|
55
|
-
end.transpose
|
56
|
-
|
57
|
-
puts lines, "Total: #{size_percent(original_sizes.inject(:+), optimized_sizes.inject(:+))}"
|
58
|
-
end
|
56
|
+
unless @to_optimize.empty?
|
57
|
+
results = Results.new
|
59
58
|
|
60
|
-
|
61
|
-
|
59
|
+
optimize_images! do |original, optimized|
|
60
|
+
results.add(original, optimized)
|
61
|
+
end
|
62
62
|
|
63
|
-
|
64
|
-
|
63
|
+
results.print
|
64
|
+
end
|
65
|
+
|
66
|
+
!@warnings
|
65
67
|
end
|
66
68
|
|
67
69
|
def self.run!(args, options)
|
@@ -70,42 +72,46 @@ class ImageOptim
|
|
70
72
|
|
71
73
|
private
|
72
74
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
def optimize_images!(&block)
|
76
|
+
@image_optim.
|
77
|
+
optimize_images!(@to_optimize.with_progress('optimizing'), &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
def find_to_optimize(paths)
|
81
|
+
to_optimize = []
|
82
|
+
paths.each do |path|
|
83
|
+
if File.file?(path)
|
84
|
+
if @image_optim.optimizable?(path)
|
85
|
+
to_optimize << path
|
79
86
|
else
|
80
|
-
warning "#{
|
87
|
+
warning "#{path} is not an image or there is no optimizer for it"
|
81
88
|
end
|
82
89
|
elsif @recursive
|
83
|
-
if File.directory?(
|
84
|
-
|
85
|
-
files << path if File.file?(path) && @image_optim.optimizable?(path)
|
86
|
-
end
|
90
|
+
if File.directory?(path)
|
91
|
+
to_optimize += find_to_optimize_recursive(path)
|
87
92
|
else
|
88
|
-
warning "#{
|
93
|
+
warning "#{path} is not a file or a directory or does not exist"
|
89
94
|
end
|
90
95
|
else
|
91
|
-
warning "#{
|
96
|
+
warning "#{path} is not a file or does not exist"
|
92
97
|
end
|
93
98
|
end
|
94
|
-
|
99
|
+
to_optimize
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_to_optimize_recursive(dir)
|
103
|
+
to_optimize = []
|
104
|
+
Find.find(dir) do |path|
|
105
|
+
next unless File.file?(path)
|
106
|
+
next unless @image_optim.optimizable?(path)
|
107
|
+
to_optimize << path
|
108
|
+
end
|
109
|
+
to_optimize
|
95
110
|
end
|
96
111
|
|
97
112
|
def warning(message)
|
98
113
|
@warnings = true
|
99
114
|
warn message
|
100
115
|
end
|
101
|
-
|
102
|
-
def size_percent(size_a, size_b)
|
103
|
-
if size_a == size_b
|
104
|
-
"------ #{Space::EMPTY_SPACE}"
|
105
|
-
else
|
106
|
-
'%5.2f%% %s' % [100 - 100.0 * size_b / size_a, Space.space(size_a - size_b)]
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
116
|
end
|
111
117
|
end
|