image_optim 0.13.3 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.rubocop.yml +56 -0
- data/.travis.yml +3 -1
- data/README.markdown +23 -10
- data/bin/image_optim +25 -15
- data/image_optim.gemspec +5 -2
- data/lib/image_optim.rb +47 -37
- data/lib/image_optim/bin_resolver.rb +17 -12
- data/lib/image_optim/bin_resolver/comparable_condition.rb +23 -7
- data/lib/image_optim/bin_resolver/simple_version.rb +2 -0
- data/lib/image_optim/config.rb +21 -13
- data/lib/image_optim/handler.rb +18 -12
- data/lib/image_optim/hash_helpers.rb +23 -13
- data/lib/image_optim/image_meta.rb +1 -0
- data/lib/image_optim/image_path.rb +14 -13
- data/lib/image_optim/option_definition.rb +11 -9
- data/lib/image_optim/option_helpers.rb +1 -2
- data/lib/image_optim/railtie.rb +18 -15
- data/lib/image_optim/runner.rb +67 -61
- data/lib/image_optim/space.rb +29 -0
- data/lib/image_optim/true_false_nil.rb +9 -1
- data/lib/image_optim/worker.rb +40 -16
- data/lib/image_optim/worker/advpng.rb +8 -1
- data/lib/image_optim/worker/gifsicle.rb +13 -1
- data/lib/image_optim/worker/jhead.rb +5 -0
- data/lib/image_optim/worker/jpegoptim.rb +17 -4
- data/lib/image_optim/worker/jpegtran.rb +9 -1
- data/lib/image_optim/worker/optipng.rb +13 -2
- data/lib/image_optim/worker/pngcrush.rb +14 -5
- data/lib/image_optim/worker/pngout.rb +10 -2
- data/lib/image_optim/worker/svgo.rb +1 -0
- data/script/update_worker_options_in_readme +42 -27
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +13 -13
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +4 -4
- data/spec/image_optim/bin_resolver_spec.rb +65 -37
- data/spec/image_optim/config_spec.rb +121 -110
- data/spec/image_optim/handler_spec.rb +29 -18
- data/spec/image_optim/hash_helpers_spec.rb +29 -27
- data/spec/image_optim/image_path_spec.rb +17 -17
- data/spec/image_optim/space_spec.rb +24 -0
- data/spec/image_optim/worker_spec.rb +18 -0
- data/spec/image_optim_spec.rb +134 -74
- metadata +27 -7
- data/script/update_instructions_in_readme +0 -44
@@ -1,11 +1,11 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
|
2
2
|
require 'rspec'
|
3
3
|
require 'image_optim/bin_resolver/comparable_condition'
|
4
4
|
|
5
5
|
describe ImageOptim::BinResolver::ComparableCondition do
|
6
6
|
is = ImageOptim::BinResolver::ComparableCondition.is
|
7
7
|
|
8
|
-
it
|
8
|
+
it 'should build conditions' do
|
9
9
|
expect(is.between?(10, 20).method).to eq(:between?)
|
10
10
|
expect(is.between?(10, 20).args).to eq([10, 20])
|
11
11
|
|
@@ -16,23 +16,23 @@ describe ImageOptim::BinResolver::ComparableCondition do
|
|
16
16
|
expect((is < 30).args).to eq([30])
|
17
17
|
end
|
18
18
|
|
19
|
-
it
|
19
|
+
it 'should stringify conditions' do
|
20
20
|
expect(is.between?(10, 20).to_s).to eq('10..20')
|
21
21
|
expect((is >= 15).to_s).to eq('>= 15')
|
22
22
|
expect((is < 30).to_s).to eq('< 30')
|
23
23
|
end
|
24
24
|
|
25
|
-
it
|
26
|
-
expect(is.between?(10, 20)).not_to
|
27
|
-
expect(is.between?(10, 20)).to
|
28
|
-
expect(is.between?(10, 20)).not_to
|
25
|
+
it 'should match conditions' do
|
26
|
+
expect(is.between?(10, 20)).not_to match 9
|
27
|
+
expect(is.between?(10, 20)).to match 15
|
28
|
+
expect(is.between?(10, 20)).not_to match 21
|
29
29
|
|
30
|
-
expect(is >= 15).not_to
|
31
|
-
expect(is >= 15).to
|
32
|
-
expect(is >= 15).to
|
30
|
+
expect(is >= 15).not_to match 14
|
31
|
+
expect(is >= 15).to match 15
|
32
|
+
expect(is >= 15).to match 16
|
33
33
|
|
34
|
-
expect(is < 30).to
|
35
|
-
expect(is < 30).not_to
|
36
|
-
expect(is < 30).not_to
|
34
|
+
expect(is < 30).to match 29
|
35
|
+
expect(is < 30).not_to match 30
|
36
|
+
expect(is < 30).not_to match 31
|
37
37
|
end
|
38
38
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
|
2
2
|
require 'rspec'
|
3
3
|
require 'image_optim/bin_resolver/simple_version'
|
4
4
|
|
@@ -7,7 +7,7 @@ describe ImageOptim::BinResolver::SimpleVersion do
|
|
7
7
|
ImageOptim::BinResolver::SimpleVersion.new(str)
|
8
8
|
end
|
9
9
|
|
10
|
-
it
|
10
|
+
it 'should compare versions' do
|
11
11
|
expect(v '1.17').to be > '0'
|
12
12
|
expect(v '1.17').to be > '0.1'
|
13
13
|
expect(v '1.17').to be > '0.9'
|
@@ -17,7 +17,7 @@ describe ImageOptim::BinResolver::SimpleVersion do
|
|
17
17
|
expect(v '1.17').to be < '2.1'
|
18
18
|
end
|
19
19
|
|
20
|
-
it
|
20
|
+
it 'should normalize versions' do
|
21
21
|
variations = %w[1 01 1.0 1.00 1.0.0 1.0.0.0]
|
22
22
|
variations.each do |a|
|
23
23
|
variations.each do |b|
|
@@ -26,7 +26,7 @@ describe ImageOptim::BinResolver::SimpleVersion do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
it
|
29
|
+
it 'should convert objects' do
|
30
30
|
expect(v 1.17).to eq('1.17')
|
31
31
|
expect(v '1.17').to eq('1.17')
|
32
32
|
expect(v(v 1.17)).to eq('1.17')
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
|
2
2
|
require 'rspec'
|
3
3
|
require 'image_optim/bin_resolver'
|
4
4
|
|
@@ -10,74 +10,93 @@ ensure
|
|
10
10
|
end
|
11
11
|
|
12
12
|
describe ImageOptim::BinResolver do
|
13
|
-
let(:image_optim){ double(:image_optim, :verbose
|
13
|
+
let(:image_optim){ double(:image_optim, :verbose => false) }
|
14
14
|
let(:resolver){ ImageOptim::BinResolver.new(image_optim) }
|
15
15
|
|
16
|
-
it
|
16
|
+
it 'should resolve bin in path' do
|
17
17
|
with_env 'LS_BIN', nil do
|
18
|
-
resolver.
|
19
|
-
FSPath.
|
18
|
+
expect(resolver).to receive(:accessible?).with(:ls).once.and_return(true)
|
19
|
+
expect(FSPath).not_to receive(:temp_dir)
|
20
20
|
|
21
21
|
5.times do
|
22
22
|
resolver.resolve!(:ls)
|
23
23
|
end
|
24
|
-
resolver.env_path.
|
24
|
+
expect(resolver.env_path).to eq([
|
25
|
+
ENV['PATH'],
|
26
|
+
ImageOptim::BinResolver::VENDOR_PATH,
|
27
|
+
].join(':'))
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
28
|
-
it
|
29
|
-
path =
|
31
|
+
it 'should resolve bin specified in ENV' do
|
32
|
+
path = 'some/path/image_optim2.3.4'
|
30
33
|
with_env 'IMAGE_OPTIM_BIN', path do
|
31
|
-
tmpdir = double(:tmpdir)
|
34
|
+
tmpdir = double(:tmpdir, :to_str => 'tmpdir')
|
32
35
|
symlink = double(:symlink)
|
33
36
|
|
34
|
-
resolver.
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
expect(resolver).to receive(:accessible?).
|
38
|
+
with(:image_optim).once.and_return(true)
|
39
|
+
expect(FSPath).to receive(:temp_dir).
|
40
|
+
once.and_return(tmpdir)
|
41
|
+
expect(tmpdir).to receive(:/).
|
42
|
+
with(:image_optim).once.and_return(symlink)
|
43
|
+
expect(symlink).to receive(:make_symlink).
|
44
|
+
with(File.expand_path(path)).once
|
38
45
|
|
39
46
|
at_exit_blocks = []
|
40
|
-
resolver.
|
47
|
+
expect(resolver).to receive(:at_exit).once do |&block|
|
41
48
|
at_exit_blocks.unshift(block)
|
42
49
|
end
|
43
50
|
|
44
51
|
5.times do
|
45
52
|
resolver.resolve!(:image_optim)
|
46
53
|
end
|
47
|
-
resolver.env_path.
|
54
|
+
expect(resolver.env_path).to eq([
|
55
|
+
tmpdir,
|
56
|
+
ENV['PATH'],
|
57
|
+
ImageOptim::BinResolver::VENDOR_PATH,
|
58
|
+
].join(':'))
|
48
59
|
|
49
|
-
FileUtils.
|
60
|
+
expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir)
|
50
61
|
at_exit_blocks.each(&:call)
|
51
62
|
end
|
52
63
|
end
|
53
64
|
|
54
|
-
it
|
65
|
+
it 'should raise on failure to resolve bin' do
|
55
66
|
with_env 'SHOULD_NOT_EXIST_BIN', nil do
|
56
|
-
resolver.
|
57
|
-
|
67
|
+
expect(resolver).to receive(:accessible?).
|
68
|
+
with(:should_not_exist).once.and_return(false)
|
69
|
+
expect(FSPath).not_to receive(:temp_dir)
|
58
70
|
|
59
71
|
5.times do
|
60
72
|
expect do
|
61
73
|
resolver.resolve!(:should_not_exist)
|
62
74
|
end.to raise_error ImageOptim::BinNotFoundError
|
63
75
|
end
|
64
|
-
resolver.env_path.
|
76
|
+
expect(resolver.env_path).to eq([
|
77
|
+
ENV['PATH'],
|
78
|
+
ImageOptim::BinResolver::VENDOR_PATH,
|
79
|
+
].join(':'))
|
65
80
|
end
|
66
81
|
end
|
67
82
|
|
68
|
-
it
|
69
|
-
path =
|
83
|
+
it 'should raise on failure to resolve bin specified in ENV' do
|
84
|
+
path = 'some/path/should_not_exist_bin'
|
70
85
|
with_env 'SHOULD_NOT_EXIST_BIN', path do
|
71
|
-
tmpdir = double(:tmpdir)
|
86
|
+
tmpdir = double(:tmpdir, :to_str => 'tmpdir')
|
72
87
|
symlink = double(:symlink)
|
73
88
|
|
74
|
-
resolver.
|
75
|
-
|
76
|
-
|
77
|
-
|
89
|
+
expect(resolver).to receive(:accessible?).
|
90
|
+
with(:should_not_exist).once.and_return(false)
|
91
|
+
expect(FSPath).to receive(:temp_dir).
|
92
|
+
once.and_return(tmpdir)
|
93
|
+
expect(tmpdir).to receive(:/).
|
94
|
+
with(:should_not_exist).once.and_return(symlink)
|
95
|
+
expect(symlink).to receive(:make_symlink).
|
96
|
+
with(File.expand_path(path)).once
|
78
97
|
|
79
98
|
at_exit_blocks = []
|
80
|
-
resolver.
|
99
|
+
expect(resolver).to receive(:at_exit).once do |&block|
|
81
100
|
at_exit_blocks.unshift(block)
|
82
101
|
end
|
83
102
|
|
@@ -86,16 +105,20 @@ describe ImageOptim::BinResolver do
|
|
86
105
|
resolver.resolve!(:should_not_exist)
|
87
106
|
end.to raise_error ImageOptim::BinNotFoundError
|
88
107
|
end
|
89
|
-
resolver.env_path.
|
108
|
+
expect(resolver.env_path).to eq([
|
109
|
+
tmpdir,
|
110
|
+
ENV['PATH'],
|
111
|
+
ImageOptim::BinResolver::VENDOR_PATH,
|
112
|
+
].join(':'))
|
90
113
|
|
91
|
-
FileUtils.
|
114
|
+
expect(FileUtils).to receive(:remove_entry_secure).with(tmpdir)
|
92
115
|
at_exit_blocks.each(&:call)
|
93
116
|
end
|
94
117
|
end
|
95
118
|
|
96
|
-
it
|
119
|
+
it 'should resolve bin only once' do
|
97
120
|
with_env 'LS_BIN', nil do
|
98
|
-
resolver.
|
121
|
+
expect(resolver).to receive(:resolve?).once.with(:ls){ sleep 0.1; true }
|
99
122
|
|
100
123
|
10.times.map do
|
101
124
|
Thread.new do
|
@@ -105,18 +128,23 @@ describe ImageOptim::BinResolver do
|
|
105
128
|
end
|
106
129
|
end
|
107
130
|
|
108
|
-
it
|
131
|
+
it 'should raise on detection of problematic version' do
|
109
132
|
with_env 'PNGCRUSH_BIN', nil do
|
110
|
-
resolver.
|
111
|
-
|
112
|
-
|
133
|
+
expect(resolver).to receive(:accessible?).
|
134
|
+
with(:pngcrush).once.and_return(true)
|
135
|
+
expect(resolver).to receive(:version).
|
136
|
+
with(:pngcrush).once.and_return('1.7.60')
|
137
|
+
expect(FSPath).not_to receive(:temp_dir)
|
113
138
|
|
114
139
|
5.times do
|
115
140
|
expect do
|
116
141
|
resolver.resolve!(:pngcrush)
|
117
142
|
end.to raise_error ImageOptim::BadBinVersion
|
118
143
|
end
|
119
|
-
resolver.env_path.
|
144
|
+
expect(resolver.env_path).to eq([
|
145
|
+
ENV['PATH'],
|
146
|
+
ImageOptim::BinResolver::VENDOR_PATH,
|
147
|
+
].join(':'))
|
120
148
|
end
|
121
149
|
end
|
122
150
|
end
|
@@ -1,153 +1,164 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
|
2
2
|
require 'rspec'
|
3
3
|
require 'image_optim/config'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
describe ImageOptim::Config do
|
6
|
+
Config = ImageOptim::Config
|
7
|
+
|
8
|
+
before do
|
9
|
+
allow(Config).to receive(:global).and_return({})
|
10
|
+
allow(Config).to receive(:local).and_return({})
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'assert_no_unused_options!' do
|
14
|
+
it 'should not raise when no unused options' do
|
15
|
+
config = Config.new({})
|
16
|
+
config.assert_no_unused_options!
|
9
17
|
end
|
10
18
|
|
11
|
-
|
12
|
-
|
13
|
-
|
19
|
+
it 'should raise when there are unused options' do
|
20
|
+
config = Config.new(:unused => true)
|
21
|
+
expect do
|
14
22
|
config.assert_no_unused_options!
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should raise when there are unused options" do
|
18
|
-
config = Config.new({:unused => true})
|
19
|
-
proc {
|
20
|
-
config.assert_no_unused_options!
|
21
|
-
}.should raise_error(ConfigurationError)
|
22
|
-
end
|
23
|
+
end.to raise_error(ImageOptim::ConfigurationError)
|
23
24
|
end
|
25
|
+
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
describe 'nice' do
|
28
|
+
it 'should be 10 by default' do
|
29
|
+
config = Config.new({})
|
30
|
+
expect(config.nice).to eq(10)
|
31
|
+
end
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
it 'should be 0 if disabled' do
|
34
|
+
config = Config.new(:nice => false)
|
35
|
+
expect(config.nice).to eq(0)
|
36
|
+
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
38
|
+
it 'should convert value to number' do
|
39
|
+
config = Config.new(:nice => '13')
|
40
|
+
expect(config.nice).to eq(13)
|
40
41
|
end
|
42
|
+
end
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
describe 'threads' do
|
45
|
+
it 'should be processor_count by default' do
|
46
|
+
config = Config.new({})
|
47
|
+
allow(config).to receive(:processor_count).and_return(13)
|
48
|
+
expect(config.threads).to eq(13)
|
49
|
+
end
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
it 'should be 1 if disabled' do
|
52
|
+
config = Config.new(:threads => false)
|
53
|
+
expect(config.threads).to eq(1)
|
54
|
+
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
56
|
+
it 'should convert value to number' do
|
57
|
+
config = Config.new(:threads => '616')
|
58
|
+
expect(config.threads).to eq(616)
|
58
59
|
end
|
60
|
+
end
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
|
62
|
+
describe 'for_worker' do
|
63
|
+
Abc = Class.new(ImageOptim::Worker) do
|
64
|
+
def image_formats
|
65
|
+
[]
|
63
66
|
end
|
67
|
+
end
|
64
68
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
+
it 'should return empty hash by default' do
|
70
|
+
config = Config.new({})
|
71
|
+
expect(config.for_worker(Abc)).to eq({})
|
72
|
+
end
|
69
73
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
+
it 'should return passed hash' do
|
75
|
+
config = Config.new(:abc => {:option => true})
|
76
|
+
expect(config.for_worker(Abc)).to eq(:option => true)
|
77
|
+
end
|
74
78
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
+
it 'should return passed false' do
|
80
|
+
config = Config.new(:abc => false)
|
81
|
+
expect(config.for_worker(Abc)).to eq(false)
|
82
|
+
end
|
79
83
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
84
|
+
it 'should raise on unknown optino' do
|
85
|
+
config = Config.new(:abc => 13)
|
86
|
+
expect do
|
87
|
+
config.for_worker(Abc)
|
88
|
+
end.to raise_error(ImageOptim::ConfigurationError)
|
86
89
|
end
|
90
|
+
end
|
87
91
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
92
|
+
describe 'class methods' do
|
93
|
+
before do
|
94
|
+
allow(Config).to receive(:global).and_call_original
|
95
|
+
allow(Config).to receive(:local).and_call_original
|
96
|
+
end
|
93
97
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
Config.
|
98
|
+
describe 'global' do
|
99
|
+
it 'should return empty hash for global config if it does not exists' do
|
100
|
+
expect(File).to receive(:file?).
|
101
|
+
with(Config::GLOBAL_CONFIG_PATH).and_return(false)
|
102
|
+
expect(Config).not_to receive(:read)
|
98
103
|
|
99
|
-
|
100
|
-
|
104
|
+
expect(Config.global).to eq({})
|
105
|
+
end
|
101
106
|
|
102
|
-
|
103
|
-
|
104
|
-
|
107
|
+
it 'should read global config if it exists' do
|
108
|
+
expect(File).to receive(:file?).
|
109
|
+
with(Config::GLOBAL_CONFIG_PATH).and_return(true)
|
110
|
+
expect(Config).to receive(:read).
|
111
|
+
with(Config::GLOBAL_CONFIG_PATH).and_return(:config => true)
|
105
112
|
|
106
|
-
|
107
|
-
end
|
113
|
+
expect(Config.global).to eq(:config => true)
|
108
114
|
end
|
115
|
+
end
|
109
116
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
Config.
|
117
|
+
describe 'local' do
|
118
|
+
it 'should return empty hash for local config if it does not exists' do
|
119
|
+
expect(File).to receive(:file?).
|
120
|
+
with(Config::LOCAL_CONFIG_PATH).and_return(false)
|
121
|
+
expect(Config).not_to receive(:read)
|
114
122
|
|
115
|
-
|
116
|
-
|
123
|
+
expect(Config.local).to eq({})
|
124
|
+
end
|
117
125
|
|
118
|
-
|
119
|
-
|
120
|
-
|
126
|
+
it 'should read local config if it exists' do
|
127
|
+
expect(File).to receive(:file?).
|
128
|
+
with(Config::LOCAL_CONFIG_PATH).and_return(true)
|
129
|
+
expect(Config).to receive(:read).
|
130
|
+
with(Config::LOCAL_CONFIG_PATH).and_return(:config => true)
|
121
131
|
|
122
|
-
|
123
|
-
end
|
132
|
+
expect(Config.local).to eq(:config => true)
|
124
133
|
end
|
134
|
+
end
|
125
135
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
136
|
+
describe 'read' do
|
137
|
+
it 'should return hash with deep symbolised keys from reader' do
|
138
|
+
stringified = {'config' => {'this' => true}}
|
139
|
+
symbolized = {:config => {:this => true}}
|
130
140
|
|
131
|
-
|
132
|
-
|
141
|
+
path = double(:path)
|
142
|
+
expect(YAML).to receive(:load_file).with(path).and_return(stringified)
|
133
143
|
|
134
|
-
|
135
|
-
|
136
|
-
YAML.should_receive(:load_file).with(path).and_return([:config])
|
137
|
-
Config.should_receive(:warn)
|
144
|
+
expect(Config.instance_eval{ read(path) }).to eq(symbolized)
|
145
|
+
end
|
138
146
|
|
139
|
-
|
140
|
-
|
147
|
+
it 'should warn and return an empty hash if reader returns non hash' do
|
148
|
+
path = double(:path)
|
149
|
+
expect(YAML).to receive(:load_file).with(path).and_return([:config])
|
150
|
+
expect(Config).to receive(:warn)
|
141
151
|
|
142
|
-
|
143
|
-
|
144
|
-
YAML.should_receive(:load_file).with(path).and_raise
|
145
|
-
Config.should_receive(:warn)
|
152
|
+
expect(Config.instance_eval{ read(path) }).to eq({})
|
153
|
+
end
|
146
154
|
|
147
|
-
|
148
|
-
|
155
|
+
it 'should warn and return an empty hash if reader raises exception' do
|
156
|
+
path = double(:path)
|
157
|
+
expect(YAML).to receive(:load_file).with(path).and_raise
|
158
|
+
expect(Config).to receive(:warn)
|
159
|
+
|
160
|
+
expect(Config.instance_eval{ read(path) }).to eq({})
|
149
161
|
end
|
150
162
|
end
|
151
|
-
|
152
163
|
end
|
153
164
|
end
|