image_optim 0.9.1 → 0.10.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/.travis.yml +0 -2
- data/README.markdown +34 -1
- data/bin/image_optim +21 -113
- data/image_optim.gemspec +2 -2
- data/lib/image_optim/bin_not_found_error.rb +3 -0
- data/lib/image_optim/bin_resolver.rb +50 -0
- data/lib/image_optim/config.rb +137 -0
- data/lib/image_optim/configuration_error.rb +3 -0
- data/lib/image_optim/handler.rb +27 -0
- data/lib/image_optim/hash_helpers.rb +35 -0
- data/lib/image_optim/image_path.rb +27 -2
- data/lib/image_optim/option_helpers.rb +1 -20
- data/lib/image_optim/railtie.rb +19 -0
- data/lib/image_optim/runner.rb +107 -0
- data/lib/image_optim/true_false_nil.rb +3 -0
- data/lib/image_optim/worker/advpng.rb +1 -0
- data/lib/image_optim/worker/jpegoptim.rb +1 -0
- data/lib/image_optim/worker/optipng.rb +2 -0
- data/lib/image_optim/worker/pngout.rb +1 -0
- data/lib/image_optim/worker.rb +25 -6
- data/lib/image_optim.rb +60 -126
- data/script/update_worker_options_in_readme +36 -0
- data/spec/image_optim/bin_resolver_spec.rb +92 -0
- data/spec/image_optim/config_spec.rb +153 -0
- data/spec/image_optim/handler_spec.rb +44 -0
- data/spec/image_optim/hash_helpers_spec.rb +74 -0
- data/spec/image_optim/image_path_spec.rb +39 -0
- data/spec/image_optim_spec.rb +26 -94
- metadata +24 -6
- data/TODO +0 -12
- data/script/options_for_readme +0 -14
@@ -0,0 +1,107 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'image_optim'
|
4
|
+
require 'image_optim/hash_helpers'
|
5
|
+
require 'image_optim/true_false_nil'
|
6
|
+
require 'progress'
|
7
|
+
require 'optparse'
|
8
|
+
require 'find'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
class ImageOptim
|
12
|
+
class Runner
|
13
|
+
module Space
|
14
|
+
SIZE_SYMBOLS = %w[B K M G T P E].freeze
|
15
|
+
PRECISION = 1
|
16
|
+
LENGTH = 4 + PRECISION + 1
|
17
|
+
|
18
|
+
EMPTY_SPACE = ' ' * LENGTH
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_writer :base10
|
22
|
+
def denominator
|
23
|
+
@denominator ||= @base10 ? 1000.0 : 1024.0
|
24
|
+
end
|
25
|
+
|
26
|
+
def space(size)
|
27
|
+
case size
|
28
|
+
when 0, nil
|
29
|
+
EMPTY_SPACE
|
30
|
+
else
|
31
|
+
log_denominator = Math.log(size) / Math.log(denominator)
|
32
|
+
degree = [log_denominator.floor, SIZE_SYMBOLS.length - 1].min
|
33
|
+
number = size / (denominator ** degree)
|
34
|
+
"#{degree == 0 ? number.to_i : "%.#{PRECISION}f" % number}#{SIZE_SYMBOLS[degree]}".rjust(LENGTH)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(args, options)
|
41
|
+
raise 'specify paths to optimize' if args.empty?
|
42
|
+
options = HashHelpers.deep_symbolise_keys(options)
|
43
|
+
@recursive = options.delete(:recursive)
|
44
|
+
@image_optim = ImageOptim.new(options)
|
45
|
+
@files = find_files(args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def run!
|
49
|
+
unless @files.empty?
|
50
|
+
lines, original_sizes, optimized_sizes =
|
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
|
59
|
+
|
60
|
+
!warnings?
|
61
|
+
end
|
62
|
+
|
63
|
+
def warnings?
|
64
|
+
!!@warnings
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.run!(args, options)
|
68
|
+
new(args, options).run!
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def find_files(args)
|
74
|
+
files = []
|
75
|
+
args.each do |arg|
|
76
|
+
if File.file?(arg)
|
77
|
+
if @image_optim.optimizable?(arg)
|
78
|
+
files << arg
|
79
|
+
else
|
80
|
+
warning "#{arg} is not an image or there is no optimizer for it"
|
81
|
+
end
|
82
|
+
elsif @recursive && File.directory?(arg)
|
83
|
+
Find.find(arg) do |path|
|
84
|
+
files << path if File.file?(path) && @image_optim.optimizable?(path)
|
85
|
+
end
|
86
|
+
else
|
87
|
+
warning "#{arg} does not exist"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
files
|
91
|
+
end
|
92
|
+
|
93
|
+
def warning(message)
|
94
|
+
@warnings = true
|
95
|
+
warn message
|
96
|
+
end
|
97
|
+
|
98
|
+
def size_percent(size_a, size_b)
|
99
|
+
if size_a == size_b
|
100
|
+
"------ #{Space::EMPTY_SPACE}"
|
101
|
+
else
|
102
|
+
'%5.2f%% %s' % [100 - 100.0 * size_b / size_a, Space.space(size_a - size_b)]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
data/lib/image_optim/worker.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
+
require 'image_optim/option_definition'
|
4
|
+
require 'image_optim/option_helpers'
|
3
5
|
require 'shellwords'
|
4
6
|
|
5
|
-
require 'image_optim'
|
6
|
-
|
7
7
|
class ImageOptim
|
8
8
|
class Worker
|
9
9
|
class << self
|
@@ -32,15 +32,22 @@ class ImageOptim
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
include OptionHelpers
|
36
|
-
|
37
35
|
# Configure (raises on extra options)
|
38
36
|
def initialize(image_optim, options = {})
|
39
37
|
@image_optim = image_optim
|
40
38
|
self.class.option_definitions.each do |option_definition|
|
41
|
-
|
39
|
+
value = if options.has_key?(option_definition.name)
|
40
|
+
options[option_definition.name]
|
41
|
+
else
|
42
|
+
option_definition.default
|
43
|
+
end
|
44
|
+
if option_definition.proc
|
45
|
+
value = option_definition.proc[value]
|
46
|
+
end
|
47
|
+
instance_variable_set("@#{option_definition.name}", value)
|
42
48
|
end
|
43
|
-
|
49
|
+
|
50
|
+
assert_no_unknown_options!(options)
|
44
51
|
end
|
45
52
|
|
46
53
|
# List of formats which worker can optimize
|
@@ -55,6 +62,10 @@ class ImageOptim
|
|
55
62
|
0
|
56
63
|
end
|
57
64
|
|
65
|
+
def <=>(other)
|
66
|
+
run_order <=> other.run_order
|
67
|
+
end
|
68
|
+
|
58
69
|
# Check if operation resulted in optimized file
|
59
70
|
def optimized?(src, dst)
|
60
71
|
dst.size? && dst.size < src.size
|
@@ -62,6 +73,14 @@ class ImageOptim
|
|
62
73
|
|
63
74
|
private
|
64
75
|
|
76
|
+
def assert_no_unknown_options!(options)
|
77
|
+
known_keys = self.class.option_definitions.map(&:name)
|
78
|
+
unknown_options = options.reject{ |key, value| known_keys.include?(key) }
|
79
|
+
unless unknown_options.empty?
|
80
|
+
raise ConfigurationError, "unknown options #{unknown_options.inspect} for #{self}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
65
84
|
# Forward bin resolving to image_optim
|
66
85
|
def resolve_bin!(bin)
|
67
86
|
@image_optim.resolve_bin!(bin)
|
data/lib/image_optim.rb
CHANGED
@@ -1,19 +1,12 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
|
1
|
+
require 'image_optim/bin_resolver'
|
2
|
+
require 'image_optim/config'
|
3
|
+
require 'image_optim/handler'
|
4
4
|
require 'image_optim/image_path'
|
5
|
-
require 'image_optim/option_helpers'
|
6
|
-
require 'image_optim/option_definition'
|
7
5
|
require 'image_optim/worker'
|
6
|
+
require 'in_threads'
|
7
|
+
require 'shellwords'
|
8
8
|
|
9
9
|
class ImageOptim
|
10
|
-
class ConfigurationError < StandardError; end
|
11
|
-
class BinNotFoundError < StandardError; end
|
12
|
-
|
13
|
-
class TrueFalseNil; end
|
14
|
-
|
15
|
-
include OptionHelpers
|
16
|
-
|
17
10
|
# Nice level
|
18
11
|
attr_reader :nice
|
19
12
|
|
@@ -43,88 +36,71 @@ class ImageOptim
|
|
43
36
|
#
|
44
37
|
# ImageOptim.new(:nice => 20)
|
45
38
|
def initialize(options = {})
|
46
|
-
@
|
47
|
-
@resolver_lock = Mutex.new
|
48
|
-
|
49
|
-
nice = options.delete(:nice)
|
50
|
-
@nice = case nice
|
51
|
-
when true, nil
|
52
|
-
10
|
53
|
-
when false
|
54
|
-
0
|
55
|
-
else
|
56
|
-
nice.to_i
|
57
|
-
end
|
39
|
+
@bin_resolver = BinResolver.new
|
58
40
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
when false
|
64
|
-
1
|
65
|
-
else
|
66
|
-
threads.to_i
|
67
|
-
end
|
68
|
-
@threads = OptionHelpers.limit_with_range(threads, 1..16)
|
69
|
-
|
70
|
-
@verbose = !!options.delete(:verbose)
|
41
|
+
config = Config.new(options)
|
42
|
+
@nice = config.nice
|
43
|
+
@threads = config.threads
|
44
|
+
@verbose = config.verbose
|
71
45
|
|
72
46
|
@workers_by_format = {}
|
73
47
|
Worker.klasses.each do |klass|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
else
|
81
|
-
raise ConfigurationError, "Got #{worker_options.inspect} for #{klass.name} options"
|
82
|
-
end
|
83
|
-
worker = klass.new(self, worker_options)
|
84
|
-
worker.image_formats.each do |format|
|
85
|
-
@workers_by_format[format] ||= []
|
86
|
-
@workers_by_format[format] << worker
|
48
|
+
if worker_options = config.for_worker(klass)
|
49
|
+
worker = klass.new(self, worker_options)
|
50
|
+
worker.image_formats.each do |format|
|
51
|
+
@workers_by_format[format] ||= []
|
52
|
+
@workers_by_format[format] << worker
|
53
|
+
end
|
87
54
|
end
|
88
55
|
end
|
89
|
-
@workers_by_format.each
|
90
|
-
workers.replace workers.sort_by(&:run_order) # There is no sort_by! in ruby 1.8
|
91
|
-
end
|
56
|
+
@workers_by_format.values.each(&:sort!)
|
92
57
|
|
93
|
-
|
58
|
+
config.assert_no_unused_options!
|
59
|
+
|
60
|
+
puts config if verbose?
|
94
61
|
end
|
95
62
|
|
96
63
|
# Get workers for image
|
97
64
|
def workers_for_image(path)
|
98
|
-
@workers_by_format[ImagePath.
|
65
|
+
@workers_by_format[ImagePath.convert(path).format]
|
99
66
|
end
|
100
67
|
|
101
|
-
# Optimize one file, return new path or nil if optimization failed
|
68
|
+
# Optimize one file, return new path as OptimizedImagePath or nil if optimization failed
|
102
69
|
def optimize_image(original)
|
103
|
-
original = ImagePath.
|
70
|
+
original = ImagePath.convert(original)
|
104
71
|
if workers = workers_for_image(original)
|
105
|
-
|
106
|
-
ts = [original, original.temp_path]
|
72
|
+
handler = Handler.new(original)
|
107
73
|
workers.each do |worker|
|
108
|
-
|
109
|
-
|
110
|
-
end
|
111
|
-
if worker.optimize(*ts.last(2))
|
112
|
-
result = ts.last
|
113
|
-
if ts.length == 3
|
114
|
-
ts[-2, 2] = ts[-1], ts[-2]
|
115
|
-
end
|
74
|
+
handler.process do |src, dst|
|
75
|
+
worker.optimize(src, dst)
|
116
76
|
end
|
117
77
|
end
|
118
|
-
result
|
78
|
+
if handler.result
|
79
|
+
ImagePath::Optimized.new(handler.result, original)
|
80
|
+
end
|
119
81
|
end
|
120
82
|
end
|
121
83
|
|
122
|
-
# Optimize one file in place, return optimization
|
84
|
+
# Optimize one file in place, return original as OptimizedImagePath or nil if optimization failed
|
123
85
|
def optimize_image!(original)
|
124
|
-
original = ImagePath.
|
86
|
+
original = ImagePath.convert(original)
|
125
87
|
if result = optimize_image(original)
|
126
88
|
result.replace(original)
|
127
|
-
|
89
|
+
ImagePath::Optimized.new(original, result.original_size)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Optimize image data, return new data or nil if optimization failed
|
94
|
+
def optimize_image_data(original_data)
|
95
|
+
format = ImageSize.new(original_data).format
|
96
|
+
ImagePath.temp_file %W[image_optim .#{format}] do |temp|
|
97
|
+
temp.binmode
|
98
|
+
temp.write(original_data)
|
99
|
+
temp.close
|
100
|
+
|
101
|
+
if result = optimize_image(temp.path)
|
102
|
+
result.read
|
103
|
+
end
|
128
104
|
end
|
129
105
|
end
|
130
106
|
|
@@ -132,19 +108,26 @@ class ImageOptim
|
|
132
108
|
# if block given yields path and result for each image and returns array of yield results
|
133
109
|
# else return array of results
|
134
110
|
def optimize_images(paths, &block)
|
135
|
-
run_method_for(paths, :optimize_image, &block)
|
111
|
+
run_method_for(paths.map{ |path| ImagePath.convert(path) }, :optimize_image, &block)
|
136
112
|
end
|
137
113
|
|
138
114
|
# Optimize multiple images in place
|
139
115
|
# if block given yields path and result for each image and returns array of yield results
|
140
116
|
# else return array of results
|
141
117
|
def optimize_images!(paths, &block)
|
142
|
-
run_method_for(paths, :optimize_image!, &block)
|
118
|
+
run_method_for(paths.map{ |path| ImagePath.convert(path) }, :optimize_image!, &block)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Optimize multiple image datas
|
122
|
+
# if block given yields original and result for each image data and returns array of yield results
|
123
|
+
# else return array of results
|
124
|
+
def optimize_images_data(datas, &block)
|
125
|
+
run_method_for(datas, :optimize_image_data, &block)
|
143
126
|
end
|
144
127
|
|
145
128
|
# Optimization methods with default options
|
146
129
|
def self.method_missing(method, *args, &block)
|
147
|
-
if method.to_s =~ /^
|
130
|
+
if method_defined?(method) && method.to_s =~ /^optimize_image/
|
148
131
|
new.send(method, *args, &block)
|
149
132
|
else
|
150
133
|
super
|
@@ -161,37 +144,14 @@ class ImageOptim
|
|
161
144
|
!!workers_for_image(path)
|
162
145
|
end
|
163
146
|
|
164
|
-
# Temp directory for symlinks to bins with path coming from ENV
|
165
|
-
attr_reader :resolve_dir
|
166
|
-
|
167
147
|
# Check existance of binary, create symlink if ENV contains path for key XXX_BIN where XXX is upper case bin name
|
168
148
|
def resolve_bin!(bin)
|
169
|
-
bin
|
170
|
-
@resolved_bins.include?(bin) || @resolver_lock.synchronize do
|
171
|
-
@resolved_bins.include?(bin) || begin
|
172
|
-
if path = ENV["#{bin}_bin".upcase]
|
173
|
-
unless @resolve_dir
|
174
|
-
@resolve_dir = FSPath.temp_dir
|
175
|
-
at_exit{ FileUtils.remove_entry_secure @resolve_dir }
|
176
|
-
end
|
177
|
-
symlink = @resolve_dir / bin
|
178
|
-
symlink.make_symlink(File.expand_path(path))
|
179
|
-
at_exit{ symlink.unlink }
|
180
|
-
|
181
|
-
@resolved_bins[bin] = bin_accessible?(symlink)
|
182
|
-
else
|
183
|
-
@resolved_bins[bin] = bin_accessible?(bin)
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
@resolved_bins[bin] or raise BinNotFoundError, "`#{bin}` not found"
|
149
|
+
@bin_resolver.resolve!(bin)
|
188
150
|
end
|
189
151
|
|
190
|
-
VENDOR_PATH = File.expand_path('../../vendor', __FILE__)
|
191
|
-
|
192
152
|
# Join resolve_dir, default path and vendor path for PATH environment variable
|
193
153
|
def env_path
|
194
|
-
|
154
|
+
@bin_resolver.env_path
|
195
155
|
end
|
196
156
|
|
197
157
|
private
|
@@ -199,7 +159,6 @@ private
|
|
199
159
|
# Run method for each path and yield each path and result if block given
|
200
160
|
def run_method_for(paths, method_name, &block)
|
201
161
|
apply_threading(paths).map do |path|
|
202
|
-
path = ImagePath.new(path)
|
203
162
|
result = send(method_name, path)
|
204
163
|
if block
|
205
164
|
block.call(path, result)
|
@@ -217,33 +176,6 @@ private
|
|
217
176
|
enum
|
218
177
|
end
|
219
178
|
end
|
220
|
-
|
221
|
-
# Check if bin can be accessed
|
222
|
-
def bin_accessible?(bin)
|
223
|
-
`env PATH=#{env_path.shellescape} which #{bin.to_s.shellescape}` != ''
|
224
|
-
end
|
225
|
-
|
226
|
-
# http://stackoverflow.com/questions/891537/ruby-detect-number-of-cpus-installed
|
227
|
-
def processor_count
|
228
|
-
@processor_count ||= case host_os = RbConfig::CONFIG['host_os']
|
229
|
-
when /darwin9/
|
230
|
-
`hwprefs cpu_count`
|
231
|
-
when /darwin/
|
232
|
-
(`which hwprefs` != '') ? `hwprefs thread_count` : `sysctl -n hw.ncpu`
|
233
|
-
when /linux/
|
234
|
-
`grep -c processor /proc/cpuinfo`
|
235
|
-
when /freebsd/
|
236
|
-
`sysctl -n hw.ncpu`
|
237
|
-
when /mswin|mingw/
|
238
|
-
require 'win32ole'
|
239
|
-
wmi = WIN32OLE.connect('winmgmts://')
|
240
|
-
cpu = wmi.ExecQuery('select NumberOfLogicalProcessors from Win32_Processor')
|
241
|
-
cpu.to_enum.first.NumberOfLogicalProcessors
|
242
|
-
else
|
243
|
-
warn "Unknown architecture (#{host_os}) assuming one processor."
|
244
|
-
1
|
245
|
-
end.to_i
|
246
|
-
end
|
247
179
|
end
|
248
180
|
|
249
181
|
%w[
|
@@ -253,3 +185,5 @@ end
|
|
253
185
|
].each do |worker|
|
254
186
|
require "image_optim/worker/#{worker}"
|
255
187
|
end
|
188
|
+
|
189
|
+
require 'image_optim/railtie' if defined?(Rails)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
5
|
+
|
6
|
+
require 'image_optim'
|
7
|
+
|
8
|
+
README_FILE = File.expand_path('../../README.markdown', __FILE__)
|
9
|
+
BEGIN_MARKER = '<!---<worker-options>-->'
|
10
|
+
END_MARKER = '<!---</worker-options>-->'
|
11
|
+
|
12
|
+
def worker_options
|
13
|
+
io = StringIO.new
|
14
|
+
|
15
|
+
ImageOptim::Worker.klasses.each_with_index do |klass, i|
|
16
|
+
unless klass.option_definitions.empty?
|
17
|
+
io.puts "### #{klass.bin_sym}"
|
18
|
+
klass.option_definitions.each do |option_definition|
|
19
|
+
io.puts "* `:#{option_definition.name}` — #{option_definition.description} *(defaults to #{option_definition.default})*"
|
20
|
+
end
|
21
|
+
io.puts
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
io.string
|
26
|
+
end
|
27
|
+
|
28
|
+
readme = File.read(README_FILE)
|
29
|
+
|
30
|
+
if readme.sub!(/#{Regexp.escape(BEGIN_MARKER)}.*#{Regexp.escape(END_MARKER)}/m, "#{BEGIN_MARKER}\n\n#{worker_options.strip}\n\n#{END_MARKER}")
|
31
|
+
File.open(README_FILE, 'w') do |f|
|
32
|
+
f.write readme
|
33
|
+
end
|
34
|
+
else
|
35
|
+
abort "Did not update worker options"
|
36
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
$:.unshift File.expand_path('../../../lib', __FILE__)
|
2
|
+
require 'rspec'
|
3
|
+
require 'image_optim/bin_resolver'
|
4
|
+
|
5
|
+
def with_env(key, value)
|
6
|
+
saved, ENV[key] = ENV[key], value
|
7
|
+
yield
|
8
|
+
ensure
|
9
|
+
ENV[key] = saved
|
10
|
+
end
|
11
|
+
|
12
|
+
describe ImageOptim::BinResolver do
|
13
|
+
it "should resolve bin in path" do
|
14
|
+
with_env 'LS_BIN', nil do
|
15
|
+
resolver = ImageOptim::BinResolver.new
|
16
|
+
resolver.should_receive(:accessible?).with(:ls).once.and_return(true)
|
17
|
+
FSPath.should_not_receive(:temp_dir)
|
18
|
+
|
19
|
+
5.times do
|
20
|
+
resolver.resolve!(:ls).should be_true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should resolve bin specified in ENV" do
|
26
|
+
path = (FSPath(__FILE__).dirname / '../bin/image_optim').relative_path_from(Dir.pwd).to_s
|
27
|
+
with_env 'IMAGE_OPTIM_BIN', path do
|
28
|
+
tmpdir = double(:tmpdir)
|
29
|
+
symlink = double(:symlink)
|
30
|
+
|
31
|
+
resolver = ImageOptim::BinResolver.new
|
32
|
+
resolver.should_receive(:accessible?).with(symlink).once.and_return(true)
|
33
|
+
FSPath.should_receive(:temp_dir).once.and_return(tmpdir)
|
34
|
+
tmpdir.should_receive(:/).with(:image_optim).once.and_return(symlink)
|
35
|
+
symlink.should_receive(:make_symlink).with(File.expand_path(path)).once
|
36
|
+
|
37
|
+
at_exit_blocks = []
|
38
|
+
resolver.should_receive(:at_exit).once do |&block|
|
39
|
+
at_exit_blocks.unshift(block)
|
40
|
+
end
|
41
|
+
|
42
|
+
5.times do
|
43
|
+
resolver.resolve!(:image_optim).should be_true
|
44
|
+
end
|
45
|
+
|
46
|
+
FileUtils.should_receive(:remove_entry_secure).with(tmpdir)
|
47
|
+
at_exit_blocks.each(&:call)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should raise on failure to resolve bin" do
|
52
|
+
with_env 'SHOULD_NOT_EXIST_BIN', nil do
|
53
|
+
resolver = ImageOptim::BinResolver.new
|
54
|
+
resolver.should_receive(:accessible?).with(:should_not_exist).once.and_return(false)
|
55
|
+
FSPath.should_not_receive(:temp_dir)
|
56
|
+
|
57
|
+
5.times do
|
58
|
+
expect do
|
59
|
+
resolver.resolve!(:should_not_exist)
|
60
|
+
end.to raise_error ImageOptim::BinNotFoundError
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should raise on failure to resolve bin specified in ENV" do
|
66
|
+
path = (FSPath(__FILE__).dirname / '../bin/should_not_exist_bin').relative_path_from(Dir.pwd).to_s
|
67
|
+
with_env 'SHOULD_NOT_EXIST_BIN', path do
|
68
|
+
tmpdir = double(:tmpdir)
|
69
|
+
symlink = double(:symlink)
|
70
|
+
|
71
|
+
resolver = ImageOptim::BinResolver.new
|
72
|
+
resolver.should_receive(:accessible?).with(symlink).once.and_return(false)
|
73
|
+
FSPath.should_receive(:temp_dir).once.and_return(tmpdir)
|
74
|
+
tmpdir.should_receive(:/).with(:should_not_exist).once.and_return(symlink)
|
75
|
+
symlink.should_receive(:make_symlink).with(File.expand_path(path)).once
|
76
|
+
|
77
|
+
at_exit_blocks = []
|
78
|
+
resolver.should_receive(:at_exit).once do |&block|
|
79
|
+
at_exit_blocks.unshift(block)
|
80
|
+
end
|
81
|
+
|
82
|
+
5.times do
|
83
|
+
expect do
|
84
|
+
resolver.resolve!(:should_not_exist)
|
85
|
+
end.to raise_error ImageOptim::BinNotFoundError
|
86
|
+
end
|
87
|
+
|
88
|
+
FileUtils.should_receive(:remove_entry_secure).with(tmpdir)
|
89
|
+
at_exit_blocks.each(&:call)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|