discourse_image_optim 0.24.4

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.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.appveyor.yml +46 -0
  3. data/.gitignore +18 -0
  4. data/.rubocop.yml +110 -0
  5. data/.travis.yml +42 -0
  6. data/CHANGELOG.markdown +316 -0
  7. data/CONTRIBUTING.markdown +11 -0
  8. data/Gemfile +16 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.markdown +358 -0
  11. data/Vagrantfile +38 -0
  12. data/bin/image_optim +28 -0
  13. data/image_optim.gemspec +34 -0
  14. data/lib/image_optim.rb +267 -0
  15. data/lib/image_optim/bin_resolver.rb +142 -0
  16. data/lib/image_optim/bin_resolver/bin.rb +115 -0
  17. data/lib/image_optim/bin_resolver/comparable_condition.rb +60 -0
  18. data/lib/image_optim/bin_resolver/error.rb +6 -0
  19. data/lib/image_optim/bin_resolver/simple_version.rb +31 -0
  20. data/lib/image_optim/cache.rb +72 -0
  21. data/lib/image_optim/cache_path.rb +16 -0
  22. data/lib/image_optim/cmd.rb +122 -0
  23. data/lib/image_optim/config.rb +219 -0
  24. data/lib/image_optim/configuration_error.rb +3 -0
  25. data/lib/image_optim/handler.rb +57 -0
  26. data/lib/image_optim/hash_helpers.rb +45 -0
  27. data/lib/image_optim/image_meta.rb +20 -0
  28. data/lib/image_optim/non_negative_integer_range.rb +11 -0
  29. data/lib/image_optim/optimized_path.rb +25 -0
  30. data/lib/image_optim/option_definition.rb +38 -0
  31. data/lib/image_optim/option_helpers.rb +17 -0
  32. data/lib/image_optim/path.rb +70 -0
  33. data/lib/image_optim/runner.rb +139 -0
  34. data/lib/image_optim/runner/glob_helpers.rb +45 -0
  35. data/lib/image_optim/runner/option_parser.rb +246 -0
  36. data/lib/image_optim/space.rb +29 -0
  37. data/lib/image_optim/true_false_nil.rb +16 -0
  38. data/lib/image_optim/worker.rb +170 -0
  39. data/lib/image_optim/worker/advpng.rb +37 -0
  40. data/lib/image_optim/worker/class_methods.rb +107 -0
  41. data/lib/image_optim/worker/gifsicle.rb +65 -0
  42. data/lib/image_optim/worker/jhead.rb +47 -0
  43. data/lib/image_optim/worker/jpegoptim.rb +63 -0
  44. data/lib/image_optim/worker/jpegrecompress.rb +49 -0
  45. data/lib/image_optim/worker/jpegtran.rb +48 -0
  46. data/lib/image_optim/worker/optipng.rb +53 -0
  47. data/lib/image_optim/worker/pngcrush.rb +56 -0
  48. data/lib/image_optim/worker/pngout.rb +40 -0
  49. data/lib/image_optim/worker/pngquant.rb +61 -0
  50. data/lib/image_optim/worker/svgo.rb +34 -0
  51. data/script/template/jquery-2.1.3.min.js +4 -0
  52. data/script/template/sortable-0.6.0.min.js +2 -0
  53. data/script/template/worker_analysis.erb +254 -0
  54. data/script/update_worker_options_in_readme +59 -0
  55. data/script/worker_analysis +589 -0
  56. data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +37 -0
  57. data/spec/image_optim/bin_resolver/simple_version_spec.rb +65 -0
  58. data/spec/image_optim/bin_resolver_spec.rb +290 -0
  59. data/spec/image_optim/cache_path_spec.rb +57 -0
  60. data/spec/image_optim/cache_spec.rb +162 -0
  61. data/spec/image_optim/cmd_spec.rb +93 -0
  62. data/spec/image_optim/config_spec.rb +254 -0
  63. data/spec/image_optim/handler_spec.rb +90 -0
  64. data/spec/image_optim/hash_helpers_spec.rb +74 -0
  65. data/spec/image_optim/image_meta_spec.rb +61 -0
  66. data/spec/image_optim/optimized_path_spec.rb +58 -0
  67. data/spec/image_optim/option_definition_spec.rb +138 -0
  68. data/spec/image_optim/option_helpers_spec.rb +25 -0
  69. data/spec/image_optim/path_spec.rb +103 -0
  70. data/spec/image_optim/runner/glob_helpers_spec.rb +21 -0
  71. data/spec/image_optim/runner/option_parser_spec.rb +105 -0
  72. data/spec/image_optim/space_spec.rb +23 -0
  73. data/spec/image_optim/worker/optipng_spec.rb +102 -0
  74. data/spec/image_optim/worker/pngquant_spec.rb +67 -0
  75. data/spec/image_optim/worker_spec.rb +303 -0
  76. data/spec/image_optim_spec.rb +259 -0
  77. data/spec/images/broken_jpeg +1 -0
  78. data/spec/images/comparison.png +0 -0
  79. data/spec/images/decompressed.jpeg +0 -0
  80. data/spec/images/icecream.gif +0 -0
  81. data/spec/images/image.jpg +0 -0
  82. data/spec/images/invisiblepixels/generate +24 -0
  83. data/spec/images/invisiblepixels/image.png +0 -0
  84. data/spec/images/lena.jpg +0 -0
  85. data/spec/images/orient/0.jpg +0 -0
  86. data/spec/images/orient/1.jpg +0 -0
  87. data/spec/images/orient/2.jpg +0 -0
  88. data/spec/images/orient/3.jpg +0 -0
  89. data/spec/images/orient/4.jpg +0 -0
  90. data/spec/images/orient/5.jpg +0 -0
  91. data/spec/images/orient/6.jpg +0 -0
  92. data/spec/images/orient/7.jpg +0 -0
  93. data/spec/images/orient/8.jpg +0 -0
  94. data/spec/images/orient/generate +23 -0
  95. data/spec/images/orient/original.jpg +0 -0
  96. data/spec/images/quant/64.png +0 -0
  97. data/spec/images/quant/generate +25 -0
  98. data/spec/images/rails.png +0 -0
  99. data/spec/images/test.svg +3 -0
  100. data/spec/images/transparency1.png +0 -0
  101. data/spec/images/transparency2.png +0 -0
  102. data/spec/images/vergroessert.jpg +0 -0
  103. data/spec/spec_helper.rb +93 -0
  104. metadata +281 -0
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/bin_resolver/comparable_condition'
3
+
4
+ describe ImageOptim::BinResolver::ComparableCondition do
5
+ let(:is){ ImageOptim::BinResolver::ComparableCondition.is }
6
+
7
+ it 'builds conditions' do
8
+ expect(is.between?(10, 20).method).to eq(:between?)
9
+ expect(is.between?(10, 20).args).to eq([10, 20])
10
+
11
+ expect((is >= 15).method).to eq(:>=)
12
+ expect((is >= 15).args).to eq([15])
13
+
14
+ expect((is < 30).method).to eq(:<)
15
+ expect((is < 30).args).to eq([30])
16
+ end
17
+
18
+ it 'stringifies conditions' do
19
+ expect(is.between?(10, 20).to_s).to eq('10..20')
20
+ expect((is >= 15).to_s).to eq('>= 15')
21
+ expect((is < 30).to_s).to eq('< 30')
22
+ end
23
+
24
+ it 'matches conditions' do
25
+ expect(is.between?(10, 20)).not_to match 9
26
+ expect(is.between?(10, 20)).to match 15
27
+ expect(is.between?(10, 20)).not_to match 21
28
+
29
+ expect(is >= 15).not_to match 14
30
+ expect(is >= 15).to match 15
31
+ expect(is >= 15).to match 16
32
+
33
+ expect(is < 30).to match 29
34
+ expect(is < 30).not_to match 30
35
+ expect(is < 30).not_to match 31
36
+ end
37
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/bin_resolver/simple_version'
3
+
4
+ describe ImageOptim::BinResolver::SimpleVersion do
5
+ def v(str)
6
+ described_class.new(str)
7
+ end
8
+
9
+ describe '#initialize' do
10
+ describe 'conversion' do
11
+ it 'converts Integer' do
12
+ expect(v(117)).to eq('117')
13
+ end
14
+
15
+ it 'converts Float' do
16
+ expect(v(1.17)).to eq('1.17')
17
+ end
18
+
19
+ it 'converts String' do
20
+ expect(v('1.17')).to eq('1.17')
21
+ end
22
+
23
+ it 'converts self' do
24
+ expect(v(v(1.17))).to eq('1.17')
25
+ end
26
+ end
27
+
28
+ describe 'normalization' do
29
+ %w[
30
+ 1
31
+ 01
32
+ 1.0
33
+ 1.00
34
+ 1.0.0
35
+ 1.0.0.0
36
+ ].each do |variation|
37
+ it "normalizes #{variation}" do
38
+ expect(v(variation)).to eq(1)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '#to_s' do
45
+ it 'returns the original value converted to String' do
46
+ expect(v(117).to_s).to eq('117')
47
+ expect(v(1.17).to_s).to eq('1.17')
48
+ expect(v('0117').to_s).to eq('0117')
49
+ end
50
+ end
51
+
52
+ describe '#<=>' do
53
+ describe 'comparing version 1.17' do
54
+ subject{ v '1.17' }
55
+
56
+ it{ is_expected.to be > '0' }
57
+ it{ is_expected.to be > '0.1' }
58
+ it{ is_expected.to be > '0.9' }
59
+ it{ is_expected.to be > '1.9' }
60
+ it{ is_expected.to be < '1.17.1' }
61
+ it{ is_expected.to be < '1.99' }
62
+ it{ is_expected.to be < '2.1' }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,290 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/bin_resolver'
3
+ require 'image_optim/cmd'
4
+ require 'image_optim/path'
5
+
6
+ describe ImageOptim::BinResolver do
7
+ def stub_env(key, value)
8
+ allow(ENV).to receive(:[]).with(key).and_return(value)
9
+ end
10
+
11
+ before do
12
+ stub_const('BinResolver', ImageOptim::BinResolver)
13
+ stub_const('Bin', BinResolver::Bin)
14
+ stub_const('SimpleVersion', BinResolver::SimpleVersion)
15
+ stub_const('Cmd', ImageOptim::Cmd)
16
+
17
+ allow(ENV).to receive(:[]).and_call_original
18
+ end
19
+
20
+ let(:image_optim){ double(:image_optim, :verbose => false, :pack => false) }
21
+ let(:resolver){ BinResolver.new(image_optim) }
22
+
23
+ describe '#full_path' do
24
+ def full_path(name)
25
+ resolver.instance_eval{ full_path(name) }
26
+ end
27
+
28
+ context 'when PATHEXT is not set' do
29
+ it 'finds binary without ext in combined path' do
30
+ stub_env 'PATH', %w[/a /b /c /d].join(File::PATH_SEPARATOR)
31
+ stub_env 'PATHEXT', nil
32
+
33
+ [
34
+ [:file?, '/a/abc', false],
35
+ [:file?, '/b/abc', true],
36
+ [:executable?, '/b/abc', false],
37
+ [:file?, '/c/abc', true],
38
+ [:executable?, '/c/abc', true],
39
+ ].each do |method, path, result|
40
+ allow(File).to receive(method).
41
+ with(File.expand_path(path)).and_return(result)
42
+ end
43
+
44
+ expect(full_path('abc')).
45
+ to eq(File.expand_path('/c/abc'))
46
+ end
47
+ end
48
+
49
+ context 'when PATHEXT is set' do
50
+ it 'finds binary with ext in combined path' do
51
+ stub_env 'PATH', %w[/a /b].join(File::PATH_SEPARATOR)
52
+ stub_env 'PATHEXT', '.com;.bat'
53
+
54
+ [
55
+ [:file?, '/a/abc.com', false],
56
+ [:file?, '/a/abc.bat', true],
57
+ [:executable?, '/a/abc.bat', false],
58
+ [:file?, '/b/abc.com', true],
59
+ [:executable?, '/b/abc.com', true],
60
+ ].each do |method, path, result|
61
+ allow(File).to receive(method).
62
+ with(File.expand_path(path)).and_return(result)
63
+ end
64
+
65
+ expect(full_path('abc')).
66
+ to eq(File.expand_path('/b/abc.com'))
67
+ end
68
+ end
69
+
70
+ it 'returns nil on failure' do
71
+ stub_env 'PATH', ''
72
+ expect(full_path('image_optim')).to be_nil
73
+ end
74
+ end
75
+
76
+ it 'combines path in order dir, pack, path, vendor' do
77
+ allow(image_optim).to receive(:pack).and_return(true)
78
+ stub_const('ImageOptim::Pack', Class.new do
79
+ def self.path
80
+ 'pack_path'
81
+ end
82
+ end)
83
+ allow(resolver).to receive(:dir).and_return('temp_dir')
84
+
85
+ expect(resolver.env_path).to eq([
86
+ 'temp_dir',
87
+ 'pack_path',
88
+ ENV['PATH'],
89
+ BinResolver::VENDOR_PATH,
90
+ ].join(File::PATH_SEPARATOR))
91
+ end
92
+
93
+ it 'resolves bin in path and returns instance of Bin' do
94
+ stub_env 'LS_BIN', nil
95
+ expect(FSPath).not_to receive(:temp_dir)
96
+ expect(resolver).to receive(:full_path).with(:ls).and_return('/bin/ls')
97
+ bin = double
98
+ expect(Bin).to receive(:new).with(:ls, '/bin/ls').and_return(bin)
99
+ expect(bin).to receive(:check!).once
100
+ expect(bin).to receive(:check_fail!).exactly(5).times
101
+
102
+ 5.times do
103
+ expect(resolver.resolve!(:ls)).to eq(bin)
104
+ end
105
+ expect(resolver.env_path).to eq([
106
+ ENV['PATH'],
107
+ BinResolver::VENDOR_PATH,
108
+ ].join(File::PATH_SEPARATOR))
109
+ end
110
+
111
+ it 'raises on failure to resolve bin' do
112
+ stub_env 'LS_BIN', nil
113
+ expect(FSPath).not_to receive(:temp_dir)
114
+ expect(resolver).to receive(:full_path).with(:ls).and_return(nil)
115
+ expect(Bin).not_to receive(:new)
116
+
117
+ 5.times do
118
+ expect do
119
+ resolver.resolve!(:ls)
120
+ end.to raise_error BinResolver::BinNotFound
121
+ end
122
+ expect(resolver.env_path).to eq([
123
+ ENV['PATH'],
124
+ BinResolver::VENDOR_PATH,
125
+ ].join(File::PATH_SEPARATOR))
126
+ end
127
+
128
+ it 'resolves bin specified in ENV' do
129
+ path = 'bin/the_optimizer'
130
+ stub_env 'THE_OPTIMIZER_BIN', path
131
+ tmpdir = double(:tmpdir, :to_str => 'tmpdir')
132
+ symlink = double(:symlink)
133
+
134
+ full_path = File.expand_path(path)
135
+ allow(File).to receive(:exist?).with(full_path).and_return(true)
136
+ allow(File).to receive(:file?).with(full_path).and_return(true)
137
+ allow(File).to receive(:executable?).with(full_path).and_return(true)
138
+
139
+ expect(FSPath).to receive(:temp_dir).
140
+ once.and_return(tmpdir)
141
+ expect(tmpdir).to receive(:/).
142
+ with(:the_optimizer).once.and_return(symlink)
143
+ expect(symlink).to receive(:make_symlink).
144
+ with(File.expand_path(path)).once
145
+
146
+ expect(resolver).not_to receive(:full_path)
147
+ bin = double
148
+ expect(Bin).to receive(:new).
149
+ with(:the_optimizer, File.expand_path(path)).and_return(bin)
150
+ expect(bin).to receive(:check!).once
151
+ expect(bin).to receive(:check_fail!).exactly(5).times
152
+
153
+ at_exit_blocks = []
154
+ expect(resolver).to receive(:at_exit).once do |&block|
155
+ at_exit_blocks.unshift(block)
156
+ end
157
+
158
+ 5.times do
159
+ resolver.resolve!(:the_optimizer)
160
+ end
161
+ expect(resolver.env_path).to eq([
162
+ tmpdir,
163
+ ENV['PATH'],
164
+ BinResolver::VENDOR_PATH,
165
+ ].join(File::PATH_SEPARATOR))
166
+
167
+ expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir)
168
+ at_exit_blocks.each(&:call)
169
+ end
170
+
171
+ describe 'checking bin' do
172
+ let(:path){ 'the_optimizer' }
173
+ let(:exist?){ true }
174
+ let(:file?){ true }
175
+ let(:executable?){ true }
176
+
177
+ before do
178
+ stub_env 'THE_OPTIMIZER_BIN', path
179
+ expect(FSPath).not_to receive(:temp_dir)
180
+ expect(resolver).not_to receive(:at_exit)
181
+ allow(File).to receive_messages(:exist? => exist?,
182
+ :file? => file?,
183
+ :executable? => executable?)
184
+ end
185
+
186
+ after do
187
+ expect(resolver.env_path).to eq([
188
+ ENV['PATH'],
189
+ BinResolver::VENDOR_PATH,
190
+ ].join(File::PATH_SEPARATOR))
191
+ end
192
+
193
+ def raises_error(error_message)
194
+ 5.times do
195
+ expect do
196
+ resolver.resolve!(:the_optimizer)
197
+ end.to raise_error RuntimeError, /#{Regexp.escape(error_message)}/
198
+ end
199
+ end
200
+
201
+ context 'presence' do
202
+ let(:exist?){ false }
203
+
204
+ it{ raises_error('doesn\'t exist') }
205
+ end
206
+
207
+ context 'been a file' do
208
+ let(:file?){ false }
209
+
210
+ it{ raises_error('is not a file') }
211
+ end
212
+
213
+ context 'been a file' do
214
+ let(:executable?){ false }
215
+
216
+ it{ raises_error('is not executable') }
217
+ end
218
+ end
219
+
220
+ it 'resolves bin only once, but checks every time' do
221
+ stub_env 'LS_BIN', nil
222
+ expect(resolver).to receive(:full_path).once.with(:ls) do
223
+ sleep 0.1
224
+ '/bin/ls'
225
+ end
226
+ bin = double
227
+ expect(Bin).to receive(:new).once.with(:ls, '/bin/ls').and_return(bin)
228
+
229
+ count = 0
230
+ mutex = Mutex.new
231
+ allow(bin).to receive(:check!).once
232
+ allow(bin).to receive(:check_fail!){ mutex.synchronize{ count += 1 } }
233
+
234
+ Array.new(10) do
235
+ Thread.new do
236
+ resolver.resolve!(:ls)
237
+ end
238
+ end.each(&:join)
239
+
240
+ expect(count).to eq(10)
241
+ end
242
+
243
+ describe 'checking version' do
244
+ before do
245
+ allow(resolver).to receive(:full_path){ |name| "/bin/#{name}" }
246
+ end
247
+
248
+ it 'raises every time if did not get bin version' do
249
+ stub_env 'PNGCRUSH_BIN', nil
250
+ bin = Bin.new(:pngcrush, '/bin/pngcrush')
251
+
252
+ expect(Bin).to receive(:new).and_return(bin)
253
+ allow(bin).to receive(:version).and_return(nil)
254
+
255
+ 5.times do
256
+ expect do
257
+ resolver.resolve!(:pngcrush)
258
+ end.to raise_error Bin::UnknownVersion, %r{pngcrush at /bin/pngcrush}
259
+ end
260
+ end
261
+
262
+ it 'raises every time on detection of misbehaving version' do
263
+ stub_env 'PNGCRUSH_BIN', nil
264
+ bin = Bin.new(:pngcrush, '/bin/pngcrush')
265
+
266
+ expect(Bin).to receive(:new).and_return(bin)
267
+ allow(bin).to receive(:version).and_return(SimpleVersion.new('1.7.60'))
268
+
269
+ 5.times do
270
+ expect do
271
+ resolver.resolve!(:pngcrush)
272
+ end.to raise_error Bin::BadVersion, /is known to produce broken pngs/
273
+ end
274
+ end
275
+
276
+ it 'warns once on detection of problematic version' do
277
+ stub_env 'ADVPNG_BIN', nil
278
+ bin = Bin.new(:advpng, '/bin/advpng')
279
+
280
+ expect(Bin).to receive(:new).and_return(bin)
281
+ allow(bin).to receive(:version).and_return(SimpleVersion.new('1.15'))
282
+
283
+ expect(bin).to receive(:warn).once.with(match(/does not use zopfli/))
284
+
285
+ 5.times do
286
+ resolver.resolve!(:pngcrush)
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/cache_path'
3
+ require 'tempfile'
4
+
5
+ describe ImageOptim::CachePath do
6
+ include CapabilityCheckHelpers
7
+
8
+ before do
9
+ stub_const('CachePath', ImageOptim::CachePath)
10
+ end
11
+
12
+ describe '#replace' do
13
+ let(:src){ CachePath.temp_file_path }
14
+ let(:dst){ CachePath.temp_file_path }
15
+
16
+ it 'moves data to destination' do
17
+ src.write('src')
18
+
19
+ src.replace(dst)
20
+
21
+ expect(dst.read).to eq('src')
22
+ end
23
+
24
+ it 'does not remove original file' do
25
+ src.replace(dst)
26
+
27
+ expect(src).to exist
28
+ end
29
+
30
+ it 'preserves attributes of destination file' do
31
+ skip 'full file modes are not support' unless any_file_modes_allowed?
32
+ mode = 0o666
33
+
34
+ dst.chmod(mode)
35
+
36
+ src.replace(dst)
37
+
38
+ got = dst.stat.mode & 0o777
39
+ expect(got).to eq(mode), format('expected %04o, got %04o', mode, got)
40
+ end
41
+
42
+ it 'does not preserve mtime of destination file' do
43
+ time = src.mtime
44
+
45
+ dst.utime(time - 1000, time - 1000)
46
+
47
+ src.replace(dst)
48
+
49
+ expect(dst.mtime).to be >= time
50
+ end
51
+
52
+ it 'changes inode of destination' do
53
+ skip 'inodes are not supported' unless inodes_supported?
54
+ expect{ src.replace(dst) }.to change{ dst.stat.ino }
55
+ end
56
+ end
57
+ end