optimist_xl 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.travis.yml +13 -0
- data/FAQ.txt +92 -0
- data/Gemfile +4 -0
- data/History.txt +177 -0
- data/README.md +81 -0
- data/Rakefile +15 -0
- data/examples/a_basic_example.rb +10 -0
- data/examples/banners1.rb +11 -0
- data/examples/banners2.rb +12 -0
- data/examples/banners3.rb +14 -0
- data/examples/medium_example.rb +15 -0
- data/examples/permitted.rb +15 -0
- data/examples/subcommands.rb +15 -0
- data/examples/types.rb +12 -0
- data/examples/types_custom.rb +34 -0
- data/lib/optimist_xl.rb +1297 -0
- data/lib/optimist_xl/chronic.rb +36 -0
- data/optimist_xl.gemspec +35 -0
- data/test/optimist_xl/command_line_error_test.rb +27 -0
- data/test/optimist_xl/help_needed_test.rb +19 -0
- data/test/optimist_xl/parser_educate_test.rb +177 -0
- data/test/optimist_xl/parser_opt_test.rb +14 -0
- data/test/optimist_xl/parser_parse_test.rb +79 -0
- data/test/optimist_xl/parser_test.rb +1404 -0
- data/test/optimist_xl/permitted_test.rb +92 -0
- data/test/optimist_xl/subcommands_test.rb +153 -0
- data/test/optimist_xl/version_needed_test.rb +19 -0
- data/test/optimist_xl_test.rb +190 -0
- data/test/support/assert_helpers.rb +52 -0
- data/test/test_helper.rb +22 -0
- metadata +140 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
module OptimistXL
|
5
|
+
|
6
|
+
class PermittedTest < ::MiniTest::Test
|
7
|
+
def setup
|
8
|
+
@p = Parser.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def parser
|
12
|
+
@p ||= Parser.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_permitted_invalid_value
|
16
|
+
err_regexp = /permitted values for option "bad" must be either nil, Range, Regexp or an Array/
|
17
|
+
assert_raises(ArgumentError) {
|
18
|
+
@p.opt 'bad', 'desc', :permitted => 1
|
19
|
+
}
|
20
|
+
assert_raises(ArgumentError) {
|
21
|
+
@p.opt 'bad', 'desc', :permitted => "A"
|
22
|
+
}
|
23
|
+
assert_raises_errmatch(ArgumentError, err_regexp) {
|
24
|
+
@p.opt 'bad', 'desc', :permitted => :abcd
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_permitted_with_string_array
|
29
|
+
@p.opt 'fiz', 'desc', :type => 'string', :permitted => ['foo', 'bar']
|
30
|
+
@p.parse(%w(--fiz foo))
|
31
|
+
assert_raises_errmatch(CommandlineError, /option '--fiz' only accepts one of: foo, bar/) {
|
32
|
+
@p.parse(%w(--fiz buz))
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_permitted_with_symbol_array
|
37
|
+
@p.opt 'fiz', 'desc', :type => 'string', :permitted => %i[dog cat]
|
38
|
+
@p.parse(%w(--fiz dog))
|
39
|
+
@p.parse(%w(--fiz cat))
|
40
|
+
assert_raises_errmatch(CommandlineError, /option '--fiz' only accepts one of: dog, cat/) {
|
41
|
+
@p.parse(%w(--fiz rat))
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_permitted_with_numeric_array
|
46
|
+
@p.opt 'mynum', 'desc', :type => Integer, :permitted => [1,2,4]
|
47
|
+
@p.parse(%w(--mynum 1))
|
48
|
+
@p.parse(%w(--mynum 4))
|
49
|
+
assert_raises_errmatch(CommandlineError, /option '--mynum' only accepts one of: 1, 2, 4/) {
|
50
|
+
@p.parse(%w(--mynum 3))
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_permitted_with_numeric_range
|
55
|
+
@p.opt 'fiz', 'desc', :type => Integer, :permitted => 1..3
|
56
|
+
opts = @p.parse(%w(--fiz 1))
|
57
|
+
assert_equal opts['fiz'], 1
|
58
|
+
opts = @p.parse(%w(--fiz 3))
|
59
|
+
assert_equal opts['fiz'], 3
|
60
|
+
assert_raises_errmatch(CommandlineError, /option '--fiz' only accepts value in range of: 1\.\.3/) {
|
61
|
+
@p.parse(%w(--fiz 4))
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_permitted_with_regexp
|
66
|
+
@p.opt 'zipcode', 'desc', :type => String, :permitted => /^[0-9]{5}$/
|
67
|
+
@p.parse(%w(--zipcode 39762))
|
68
|
+
err_regexp = %r|option '--zipcode' only accepts value matching: ...0.9..5|
|
69
|
+
assert_raises_errmatch(CommandlineError, err_regexp) {
|
70
|
+
@p.parse(%w(--zipcode A9A9AA))
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_permitted_with_reason
|
75
|
+
# test all keys passed into the formatter for the permitted_response
|
76
|
+
@p.opt 'zipcode', 'desc', type: String, permitted: /^[0-9]{5}$/,
|
77
|
+
permitted_response: "opt %{arg} should be a zipcode but you have %{value}"
|
78
|
+
@p.opt :wig, 'wig', type: Integer, permitted: 1..4,
|
79
|
+
permitted_response: "opt %{arg} exceeded four wigs (%{valid_string}), %{permitted}, but you gave '%{given}'"
|
80
|
+
err_regexp = %r|opt --zipcode should be a zipcode but you have A9A9AA|
|
81
|
+
assert_raises_errmatch(CommandlineError, err_regexp) {
|
82
|
+
@p.parse(%w(--zipcode A9A9AA))
|
83
|
+
}
|
84
|
+
err_regexp = %r|opt --wig exceeded four wigs \(value in range of: 1\.\.4\), 1\.\.4, but you gave '5'|
|
85
|
+
assert_raises_errmatch(CommandlineError, err_regexp) {
|
86
|
+
@p.parse(%w(--wig 5))
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
module OptimistXL
|
5
|
+
|
6
|
+
module SubcommandTests
|
7
|
+
|
8
|
+
def if_did_you_mean_enabled
|
9
|
+
if (Module::const_defined?("DidYouMean") &&
|
10
|
+
Module::const_defined?("DidYouMean::JaroWinkler") &&
|
11
|
+
Module::const_defined?("DidYouMean::Levenshtein"))
|
12
|
+
yield
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# fails when no args provided
|
17
|
+
def test_subcommand_noargs
|
18
|
+
assert_raises(OptimistXL::CommandlineError, /No subcommand provided/) do
|
19
|
+
@p.parse([])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# ok when global help provided
|
24
|
+
def test_subcommand_global_help
|
25
|
+
assert_raises(OptimistXL::HelpNeeded) do
|
26
|
+
@p.parse(%w(-h))
|
27
|
+
end
|
28
|
+
sio = StringIO.new "w"
|
29
|
+
@p.educate sio
|
30
|
+
assert_match(/list\s+show the list/, sio.string)
|
31
|
+
assert_match(/create\s*\n/, sio.string)
|
32
|
+
end
|
33
|
+
|
34
|
+
# fails when invalid param given
|
35
|
+
def test_subcommand_invalid_opt
|
36
|
+
assert_raises_errmatch(OptimistXL::CommandlineError, /unknown argument '--boom'/) do
|
37
|
+
@p.parse(%w(--boom))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# fails when invalid subcommand given
|
42
|
+
def test_subcommand_invalid_subcmd
|
43
|
+
assert_raises_errmatch(OptimistXL::CommandlineError, /unknown subcommand 'boom'/) do
|
44
|
+
@p.parse(%w(boom))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# ok when valid subcommand given
|
49
|
+
def test_subcommand_ok_noopts
|
50
|
+
@p.parse(%w(list))
|
51
|
+
@p.parse(%w(create))
|
52
|
+
end
|
53
|
+
|
54
|
+
# ok when valid subcommand given with help param
|
55
|
+
def test_subcommand_help_subcmd1
|
56
|
+
err = assert_raises(OptimistXL::HelpNeeded) do
|
57
|
+
@p.parse(%w(list --help))
|
58
|
+
end
|
59
|
+
|
60
|
+
sio = StringIO.new "w"
|
61
|
+
err.parser.educate sio
|
62
|
+
assert_match(/all.*list all the things/, sio.string)
|
63
|
+
assert_match(/help.*Show this message/, sio.string)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_subcommand_help_subcmd2
|
67
|
+
err = assert_raises(OptimistXL::HelpNeeded) do
|
68
|
+
@p.parse(%w(create --help))
|
69
|
+
end
|
70
|
+
sio = StringIO.new "w"
|
71
|
+
err.parser.educate sio
|
72
|
+
assert_match(/partial.*create a partial thing/, sio.string)
|
73
|
+
assert_match(/name.*creation name/, sio.string)
|
74
|
+
assert_match(/help.*Show this message/, sio.string)
|
75
|
+
end
|
76
|
+
|
77
|
+
# fails when valid subcommand given with invalid param
|
78
|
+
def test_subcommand_invalid_subopt
|
79
|
+
assert_raises_errmatch(OptimistXL::CommandlineError, /unknown argument '--foo' for command 'list'/) do
|
80
|
+
@p.parse(%w(list --foo))
|
81
|
+
end
|
82
|
+
assert_raises_errmatch(OptimistXL::CommandlineError, /unknown argument '--bar' for command 'create'/) do
|
83
|
+
@p.parse(%w(create --bar))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# ok when valid subcommand given with valid params
|
88
|
+
def test_subcommand_ok
|
89
|
+
@p.parse(%w(list --all))
|
90
|
+
@p.parse(%w(create --partial --name duck))
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
class SubcommandsWithGlobalOptTest < ::MiniTest::Test
|
97
|
+
include SubcommandTests
|
98
|
+
def setup
|
99
|
+
@p = Parser.new
|
100
|
+
@p.opt :some_global_stropt, 'Some global string option', type: :string, short: :none
|
101
|
+
@p.opt :some_global_flag, 'Some global flag'
|
102
|
+
@p.subcmd :list, "show the list" do
|
103
|
+
opt :all, 'list all the things', type: :boolean
|
104
|
+
end
|
105
|
+
@p.subcmd "create" do
|
106
|
+
opt :partial, 'create a partial thing', type: :boolean
|
107
|
+
opt :name, 'creation name', type: :string
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_subcommand_ok_gopts
|
112
|
+
@p.parse(%w(--some-global-flag list --all))
|
113
|
+
@p.parse(%w(--some-global-stropt GHI create --partial --name duck))
|
114
|
+
# handles minimal-length partial-long arguments
|
115
|
+
@p.parse(%w(--some-global-s GHI create --par --na duck))
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_subcommand_invalid_gopts
|
119
|
+
assert_raises_errmatch(OptimistXL::CommandlineError, /unknown argument '--all'/) do
|
120
|
+
@p.parse(%w(--all list --all))
|
121
|
+
end
|
122
|
+
# handles misspellings property on subcommands
|
123
|
+
if_did_you_mean_enabled do
|
124
|
+
err_regex = /unknown argument '--partul' for command 'create'. Did you mean: \[--partial\]/
|
125
|
+
assert_raises_errmatch(OptimistXL::CommandlineError, err_regex) do
|
126
|
+
@p.parse(%w(--some-global-stropt GHI create --partul --name duck))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
class SubcommandsWithoutGlobalOptTest < ::MiniTest::Test
|
134
|
+
include SubcommandTests
|
135
|
+
def setup
|
136
|
+
@p = Parser.new
|
137
|
+
@p.subcmd :list, "show the list" do
|
138
|
+
opt :all, 'list all the things', type: :boolean
|
139
|
+
end
|
140
|
+
@p.subcmd "create" do
|
141
|
+
opt :partial, 'create a partial thing', type: :boolean
|
142
|
+
opt :name, 'creation name', type: :string
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_subcommand_invalid_gopts
|
147
|
+
assert_raises_errmatch(OptimistXL::CommandlineError, /unknown argument '--some-global-flag'/) do
|
148
|
+
@p.parse(%w(--some-global-flag list --all))
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module OptimistXL
|
4
|
+
class VersionNeededTest < ::MiniTest::Test
|
5
|
+
def test_class
|
6
|
+
assert_kind_of Exception, vn("message")
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_message
|
10
|
+
assert "message", vn("message").message
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def vn(*args)
|
16
|
+
VersionNeeded.new(*args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class OptimistXLTest < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
OptimistXL.send(:instance_variable_set, "@last_parser", nil)
|
6
|
+
end
|
7
|
+
|
8
|
+
def parser(&block)
|
9
|
+
OptimistXL::Parser.new(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_options
|
13
|
+
opts = OptimistXL.options %w(-f) do
|
14
|
+
opt :f
|
15
|
+
end
|
16
|
+
|
17
|
+
assert_equal true, opts[:f]
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_options_die_default
|
21
|
+
assert_stderr(/Error: unknown argument.*Try --help/m) do
|
22
|
+
assert_system_exit(-1) do
|
23
|
+
OptimistXL.options %w(-f) do
|
24
|
+
opt :x
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_options_die_educate_on_error
|
31
|
+
assert_stderr(/Error: unknown argument.*Options/m) do
|
32
|
+
assert_system_exit(-1) do
|
33
|
+
OptimistXL.options %w(-f) do
|
34
|
+
opt :x
|
35
|
+
educate_on_error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_die_without_options_ever_run
|
42
|
+
assert_raises(ArgumentError) { OptimistXL.die 'hello' }
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_die
|
46
|
+
assert_stderr(/Error: issue with parsing/) do
|
47
|
+
assert_system_exit(-1) do
|
48
|
+
OptimistXL.options []
|
49
|
+
OptimistXL.die "issue with parsing"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_die_custom_error_code
|
55
|
+
assert_stderr(/Error: issue with parsing/) do
|
56
|
+
assert_system_exit(5) do
|
57
|
+
OptimistXL.options []
|
58
|
+
OptimistXL.die "issue with parsing", nil, 5
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_die_custom_error_code_two_args
|
64
|
+
assert_stderr(/Error: issue with parsing/) do
|
65
|
+
assert_system_exit(5) do
|
66
|
+
OptimistXL.options []
|
67
|
+
OptimistXL.die "issue with parsing", 5
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_educate_without_options_ever_run
|
73
|
+
assert_raises(ArgumentError) { OptimistXL.educate }
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_educate
|
77
|
+
assert_stdout(/Show this message/) do
|
78
|
+
assert_system_exit(0) do
|
79
|
+
OptimistXL.options []
|
80
|
+
OptimistXL.educate
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_with_standard_exception_options
|
86
|
+
p = parser do
|
87
|
+
opt :f
|
88
|
+
end
|
89
|
+
|
90
|
+
opts = OptimistXL::with_standard_exception_handling p do
|
91
|
+
p.parse %w(-f)
|
92
|
+
end
|
93
|
+
|
94
|
+
assert_equal true, opts[:f]
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_with_standard_exception_version_exception
|
98
|
+
p = parser do
|
99
|
+
version "5.5"
|
100
|
+
end
|
101
|
+
|
102
|
+
assert_stdout(/5\.5/) do
|
103
|
+
assert_system_exit(0) do
|
104
|
+
OptimistXL::with_standard_exception_handling p do
|
105
|
+
raise OptimistXL::VersionNeeded
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_with_standard_exception_version_flag
|
112
|
+
p = parser do
|
113
|
+
version "5.5"
|
114
|
+
end
|
115
|
+
|
116
|
+
assert_stdout(/5\.5/) do
|
117
|
+
assert_system_exit(0) do
|
118
|
+
OptimistXL::with_standard_exception_handling p do
|
119
|
+
p.parse %w(-v)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_with_standard_exception_die_exception
|
126
|
+
assert_stderr(/Error: cl error/) do
|
127
|
+
assert_system_exit(-1) do
|
128
|
+
p = parser
|
129
|
+
OptimistXL.with_standard_exception_handling(p) do
|
130
|
+
raise ::OptimistXL::CommandlineError.new('cl error')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_with_standard_exception_die_exception_custom_error
|
137
|
+
assert_stderr(/Error: cl error/) do
|
138
|
+
assert_system_exit(5) do
|
139
|
+
p = parser
|
140
|
+
OptimistXL.with_standard_exception_handling(p) do
|
141
|
+
raise ::OptimistXL::CommandlineError.new('cl error', 5)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_with_standard_exception_die
|
148
|
+
assert_stderr(/Error: cl error/) do
|
149
|
+
assert_system_exit(-1) do
|
150
|
+
p = parser
|
151
|
+
OptimistXL.with_standard_exception_handling(p) do
|
152
|
+
p.die 'cl error'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_with_standard_exception_die_custom_error
|
159
|
+
assert_stderr(/Error: cl error/) do
|
160
|
+
assert_system_exit(3) do
|
161
|
+
p = parser
|
162
|
+
OptimistXL.with_standard_exception_handling(p) do
|
163
|
+
p.die 'cl error', nil, 3
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_with_standard_exception_help_needed
|
170
|
+
assert_stdout(/Options/) do
|
171
|
+
assert_system_exit(0) do
|
172
|
+
p = parser
|
173
|
+
OptimistXL.with_standard_exception_handling(p) do
|
174
|
+
raise OptimistXL::HelpNeeded.new(parser: p)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_with_standard_exception_help_needed_flag
|
181
|
+
assert_stdout(/Options/) do
|
182
|
+
assert_system_exit(0) do
|
183
|
+
p = parser
|
184
|
+
OptimistXL.with_standard_exception_handling(p) do
|
185
|
+
p.parse(%w(-h))
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|