image_optim 0.17.1 → 0.18.0

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 (43) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +1 -0
  3. data/.travis.yml +5 -18
  4. data/CHANGELOG.markdown +10 -0
  5. data/README.markdown +31 -1
  6. data/bin/image_optim +3 -137
  7. data/image_optim.gemspec +6 -3
  8. data/lib/image_optim.rb +20 -3
  9. data/lib/image_optim/bin_resolver.rb +28 -1
  10. data/lib/image_optim/bin_resolver/bin.rb +17 -7
  11. data/lib/image_optim/cmd.rb +49 -0
  12. data/lib/image_optim/config.rb +64 -4
  13. data/lib/image_optim/image_path.rb +5 -0
  14. data/lib/image_optim/option_definition.rb +5 -3
  15. data/lib/image_optim/runner.rb +1 -2
  16. data/lib/image_optim/runner/option_parser.rb +216 -0
  17. data/lib/image_optim/worker.rb +32 -17
  18. data/lib/image_optim/worker/advpng.rb +7 -1
  19. data/lib/image_optim/worker/gifsicle.rb +16 -3
  20. data/lib/image_optim/worker/jhead.rb +15 -8
  21. data/lib/image_optim/worker/jpegoptim.rb +6 -2
  22. data/lib/image_optim/worker/jpegtran.rb +10 -3
  23. data/lib/image_optim/worker/optipng.rb +6 -1
  24. data/lib/image_optim/worker/pngcrush.rb +8 -1
  25. data/lib/image_optim/worker/pngout.rb +8 -1
  26. data/lib/image_optim/worker/svgo.rb +4 -1
  27. data/script/worker_analysis +523 -0
  28. data/script/worker_analysis.haml +153 -0
  29. data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +4 -5
  30. data/spec/image_optim/bin_resolver/simple_version_spec.rb +44 -21
  31. data/spec/image_optim/bin_resolver_spec.rb +63 -29
  32. data/spec/image_optim/cmd_spec.rb +66 -0
  33. data/spec/image_optim/config_spec.rb +38 -38
  34. data/spec/image_optim/handler_spec.rb +15 -12
  35. data/spec/image_optim/hash_helpers_spec.rb +14 -13
  36. data/spec/image_optim/image_path_spec.rb +22 -7
  37. data/spec/image_optim/runner/glob_helpers_spec.rb +6 -5
  38. data/spec/image_optim/runner/option_parser_spec.rb +99 -0
  39. data/spec/image_optim/space_spec.rb +5 -4
  40. data/spec/image_optim/worker_spec.rb +6 -5
  41. data/spec/image_optim_spec.rb +209 -237
  42. data/spec/spec_helper.rb +3 -0
  43. metadata +43 -11
@@ -1,21 +1,22 @@
1
- $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
- require 'rspec'
1
+ require 'spec_helper'
3
2
  require 'image_optim/config'
4
3
 
5
4
  describe ImageOptim::Config do
6
- IOConfig = ImageOptim::Config
5
+ before do
6
+ stub_const('IOConfig', ImageOptim::Config)
7
+ end
7
8
 
8
- describe 'assert_no_unused_options!' do
9
+ describe :assert_no_unused_options! do
9
10
  before do
10
11
  allow(IOConfig).to receive(:read_options).and_return({})
11
12
  end
12
13
 
13
- it 'should not raise when no unused options' do
14
+ it 'does not raise when no unused options' do
14
15
  config = IOConfig.new({})
15
16
  config.assert_no_unused_options!
16
17
  end
17
18
 
18
- it 'should raise when there are unused options' do
19
+ it 'raises when there are unused options' do
19
20
  config = IOConfig.new(:unused => true)
20
21
  expect do
21
22
  config.assert_no_unused_options!
@@ -23,80 +24,79 @@ describe ImageOptim::Config do
23
24
  end
24
25
  end
25
26
 
26
- describe 'nice' do
27
+ describe :nice do
27
28
  before do
