discourse_image_optim 0.24.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.appveyor.yml +46 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +110 -0
- data/.travis.yml +42 -0
- data/CHANGELOG.markdown +316 -0
- data/CONTRIBUTING.markdown +11 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +358 -0
- data/Vagrantfile +38 -0
- data/bin/image_optim +28 -0
- data/image_optim.gemspec +34 -0
- data/lib/image_optim.rb +267 -0
- data/lib/image_optim/bin_resolver.rb +142 -0
- data/lib/image_optim/bin_resolver/bin.rb +115 -0
- data/lib/image_optim/bin_resolver/comparable_condition.rb +60 -0
- data/lib/image_optim/bin_resolver/error.rb +6 -0
- data/lib/image_optim/bin_resolver/simple_version.rb +31 -0
- data/lib/image_optim/cache.rb +72 -0
- data/lib/image_optim/cache_path.rb +16 -0
- data/lib/image_optim/cmd.rb +122 -0
- data/lib/image_optim/config.rb +219 -0
- data/lib/image_optim/configuration_error.rb +3 -0
- data/lib/image_optim/handler.rb +57 -0
- data/lib/image_optim/hash_helpers.rb +45 -0
- data/lib/image_optim/image_meta.rb +20 -0
- data/lib/image_optim/non_negative_integer_range.rb +11 -0
- data/lib/image_optim/optimized_path.rb +25 -0
- data/lib/image_optim/option_definition.rb +38 -0
- data/lib/image_optim/option_helpers.rb +17 -0
- data/lib/image_optim/path.rb +70 -0
- data/lib/image_optim/runner.rb +139 -0
- data/lib/image_optim/runner/glob_helpers.rb +45 -0
- data/lib/image_optim/runner/option_parser.rb +246 -0
- data/lib/image_optim/space.rb +29 -0
- data/lib/image_optim/true_false_nil.rb +16 -0
- data/lib/image_optim/worker.rb +170 -0
- data/lib/image_optim/worker/advpng.rb +37 -0
- data/lib/image_optim/worker/class_methods.rb +107 -0
- data/lib/image_optim/worker/gifsicle.rb +65 -0
- data/lib/image_optim/worker/jhead.rb +47 -0
- data/lib/image_optim/worker/jpegoptim.rb +63 -0
- data/lib/image_optim/worker/jpegrecompress.rb +49 -0
- data/lib/image_optim/worker/jpegtran.rb +48 -0
- data/lib/image_optim/worker/optipng.rb +53 -0
- data/lib/image_optim/worker/pngcrush.rb +56 -0
- data/lib/image_optim/worker/pngout.rb +40 -0
- data/lib/image_optim/worker/pngquant.rb +61 -0
- data/lib/image_optim/worker/svgo.rb +34 -0
- data/script/template/jquery-2.1.3.min.js +4 -0
- data/script/template/sortable-0.6.0.min.js +2 -0
- data/script/template/worker_analysis.erb +254 -0
- data/script/update_worker_options_in_readme +59 -0
- data/script/worker_analysis +589 -0
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +37 -0
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +65 -0
- data/spec/image_optim/bin_resolver_spec.rb +290 -0
- data/spec/image_optim/cache_path_spec.rb +57 -0
- data/spec/image_optim/cache_spec.rb +162 -0
- data/spec/image_optim/cmd_spec.rb +93 -0
- data/spec/image_optim/config_spec.rb +254 -0
- data/spec/image_optim/handler_spec.rb +90 -0
- data/spec/image_optim/hash_helpers_spec.rb +74 -0
- data/spec/image_optim/image_meta_spec.rb +61 -0
- data/spec/image_optim/optimized_path_spec.rb +58 -0
- data/spec/image_optim/option_definition_spec.rb +138 -0
- data/spec/image_optim/option_helpers_spec.rb +25 -0
- data/spec/image_optim/path_spec.rb +103 -0
- data/spec/image_optim/runner/glob_helpers_spec.rb +21 -0
- data/spec/image_optim/runner/option_parser_spec.rb +105 -0
- data/spec/image_optim/space_spec.rb +23 -0
- data/spec/image_optim/worker/optipng_spec.rb +102 -0
- data/spec/image_optim/worker/pngquant_spec.rb +67 -0
- data/spec/image_optim/worker_spec.rb +303 -0
- data/spec/image_optim_spec.rb +259 -0
- data/spec/images/broken_jpeg +1 -0
- data/spec/images/comparison.png +0 -0
- data/spec/images/decompressed.jpeg +0 -0
- data/spec/images/icecream.gif +0 -0
- data/spec/images/image.jpg +0 -0
- data/spec/images/invisiblepixels/generate +24 -0
- data/spec/images/invisiblepixels/image.png +0 -0
- data/spec/images/lena.jpg +0 -0
- data/spec/images/orient/0.jpg +0 -0
- data/spec/images/orient/1.jpg +0 -0
- data/spec/images/orient/2.jpg +0 -0
- data/spec/images/orient/3.jpg +0 -0
- data/spec/images/orient/4.jpg +0 -0
- data/spec/images/orient/5.jpg +0 -0
- data/spec/images/orient/6.jpg +0 -0
- data/spec/images/orient/7.jpg +0 -0
- data/spec/images/orient/8.jpg +0 -0
- data/spec/images/orient/generate +23 -0
- data/spec/images/orient/original.jpg +0 -0
- data/spec/images/quant/64.png +0 -0
- data/spec/images/quant/generate +25 -0
- data/spec/images/rails.png +0 -0
- data/spec/images/test.svg +3 -0
- data/spec/images/transparency1.png +0 -0
- data/spec/images/transparency2.png +0 -0
- data/spec/images/vergroessert.jpg +0 -0
- data/spec/spec_helper.rb +93 -0
- metadata +281 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'image_optim/handler'
|
3
|
+
|
4
|
+
describe ImageOptim::Handler do
|
5
|
+
before do
|
6
|
+
stub_const('Handler', ImageOptim::Handler)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'uses original as source for first conversion '\
|
10
|
+
'and two temp files for further conversions' do
|
11
|
+
original = double(:original)
|
12
|
+
allow(original).to receive(:respond_to?).with(:temp_path).and_return(true)
|
13
|
+
|
14
|
+
handler = Handler.new(original)
|
15
|
+
temp_a = double(:temp_a)
|
16
|
+
temp_b = double(:temp_b)
|
17
|
+
expect(original).to receive(:temp_path).and_return(temp_a, temp_b)
|
18
|
+
|
19
|
+
# first unsuccessful run
|
20
|
+
handler.process do |src, dst|
|
21
|
+
expect([src, dst]).to eq([original, temp_a]); false
|
22
|
+
end
|
23
|
+
expect(handler.result).to be_nil
|
24
|
+
|
25
|
+
# first successful run
|
26
|
+
handler.process do |src, dst|
|
27
|
+
expect([src, dst]).to eq([original, temp_a]); true
|
28
|
+
end
|
29
|
+
expect(handler.result).to eq(temp_a)
|
30
|
+
|
31
|
+
# second unsuccessful run
|
32
|
+
handler.process do |src, dst|
|
33
|
+
expect([src, dst]).to eq([temp_a, temp_b]); false
|
34
|
+
end
|
35
|
+
expect(handler.result).to eq(temp_a)
|
36
|
+
|
37
|
+
# second successful run
|
38
|
+
handler.process do |src, dst|
|
39
|
+
expect([src, dst]).to eq([temp_a, temp_b]); true
|
40
|
+
end
|
41
|
+
expect(handler.result).to eq(temp_b)
|
42
|
+
|
43
|
+
# third successful run
|
44
|
+
handler.process do |src, dst|
|
45
|
+
expect([src, dst]).to eq([temp_b, temp_a]); true
|
46
|
+
end
|
47
|
+
expect(handler.result).to eq(temp_a)
|
48
|
+
|
49
|
+
# forth successful run
|
50
|
+
handler.process do |src, dst|
|
51
|
+
expect([src, dst]).to eq([temp_a, temp_b]); true
|
52
|
+
end
|
53
|
+
expect(handler.result).to eq(temp_b)
|
54
|
+
|
55
|
+
expect(temp_a).to receive(:unlink).once
|
56
|
+
handler.cleanup
|
57
|
+
handler.cleanup
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '.for' do
|
61
|
+
it 'yields instance, runs cleanup and returns result' do
|
62
|
+
original = double
|
63
|
+
handler = double
|
64
|
+
result = double
|
65
|
+
|
66
|
+
expect(Handler).to receive(:new).
|
67
|
+
with(original).and_return(handler)
|
68
|
+
expect(handler).to receive(:process)
|
69
|
+
expect(handler).to receive(:cleanup)
|
70
|
+
expect(handler).to receive(:result).and_return(result)
|
71
|
+
|
72
|
+
expect(Handler.for(original, &:process)).to eq(result)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'cleans up if exception is raised' do
|
76
|
+
original = double
|
77
|
+
handler = double
|
78
|
+
|
79
|
+
expect(Handler).to receive(:new).
|
80
|
+
with(original).and_return(handler)
|
81
|
+
expect(handler).to receive(:cleanup)
|
82
|
+
|
83
|
+
expect do
|
84
|
+
Handler.for(original) do
|
85
|
+
fail 'hello'
|
86
|
+
end
|
87
|
+
end.to raise_error 'hello'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'image_optim/hash_helpers'
|
3
|
+
|
4
|
+
describe ImageOptim::HashHelpers do
|
5
|
+
before do
|
6
|
+
stub_const('HashHelpers', ImageOptim::HashHelpers)
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'stringify/simbolyze' do
|
10
|
+
symbol_keys = {
|
11
|
+
:a => 1,
|
12
|
+
:b => {
|
13
|
+
:c => [:a, 'a'],
|
14
|
+
:d => {},
|
15
|
+
},
|
16
|
+
}
|
17
|
+
|
18
|
+
string_keys = {
|
19
|
+
'a' => 1,
|
20
|
+
'b' => {
|
21
|
+
'c' => [:a, 'a'],
|
22
|
+
'd' => {},
|
23
|
+
},
|
24
|
+
}
|
25
|
+
|
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)
|
29
|
+
end
|
30
|
+
|
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)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'deep merges hashes' do
|
38
|
+
merge_a = {
|
39
|
+
:a => {
|
40
|
+
:b => 1,
|
41
|
+
:c => {
|
42
|
+
:d => 2,
|
43
|
+
:e => {:f => true},
|
44
|
+
},
|
45
|
+
},
|
46
|
+
:y => 10,
|
47
|
+
}
|
48
|
+
|
49
|
+
merge_b = {
|
50
|
+
:a => {
|
51
|
+
:b => 2,
|
52
|
+
:c => {
|
53
|
+
:d => 3,
|
54
|
+
:e => false,
|
55
|
+
},
|
56
|
+
},
|
57
|
+
'z' => 20,
|
58
|
+
}
|
59
|
+
|
60
|
+
merge_result = {
|
61
|
+
:a => {
|
62
|
+
:b => 2,
|
63
|
+
:c => {
|
64
|
+
:d => 3,
|
65
|
+
:e => false,
|
66
|
+
},
|
67
|
+
},
|
68
|
+
:y => 10,
|
69
|
+
'z' => 20,
|
70
|
+
}
|
71
|
+
|
72
|
+
expect(HashHelpers.deep_merge(merge_a, merge_b)).to eq(merge_result)
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'image_optim/image_meta'
|
2
|
+
|
3
|
+
describe ImageOptim::ImageMeta do
|
4
|
+
let(:image_path){ 'spec/images/lena.jpg' }
|
5
|
+
let(:non_image_path){ __FILE__ }
|
6
|
+
let(:broken_image_path){ 'spec/images/broken_jpeg' }
|
7
|
+
|
8
|
+
describe '.format_for_path' do
|
9
|
+
context 'for an image' do
|
10
|
+
it 'returns format' do
|
11
|
+
expect(described_class.format_for_path(image_path)).
|
12
|
+
to eq(:jpeg)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'for broken image' do
|
17
|
+
it 'warns and returns nil' do
|
18
|
+
expect(described_class).to receive(:warn)
|
19
|
+
|
20
|
+
expect(described_class.format_for_path(broken_image_path)).
|
21
|
+
to eq(nil)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'for not an image' do
|
26
|
+
it 'does not warn and returns nil' do
|
27
|
+
expect(described_class).not_to receive(:warn)
|
28
|
+
|
29
|
+
expect(described_class.format_for_path(non_image_path)).
|
30
|
+
to eq(nil)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '.format_for_data' do
|
36
|
+
context 'for image data' do
|
37
|
+
it 'returns format' do
|
38
|
+
expect(described_class.format_for_data(File.read(image_path))).
|
39
|
+
to eq(:jpeg)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'for broken image data' do
|
44
|
+
it 'warns and returns nil' do
|
45
|
+
expect(described_class).to receive(:warn)
|
46
|
+
|
47
|
+
expect(described_class.format_for_data(File.read(broken_image_path))).
|
48
|
+
to eq(nil)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'for not an image data' do
|
53
|
+
it 'does not warn and returns nil' do
|
54
|
+
expect(described_class).not_to receive(:warn)
|
55
|
+
|
56
|
+
expect(described_class.format_for_data(File.read(non_image_path))).
|
57
|
+
to eq(nil)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'image_optim/optimized_path'
|
2
|
+
|
3
|
+
describe ImageOptim::OptimizedPath do
|
4
|
+
describe '#initialize' do
|
5
|
+
context 'when second argument is original' do
|
6
|
+
subject{ described_class.new('a', 'b') }
|
7
|
+
|
8
|
+
before do
|
9
|
+
allow_any_instance_of(ImageOptim::Path).
|
10
|
+
to receive(:size).and_return(616)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'delegates to optimized path as Path' do
|
14
|
+
is_expected.to eq(ImageOptim::Path.new('a'))
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns original path as Path for original' do
|
18
|
+
expect(subject.original).to eq(ImageOptim::Path.new('b'))
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'returns original size for original_size' do
|
22
|
+
expect(subject.original_size).to eq(616)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when second argument is size' do
|
27
|
+
subject{ described_class.new('a', 616) }
|
28
|
+
|
29
|
+
it 'delegates to optimized path as Path' do
|
30
|
+
is_expected.to eq(ImageOptim::Path.new('a'))
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns optimized path as Path for original' do
|
34
|
+
expect(subject.original).to eq(ImageOptim::Path.new('a'))
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns size for original_size' do
|
38
|
+
expect(subject.original_size).to eq(616)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when no second argument' do
|
43
|
+
subject{ described_class.new('a') }
|
44
|
+
|
45
|
+
it 'delegates to optimized path as Path' do
|
46
|
+
is_expected.to eq(ImageOptim::Path.new('a'))
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns nil for original' do
|
50
|
+
expect(subject.original).to eq(nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns nil for original_size' do
|
54
|
+
expect(subject.original_size).to eq(nil)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'image_optim/option_definition'
|
3
|
+
|
4
|
+
describe ImageOptim::OptionDefinition do
|
5
|
+
describe 'initialization' do
|
6
|
+
context 'when type is not specified explicitly' do
|
7
|
+
subject{ described_class.new('abc', :def, 'desc') }
|
8
|
+
|
9
|
+
describe '#name' do
|
10
|
+
it{ expect(subject.name).to eq(:abc) }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#default' do
|
14
|
+
it{ expect(subject.default).to eq(:def) }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#type' do
|
18
|
+
it{ expect(subject.type).to eq(Symbol) }
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#description' do
|
22
|
+
it{ expect(subject.description).to eq('desc') }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when type is specified explicitly' do
|
27
|
+
subject{ described_class.new('abc', :def, Hash, 'desc') }
|
28
|
+
|
29
|
+
describe '#name' do
|
30
|
+
it{ expect(subject.name).to eq(:abc) }
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#default' do
|
34
|
+
it{ expect(subject.default).to eq(:def) }
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#type' do
|
38
|
+
it{ expect(subject.type).to eq(Hash) }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#description' do
|
42
|
+
it{ expect(subject.description).to eq('desc') }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#value' do
|
48
|
+
context 'when proc not given' do
|
49
|
+
subject{ described_class.new('abc', :def, 'desc') }
|
50
|
+
|
51
|
+
context 'when option not provided' do
|
52
|
+
it 'returns default' do
|
53
|
+
expect(subject.value(nil, {})).to eq(:def)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when option is nil' do
|
58
|
+
it 'returns nil' do
|
59
|
+
expect(subject.value(nil, :abc => nil)).to eq(nil)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when option is set' do
|
64
|
+
it 'returns value' do
|
65
|
+
expect(subject.value(nil, :abc => 123)).to eq(123)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when proc given' do
|
71
|
+
subject do
|
72
|
+
# not using &:inspect due to ruby Bug #13087
|
73
|
+
# to_s is just to calm rubocop
|
74
|
+
described_class.new('abc', :def, 'desc'){ |o| o.inspect.to_s }
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when option not provided' do
|
78
|
+
it 'returns default passed through proc' do
|
79
|
+
expect(subject.value(nil, {})).to eq(':def')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when option is nil' do
|
84
|
+
it 'returns nil passed through proc' do
|
85
|
+
expect(subject.value(nil, :abc => nil)).to eq('nil')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when option is set' do
|
90
|
+
it 'returns value passed through proc' do
|
91
|
+
expect(subject.value(nil, :abc => 123)).to eq('123')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when proc with arity 2 given' do
|
97
|
+
subject do
|
98
|
+
described_class.new('abc', :def, 'desc'){ |a, b| [a.inspect, b] }
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'when option not provided' do
|
102
|
+
it 'returns default passed through proc' do
|
103
|
+
expect(subject.value(nil, {})).to eq([':def', subject])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when option is nil' do
|
108
|
+
it 'returns nil passed through proc' do
|
109
|
+
expect(subject.value(nil, :abc => nil)).to eq(['nil', subject])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when option is set' do
|
114
|
+
it 'returns value passed through proc' do
|
115
|
+
expect(subject.value(nil, :abc => 123)).to eq(['123', subject])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#default_description' do
|
122
|
+
context 'when default is not a string' do
|
123
|
+
subject{ described_class.new('abc', :def, 'desc') }
|
124
|
+
|
125
|
+
it 'returns inspected value in backticks' do
|
126
|
+
expect(subject.default_description).to eq('`:def`')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when default is a string' do
|
131
|
+
subject{ described_class.new('abc', '`1`', 'desc') }
|
132
|
+
|
133
|
+
it 'returns it as is' do
|
134
|
+
expect(subject.default_description).to eq('`1`')
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'image_optim/option_helpers'
|
3
|
+
|
4
|
+
describe ImageOptim::OptionHelpers do
|
5
|
+
describe '.limit_with_range' do
|
6
|
+
{
|
7
|
+
2..4 => 'inclusive',
|
8
|
+
2...5 => 'exclusive',
|
9
|
+
}.each do |range, type|
|
10
|
+
context "for an end #{type} range" do
|
11
|
+
it 'returns number when it is in range' do
|
12
|
+
expect(described_class.limit_with_range(4, range)).to eq(4)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'returns begin when it is less than minimum' do
|
16
|
+
expect(described_class.limit_with_range(1, range)).to eq(2)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns end when it is more than maximum' do
|
20
|
+
expect(described_class.limit_with_range(10, range)).to eq(4)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|