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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4cc5e84e0c8dff3c8fd0af654608b8ee958b3283463f7f945076964ae9d3eae
4
- data.tar.gz: ca77fd28eac0fb79493ed19600eaae18fdb7ea244f9025fdc909e7791a32133e
3
+ metadata.gz: 79257562b4927a5ec9d03fbbbc649b45dd060a3567897b0b0c10e29fbb759561
4
+ data.tar.gz: 524acbb993b15449b795f1aaa1c49bd477dbf18f102c2c516f3eafbb1a9dc194
5
5
  SHA512:
6
- metadata.gz: fcbd37ef3c8899889ebcf1912f0200f7bab98399779e32658b39d55af99da87f452dfec263e91d15a177e1700d566705891f455191758ebc23f9c68d47b96cfa
7
- data.tar.gz: 0b1379b801324ee93c546c4672abec97385cd7e5c50624e8657c9049d4f4e06135d167783d46901385209aab8b0ae364dfaf40887a67f8931c8a3b6f36255352
6
+ metadata.gz: 3aa22f9ea0ee4fb7a2aaaca83a577a842105b77fbe9f8399239d051a687b5c181a99f4d8cd4208b1dba8a6eb84d996a0980cd9b3182c11a4963bddf5979ed99c
7
+ data.tar.gz: 7e74d060fc2d3977441a99dbb971a5961f9d414cf286de48702bec28ae259230540994497b95a8d45ba5e129a554f8393032bbbbb1ba6952d0e64762045bdfde
@@ -56,7 +56,7 @@ Metrics/BlockLength:
56
56
  - 'spec/**/*.rb'
57
57
 
58
58
  Metrics/ClassLength:
59
- Max: 150
59
+ Max: 200
60
60
 
61
61
  Metrics/CyclomaticComplexity:
62
62
  Max: 10