28
29
  allow(IOConfig).to receive(:read_options).and_return({})
29
30
  end
30
31
 
31
- it 'should be 10 by default' do
32
+ it 'is 10 by default' do
32
33
  config = IOConfig.new({})
33
34
  expect(config.nice).to eq(10)
34
35
  end
35
36
 
36
- it 'should be 0 if disabled' do
37
+ it 'is 0 if disabled' do
37
38
  config = IOConfig.new(:nice => false)
38
39
  expect(config.nice).to eq(0)
39
40
  end
40
41
 
41
- it 'should convert value to number' do
42
+ it 'converts value to number' do
42
43
  config = IOConfig.new(:nice => '13')
43
44
  expect(config.nice).to eq(13)
44
45
  end
45
46
  end
46
47
 
47
- describe 'threads' do
48
+ describe :threads do
48
49
  before do
49
50
  allow(IOConfig).to receive(:read_options).and_return({})
50
51
  end
51
52
 
52
- it 'should be processor_count by default' do
53
+ it 'is processor_count by default' do
53
54
  config = IOConfig.new({})
54
55
  allow(config).to receive(:processor_count).and_return(13)
55
56
  expect(config.threads).to eq(13)
56
57
  end
57
58
 
58
- it 'should be 1 if disabled' do
59
+ it 'is 1 if disabled' do
59
60
  config = IOConfig.new(:threads => false)
60
61
  expect(config.threads).to eq(1)
61
62
  end
62
63
 
63
- it 'should convert value to number' do
64
+ it 'converts value to number' do
64
65
  config = IOConfig.new(:threads => '616')
65
66
  expect(config.threads).to eq(616)
66
67
  end
67
68
  end
68
69
 
69
- describe 'for_worker' do
70
+ describe :for_worker do
70
71
  before do
71
72
  allow(IOConfig).to receive(:read_options).and_return({})
72
- end
73
+ stub_const('Abc', Class.new do
74
+ def self.bin_sym
75
+ :abc
76
+ end
73
77
 
74
- Abc = Class.new do
75
- def self.bin_sym
76
- :abc
77
- end
78
-
79
- def image_formats
80
- []
81
- end
78
+ def image_formats
79
+ []
80
+ end
81
+ end)
82
82
  end
83
83
 
84
- it 'should return empty hash by default' do
84
+ it 'returns empty hash by default' do
85
85
  config = IOConfig.new({})
86
86
  expect(config.for_worker(Abc)).to eq({})
87
87
  end
88
88
 
89
- it 'should return passed hash' do
89
+ it 'returns passed hash' do
90
90
  config = IOConfig.new(:abc => {:option => true})
91
91
  expect(config.for_worker(Abc)).to eq(:option => true)
92
92
  end
93
93
 
94
- it 'should return passed false' do
94
+ it 'returns passed false' do
95
95
  config = IOConfig.new(:abc => false)
96
96
  expect(config.for_worker(Abc)).to eq(false)
97
97
  end
98
98
 
99
- it 'should raise on unknown option' do
99
+ it 'raises on unknown option' do
100
100
  config = IOConfig.new(:abc => 13)
101
101
  expect do
102
102
  config.for_worker(Abc)
@@ -105,7 +105,7 @@ describe ImageOptim::Config do
105
105
  end
106
106
 
107
107
  describe 'config' do
108
- it 'should read options from default locations' do
108
+ it 'reads options from default locations' do
109
109
  expect(IOConfig).to receive(:read_options).
110
110
  with(IOConfig::GLOBAL_PATH).and_return(:a => 1, :b => 2, :c => 3)
111
111
  expect(IOConfig).to receive(:read_options).
@@ -118,14 +118,14 @@ describe ImageOptim::Config do
118
118
  config.assert_no_unused_options!
119
119
  end
120
120
 
121
- it 'should not read options with empty config_paths' do
121
+ it 'does not read options with empty config_paths' do
122
122
  expect(IOConfig).not_to receive(:read_options)
