openstreetmap-image_optim 0.21.0.1

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 (94) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rubocop.yml +65 -0
  4. data/.travis.yml +42 -0
  5. data/CHANGELOG.markdown +272 -0
  6. data/CONTRIBUTING.markdown +10 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.markdown +344 -0
  10. data/Vagrantfile +33 -0
  11. data/bin/image_optim +28 -0
  12. data/image_optim.gemspec +29 -0
  13. data/lib/image_optim.rb +228 -0
  14. data/lib/image_optim/bin_resolver.rb +144 -0
  15. data/lib/image_optim/bin_resolver/bin.rb +105 -0
  16. data/lib/image_optim/bin_resolver/comparable_condition.rb +60 -0
  17. data/lib/image_optim/bin_resolver/error.rb +6 -0
  18. data/lib/image_optim/bin_resolver/simple_version.rb +31 -0
  19. data/lib/image_optim/cmd.rb +49 -0
  20. data/lib/image_optim/config.rb +205 -0
  21. data/lib/image_optim/configuration_error.rb +3 -0
  22. data/lib/image_optim/handler.rb +57 -0
  23. data/lib/image_optim/hash_helpers.rb +45 -0
  24. data/lib/image_optim/image_meta.rb +25 -0
  25. data/lib/image_optim/image_path.rb +68 -0
  26. data/lib/image_optim/non_negative_integer_range.rb +11 -0
  27. data/lib/image_optim/option_definition.rb +32 -0
  28. data/lib/image_optim/option_helpers.rb +17 -0
  29. data/lib/image_optim/railtie.rb +38 -0
  30. data/lib/image_optim/runner.rb +139 -0
  31. data/lib/image_optim/runner/glob_helpers.rb +45 -0
  32. data/lib/image_optim/runner/option_parser.rb +227 -0
  33. data/lib/image_optim/space.rb +29 -0
  34. data/lib/image_optim/true_false_nil.rb +16 -0
  35. data/lib/image_optim/worker.rb +159 -0
  36. data/lib/image_optim/worker/advpng.rb +35 -0
  37. data/lib/image_optim/worker/class_methods.rb +91 -0
  38. data/lib/image_optim/worker/gifsicle.rb +63 -0
  39. data/lib/image_optim/worker/jhead.rb +43 -0
  40. data/lib/image_optim/worker/jpegoptim.rb +58 -0
  41. data/lib/image_optim/worker/jpegrecompress.rb +44 -0
  42. data/lib/image_optim/worker/jpegtran.rb +46 -0
  43. data/lib/image_optim/worker/optipng.rb +45 -0
  44. data/lib/image_optim/worker/pngcrush.rb +54 -0
  45. data/lib/image_optim/worker/pngout.rb +38 -0
  46. data/lib/image_optim/worker/pngquant.rb +51 -0
  47. data/lib/image_optim/worker/svgo.rb +32 -0
  48. data/script/template/jquery-2.1.3.min.js +4 -0
  49. data/script/template/sortable-0.6.0.min.js +2 -0
  50. data/script/template/worker_analysis.erb +254 -0
  51. data/script/update_worker_options_in_readme +60 -0
  52. data/script/worker_analysis +599 -0
  53. data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +37 -0
  54. data/spec/image_optim/bin_resolver/simple_version_spec.rb +57 -0
  55. data/spec/image_optim/bin_resolver_spec.rb +272 -0
  56. data/spec/image_optim/cmd_spec.rb +66 -0
  57. data/spec/image_optim/config_spec.rb +217 -0
  58. data/spec/image_optim/handler_spec.rb +95 -0
  59. data/spec/image_optim/hash_helpers_spec.rb +76 -0
  60. data/spec/image_optim/image_path_spec.rb +54 -0
  61. data/spec/image_optim/railtie_spec.rb +121 -0
  62. data/spec/image_optim/runner/glob_helpers_spec.rb +25 -0
  63. data/spec/image_optim/runner/option_parser_spec.rb +99 -0
  64. data/spec/image_optim/space_spec.rb +25 -0
  65. data/spec/image_optim/worker_spec.rb +192 -0
  66. data/spec/image_optim_spec.rb +242 -0
  67. data/spec/images/comparison.png +0 -0
  68. data/spec/images/decompressed.jpeg +0 -0
  69. data/spec/images/icecream.gif +0 -0
  70. data/spec/images/image.jpg +0 -0
  71. data/spec/images/invisiblepixels/generate +24 -0
  72. data/spec/images/invisiblepixels/image.png +0 -0
  73. data/spec/images/lena.jpg +0 -0
  74. data/spec/images/orient/0.jpg +0 -0
  75. data/spec/images/orient/1.jpg +0 -0
  76. data/spec/images/orient/2.jpg +0 -0
  77. data/spec/images/orient/3.jpg +0 -0
  78. data/spec/images/orient/4.jpg +0 -0
  79. data/spec/images/orient/5.jpg +0 -0
  80. data/spec/images/orient/6.jpg +0 -0
  81. data/spec/images/orient/7.jpg +0 -0
  82. data/spec/images/orient/8.jpg +0 -0
  83. data/spec/images/orient/generate +23 -0
  84. data/spec/images/orient/original.jpg +0 -0
  85. data/spec/images/quant/64.png +0 -0
  86. data/spec/images/quant/generate +25 -0
  87. data/spec/images/rails.png +0 -0
  88. data/spec/images/test.svg +3 -0
  89. data/spec/images/transparency1.png +0 -0
  90. data/spec/images/transparency2.png +0 -0
  91. data/spec/images/vergroessert.jpg +0 -0
  92. data/spec/spec_helper.rb +64 -0
  93. data/vendor/jpegrescan +143 -0
  94. metadata +308 -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
