discourse_image_optim 0.26.1 → 0.26.2

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