123
123
 
124
124
  config = IOConfig.new(:config_paths => [])
125
125
  config.assert_no_unused_options!
126
126
  end
127
127
 
128
- it 'should read options from specified paths' do
128
+ it 'reads options from specified paths' do
129
129
  expect(IOConfig).to receive(:read_options).
130
130
  with('/etc/image_optim.yml').and_return(:a => 1, :b => 2, :c => 3)
131
131
  expect(IOConfig).to receive(:read_options).
@@ -141,7 +141,7 @@ describe ImageOptim::Config do
141
141
  config.assert_no_unused_options!
142
142
  end
143
143
 
144
- it 'should convert config_paths to array' do
144
+ it 'converts config_paths to array' do
145
145
  expect(IOConfig).to receive(:read_options).
146
146
  with('config/image_optim.yml').and_return({})
147
147
 
@@ -151,11 +151,11 @@ describe ImageOptim::Config do
151
151
  end
152
152
 
153
153
  describe 'class methods' do
154
- describe 'read_options' do
154
+ describe :read_options do
155
155
  let(:path){ double(:path) }
156
156
  let(:full_path){ double(:full_path) }
157
157
 
158
- it 'should warn if expand path fails' do
158
+ it 'warns if expand path fails' do
159
159
  expect(IOConfig).to receive(:warn)
160
160
  expect(File).to receive(:expand_path).
161
161
  with(path).and_raise(ArgumentError)
@@ -164,7 +164,7 @@ describe ImageOptim::Config do
164
164
  expect(IOConfig.read_options(path)).to eq({})
165
165
  end
166
166
 
167
- it 'should return empty hash if path is not a file' do
167
+ it 'returns empty hash if path is not a file' do
168
168
  expect(IOConfig).not_to receive(:warn)
169
169
  expect(File).to receive(:expand_path).
170
170
  with(path).and_return(full_path)
@@ -174,7 +174,7 @@ describe ImageOptim::Config do
174
174
  expect(IOConfig.read_options(path)).to eq({})
175
175
  end
176
176
 
177
- it 'should return hash with deep symbolised keys from reader' do
177
+ it 'returns hash with deep symbolised keys from reader' do
178
178
  stringified = {'config' => {'this' => true}}
179
179
  symbolized = {:config => {:this => true}}
180
180
 
@@ -189,7 +189,7 @@ describe ImageOptim::Config do
189
189
  expect(IOConfig.read_options(path)).to eq(symbolized)
190
190
  end
191
191
 
192
- it 'should warn and return an empty hash if reader returns non hash' do
192
+ it 'warns and returns an empty hash if reader returns non hash' do
193
193
  expect(IOConfig).to receive(:warn)
194
194
  expect(File).to receive(:expand_path).
195
195
  with(path).and_return(full_path)
@@ -201,7 +201,7 @@ describe ImageOptim::Config do
201
201
  expect(IOConfig.read_options(path)).to eq({})
202
202
  end
203
203
 
204
- it 'should warn and return an empty hash if reader raises exception' do
204
+ it 'warns and returns an empty hash if reader raises exception' do
205
205
  expect(IOConfig).to receive(:warn)
206
206
  expect(File).to receive(:expand_path).
207
207
  with(path).and_return(full_path)
@@ -1,16 +1,19 @@
1
- $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
- require 'rspec'
1
+ require 'spec_helper'
3
2
  require 'image_optim/handler'
4
3
 
5
4
  describe ImageOptim::Handler do
6
- it 'should use original as source for first conversion '\
5
+ before do
6
+ stub_const('Handler', ImageOptim::Handler)
7
+ end
8
+
9
+ it 'uses original as source for first conversion '\
7
10
  'and two temp files for further conversions' do
8
11
  original = double(:original)
9
12
  allow(original).to receive(:temp_path) do
10
13
  fail 'temp_path called unexpectedly'
11
14
  end
12
15
 