+ 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,57 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/bin_resolver/simple_version'
3
+
4
+ describe ImageOptim::BinResolver::SimpleVersion do
5
+ helpers = Module.new do
6
+ def v(str)
7
+ ImageOptim::BinResolver::SimpleVersion.new(str)
8
+ end
9
+ end
10
+ include helpers
11
+ extend helpers
12
+
13
+ describe 'compares version 1.17' do
14
+ subject{ v '1.17' }
15
+
16
+ it{ is_expected.to be > '0' }
17
+ it{ is_expected.to be > '0.1' }
18
+ it{ is_expected.to be > '0.9' }
19
+ it{ is_expected.to be > '1.9' }
20
+ it{ is_expected.to be < '1.17.1' }
21
+ it{ is_expected.to be < '1.99' }
22
+ it{ is_expected.to be < '2.1' }
23
+ end
24
+
25
+ describe 'normalization' do
26
+ %w[
27
+ 1
28
+ 01
29
+ 1.0
30
+ 1.00
31
+ 1.0.0
32
+ 1.0.0.0
33
+ ].each do |variation|
34
+ it "normalizes #{variation}" do
35
+ expect(v variation).to eq(1)
36
+ end
37
+ end
38
+ end
39
+
40
+ describe 'conversion' do
41
+ it 'converts Integer' do
42
+ expect(v 117).to eq('117')
43
+ end
44
+
45
+ it 'converts Float' do
46
+ expect(v 1.17).to eq('1.17')
47
+ end
48
+
49
+ it 'converts String' do
50
+ expect(v '1.17').to eq('1.17')
51
+ end
52
+
53
+ it 'converts self' do
54
+ expect(v(v 1.17)).to eq('1.17')
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,272 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/bin_resolver'
3
+ require 'image_optim/cmd'
4
+
5
+ describe ImageOptim::BinResolver do
6
+ def with_env(key, value)
7
+ saved, ENV[key] = ENV[key], value
8
+ yield
9
+ ensure
10
+ ENV[key] = saved
11
+ end
12
+
13
+ before do
14
+ stub_const('BinResolver', ImageOptim::BinResolver)
15
+ stub_const('Bin', BinResolver::Bin)
16
+ stub_const('SimpleVersion', BinResolver::SimpleVersion)
17
+ stub_const('Cmd', ImageOptim::Cmd)
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
+ def command_v(name)
29
+ path = Cmd.capture("sh -c 'command -v #{name}' 2> /dev/null").strip
30
+ path unless path.empty?
31
+ end
32
+
33
+ it 'finds binary in path' do
34
+ with_env 'PATH', 'bin' do
35
+ expect(full_path('image_optim')).
36
+ to eq(File.expand_path('bin/image_optim'))
37
+ end
38
+ end
39
+
40
+ it 'finds bin in vendor' do
41
+ with_env 'PATH', nil do
42
+ expect(full_path('jpegrescan')).
43
+ to eq(File.expand_path('vendor/jpegrescan'))
44
+ end
45
+ end
46
+
47
+ it 'finds bin in pack' do
48
+ allow(image_optim).to receive(:pack).and_return(true)
49
+ stub_const('ImageOptim::Pack', Class.new do
50
+ def self.path
51
+ 'script'
52
+ end
53
+ end)
54
+
55
+ with_env 'PATH', nil do
56
+ expect(full_path('update_worker_options_in_readme')).
57
+ to eq(File.expand_path('script/update_worker_options_in_readme'))
58
+ end
59
+ end
60
+
61
+ it 'works with different path separator' do
62
+ stub_const('File::PATH_SEPARATOR', 'O_o')
63
+ with_env 'PATH', 'bin' do
64
+ expect(full_path('image_optim')).
65
+ to eq(File.expand_path('bin/image_optim'))
66
+ end
67
+ end
68
+
69
+ it 'returns nil on failure' do
70
+ with_env 'PATH', 'lib' do
71
+ expect(full_path('image_optim')).to be_nil
72
+ end
73
+ end
74
+
75
+ %w[ls sh which bash image_optim does_not_exist].each do |name|
76
+ it "returns same path as `command -v` for #{name}" do
77
+ expect(full_path(name)).to eq(command_v(name))
78
+ end
79
+ end
80
+ end
81
+
82
+ it 'combines path in order dir:pack:path:vendor' do
83
+ allow(image_optim).to receive(:pack).and_return(true)
84
+ stub_const('ImageOptim::Pack', Class.new do
85
+ def self.path
86
+ 'pack_path'
87
+ end
88
+ end)
89
+ allow(resolver).to receive(:dir).and_return('temp_dir')
90
+
91
+ expect(resolver.env_path).to eq([
92
+ 'temp_dir',
93
+ 'pack_path',
94
+ ENV['PATH'],
95
+ BinResolver::VENDOR_PATH,
96
+ ].join(':'))
97
+ end
98
+
99
+ it 'resolves bin in path and returns instance of Bin' do
100
+ with_env 'LS_BIN', nil do
101
+ expect(FSPath).not_to receive(:temp_dir)
102
+ expect(resolver).to receive(:full_path).with(:ls).and_return('/bin/ls')
103
+ bin = double
104
+ expect(Bin).to receive(:new).with(:ls, '/bin/ls').and_return(bin)
105
+ expect(bin).to receive(:check!).once
106
+ expect(bin).to receive(:check_fail!).exactly(5).times
107
+
108
+ 5.times do
109
+ expect(resolver.resolve!(:ls)).to eq(bin)
110
+ end
111
+ expect(resolver.env_path).to eq([
112
+ ENV['PATH'],
113
+ BinResolver::VENDOR_PATH,
114
+ ].join(':'))
115
+ end
116
+ end
117
+
118
+ it 'raises on failure to resolve bin' do
119
+ with_env 'LS_BIN', nil do
120
+ expect(FSPath).not_to receive(:temp_dir)
121
+ expect(resolver).to receive(:full_path).with(:ls).and_return(nil)
122
+ expect(Bin).not_to receive(:new)
123
+
124
+ 5.times do
125
+ expect do
126
+ resolver.resolve!(:ls)
127
+ end.to raise_error BinResolver::BinNotFound
128
+ end
129
+ expect(resolver.env_path).to eq([
130
+ ENV['PATH'],
131
+ BinResolver::VENDOR_PATH,
132
+ ].join(':'))
133
+ end
134
+ end
135
+
136
+ it 'resolves bin specified in ENV' do
137
+ path = 'bin/image_optim'
138
+ with_env 'IMAGE_OPTIM_BIN', path do
139
+ tmpdir = double(:tmpdir, :to_str => 'tmpdir')
140
+ symlink = double(:symlink)
141
+
142
+ expect(FSPath).to receive(:temp_dir).
143
+ once.and_return(tmpdir)
144
+ expect(tmpdir).to receive(:/).
145
+ with(:image_optim).once.and_return(symlink)
146
+ expect(symlink).to receive(:make_symlink).
147
+ with(File.expand_path(path)).once
148
+
149
+ expect(resolver).not_to receive(:full_path)
150
+ bin = double
151
+ expect(Bin).to receive(:new).
152
+ with(:image_optim, File.expand_path(path)).and_return(bin)
153
+ expect(bin).to receive(:check!).once
154
+ expect(bin).to receive(:check_fail!).exactly(5).times
155
+
156
+ at_exit_blocks = []
157
+ expect(resolver).to receive(:at_exit).once do |&block|
158
+ at_exit_blocks.unshift(block)
159
+ end
160
+
161
+ 5.times do
162
+ resolver.resolve!(:image_optim)
163
+ end
164
+ expect(resolver.env_path).to eq([
165
+ tmpdir,
166
+ ENV['PATH'],
167
+ BinResolver::VENDOR_PATH,
168
+ ].join(':'))
169
+
170
+ expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir)
171
+ at_exit_blocks.each(&:call)
172
+ end
173
+ end
174
+
175
+ {
176
+ 'some/path/does/not/exist' => 'doesn\'t exist',
177
+ '.' => 'is not a file',
178
+ __FILE__ => 'is not executable',
179
+ }.each do |path, error_message|
180
+ it "raises when bin specified in ENV #{error_message}" do
181
+ with_env 'IMAGE_OPTIM_BIN', path do
182
+ expect(FSPath).not_to receive(:temp_dir)
183
+ expect(resolver).not_to receive(:at_exit)
184
+
185
+ 5.times do
186
+ expect do
187
+ resolver.resolve!(:image_optim)
188
+ end.to raise_error RuntimeError, /#{Regexp.escape(error_message)}/
189
+ end
190
+ expect(resolver.env_path).to eq([
191
+ ENV['PATH'],
192
+ BinResolver::VENDOR_PATH,
193
+ ].join(':'))
194
+ end
195
+ end
196
+ end
197
+
198
+ it 'resolves bin only once, but checks every time' do
199
+ with_env 'LS_BIN', nil do
200
+ expect(resolver).to receive(:full_path).once.with(:ls) do
201
+ sleep 0.1
202
+ '/bin/ls'
203
+ end
204
+ bin = double
205
+ expect(Bin).to receive(:new).once.with(:ls, '/bin/ls').and_return(bin)
206
+
207
+ count = 0
208
+ mutex = Mutex.new
209
+ allow(bin).to receive(:check!).once
210
+ allow(bin).to receive(:check_fail!){ mutex.synchronize{ count += 1 } }
211
+
212
+ 10.times.map do
213
+ Thread.new do
214
+ resolver.resolve!(:ls)
215
+ end
216
+ end.each(&:join)
217
+
218
+ expect(count).to eq(10)
219
+ end
220
+ end
221
+
222
+ describe 'checking version' do
223
+ before do
224
+ allow(resolver).to receive(:full_path){ |name| "/bin/#{name}" }
225
+ end
226
+
227
+ it 'raises every time if did not get bin version' do
228
+ with_env 'PNGCRUSH_BIN', nil do
229
+ bin = Bin.new(:pngcrush, '/bin/pngcrush')
230
+
231
+ expect(Bin).to receive(:new).and_return(bin)
232
+ allow(bin).to receive(:version).and_return(nil)
233
+
234
+ 5.times do
235
+ expect do
236
+ resolver.resolve!(:pngcrush)
237
+ end.to raise_error Bin::UnknownVersion
238
+ end
239
+ end
240
+ end
241
+
242
+ it 'raises every time on detection of misbehaving version' do
243
+ with_env 'PNGCRUSH_BIN', nil do
244
+ bin = Bin.new(:pngcrush, '/bin/pngcrush')
245
+
246
+ expect(Bin).to receive(:new).and_return(bin)
247
+ allow(bin).to receive(:version).and_return(SimpleVersion.new('1.7.60'))
248
+
249
+ 5.times do
250
+ expect do
251
+ resolver.resolve!(:pngcrush)
252
+ end.to raise_error Bin::BadVersion
253
+ end
254
+ end
255
+ end
256
+
257
+ it 'warns once on detection of problematic version' do
258
+ with_env 'ADVPNG_BIN', nil do
259
+ bin = Bin.new(:advpng, '/bin/advpng')
260
+
261
+ expect(Bin).to receive(:new).and_return(bin)
262
+ allow(bin).to receive(:version).and_return(SimpleVersion.new('1.15'))
263
+
264
+ expect(bin).to receive(:warn).once
265
+
266
+ 5.times do
267
+ resolver.resolve!(:pngcrush)
268
+ end
269
+ end
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/cmd'
3
+
4
+ describe ImageOptim::Cmd do
5
+ before do
6
+ stub_const('Cmd', ImageOptim::Cmd)
7
+ end
8
+
9
+ def expect_int_exception(&block)
10
+ expect(&block).to raise_error(SignalException) do |error|
11
+ expect(error.message.to_s).to match(/INT|#{Signal.list['INT']}/)
12
+ end
13
+ end
14
+
15
+ describe :run do
16
+ it 'calls system and returns result' do
17
+ status = double
18
+ expect(Cmd).to receive(:system).with('cmd', 'arg').and_return(status)
19
+ allow(Cmd).to receive(:check_status!)
20
+ expect(Cmd.run('cmd', 'arg')).to eq(status)
21
+ end
22
+
23
+ it 'returns process success status' do
24
+ expect(Cmd.run('sh -c exit\ 0')).to eq(true)
25
+ expect($CHILD_STATUS.exitstatus).to eq(0)
26
+
27
+ expect(Cmd.run('sh -c exit\ 1')).to eq(false)
28
+ expect($CHILD_STATUS.exitstatus).to eq(1)
29
+
30
+ expect(Cmd.run('sh -c exit\ 66')).to eq(false)
31
+ expect($CHILD_STATUS.exitstatus).to eq(66)
32
+ end
33
+
34
+ it 'raises SignalException if process terminates after signal' do
35
+ expect_int_exception do
36
+ Cmd.run('kill -s INT $$')
37
+ end
38
+ end
39
+ end
40
+
41
+ describe :capture do
42
+ it 'calls ` and returns result' do
43
+ output = double
44
+ expect(Cmd).to receive(:`).with('cmd arg arg+').and_return(output)
45
+ allow(Cmd).to receive(:check_status!)
46
+ expect(Cmd.capture('cmd arg arg+')).to eq(output)
47
+ end
48
+
49
+ it 'returns output' do
50
+ expect(Cmd.capture('echo test')).to eq("test\n")
51
+ expect($CHILD_STATUS.exitstatus).to eq(0)
52
+
53
+ expect(Cmd.capture('printf more; sh -c exit\ 1')).to eq('more')
54
+ expect($CHILD_STATUS.exitstatus).to eq(1)
55
+
56
+ expect(Cmd.capture('sh -c exit\ 66')).to eq('')
57
+ expect($CHILD_STATUS.exitstatus).to eq(66)
58
+ end
59
+
60
+ it 'raises SignalException if process terminates after signal' do
61
+ expect_int_exception do
62
+ Cmd.capture('kill -s INT $$')
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,217 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/config'
3
+
4
+ describe ImageOptim::Config do
5
+ before do
6
+ stub_const('IOConfig', ImageOptim::Config)
7
+ end
8
+
9
+ describe :assert_no_unused_options! do
10
+ before do
11
+ allow(IOConfig).to receive(:read_options).and_return({})
12
+ end
13
+
14
+ it 'does not raise when no unused options' do
15
+ config = IOConfig.new({})
16
+ config.assert_no_unused_options!
17
+ end
18
+
19
+ it 'raises when there are unused options' do
20
+ config = IOConfig.new(:unused => true)
21
+ expect do
22
+ config.assert_no_unused_options!
23
+ end.to raise_error(ImageOptim::ConfigurationError)
24
+ end
25
+ end
26
+
27
+ describe :nice do
28
+ before do
29
+ allow(IOConfig).to receive(:read_options).and_return({})
30
+ end
31
+
32
+ it 'is 10 by default' do
33
+ config = IOConfig.new({})
34
+ expect(config.nice).to eq(10)
35
+ end
36
+
37
+ it 'is 0 if disabled' do
38
+ config = IOConfig.new(:nice => false)
39
+ expect(config.nice).to eq(0)
40
+ end
41
+
42
+ it 'converts value to number' do
43
+ config = IOConfig.new(:nice => '13')
44
+ expect(config.nice).to eq(13)
45
+ end
46
+ end
47
+
48
+ describe :threads do
49
+ before do
50
+ allow(IOConfig).to receive(:read_options).and_return({})
51
+ end
52
+
53
+ it 'is processor_count by default' do
54
+ config = IOConfig.new({})
55
+ allow(config).to receive(:processor_count).and_return(13)
56
+ expect(config.threads).to eq(13)
57
+ end
58
+
59
+ it 'is 1 if disabled' do
60
+ config = IOConfig.new(:threads => false)
61
+ expect(config.threads).to eq(1)
62
+ end
63
+
64
+ it 'converts value to number' do
65
+ config = IOConfig.new(:threads => '616')
66
+ expect(config.threads).to eq(616)
67
+ end
68
+ end
69
+
70
+ describe :for_worker do
71
+ before do
72
+ allow(IOConfig).to receive(:read_options).and_return({})
73
+ stub_const('Abc', Class.new do
74
+ def self.bin_sym
75
+ :abc
76
+ end
77
+
78
+ def image_formats
79
+ []
80
+ end
81
+ end)
82
+ end
83
+
84
+ it 'returns empty hash by default' do
85
+ config = IOConfig.new({})
86
+ expect(config.for_worker(Abc)).to eq({})
87
+ end
88
+
89
+ it 'returns passed hash' do
90
+ config = IOConfig.new(:abc => {:option => true})
91
+ expect(config.for_worker(Abc)).to eq(:option => true)
92
+ end
93
+
94
+ it 'returns {:disable => true} for false' do
95
+ config = IOConfig.new(:abc => false)
96
+ expect(config.for_worker(Abc)).to eq(:disable => true)
97
+ end
98
+
99
+ it 'raises on unknown option' do
100
+ config = IOConfig.new(:abc => 13)
101
+ expect do
102
+ config.for_worker(Abc)
103
+ end.to raise_error(ImageOptim::ConfigurationError)
104
+ end
105
+ end
106
+
107
+ describe 'config' do
108
+ it 'reads options from default locations' do
109
+ expect(IOConfig).to receive(:read_options).
110
+ with(IOConfig::GLOBAL_PATH).and_return(:a => 1, :b => 2, :c => 3)
111
+ expect(IOConfig).to receive(:read_options).
112
+ with(IOConfig::LOCAL_PATH).and_return(:a => 10, :b => 20)
113
+
114
+ config = IOConfig.new(:a => 100)
115
+ expect(config.get!(:a)).to eq(100)
116
+ expect(config.get!(:b)).to eq(20)
117
+ expect(config.get!(:c)).to eq(3)
118
+ config.assert_no_unused_options!
119
+ end
120
+
121
+ it 'does not read options with empty config_paths' do
122
+ expect(IOConfig).not_to receive(:read_options)
123
+
124
+ config = IOConfig.new(:config_paths => [])
125
+ config.assert_no_unused_options!
126
+ end
127
+
128
+ it 'reads options from specified paths' do
129
+ expect(IOConfig).to receive(:read_options).
130
+ with('/etc/image_optim.yml').and_return(:a => 1, :b => 2, :c => 3)
131
+ expect(IOConfig).to receive(:read_options).
132
+ with('config/image_optim.yml').and_return(:a => 10, :b => 20)
133
+
134
+ config = IOConfig.new(:a => 100, :config_paths => %w[
135
+ /etc/image_optim.yml
136
+ config/image_optim.yml
137
+ ])
138
+ expect(config.get!(:a)).to eq(100)
139
+ expect(config.get!(:b)).to eq(20)
140
+ expect(config.get!(:c)).to eq(3)
141
+ config.assert_no_unused_options!
142
+ end
143
+
144
+ it 'converts config_paths to array' do
145
+ expect(IOConfig).to receive(:read_options).
146
+ with('config/image_optim.yml').and_return({})
147
+
148
+ config = IOConfig.new(:config_paths => 'config/image_optim.yml')
149
+ config.assert_no_unused_options!
150
+ end
151
+ end
152
+
153
+ describe 'class methods' do
154
+ describe :read_options do
155
+ let(:path){ double(:path) }
156
+ let(:full_path){ double(:full_path) }
157
+
158
+ it 'warns if expand path fails' do
159
+ expect(IOConfig).to receive(:warn)
160
+ expect(File).to receive(:expand_path).
161
+ with(path).and_raise(ArgumentError)
162
+ expect(File).not_to receive(:file?)
163
+
164
+ expect(IOConfig.read_options(path)).to eq({})
165
+ end
166
+
167
+ it 'returns empty hash if path is not a file' do
168
+ expect(IOConfig).not_to receive(:warn)
169
+ expect(File).to receive(:expand_path).
170
+ with(path).and_return(full_path)
171
+ expect(File).to receive(:file?).
172
+ with(full_path).and_return(false)
173
+
174
+ expect(IOConfig.read_options(path)).to eq({})
175
+ end
176
+
177
+ it 'returns hash with deep symbolised keys from reader' do
178
+ stringified = {'config' => {'this' => true}}
179
+ symbolized = {:config => {:this => true}}
180
+
181
+ expect(IOConfig).not_to receive(:warn)
182
+ expect(File).to receive(:expand_path).
183
+ with(path).and_return(full_path)
184
+ expect(File).to receive(:file?).
185
+ with(full_path).and_return(true)
186
+ expect(YAML).to receive(:load_file).
187
+ with(full_path).and_return(stringified)
188
+
189
+ expect(IOConfig.read_options(path)).to eq(symbolized)
190
+ end
191
+
192
+ it 'warns and returns an empty hash if reader returns non hash' do
193
+ expect(IOConfig).to receive(:warn)
194
+ expect(File).to receive(:expand_path).
195
+ with(path).and_return(full_path)
196
+ expect(File).to receive(:file?).
197
+ with(full_path).and_return(true)
198
+ expect(YAML).to receive(:load_file).
199
+ with(full_path).and_return([:config])
200
+
201
+ expect(IOConfig.read_options(path)).to eq({})
202
+ end
203
+
204
+ it 'warns and returns an empty hash if reader raises exception' do
205
+ expect(IOConfig).to receive(:warn)
206
+ expect(File).to receive(:expand_path).
207
+ with(path).and_return(full_path)
208
+ expect(File).to receive(:file?).
209
+ with(full_path).and_return(true)
210
+ expect(YAML).to receive(:load_file).
211
+ with(full_path).and_raise
212
+
213
+ expect(IOConfig.read_options(path)).to eq({})
214
+ end
215
+ end
216
+ end
217
+ end