@@ -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 [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)
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 [#3](https://github.com/toy/image_optim/issues/3) [@fabiomcosta](https://github.com/fabiomcosta)
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
 
@@ -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
- (will use [zopfli](https://code.google.com/p/zopfli/) on default/maximum level 4)
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 [image_optim_rails gem](https://github.com/toy/image_optim_rails).
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 _(defaults to `10`)_
269
- * `:threads` — Number of threads or disable _(defaults to number of processors)_
270
- * `:verbose` — Verbose output _(defaults to `false`)_
271
- * `: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`)_
272
- * `:skip_missing_workers` — Skip workers with missing or problematic binaries _(defaults to `false`)_
273
- * `:allow_lossy` — Allow lossy workers and optimizations _(defaults to `false`)_
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` Number of seconds before workers are timed out. Must be greater than 0 to enable timeout. _(defaults to `0`)_
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) _(defaults to `3`)_
293
- * `:careful` — Avoid bugs with some software _(defaults to `false`)_
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
- * `:allow_lossy` — Allow limiting maximum quality _(defaults to `false`)_
303
- * `:strip` — List of extra markers to strip: `:comments`, `:exif`, `:iptc`, `:icc` or `:all` _(defaults to `:all`)_
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
- * `:allow_lossy` — Allow worker, it is always lossy _(defaults to `false`)_
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
- * `:copy_chunks` — Copy all chunks _(defaults to `false`)_
316
- * `:progressive` — Create progressive JPEG file _(defaults to `true`)_
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
- * `:level` — Optimization level preset: `0` is least, `7` is best _(defaults to `6`)_
323
- * `:interlace` — Interlace: `true` - interlace on, `false` - interlace off, `nil` - as is in original image _(defaults to `false`)_
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
- * `:chunks` — List of chunks to remove or `:alla` - all except tRNS/transparency or `:allb` - all except tRNS and gAMA/gamma _(defaults to `:alla`)_
330
- * `:fix` — Fix otherwise fatal conditions such as bad CRCs _(defaults to `false`)_
331
- * `:brute` — Brute force try all methods, very time-consuming and generally not worthwhile _(defaults to `false`)_
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
- * `:copy_chunks` — Copy optional chunks _(defaults to `false`)_
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
- * `:allow_lossy` — Allow quality option _(defaults to `false`)_
344
- * `:max_colors` — Maximum number of colors to use _(defaults to `256`)_
345
- * `: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)_
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
- * `:disable_plugins` — List of plugins to disable _(defaults to `[]`)_
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
 
@@ -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.1'
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']
@@ -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
- workers.each do |worker|
115
- handler.process do |src, dst|
116
- worker.optimize(src, dst)
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|
@@ -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
- success = system(*args)
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
- check_status!
16
+ begin
17
+ pid = Process.spawn(*args)
18
+ ensure
19
+ yield pid if block_given?
20
+ end
17
21
 
18
- success
19
- end
22
+ begin
23
+ Process.waitpid(pid)
24
+ rescue Errno::ECHILD
25
+ return
26
+ end
20
27
 
21
- def support_timeout?
22
- if defined?(JRUBY_VERSION)
23
- JRUBY_VERSION >= '9.0.0.0'
28
+ check_status!
29
+ $CHILD_STATUS.success?
24
30
  else
25
- RUBY_VERSION >= '1.9'
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
- success = false
38
- init_options!(args)
33
+ check_status!
39
34
 
40
- begin
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
@@ -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 workers') do |timeout|
185
- options[:timeout] = timeout.to_i
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
@@ -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
- seconds_to_timeout = timeout || @image_optim.timeout
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
@@ -15,8 +15,6 @@ class ImageOptim
15
15
  OptionHelpers.limit_with_range(v.to_i, 0..4)
16
16
  end
17
17
 
18
- TIMEOUT_OPTION = timeout_option
19
-
20
18
  def run_order
21
19
  4
22
20
  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
- [:allow_lossy, :timeout].each do |option_key|
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
@@ -35,8 +35,6 @@ class ImageOptim
35
35
  CAREFUL_OPTION =
36
36
  option(:careful, false, 'Avoid bugs with some software'){ |v| !!v }
37
37
 
38
- TIMEOUT_OPTION = timeout_option
39
-
40
38
  def optimize(src, dst)
41
39
  args = %W[
42
40
  --output=#{dst}
@@ -7,8 +7,6 @@ class ImageOptim
7
7
  #
8
8
  # Jhead internally uses jpegtran which should be on path
9
9
  class Jhead < Worker
10
- TIMEOUT_OPTION = timeout_option
11
-
12
10
  ORIENTED = 2..8 # not top-left
13
11
 
14
12
  # Works on jpegs
@@ -38,8 +38,6 @@ class ImageOptim
38
38
  end
39
39
  end
40
40
 
41
- TIMEOUT_OPTION = timeout_option
42
-
43
41
  # Run earlier if max_quality is less than 100
44
42
  def run_order
45
43
  max_quality < 100 ? -1 : 0
@@ -24,8 +24,6 @@ class ImageOptim
24
24
  OptionHelpers.limit_with_range(v.to_i, 0...QUALITY_NAMES.length)
25
25
  end
26
26
 
27
- TIMEOUT_OPTION = timeout_option
28
-
29
27
  def used_bins
30
28
  [:'jpeg-recompress']
31
29
  end
@@ -17,8 +17,6 @@ class ImageOptim
17
17
  option(:jpegrescan, true, 'Use jpegtran through jpegrescan, '\
18
18
  'ignore progressive option'){ |v| !!v }
19
19
 
20
- TIMEOUT_OPTION = timeout_option
21
-
22
20
  def used_bins
23
21
  jpegrescan ? [:jpegtran, :jpegrescan] : [:jpegtran]
24
22
  end
@@ -24,8 +24,6 @@ class ImageOptim
24
24
  STRIP_OPTION =
25
25
  option(:strip, true, 'Remove all auxiliary chunks'){ |v| !!v }
26
26
 
27
- TIMEOUT_OPTION = timeout_option
28
-
29
27
  def run_order
30
28
  -4
31
29
  end
@@ -22,8 +22,6 @@ class ImageOptim
22
22
  BLACKEN_OPTION =
23
23
  option(:blacken, true, 'Blacken fully transparent pixels'){ |v| !!v }
24
24
 
25
- TIMEOUT_OPTION = timeout_option
26
-
27
25
  def run_order
28
26
  -6
29
27
  end
@@ -18,8 +18,6 @@ class ImageOptim
18
18
  OptionHelpers.limit_with_range(v.to_i, 0..4)
19
19
  end
20
20
 
21
- TIMEOUT_OPTION = timeout_option
22
-
23
21
  def run_order
24
22
  2
25
23
  end
@@ -44,8 +44,6 @@ class ImageOptim
44
44
  OptionHelpers.limit_with_range(v.to_i, 1..11)
45
45
  end
46
46
 
47
- TIMEOUT_OPTION = timeout_option
48
-
49
47
  def run_order
50
48
  -2
51
49
  end
@@ -14,8 +14,6 @@ class ImageOptim
14
14
  Array(v).map(&:to_s)
15
15
  end
16
16
 
17
- TIMEOUT_OPTION = timeout_option
18
-
19
17
  def optimize(src, dst)
20
18
  args = %W[
21
19
  --input #{src}
@@ -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, :timeout => 30) }
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 'configuring workers' do
227
- [:allow_lossy, :timeout].each do |option|
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.each{ |klass| klass.send(:attr_reader, :allow_lossy) }
232
+ klasses[0].send(:attr_reader, :allow_lossy)
252
233
  expect(klasses[0]).to receive(:init).
253
- with(image_optim, hash_including(:allow_lossy => false))
234
+ with(image_optim, hash_including(:allow_lossy))
254
235
  expect(klasses[1]).to receive(:init).
255
- with(image_optim, hash_including(:allow_lossy => :b))
236
+ with(image_optim, hash_not_including(:allow_lossy))
256
237
 
257
- Worker.create_all(image_optim, &options_proc)
238
+ Worker.create_all(image_optim){ {} }
258
239
  end
259
240
 
260
- it 'allows overriding timeout per worker' do
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] ? {:timeout => 50} : {}
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, :timeout) }
249
+ klasses.each{ |klass| klass.send(:attr_reader, :allow_lossy) }
269
250
  expect(klasses[0]).to receive(:init).
270
- with(image_optim, hash_including(:timeout => 30))
251
+ with(image_optim, hash_including(:allow_lossy => false))
271
252
  expect(klasses[1]).to receive(:init).
272
- with(image_optim, hash_including(:timeout => 50))
253
+ with(image_optim, hash_including(:allow_lossy => :b))
273
254
 
274
255
  Worker.create_all(image_optim, &options_proc)
275
256
  end
@@ -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.1
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-03 00:00:00.000000000 Z
11
+ date: 2018-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fspath