13
- handler = ImageOptim::Handler.new(original)
16
+ handler = Handler.new(original)
14
17
  temp_a = double(:temp_a)
15
18
  temp_b = double(:temp_b)
16
19
  expect(original).to receive(:temp_path).once.and_return(temp_a)
@@ -58,32 +61,32 @@ describe ImageOptim::Handler do
58
61
  end
59
62
 
60
63
  describe :open do
61
- it 'should yield instance, run cleanup and return result' do
64
+ it 'yields instance, runs cleanup and returns result' do
62
65
  original = double
63
66
  handler = double
64
67
  result = double
65
68
 
66
- expect(ImageOptim::Handler).to receive(:new).
67
- with(original).and_return(handler)
69
+ expect(Handler).to receive(:new).
70
+ with(original).and_return(handler)
68
71
  expect(handler).to receive(:process)
69
72
  expect(handler).to receive(:cleanup)
70
73
  expect(handler).to receive(:result).and_return(result)
71
74
 
72
- expect(ImageOptim::Handler.for(original) do |h|
75
+ expect(Handler.for(original) do |h|
73
76
  h.process
74
77
  end).to eq(result)
75
78
  end
76
79
 
77
- it 'should cleanup if exception is raised' do
80
+ it 'cleans up if exception is raised' do
78
81
  original = double
79
82
  handler = double
80
83
 
81
- expect(ImageOptim::Handler).to receive(:new).
82
- with(original).and_return(handler)
84
+ expect(Handler).to receive(:new).
85
+ with(original).and_return(handler)
83
86
  expect(handler).to receive(:cleanup)
84
87
 
85
88
  expect do
86
- ImageOptim::Handler.for(original) do
89
+ Handler.for(original) do
87
90
  fail 'hello'
88
91
  end
89
92
  end.to raise_error 'hello'
@@ -1,12 +1,13 @@
1
- $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
- require 'rspec'
1
+ require 'spec_helper'
3
2
  require 'image_optim/hash_helpers'
4
3
 
5
4
  describe ImageOptim::HashHelpers do
6
- HH = ImageOptim::HashHelpers
5
+ before do
6
+ stub_const('HashHelpers', ImageOptim::HashHelpers)
7
+ end
7
8
 
8
9
  context 'stringify/simbolyze' do
9
- WITH_SYMBOL_KEYS = {
10
+ symbol_keys = {
10
11
  :a => 1,
11
12
  :b => {
12
13
  :c => [:a, 'a'],
@@ -14,7 +15,7 @@ describe ImageOptim::HashHelpers do
14
15
  },
15
16
  }
16
17
 
17
- WITH_STRING_KEYS = {
18
+ string_keys = {
18
19
  'a' => 1,
19
20
  'b' => {
20
21
  'c' => [:a, 'a'],
@@ -22,18 +23,18 @@ describe ImageOptim::HashHelpers do
22
23
  },
23
24
  }
24
25
 
25
- it 'should deep stringify hash keys' do
26
- expect(HH.deep_stringify_keys(WITH_SYMBOL_KEYS)).to eq(WITH_STRING_KEYS)
27
- expect(HH.deep_stringify_keys(WITH_STRING_KEYS)).to eq(WITH_STRING_KEYS)
26
+ it 'deep stringifies hash keys' do
27
+ expect(HashHelpers.deep_stringify_keys(symbol_keys)).to eq(string_keys)
28
+ expect(HashHelpers.deep_stringify_keys(string_keys)).to eq(string_keys)
28
29
  end
29
30
 
30
- it 'should deep symbolise hash keys' do
31
- expect(HH.deep_symbolise_keys(WITH_STRING_KEYS)).to eq(WITH_SYMBOL_KEYS)
32
- expect(HH.deep_symbolise_keys(WITH_SYMBOL_KEYS)).to eq(WITH_SYMBOL_KEYS)
31
+ it 'deep symbolises hash keys' do
32
+ expect(HashHelpers.deep_symbolise_keys(string_keys)).to eq(symbol_keys)
33
+ expect(HashHelpers.deep_symbolise_keys(symbol_keys)).to eq(symbol_keys)
33
34
  end
34
35
  end
35
36
 
36
- it 'should deep merge hashes' do
37
+ it 'deep merges hashes' do
37
38
  merge_a = {
38
39
  :a => {
39
40
  :b => 1,
@@ -70,7 +71,7 @@ describe ImageOptim::HashHelpers do
70
71
  'z' => 20,
71
72
  }
72
73
 
73
- expect(HH.deep_merge(merge_a, merge_b)).to eq(merge_result)
74
+ expect(HashHelpers.deep_merge(merge_a, merge_b)).to eq(merge_result)
74
75
  end
75
76
 
76
77
  end
@@ -1,12 +1,13 @@
1
- $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
- require 'rspec'
1
+ require 'spec_helper'
3
2
  require 'image_optim/image_path'
4
3
 
5
4
  describe ImageOptim::ImagePath do
6
- ImagePath = ImageOptim::ImagePath
5
+ before do
6
+ stub_const('ImagePath', ImageOptim::ImagePath)
7
+ end
7
8
 
8
- describe 'convert' do
9
- it 'should return ImagePath for string' do
9
+ describe :convert do
10
+ it 'returns ImagePath for string' do
10
11
  path = 'a'
11
12
 
12
13
  expect(ImagePath.convert(path)).to be_a(ImagePath)
@@ -16,7 +17,7 @@ describe ImageOptim::ImagePath do
16
17
  expect(ImagePath.convert(path)).not_to be(path)
17
18
  end
18
19
 
19
- it 'should return ImagePath for Pathname' do
20
+ it 'returns ImagePath for Pathname' do
20
21
  pathname = Pathname.new('a')
21
22
 
22
23
  expect(ImagePath.convert(pathname)).to be_a(ImagePath)
@@ -26,7 +27,7 @@ describe ImageOptim::ImagePath do
26
27
  expect(ImagePath.convert(pathname)).not_to be(pathname)
27
28
  end
28
29
 
29
- it 'should return same instance for ImagePath' do
30
+ it 'returns same instance for ImagePath' do
30
31
  image_path = ImagePath.new('a')
31
32
 
32
33
  expect(ImagePath.convert(image_path)).to be_a(ImagePath)
@@ -36,4 +37,18 @@ describe ImageOptim::ImagePath do
36
37
  expect(ImagePath.convert(image_path)).to be(image_path)
37
38
  end
38
39
  end
40
+
41
+ describe :binread do
42
+ it 'reads binary data' do
43
+ data = (0..255).to_a.pack('c*')
44
+
45
+ path = ImagePath.temp_file_path
46
+ path.write(data)
47
+
48
+ expect(path.binread).to eq(data)
49
+ if ''.respond_to?(:encoding)
50
+ expect(path.binread.encoding).to eq(Encoding.find('ASCII-8BIT'))
51
+ end
52
+ end
53
+ end
39
54
  end
@@ -1,9 +1,10 @@
1
- $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
- require 'rspec'
1
+ require 'spec_helper'
3
2
  require 'image_optim/runner/glob_helpers'
4
3
 
5
4
  describe ImageOptim::Runner::GlobHelpers do
6
- GH = ImageOptim::Runner::GlobHelpers
5
+ before do
6
+ stub_const('GlobHelpers', ImageOptim::Runner::GlobHelpers)
7
+ end
7
8
 
8
9
  describe :expand_braces do
9
10
  {
@@ -16,8 +17,8 @@ describe ImageOptim::Runner::GlobHelpers do
16
17
  '{\{a,b\},\{c,d\}}' => %w[\\{a b\\} \\{c d\\}],
17
18
  'test{ing,}' => %w[testing test],
18
19
  }.each do |glob, expected|
19
- it "should expand #{glob}" do
20
- expect(GH.expand_braces(glob)).to match_array(expected)
20
+ it "expands #{glob}" do
21
+ expect(GlobHelpers.expand_braces(glob)).to match_array(expected)
21
22
  end
22
23
  end
23
24
  end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/runner/option_parser'
3
+
4
+ describe ImageOptim::Runner::OptionParser do
5
+ before do
6
+ stub_const('OptionParser', ImageOptim::Runner::OptionParser)
7
+ end
8
+
9
+ describe :parse! do
10
+ it 'returns empty hash for arguments without options' do
11
+ args = %w[foo bar]
12
+ expect(OptionParser.parse!(args)).to eq({})
13
+ expect(args).to eq(%w[foo bar])
14
+ end
15
+
16
+ it 'removes options from arguments' do
17
+ args = %w[-r foo bar]
18
+ OptionParser.parse!(args)
19
+ expect(args).to eq(%w[foo bar])
20
+ end
21
+
22
+ it 'stops parsing optiosn after --' do
23
+ args = %w[-- -r foo bar]
24
+ OptionParser.parse!(args)
25
+ expect(args).to eq(%w[-r foo bar])
26
+ end
27
+
28
+ describe 'boolean option recursive' do
29
+ %w[-r -R --recursive].each do |flag|
30
+ it "is parsed from #{flag}" do
31
+ args = %W[#{flag} foo bar]
32
+ expect(OptionParser.parse!(args)).to eq(:recursive => true)
33
+ expect(args).to eq(%w[foo bar])
34
+ end
35
+ end
36
+ end
37
+
38
+ describe 'numeric option threads' do
39
+ it 'is parsed with space separator' do
40
+ args = %w[--threads 616 foo bar]
41
+ expect(OptionParser.parse!(args)).to eq(:threads => 616)
42
+ expect(args).to eq(%w[foo bar])
43
+ end
44
+
45
+ it 'is parsed with equal separator' do
46
+ args = %w[--threads=616 foo bar]
47
+ expect(OptionParser.parse!(args)).to eq(:threads => 616)
48
+ expect(args).to eq(%w[foo bar])
49
+ end
50
+
51
+ it 'is parsed with no- prefix' do
52
+ args = %w[--no-threads 616 foo bar]
53
+ expect(OptionParser.parse!(args)).to eq(:threads => false)
54
+ expect(args).to eq(%w[616 foo bar])
55
+ end
56
+ end
57
+
58
+ describe 'help option' do
59
+ it 'prints help text to stdout and exits' do
60
+ parser = OptionParser.new({})
61
+ expect(OptionParser).to receive(:new).and_return(parser)
62
+
63
+ help = double
64
+ expect(parser).to receive(:help).and_return(help)
65
+
66
+ expect do
67
+ OptionParser.parse!(%w[--help])
68
+ end.to output("#{help}\n").to_stdout &
69
+ raise_error(SystemExit){ |e| expect(e.status).to eq(0) }
70
+ end
71
+ end
72
+
73
+ describe 'wrong option' do
74
+ it 'prints help text to stdout and exits' do
75
+ parser = OptionParser.new({})
76
+ expect(OptionParser).to receive(:new).and_return(parser)
77
+
78
+ help = double
79
+ expect(parser).to receive(:help).and_return(help)
80
+
81
+ expect do
82
+ OptionParser.parse!(%w[--unknown-option])
83
+ end.to output("invalid option: --unknown-option\n\n#{help}\n").
84
+ to_stderr & raise_error(SystemExit){ |e| expect(e.status).to eq(1) }
85
+ end
86
+ end
87
+ end
88
+
89
+ describe :help do
90
+ it 'returns wrapped text' do
91
+ parser = OptionParser.new({})
92
+
93
+ allow(parser).to receive(:terminal_columns).and_return(80)
94
+
95
+ expect(parser.help.split("\n")).
96
+ to all(satisfy{ |line| line.length <= 80 })
97
+ end
98
+ end
99
+ end