opt_parse_validator 0.0.2

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +4 -0
  5. data/.simplecov +4 -0
  6. data/.travis.yml +15 -0
  7. data/Gemfile +6 -0
  8. data/README.md +40 -0
  9. data/Rakefile +9 -0
  10. data/lib/opt_parse_validator/hacks.rb +15 -0
  11. data/lib/opt_parse_validator/options_file.rb +62 -0
  12. data/lib/opt_parse_validator/opts/base.rb +74 -0
  13. data/lib/opt_parse_validator/opts/boolean.rb +20 -0
  14. data/lib/opt_parse_validator/opts/choice.rb +33 -0
  15. data/lib/opt_parse_validator/opts/credentials.rb +14 -0
  16. data/lib/opt_parse_validator/opts/directory_path.rb +10 -0
  17. data/lib/opt_parse_validator/opts/file_path.rb +24 -0
  18. data/lib/opt_parse_validator/opts/integer.rb +12 -0
  19. data/lib/opt_parse_validator/opts/path.rb +61 -0
  20. data/lib/opt_parse_validator/opts/positive_integer.rb +13 -0
  21. data/lib/opt_parse_validator/opts/proxy.rb +5 -0
  22. data/lib/opt_parse_validator/opts/string.rb +6 -0
  23. data/lib/opt_parse_validator/opts/uri.rb +24 -0
  24. data/lib/opt_parse_validator/opts/url.rb +9 -0
  25. data/lib/opt_parse_validator/opts.rb +3 -0
  26. data/lib/opt_parse_validator/version.rb +4 -0
  27. data/lib/opt_parse_validator.rb +77 -0
  28. data/opt_parse_validator.gemspec +32 -0
  29. data/spec/fixtures/options_file/default.json +4 -0
  30. data/spec/fixtures/options_file/malformed.json +4 -0
  31. data/spec/fixtures/options_file/override.yml +1 -0
  32. data/spec/fixtures/options_file/unsupported.ext +1 -0
  33. data/spec/fixtures/r.txt +1 -0
  34. data/spec/fixtures/rwx.txt +1 -0
  35. data/spec/lib/opt_parse_validator/options_file_spec.rb +50 -0
  36. data/spec/lib/opt_parse_validator/opts/base_spec.rb +169 -0
  37. data/spec/lib/opt_parse_validator/opts/boolean_spec.rb +36 -0
  38. data/spec/lib/opt_parse_validator/opts/choice_spec.rb +78 -0
  39. data/spec/lib/opt_parse_validator/opts/credentials_spec.rb +23 -0
  40. data/spec/lib/opt_parse_validator/opts/direcyory_path_spec.rb +25 -0
  41. data/spec/lib/opt_parse_validator/opts/file_path_spec.rb +93 -0
  42. data/spec/lib/opt_parse_validator/opts/integer_spec.rb +19 -0
  43. data/spec/lib/opt_parse_validator/opts/path_spec.rb +7 -0
  44. data/spec/lib/opt_parse_validator/opts/positive_integer_spec.rb +19 -0
  45. data/spec/lib/opt_parse_validator/opts/proxy_spec.rb +12 -0
  46. data/spec/lib/opt_parse_validator/opts/uri_spec.rb +58 -0
  47. data/spec/lib/opt_parse_validator/opts/url_spec.rb +28 -0
  48. data/spec/lib/opt_parse_validator/version_spec.rb +7 -0
  49. data/spec/lib/opt_parse_validator_spec.rb +152 -0
  50. data/spec/spec_helper.rb +25 -0
  51. metadata +213 -0
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptParser do
4
+
5
+ subject(:parser) { described_class.new }
6
+ let(:fixtures) { File.join(FIXTURES, 'options_file') }
7
+ let(:default_file) { File.join(fixtures, 'default.json') }
8
+ let(:override_file) { File.join(fixtures, 'override.yml') }
9
+
10
+ describe '#load_options_files' do
11
+
12
+ context 'when error' do
13
+ before { parser.options_files << config_file }
14
+
15
+ context 'when the format is not supported' do
16
+ let(:config_file) { File.join(fixtures, 'unsupported.ext') }
17
+ let(:exception) { "The format #{File.extname(config_file).delete('.')} is not supported" }
18
+
19
+ it 'raises an error' do
20
+ expect { parser.load_options_files }.to raise_error(exception)
21
+ end
22
+ end
23
+
24
+ context 'when file content is malformed' do
25
+ let(:config_file) { File.join(fixtures, 'malformed.json') }
26
+ let(:exception) { "Parse Error, #{config_file} seems to be malformed" }
27
+
28
+ it 'raises an error' do
29
+ expect { parser.load_options_files }.to raise_error(exception)
30
+ end
31
+ end
32
+ end
33
+
34
+ context 'otherwise' do
35
+ let(:verbose_opt) { OptParseValidator::OptBoolean.new(%w(-v --verbose)) }
36
+ let(:override_opt) { OptParseValidator::OptString.new(['--override-me VALUE'], normalize: :to_sym) }
37
+
38
+ let(:opts) { [verbose_opt, override_opt] }
39
+
40
+ let(:expected) { { verbose: true, override_me: :Yeaa } }
41
+
42
+ it 'sets everything correctly and get the right results' do
43
+ parser.options_files << default_file << override_file
44
+ parser.add(*opts)
45
+
46
+ expect(parser.results([])).to eq expected
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,169 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptBase do
4
+ subject(:opt) { described_class.new(option, attrs) }
5
+ let(:option) { %w(-v --verbose) }
6
+ let(:attrs) { {} }
7
+
8
+ describe '#to_long' do
9
+ after { expect(described_class.new(@option).to_long).to eq @expected }
10
+
11
+ context 'when not found' do
12
+ it 'returns nil' do
13
+ @option = %w(-v)
14
+ @expected = nil
15
+ end
16
+ end
17
+
18
+ context 'when found' do
19
+ it 'returns the long name' do
20
+ @option = ['-v', '--[no-]verbose VALUE [OPT]', 'Verbose Mode']
21
+ @expected = '--verbose'
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#to_sym' do
27
+ after :each do
28
+ if @exception
29
+ expect { described_class.new(@option).to_sym }.to raise_error(@exception)
30
+ else
31
+ expect(described_class.new(@option).to_sym).to eq(@expected)
32
+ end
33
+ end
34
+
35
+ context 'without REQUIRED or OPTIONAL arguments' do
36
+ context 'with short option' do
37
+ it 'returns :test' do
38
+ @option = %w(-t --test Testing)
39
+ @expected = :test
40
+ end
41
+
42
+ it 'returns :its_a_long_option' do
43
+ @option = ['-l', '--its-a-long-option', "Testing '-' replacement"]
44
+ @expected = :its_a_long_option
45
+ end
46
+ end
47
+
48
+ context 'without short option' do
49
+ it 'returns :long' do
50
+ @option = ['--long']
51
+ @expected = :long
52
+ end
53
+
54
+ it 'returns :long_option' do
55
+ @option = ['--long-option', 'No short !']
56
+ @expected = :long_option
57
+ end
58
+ end
59
+
60
+ context 'without long option' do
61
+ it 'raises an error' do
62
+ @option = ['-v', 'long option missing']
63
+ @exception = 'Could not find option symbol for ["-v", "long option missing"]'
64
+ end
65
+
66
+ it 'raises an error' do
67
+ @option = ['long option missing']
68
+ @exception = 'Could not find option symbol for ["long option missing"]'
69
+ end
70
+ end
71
+
72
+ context 'with multiple long option names (like alias)' do
73
+ it 'returns the first long option found' do
74
+ @option = %w(--check-long --cl)
75
+ @expected = :check_long
76
+ end
77
+ end
78
+ end
79
+
80
+ context 'when negative prefix name' do
81
+ it 'returns the positive option symbol' do
82
+ @option = %w(-v --[no-]verbose)
83
+ @expected = :verbose
84
+ end
85
+ end
86
+
87
+ context 'with REQUIRED or OPTIONAL arguments' do
88
+ it 'should removed the OPTIONAL argument' do
89
+ @option = ['-p', '--page [PAGE_NUMBER]']
90
+ @expected = :page
91
+ end
92
+
93
+ it 'should removed the REQUIRED argument' do
94
+ @option = ['--url TARGET_URL']
95
+ @expected = :url
96
+ end
97
+ end
98
+ end
99
+
100
+ describe '#new, #required?' do
101
+ context 'when no :required' do
102
+ its(:option) { should eq(option) }
103
+ its(:required?) { should be_falsey }
104
+ its(:to_sym) { should eq(:verbose) }
105
+ end
106
+
107
+ context 'when :required' do
108
+ let(:attrs) { { required: true } }
109
+
110
+ its(:required?) { should be true }
111
+ end
112
+ end
113
+
114
+ describe '#normalize' do
115
+ after { expect(opt.normalize(@value)).to eq @expected }
116
+
117
+ context 'when no :normalize attribute' do
118
+ it 'returns the value' do
119
+ @value = 'test'
120
+ @expected = @value
121
+ end
122
+ end
123
+
124
+ context 'when a single normalization' do
125
+ let(:attrs) { { normalize: :to_sym } }
126
+
127
+ context 'when the value does not have a to_sym method' do
128
+ it 'returns the value' do
129
+ @value = 1.0
130
+ @expected = @value
131
+ end
132
+ end
133
+
134
+ context 'when a to_sym method' do
135
+ it 'returns the symbol' do
136
+ @value = 'test'
137
+ @expected = :test
138
+ end
139
+ end
140
+ end
141
+
142
+ context 'when multiple normalization' do
143
+ let(:attrs) { { normalize: [:to_sym, 2.0, :upcase] } }
144
+
145
+ it 'apply each of them (if possible)' do
146
+ @value = 'test'
147
+ @expected = :TEST
148
+ end
149
+ end
150
+ end
151
+
152
+ describe '#validate' do
153
+ context 'when an empty or nil value' do
154
+ it 'raises an error' do
155
+ [nil, ''].each do |value|
156
+ expect { opt.validate(value) }
157
+ .to raise_error('Empty option value supplied')
158
+ end
159
+ end
160
+ end
161
+
162
+ context 'when a valid value' do
163
+ it 'returns it' do
164
+ expect(opt.validate('testing')).to eq 'testing'
165
+ end
166
+ end
167
+ end
168
+
169
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptBoolean do
4
+
5
+ subject(:opt) { described_class.new(['-b', '--bool BOOL']) }
6
+
7
+ describe '#validate' do
8
+ context 'when does not match TRUE_PATTERN and FALSE_PATTERN' do
9
+ it 'raises an error' do
10
+ expect { opt.validate("true\nfalse") }
11
+ .to raise_error('Invalid boolean value, expected true|t|yes|y|1|false|f|no|n|0')
12
+ end
13
+ end
14
+
15
+ context 'when matches TRUE_PATTERN' do
16
+ after { expect(opt.validate(@argument)).to be true }
17
+
18
+ %w(true t yes y 1).each do |arg|
19
+ it 'returns true' do
20
+ @argument = arg
21
+ end
22
+ end
23
+ end
24
+
25
+ context 'when matches FALSE_PATTERN' do
26
+ after { expect(opt.validate(@argument)).to be false }
27
+
28
+ %w(false f no n 0).each do |arg|
29
+ it 'returns false' do
30
+ @argument = arg
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptChoice do
4
+ subject(:opt) { described_class.new(option, attrs) }
5
+ let(:option) { %w(-f --format FORMAT) }
6
+ let(:attrs) { { choices: %w(json cli) } }
7
+
8
+ describe '#new' do
9
+ context 'when errors' do
10
+ after { expect { opt }.to raise_error(@exception) }
11
+
12
+ context 'when :choices not provided' do
13
+ let(:attrs) { {} }
14
+
15
+ it 'raises an error' do
16
+ @exception = 'The :choices attribute is mandatory'
17
+ end
18
+ end
19
+
20
+ context 'when :choices is not an array' do
21
+ let(:attrs) { { choices: 'wrong type' } }
22
+
23
+ it 'raises an error' do
24
+ @exception = 'The :choices attribute must be an array'
25
+ end
26
+ end
27
+ end
28
+
29
+ context 'when valid' do
30
+ it 'sets the option correctly' do
31
+ expect { opt }.to_not raise_error
32
+ expect(opt.attrs[:choices]).to eq attrs[:choices]
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '#validate' do
38
+ after :each do
39
+ if @exception
40
+ expect { opt.validate(@value) }.to raise_error(@exception)
41
+ else
42
+ expect(opt.validate(@value)).to eq(@expected)
43
+ end
44
+ end
45
+
46
+ context 'when the value is not in the choices' do
47
+ it 'raises an error' do
48
+ @value = 'invalid-format'
49
+ @exception = "'invalid-format' is not a valid choice, expected one of the followings: json,cli"
50
+ end
51
+
52
+ context 'when :case_sensitive' do
53
+ let(:attrs) { { choices: %w(json cli), case_sensitive: true } }
54
+
55
+ it 'raises an error' do
56
+ @value = 'JSON'
57
+ @exception = "'JSON' is not a valid choice, expected one of the followings: json,cli"
58
+ end
59
+ end
60
+ end
61
+
62
+ context 'when valid choice' do
63
+ it 'returns the choice' do
64
+ @value = 'JSON'
65
+ @expected = 'json'
66
+ end
67
+
68
+ context 'when :case_sensitive' do
69
+ let(:attrs) { { choices: %w(JSON cli), case_sensitive: true } }
70
+
71
+ it 'raises an error' do
72
+ @value = 'JSON'
73
+ @expected = 'JSON'
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptCredentials do
4
+
5
+ subject(:opt) { described_class.new(['-l', '--login USERNAME:PASSWORD']) }
6
+
7
+ describe '#validate' do
8
+ context 'when incorrect format' do
9
+ it 'raises an error' do
10
+ expect { opt.validate('wrong') }
11
+ .to raise_error 'Incorrect credentials format, username:password expected'
12
+ end
13
+ end
14
+
15
+ context 'when valid format' do
16
+ it 'returns a hash with :username and :password' do
17
+ expect(opt.validate('admin:P@ssw:rd'))
18
+ .to eq(username: 'admin', password: 'P@ssw:rd')
19
+ end
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptDirectoryPath do
4
+
5
+ subject(:opt) { described_class.new(['-d', '--dir DIR'], attrs) }
6
+ let(:attrs) { {} }
7
+ let(:dir) { File.join(FIXTURES, 'options_file') }
8
+
9
+ its(:attrs) { should eq directory: true }
10
+
11
+ describe '#validate' do
12
+ context 'when it is a directory' do
13
+ it 'returns the path' do
14
+ expect(opt.validate(dir)).to eq dir
15
+ end
16
+ end
17
+
18
+ context 'when it\s not ' do
19
+ it 'raises an error' do
20
+ expect { opt.validate('yolo.txt') }.to raise_error "'yolo.txt' is not a directory"
21
+ end
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptFilePath do
4
+
5
+ subject(:opt) { described_class.new(['-f', '--file FILE_PATH'], attrs) }
6
+ let(:attrs) { {} }
7
+ let(:rwx_file) { File.join(FIXTURES, 'rwx.txt') }
8
+ let(:r_file) { File.join(FIXTURES, 'r.txt') }
9
+
10
+ its(:attrs) { should eq file: true }
11
+
12
+ describe '#validate' do
13
+ context 'when :extensions' do
14
+ let(:attrs) { { extensions: 'txt' } }
15
+
16
+ its('allowed_attrs.first') { should eq :extensions }
17
+
18
+ context 'when it matches' do
19
+ it 'returns the path' do
20
+ expect(opt.validate(rwx_file)).to eq rwx_file
21
+ end
22
+ end
23
+
24
+ context 'when it does no match' do
25
+ it 'raises an error' do
26
+ expect { opt.validate('yolo.aa') }
27
+ .to raise_error "The extension of 'yolo.aa' is not allowed"
28
+ end
29
+ end
30
+ end
31
+
32
+ context 'when :executable' do
33
+ let(:attrs) { { executable: true } }
34
+
35
+ it 'returns the path if the file is +x' do
36
+ expect(opt.validate(rwx_file)).to eq rwx_file
37
+ end
38
+
39
+ it 'raises an error if not ' do
40
+ expect { opt.validate(r_file) }.to raise_error "'#{r_file}' is not executable"
41
+ end
42
+ end
43
+
44
+ context 'when :readable' do
45
+ let(:attrs) { { readable: true, exists: false } }
46
+
47
+ it 'returns the path if the file is +r' do
48
+ expect(opt.validate(rwx_file)).to eq rwx_file
49
+ end
50
+
51
+ it 'raises an error otherwise' do
52
+ file = File.join(FIXTURES, 'yolo.txt')
53
+
54
+ expect { opt.validate(file) }.to raise_error "'#{file}' is not readable"
55
+ end
56
+ end
57
+
58
+ context 'when :writable' do
59
+ context 'when the path exists' do
60
+ let(:attrs) { { writable: true } }
61
+
62
+ it 'returns the path if the path is +x' do
63
+ expect(opt.validate(rwx_file)).to eq rwx_file
64
+ end
65
+
66
+ it 'raises an error otherwise' do
67
+ expect { opt.validate(r_file) }.to raise_error "'#{r_file}' is not writable"
68
+ end
69
+ end
70
+
71
+ context 'when it does not exist' do
72
+ let(:attrs) { { writable: true, exists: false } }
73
+
74
+ context 'when the parent directory is +w' do
75
+ let(:file) { File.join(FIXTURES, 'options_file', 'not_there.txt') }
76
+
77
+ it 'returns the path' do
78
+ expect(opt.validate(file)).to eq file
79
+ end
80
+ end
81
+
82
+ context 'when the parent directory is not +w' do
83
+ let(:file) { File.join(FIXTURES, 'hfjhg', 'yolo.rb') }
84
+
85
+ it 'raises an error' do
86
+ expect { opt.validate(file) }.to raise_error "'#{file}' is not writable"
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptInteger do
4
+
5
+ subject(:opt) { described_class.new(['-i', '--int INT']) }
6
+
7
+ describe '#validate' do
8
+ context 'when not an integer' do
9
+ it 'raises an error' do
10
+ expect { opt.validate('a') }.to raise_error('a is not an integer')
11
+ end
12
+ end
13
+
14
+ it 'returns the integer' do
15
+ expect(opt.validate('12')).to eq 12
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptPath do
4
+
5
+ # Everything is handled in OptFilePath & OptDirectoryPath
6
+
7
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptPositiveInteger do
4
+
5
+ subject(:opt) { described_class.new(['-i', '--int INT']) }
6
+
7
+ describe '#validate' do
8
+ context 'when not > 0' do
9
+ it 'raises an error' do
10
+ expect { opt.validate('-3') }.to raise_error('-3 is not > 0')
11
+ end
12
+ end
13
+
14
+ it 'returns the integer' do
15
+ expect(opt.validate('20')).to eq 20
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptProxy do
4
+
5
+ subject(:opt) { described_class.new(['--proxy PROXY'], attrs) }
6
+ let(:attrs) { { protocols: %w(http https socks socks5 socks4) } }
7
+
8
+ describe '#validate' do
9
+ # Handled by OptURI
10
+ end
11
+
12
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptURI do
4
+
5
+ subject(:opt) { described_class.new(['-u', '--uri URI'], attrs) }
6
+ let(:attrs) { {} }
7
+
8
+ describe '#new, #allowed_protocols' do
9
+ context 'when no attrs supplied' do
10
+ its(:allowed_protocols) { should be_empty }
11
+ end
12
+
13
+ context 'when only one protocol supplied' do
14
+ let(:attrs) { { protocols: 'http' } }
15
+
16
+ it 'sets it' do
17
+ opt.allowed_protocols << 'ftp'
18
+ expect(opt.allowed_protocols).to eq %w(http ftp)
19
+ end
20
+ end
21
+
22
+ context 'when multiple protocols are given' do
23
+ let(:attrs) { { protocols: %w(ftp https) } }
24
+
25
+ it 'sets them' do
26
+ expect(opt.allowed_protocols).to eq attrs[:protocols]
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '#validate' do
32
+ context 'when the allowed_protocols is empty' do
33
+ it 'accepts all protocols' do
34
+ %w(http ftp file).each do |p|
35
+ expected = "#{p}://testing"
36
+
37
+ expect(opt.validate(expected)).to eq expected
38
+ end
39
+ end
40
+ end
41
+
42
+ context 'when allowed_protocols is set' do
43
+ let(:attrs) { { protocols: %w(https) } }
44
+
45
+ it 'raises an error if the protocol is not allowed' do
46
+ expect { opt.validate('ftp://ishouldnotbethere') }
47
+ .to raise_error(Addressable::URI::InvalidURIError)
48
+ end
49
+
50
+ it 'returns the uri string if valid' do
51
+ expected = 'https://example.com/'
52
+
53
+ expect(opt.validate(expected)).to eq expected
54
+ end
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator::OptURL do
4
+
5
+ subject(:opt) { described_class.new(['-u', '--url URL']) }
6
+
7
+ describe '#validate' do
8
+ context 'when the url is empty' do
9
+ it 'raises an error' do
10
+ expect { opt.validate('') }.to raise_error(Addressable::URI::InvalidURIError)
11
+ end
12
+ end
13
+
14
+ context 'when the protocol is not allowed' do
15
+ it 'raises an error' do
16
+ expect { opt.validate('ftp://ftp.domain.com') }
17
+ .to raise_error(Addressable::URI::InvalidURIError)
18
+ end
19
+ end
20
+
21
+ it 'returns the url' do
22
+ url = 'https://duckduckgo.com/'
23
+
24
+ expect(opt.validate(url)).to eq url
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe OptParseValidator do
4
+ it 'returns the version' do
5
+ expect(OptParseValidator::VERSION).to be >= '0'
6
+ end
7
+ end