optimist_xl 3.1.1
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.
- 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
|