image_optim 0.17.1 → 0.18.0

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