discourse_image_optim 0.26.1 → 0.26.2
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 +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.markdown +4 -5
- data/README.markdown +36 -60
- data/image_optim.gemspec +1 -1
- data/lib/image_optim.rb +48 -3
- data/lib/image_optim/cmd.rb +19 -72
- data/lib/image_optim/config.rb +5 -5
- data/lib/image_optim/runner/option_parser.rb +2 -2
- data/lib/image_optim/worker.rb +3 -13
- data/lib/image_optim/worker/advpng.rb +0 -2
- data/lib/image_optim/worker/class_methods.rb +2 -15
- data/lib/image_optim/worker/gifsicle.rb +0 -2
- data/lib/image_optim/worker/jhead.rb +0 -2
- data/lib/image_optim/worker/jpegoptim.rb +0 -2
- data/lib/image_optim/worker/jpegrecompress.rb +0 -2
- data/lib/image_optim/worker/jpegtran.rb +0 -2
- data/lib/image_optim/worker/optipng.rb +0 -2
- data/lib/image_optim/worker/pngcrush.rb +0 -2
- data/lib/image_optim/worker/pngout.rb +0 -2
- data/lib/image_optim/worker/pngquant.rb +0 -2
- data/lib/image_optim/worker/svgo.rb +0 -2
- data/spec/image_optim/cmd_spec.rb +8 -30
- data/spec/image_optim/config_spec.rb +16 -16
- data/spec/image_optim/worker_spec.rb +12 -31
- data/spec/image_optim_spec.rb +15 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79257562b4927a5ec9d03fbbbc649b45dd060a3567897b0b0c10e29fbb759561
|
4
|
+
data.tar.gz: 524acbb993b15449b795f1aaa1c49bd477dbf18f102c2c516f3eafbb1a9dc194
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3aa22f9ea0ee4fb7a2aaaca83a577a842105b77fbe9f8399239d051a687b5c181a99f4d8cd4208b1dba8a6eb84d996a0980cd9b3182c11a4963bddf5979ed99c
|
7
|
+
data.tar.gz: 7e74d060fc2d3977441a99dbb971a5961f9d414cf286de48702bec28ae259230540994497b95a8d45ba5e129a554f8393032bbbbb1ba6952d0e64762045bdfde
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
@@ -2,13 +2,12 @@
|
|
2
2
|
|
3
3
|
## unreleased
|
4
4
|
|
5
|
+
* Add `timeout` option to ImageOptim [#21](https://github.com/toy/image_optim/issues/21) [@tgxworld](http://github.com/tgxworld)
|
6
|
+
|
5
7
|
## v0.26.1 (2017-12-14)
|
6
8
|
|
7
9
|
* Require `'date'` which is used in parsing pngout version [toy/image_optim_pack#14](https://github.com/toy/image_optim_pack/issues/14) [@toy](https://github.com/toy)
|
8
10
|
|
9
|
-
* Add `timeout` option to workers [#21](https://github.com/toy/image_optim/issues/21)
|
10
|
-
[@tgxworld](http://github.com/tgxworld)
|
11
|
-
|
12
11
|
## v0.26.0 (2017-11-13)
|
13
12
|
|
14
13
|
* Enable `jpegrescan` by default after removing its dependency on `File::Slurp` and fixing for windows [#153](https://github.com/toy/image_optim/issues/153) [@toy](https://github.com/toy)
|
@@ -36,7 +35,7 @@
|
|
36
35
|
|
37
36
|
## v0.24.0 (2016-08-14)
|
38
37
|
|
39
|
-
* Rails image assets optimization is extracted into [
|
38
|
+
* Rails image assets optimization is extracted into [image\_optim\_rails gem](https://github.com/toy/image_optim_rails) [#127](https://github.com/toy/image_optim/issues/127) [@toy](https://github.com/toy)
|
40
39
|
* Add proper handling of `ImageOptim.respond_to?` [@toy](https://github.com/toy)
|
41
40
|
* Fix an issue not working OptiPNG `interlace` option [#136](https://github.com/toy/image_optim/pull/136) [@mrk21](https://github.com/mrk21)
|
42
41
|
* Minimize number of file system calls in default implementation of `optimized?` [#137](https://github.com/toy/image_optim/issues/137) [@toy](https://github.com/toy)
|
@@ -283,7 +282,7 @@
|
|
283
282
|
|
284
283
|
* Warn if directly added files are not images or are not optimizable [@toy](https://github.com/toy)
|
285
284
|
* Recursively scan directories for images [#4](https://github.com/toy/image_optim/issues/4) [@toy](https://github.com/toy)
|
286
|
-
* Typo in bin/image_optim
|
285
|
+
* Typo in bin/image_optim [#3](https://github.com/toy/image_optim/issues/3) [@fabiomcosta](https://github.com/fabiomcosta)
|
287
286
|
|
288
287
|
## v0.5.1 (2012-08-07)
|
289
288
|
|
data/README.markdown
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
Optimize (lossless compress, optionally lossy) images (jpeg, png, gif, svg) using external utilities:
|
12
12
|
|
13
13
|
* [advpng](http://advancemame.sourceforge.net/doc-advpng.html) from [AdvanceCOMP](http://advancemame.sourceforge.net/comp-readme.html)
|
14
|
-
|
14
|
+
(will use [zopfli](https://code.google.com/p/zopfli/) on default/maximum level 4)
|
15
15
|
* [gifsicle](http://www.lcdf.org/gifsicle/)
|
16
16
|
* [jhead](http://www.sentex.net/~mwandel/jhead/)
|
17
17
|
* [jpegoptim](http://www.kokkonen.net/tjko/projects.html)
|
@@ -57,11 +57,9 @@ gem 'image_optim_pack'
|
|
57
57
|
With version:
|
58
58
|
|
59
59
|
<!---<update-version>-->
|
60
|
-
|
61
60
|
```ruby
|
62
61
|
gem 'image_optim', '~> 0.26'
|
63
62
|
```
|
64
|
-
|
65
63
|
<!---</update-version>-->
|
66
64
|
|
67
65
|
If you want to check latest changes:
|
@@ -243,7 +241,7 @@ image_optim.optimize_images_data(datas)
|
|
243
241
|
|
244
242
|
### From rails
|
245
243
|
|
246
|
-
Rails image assets optimization is extracted into [
|
244
|
+
Rails image assets optimization is extracted into [image\_optim\_rails gem](https://github.com/toy/image_optim_rails).
|
247
245
|
|
248
246
|
## Configuration
|
249
247
|
|
@@ -265,92 +263,70 @@ optipng:
|
|
265
263
|
|
266
264
|
## Options
|
267
265
|
|
268
|
-
* `:nice` — Nice level, priority of all used tools with higher value meaning lower priority, in range `-20..19`, negative values can be set only if run by root user
|
269
|
-
* `:threads` — Number of threads or disable
|
270
|
-
* `:verbose` — Verbose output
|
271
|
-
* `:pack` — Require
|
272
|
-
* `:skip_missing_workers` — Skip workers with missing or problematic binaries
|
273
|
-
* `:allow_lossy` — Allow lossy workers and optimizations
|
266
|
+
* `:nice` — Nice level, priority of all used tools with higher value meaning lower priority, in range `-20..19`, negative values can be set only if run by root user *(defaults to `10`)*
|
267
|
+
* `:threads` — Number of threads or disable *(defaults to number of processors)*
|
268
|
+
* `:verbose` — Verbose output *(defaults to `false`)*
|
269
|
+
* `:pack` — Require image\_optim\_pack or disable it, by default image\_optim\_pack will be used if available, will turn on `:skip-missing-workers` unless explicitly disabled *(defaults to `nil`)*
|
270
|
+
* `:skip_missing_workers` — Skip workers with missing or problematic binaries *(defaults to `false`)*
|
271
|
+
* `:allow_lossy` — Allow lossy workers and optimizations *(defaults to `false`)*
|
274
272
|
* `:cache_dir` — Configure cache directory
|
275
273
|
* `:cache_worker_digests` - Also cache worker digests along with original file digest and worker options: updating workers invalidates cache
|
276
|
-
* `:timeout`
|
274
|
+
* `:timeout` - Number of seconds before ImageOptim is timed out.
|
277
275
|
|
278
276
|
Worker can be disabled by passing `false` instead of options hash or by setting option `:disable` to `true`.
|
279
277
|
|
280
278
|
<!---<worker-options>-->
|
281
|
-
|
282
279
|
<!-- markdown for worker options is generated by `script/update_worker_options_in_readme` -->
|
283
280
|
|
284
281
|
### advpng:
|
285
|
-
|
286
|
-
* `:level` — Compression level: `0` - don't compress, `1` - fast, `2` - normal, `3` - extra, `4` - extreme _(defaults to `4`)_
|
287
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
282
|
+
* `:level` — Compression level: `0` - don't compress, `1` - fast, `2` - normal, `3` - extra, `4` - extreme *(defaults to `4`)*
|
288
283
|
|
289
284
|
### gifsicle:
|
290
|
-
|
291
285
|
* `:interlace` — Interlace: `true` - interlace on, `false` - interlace off, `nil` - as is in original image (defaults to running two instances, one with interlace off and one with on)
|
292
|
-
* `:level` — Compression level: `1` - light and fast, `2` - normal, `3` - heavy (slower)
|
293
|
-
* `:careful` — Avoid bugs with some software
|
294
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
286
|
+
* `:level` — Compression level: `1` - light and fast, `2` - normal, `3` - heavy (slower) *(defaults to `3`)*
|
287
|
+
* `:careful` — Avoid bugs with some software *(defaults to `false`)*
|
295
288
|
|
296
289
|
### jhead:
|
297
|
-
|
298
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
290
|
+
Worker has no options
|
299
291
|
|
300
292
|
### jpegoptim:
|
301
|
-
|
302
|
-
* `:
|
303
|
-
* `:
|
304
|
-
* `:max_quality` — Maximum image quality factor `0`..`100`, ignored in default/lossless mode _(defaults to `100`)_
|
305
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
293
|
+
* `:allow_lossy` — Allow limiting maximum quality *(defaults to `false`)*
|
294
|
+
* `:strip` — List of extra markers to strip: `:comments`, `:exif`, `:iptc`, `:icc` or `:all` *(defaults to `:all`)*
|
295
|
+
* `:max_quality` — Maximum image quality factor `0`..`100`, ignored in default/lossless mode *(defaults to `100`)*
|
306
296
|
|
307
297
|
### jpegrecompress:
|
308
|
-
|
309
|
-
* `:
|
310
|
-
* `:quality` — JPEG quality preset: `0` - low, `1` - medium, `2` - high, `3` - veryhigh _(defaults to `3`)_
|
311
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
298
|
+
* `:allow_lossy` — Allow worker, it is always lossy *(defaults to `false`)*
|
299
|
+
* `:quality` — JPEG quality preset: `0` - low, `1` - medium, `2` - high, `3` - veryhigh *(defaults to `3`)*
|
312
300
|
|
313
301
|
### jpegtran:
|
314
|
-
|
315
|
-
* `:
|
316
|
-
* `:
|
317
|
-
* `:jpegrescan` — Use jpegtran through jpegrescan, ignore progressive option _(defaults to `false`)_
|
318
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
302
|
+
* `:copy_chunks` — Copy all chunks *(defaults to `false`)*
|
303
|
+
* `:progressive` — Create progressive JPEG file *(defaults to `true`)*
|
304
|
+
* `:jpegrescan` — Use jpegtran through jpegrescan, ignore progressive option *(defaults to `true`)*
|
319
305
|
|
320
306
|
### optipng:
|
321
|
-
|
322
|
-
* `:
|
323
|
-
* `:
|
324
|
-
* `:strip` — Remove all auxiliary chunks _(defaults to `true`)_
|
325
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
307
|
+
* `:level` — Optimization level preset: `0` is least, `7` is best *(defaults to `6`)*
|
308
|
+
* `:interlace` — Interlace: `true` - interlace on, `false` - interlace off, `nil` - as is in original image *(defaults to `false`)*
|
309
|
+
* `:strip` — Remove all auxiliary chunks *(defaults to `true`)*
|
326
310
|
|
327
311
|
### pngcrush:
|
328
|
-
|
329
|
-
* `:
|
330
|
-
* `:
|
331
|
-
* `:
|
332
|
-
* `:blacken` — Blacken fully transparent pixels _(defaults to `true`)_
|
333
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
312
|
+
* `:chunks` — List of chunks to remove or `:alla` - all except tRNS/transparency or `:allb` - all except tRNS and gAMA/gamma *(defaults to `:alla`)*
|
313
|
+
* `:fix` — Fix otherwise fatal conditions such as bad CRCs *(defaults to `false`)*
|
314
|
+
* `:brute` — Brute force try all methods, very time-consuming and generally not worthwhile *(defaults to `false`)*
|
315
|
+
* `:blacken` — Blacken fully transparent pixels *(defaults to `true`)*
|
334
316
|
|
335
317
|
### pngout:
|
336
|
-
|
337
|
-
* `:
|
338
|
-
* `:strategy` — Strategy: `0` - xtreme, `1` - intense, `2` - longest Match, `3` - huffman Only, `4` - uncompressed _(defaults to `0`)_
|
339
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
318
|
+
* `:copy_chunks` — Copy optional chunks *(defaults to `false`)*
|
319
|
+
* `:strategy` — Strategy: `0` - xtreme, `1` - intense, `2` - longest Match, `3` - huffman Only, `4` - uncompressed *(defaults to `0`)*
|
340
320
|
|
341
321
|
### pngquant:
|
342
|
-
|
343
|
-
* `:
|
344
|
-
* `:
|
345
|
-
* `:
|
346
|
-
* `:speed` — speed/quality trade-off: `1` - slow, `3` - default, `11` - fast & rough _(defaults to `3`)_
|
347
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
322
|
+
* `:allow_lossy` — Allow quality option *(defaults to `false`)*
|
323
|
+
* `:max_colors` — Maximum number of colors to use *(defaults to `256`)*
|
324
|
+
* `:quality` — min..max - don't save below min, use less colors below max (both in range `0..100`; in yaml - `!ruby/range 0..100`), ignored in default/lossless mode *(defaults to `100..100`, `0..100` in lossy mode)*
|
325
|
+
* `:speed` — speed/quality trade-off: `1` - slow, `3` - default, `11` - fast & rough *(defaults to `3`)*
|
348
326
|
|
349
327
|
### svgo:
|
350
|
-
|
351
|
-
* `:
|
352
|
-
* `:enable_plugins` — List of plugins to enable _(defaults to `[]`)_
|
353
|
-
* `:timeout` — Number of seconds before worker is timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
|
328
|
+
* `:disable_plugins` — List of plugins to disable *(defaults to `[]`)*
|
329
|
+
* `:enable_plugins` — List of plugins to enable *(defaults to `[]`)*
|
354
330
|
|
355
331
|
<!---</worker-options>-->
|
356
332
|
|
data/image_optim.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'discourse_image_optim'
|
5
|
-
s.version = '0.26.
|
5
|
+
s.version = '0.26.2'
|
6
6
|
s.summary = %q{Optimize (lossless compress, optionally lossy) images (jpeg, png, gif, svg) using external utilities (advpng, gifsicle, jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, pngcrush, pngout, pngquant, svgo)}
|
7
7
|
s.homepage = "http://github.com/toy/#{s.name}"
|
8
8
|
s.authors = ['Ivan Kuchin']
|
data/lib/image_optim.rb
CHANGED
@@ -20,6 +20,8 @@ end
|
|
20
20
|
|
21
21
|
# Main interface
|
22
22
|
class ImageOptim
|
23
|
+
class TimeoutExceeded < StandardError; end
|
24
|
+
|
23
25
|
# Nice level
|
24
26
|
attr_reader :nice
|
25
27
|
|
@@ -111,10 +113,22 @@ class ImageOptim
|
|
111
113
|
|
112
114
|
optimized = @cache.fetch(original) do
|
113
115
|
Handler.for(original) do |handler|
|
114
|
-
|
115
|
-
|
116
|
-
|
116
|
+
current_worker = nil
|
117
|
+
|
118
|
+
begin
|
119
|
+
with_timeout(@timeout) do
|
120
|
+
workers.each do |worker|
|
121
|
+
current_worker = worker
|
122
|
+
handler.process{ |src, dst| worker.optimize(src, dst) }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
rescue TimeoutExceeded => e
|
126
|
+
if current_worker && current_worker.pid
|
127
|
+
pid = current_worker.pid
|
128
|
+
cleanup_process(pid)
|
117
129
|
end
|
130
|
+
|
131
|
+
raise e
|
118
132
|
end
|
119
133
|
end
|
120
134
|
end
|
@@ -228,6 +242,37 @@ class ImageOptim
|
|
228
242
|
|
229
243
|
private
|
230
244
|
|
245
|
+
def with_timeout(timeout)
|
246
|
+
if timeout && timeout >= 0
|
247
|
+
thread = Thread.new{ yield if block_given? }
|
248
|
+
if thread.respond_to?(:report_on_exception)
|
249
|
+
thread.report_on_exception = false
|
250
|
+
end
|
251
|
+
fail TimeoutExceeded if thread.join(timeout).nil?
|
252
|
+
elsif block_given?
|
253
|
+
yield
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def cleanup_process(pid)
|
258
|
+
Process.detach(pid)
|
259
|
+
Process.kill('-TERM', pid)
|
260
|
+
now = Time.now
|
261
|
+
|
262
|
+
while Time.now - now < 10
|
263
|
+
begin
|
264
|
+
Process.getpgid(pid)
|
265
|
+
sleep 0.1
|
266
|
+
rescue Errno::ESRCH
|
267
|
+
break
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
Process.kill('-KILL', pid) if Process.getpgid(pid)
|
272
|
+
rescue Errno::ESRCH
|
273
|
+
pid
|
274
|
+
end
|
275
|
+
|
231
276
|
def log_workers_by_format
|
232
277
|
$stderr << "Workers by format:\n"
|
233
278
|
@workers_by_format.each do |format, workers|
|
data/lib/image_optim/cmd.rb
CHANGED
@@ -1,60 +1,39 @@
|
|
1
1
|
require 'English'
|
2
|
-
require 'open3'
|
3
2
|
|
4
3
|
class ImageOptim
|
5
4
|
# Helper for running commands
|
6
5
|
module Cmd
|
7
|
-
class TimeoutExceeded < StandardError; end
|
8
|
-
|
9
6
|
class << self
|
10
7
|
# Run using `system`
|
11
8
|
# Return success status
|
12
9
|
# Will raise SignalException if process was interrupted
|
13
10
|
def run(*args)
|
14
|
-
|
11
|
+
if Process.respond_to?(:spawn)
|
12
|
+
if args.last.is_a?(Hash)
|
13
|
+
args.last[Gem.win_platform? ? :new_pgroup : :pgroup] = true
|
14
|
+
end
|
15
15
|
|
16
|
-
|
16
|
+
begin
|
17
|
+
pid = Process.spawn(*args)
|
18
|
+
ensure
|
19
|
+
yield pid if block_given?
|
20
|
+
end
|
17
21
|
|
18
|
-
|
19
|
-
|
22
|
+
begin
|
23
|
+
Process.waitpid(pid)
|
24
|
+
rescue Errno::ECHILD
|
25
|
+
return
|
26
|
+
end
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
JRUBY_VERSION >= '9.0.0.0'
|
28
|
+
check_status!
|
29
|
+
$CHILD_STATUS.success?
|
24
30
|
else
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Run the specified command, and kill it off if it runs longer
|
30
|
-
# than `timeout` seconds.
|
31
|
-
#
|
32
|
-
# Return success status
|
33
|
-
# Will raise an error when command timeouts
|
34
|
-
def run_with_timeout(timeout, *args)
|
35
|
-
return run(*args) unless timeout > 0 && support_timeout?
|
31
|
+
success = system(*args)
|
36
32
|
|
37
|
-
|
38
|
-
init_options!(args)
|
33
|
+
check_status!
|
39
34
|
|
40
|
-
|
41
|
-
stdin, stdout, thread = Open3.popen2(*args)
|
42
|
-
stdin.close
|
43
|
-
stdout.close
|
44
|
-
|
45
|
-
pid = thread[:pid]
|
46
|
-
|
47
|
-
if thread.join(timeout).nil?
|
48
|
-
cleanup_process(pid)
|
49
|
-
thread.kill
|
50
|
-
fail TimeoutExceeded
|
51
|
-
else
|
52
|
-
status = thread.value.exitstatus
|
53
|
-
success = status.zero? if status
|
54
|
-
end
|
35
|
+
success
|
55
36
|
end
|
56
|
-
|
57
|
-
success
|
58
37
|
end
|
59
38
|
|
60
39
|
# Run using backtick
|
@@ -86,38 +65,6 @@ class ImageOptim
|
|
86
65
|
|
87
66
|
fail SignalException, status.termsig
|
88
67
|
end
|
89
|
-
|
90
|
-
def init_options!(args)
|
91
|
-
pgroup_opt = Gem.win_platform? ? :new_pgroup : :pgroup
|
92
|
-
|
93
|
-
if args.last.is_a?(Hash)
|
94
|
-
args.last[pgroup_opt] = true
|
95
|
-
else
|
96
|
-
args.push(pgroup_opt => true)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def cleanup_process(pid)
|
101
|
-
Thread.new do
|
102
|
-
Process.kill('-TERM', pid)
|
103
|
-
Process.detach(pid)
|
104
|
-
now = Time.now
|
105
|
-
|
106
|
-
while Time.now - now < 10
|
107
|
-
begin
|
108
|
-
Process.kill(0, pid)
|
109
|
-
sleep 0.001
|
110
|
-
next
|
111
|
-
rescue Errno::ESRCH
|
112
|
-
break
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
if Process.getpgid(pid)
|
117
|
-
Process.kill('-KILL', pid)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
68
|
end
|
122
69
|
end
|
123
70
|
end
|
data/lib/image_optim/config.rb
CHANGED
@@ -114,6 +114,11 @@ class ImageOptim
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
+
def timeout
|
118
|
+
timeout = get!(:timeout)
|
119
|
+
timeout ? timeout.to_f : nil
|
120
|
+
end
|
121
|
+
|
117
122
|
# Verbose mode, converted to boolean
|
118
123
|
def verbose
|
119
124
|
!!get!(:verbose)
|
@@ -157,11 +162,6 @@ class ImageOptim
|
|
157
162
|
!!get!(:cache_worker_digests)
|
158
163
|
end
|
159
164
|
|
160
|
-
def timeout
|
161
|
-
timeout = get!(:timeout)
|
162
|
-
timeout ? timeout.to_i : 0
|
163
|
-
end
|
164
|
-
|
165
165
|
# Options for worker class by its `bin_sym`:
|
166
166
|
# * `Hash` passed as is
|
167
167
|
# * `{}` for `true` or `nil`
|
@@ -181,8 +181,8 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
181
181
|
options[:allow_lossy] = allow_lossy
|
182
182
|
end
|
183
183
|
|
184
|
-
op.on('--timeout N', 'Sets a timeout for
|
185
|
-
options[:timeout] = timeout
|
184
|
+
op.on('--timeout N', Integer, 'Sets a timeout for optimization') do |timeout|
|
185
|
+
options[:timeout] = timeout
|
186
186
|
end
|
187
187
|
|
188
188
|
op.separator nil
|
data/lib/image_optim/worker.rb
CHANGED
@@ -12,14 +12,14 @@ class ImageOptim
|
|
12
12
|
class Worker
|
13
13
|
extend ClassMethods
|
14
14
|
|
15
|
-
class TimeoutExceeded < StandardError; end
|
16
|
-
|
17
15
|
class << self
|
18
16
|
# Default init for worker is new
|
19
17
|
# Check example of override in gifsicle worker
|
20
18
|
alias_method :init, :new
|
21
19
|
end
|
22
20
|
|
21
|
+
attr_accessor :pid
|
22
|
+
|
23
23
|
# Configure (raises on extra options)
|
24
24
|
def initialize(image_optim, options = {})
|
25
25
|
unless image_optim.is_a?(ImageOptim)
|
@@ -155,17 +155,7 @@ class ImageOptim
|
|
155
155
|
].flatten
|
156
156
|
end
|
157
157
|
|
158
|
-
|
159
|
-
|
160
|
-
if seconds_to_timeout > 0
|
161
|
-
begin
|
162
|
-
Cmd.run_with_timeout(seconds_to_timeout, *args)
|
163
|
-
rescue Cmd::TimeoutExceeded
|
164
|
-
raise ImageOptim::Worker::TimeoutExceeded
|
165
|
-
end
|
166
|
-
else
|
167
|
-
Cmd.run(*args)
|
168
|
-
end
|
158
|
+
Cmd.run(*args){ |pid| self.pid = pid }
|
169
159
|
end
|
170
160
|
end
|
171
161
|
end
|
@@ -32,15 +32,6 @@ class ImageOptim
|
|
32
32
|
@option_definitions ||= []
|
33
33
|
end
|
34
34
|
|
35
|
-
def timeout_option
|
36
|
-
option(
|
37
|
-
:timeout,
|
38
|
-
0,
|
39
|
-
'Number of seconds before worker is timed out. Must be greater than' \
|
40
|
-
'0 to enable timeout.'
|
41
|
-
){ |v| v }
|
42
|
-
end
|
43
|
-
|
44
35
|
def option(name, default, type, description = nil, &proc)
|
45
36
|
attr_reader name
|
46
37
|
OptionDefinition.new(name, default, type, description, &proc).
|
@@ -92,13 +83,9 @@ class ImageOptim
|
|
92
83
|
klasses.map do |klass|
|
93
84
|
options = options_proc[klass]
|
94
85
|
next if options[:disable]
|
95
|
-
|
96
|
-
|
97
|
-
if !options.key?(option_key) && klass.method_defined?(option_key)
|
98
|
-
options[option_key] = image_optim.send(option_key)
|
99
|
-
end
|
86
|
+
if !options.key?(:allow_lossy) && klass.method_defined?(:allow_lossy)
|
87
|
+
options[:allow_lossy] = image_optim.allow_lossy
|
100
88
|
end
|
101
|
-
|
102
89
|
klass.init(image_optim, options)
|
103
90
|
end.compact.flatten
|
104
91
|
end
|
@@ -15,13 +15,6 @@ describe ImageOptim::Cmd do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
describe '.run' do
|
18
|
-
it 'calls system and returns result' do
|
19
|
-
status = double
|
20
|
-
expect(Cmd).to receive(:system).with('cmd', 'arg').and_return(status)
|
21
|
-
allow(Cmd).to receive(:check_status!)
|
22
|
-
expect(Cmd.run('cmd', 'arg')).to eq(status)
|
23
|
-
end
|
24
|
-
|
25
18
|
it 'returns process success status' do
|
26
19
|
expect(Cmd.run('sh -c "exit 0"')).to eq(true)
|
27
20
|
expect($CHILD_STATUS.exitstatus).to eq(0)
|
@@ -33,6 +26,14 @@ describe ImageOptim::Cmd do
|
|
33
26
|
expect($CHILD_STATUS.exitstatus).to eq(66)
|
34
27
|
end
|
35
28
|
|
29
|
+
it 'accepts a block that yields the pid' do
|
30
|
+
expect(
|
31
|
+
Cmd.run('sh -c "exit 66"') do |pid|
|
32
|
+
expect(pid.is_a?(Integer)).to eq(true)
|
33
|
+
end
|
34
|
+
).to eq(false)
|
35
|
+
end
|
36
|
+
|
36
37
|
it 'raises SignalException if process terminates after signal' do
|
37
38
|
skip 'signals are not supported' unless signals_supported?
|
38
39
|
expect_int_exception do
|
@@ -41,29 +42,6 @@ describe ImageOptim::Cmd do
|
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
44
|
-
describe '.run_with_timeout', :if => ImageOptim::Cmd.support_timeout? do
|
45
|
-
it 'spawns a process in a thread and returns status' do
|
46
|
-
expect(Cmd.run_with_timeout(20, 'sh -c "exit 0"')).to eq(true)
|
47
|
-
|
48
|
-
[1, 66].each do |status|
|
49
|
-
expect(Cmd.run_with_timeout(20, "sh -c \"exit #{status}\"")).
|
50
|
-
to eq(false)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'raises Timeout::Error if process timeout' do
|
55
|
-
expect{ Cmd.run_with_timeout(0.001, 'sleep 1') }.
|
56
|
-
to raise_error(Cmd::TimeoutExceeded)
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'calls system if timeout is <= zero' do
|
60
|
-
expect(Cmd.run_with_timeout(0, 'sh -c "sleep 0.001; exit 0"')).to eq(true)
|
61
|
-
|
62
|
-
expect(Cmd.run_with_timeout(-1, 'sh -c "sleep 0.001; exit 1"')).
|
63
|
-
to eq(false)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
45
|
describe '.capture' do
|
68
46
|
it 'calls ` and returns result' do
|
69
47
|
output = double
|
@@ -94,22 +94,6 @@ describe ImageOptim::Config do
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
-
describe '#timeout' do
|
98
|
-
before do
|
99
|
-
allow(IOConfig).to receive(:read_options).and_return({})
|
100
|
-
end
|
101
|
-
|
102
|
-
it 'is 0 by default' do
|
103
|
-
config = IOConfig.new({})
|
104
|
-
expect(config.timeout).to eq(0)
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'converts value to number' do
|
108
|
-
config = IOConfig.new(:timeout => '15')
|
109
|
-
expect(config.timeout).to eq(15)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
97
|
describe '#for_worker' do
|
114
98
|
before do
|
115
99
|
allow(IOConfig).to receive(:read_options).and_return({})
|
@@ -251,4 +235,20 @@ describe ImageOptim::Config do
|
|
251
235
|
expect(IOConfig.read_options(path)).to eq({})
|
252
236
|
end
|
253
237
|
end
|
238
|
+
|
239
|
+
describe '#timeout' do
|
240
|
+
before do
|
241
|
+
allow(IOConfig).to receive(:read_options).and_return({})
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'is nil by default' do
|
245
|
+
config = IOConfig.new({})
|
246
|
+
expect(config.timeout).to eq(nil)
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'converts value to a float' do
|
250
|
+
config = IOConfig.new(:timeout => '15.1')
|
251
|
+
expect(config.timeout).to eq(15.1)
|
252
|
+
end
|
253
|
+
end
|
254
254
|
end
|
@@ -129,7 +129,7 @@ describe ImageOptim::Worker do
|
|
129
129
|
workers.map{ |worker| class_double(Worker, :init => worker) }
|
130
130
|
end
|
131
131
|
|
132
|
-
let(:image_optim){ double(:allow_lossy => false
|
132
|
+
let(:image_optim){ double(:allow_lossy => false) }
|
133
133
|
|
134
134
|
it 'creates all workers for which options_proc returns true' do
|
135
135
|
workers = Array.new(3){ worker_double }
|
@@ -223,53 +223,34 @@ describe ImageOptim::Worker do
|
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
|
-
describe '
|
227
|
-
|
228
|
-
it "passes #{option} if worker has such attribute" do
|
229
|
-
klasses = worker_class_doubles([worker_double, worker_double])
|
230
|
-
|
231
|
-
allow(Worker).to receive(:klasses).and_return(klasses)
|
232
|
-
|
233
|
-
klasses[0].send(:attr_reader, option)
|
234
|
-
expect(klasses[0]).to receive(:init).
|
235
|
-
with(image_optim, hash_including(option))
|
236
|
-
expect(klasses[1]).to receive(:init).
|
237
|
-
with(image_optim, hash_not_including(option))
|
238
|
-
|
239
|
-
Worker.create_all(image_optim){ {} }
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
it 'allows overriding allow_lossy per worker' do
|
226
|
+
describe 'passing allow_lossy' do
|
227
|
+
it 'passes allow_lossy if worker has such attribute' do
|
244
228
|
klasses = worker_class_doubles([worker_double, worker_double])
|
245
|
-
options_proc = proc do |klass|
|
246
|
-
klass == klasses[1] ? {:allow_lossy => :b} : {}
|
247
|
-
end
|
248
229
|
|
249
230
|
allow(Worker).to receive(:klasses).and_return(klasses)
|
250
231
|
|
251
|
-
klasses.
|
232
|
+
klasses[0].send(:attr_reader, :allow_lossy)
|
252
233
|
expect(klasses[0]).to receive(:init).
|
253
|
-
with(image_optim, hash_including(:allow_lossy
|
234
|
+
with(image_optim, hash_including(:allow_lossy))
|
254
235
|
expect(klasses[1]).to receive(:init).
|
255
|
-
with(image_optim,
|
236
|
+
with(image_optim, hash_not_including(:allow_lossy))
|
256
237
|
|
257
|
-
Worker.create_all(image_optim
|
238
|
+
Worker.create_all(image_optim){ {} }
|
258
239
|
end
|
259
240
|
|
260
|
-
it 'allows overriding
|
241
|
+
it 'allows overriding per worker' do
|
261
242
|
klasses = worker_class_doubles([worker_double, worker_double])
|
262
243
|
options_proc = proc do |klass|
|
263
|
-
klass == klasses[1] ? {:
|
244
|
+
klass == klasses[1] ? {:allow_lossy => :b} : {}
|
264
245
|
end
|
265
246
|
|
266
247
|
allow(Worker).to receive(:klasses).and_return(klasses)
|
267
248
|
|
268
|
-
klasses.each{ |klass| klass.send(:attr_reader, :
|
249
|
+
klasses.each{ |klass| klass.send(:attr_reader, :allow_lossy) }
|
269
250
|
expect(klasses[0]).to receive(:init).
|
270
|
-
with(image_optim, hash_including(:
|
251
|
+
with(image_optim, hash_including(:allow_lossy => false))
|
271
252
|
expect(klasses[1]).to receive(:init).
|
272
|
-
with(image_optim, hash_including(:
|
253
|
+
with(image_optim, hash_including(:allow_lossy => :b))
|
273
254
|
|
274
255
|
Worker.create_all(image_optim, &options_proc)
|
275
256
|
end
|
data/spec/image_optim_spec.rb
CHANGED
@@ -102,6 +102,21 @@ describe ImageOptim do
|
|
102
102
|
expect(ImageOptim.optimize_image(path)).to be_nil
|
103
103
|
end
|
104
104
|
end
|
105
|
+
|
106
|
+
it 'allows timeout to be configured' do
|
107
|
+
base_options = {:timeout => 0.001}
|
108
|
+
image_optim = ImageOptim.new(base_options)
|
109
|
+
|
110
|
+
original_threads = Thread.list
|
111
|
+
|
112
|
+
expect do
|
113
|
+
image_optim.optimize_image(test_images.first)
|
114
|
+
end.to raise_error(ImageOptim::TimeoutExceeded)
|
115
|
+
|
116
|
+
# Ensure we don't leak any threads
|
117
|
+
(Thread.list - original_threads).each(&:join)
|
118
|
+
expect(Thread.list.count).to eq(original_threads.count)
|
119
|
+
end
|
105
120
|
end
|
106
121
|
|
107
122
|
describe '#optimize_image!' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: discourse_image_optim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.26.
|
4
|
+
version: 0.26.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Kuchin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fspath
|