gli 2.0.0.rc3 → 2.0.0.rc4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -2
- data/lib/gli/app.rb +26 -0
- data/lib/gli/app_support.rb +16 -4
- data/lib/gli/command.rb +2 -0
- data/lib/gli/command_support.rb +5 -0
- data/lib/gli/commands/help.rb +2 -1
- data/lib/gli/dsl.rb +1 -0
- data/lib/gli/gli_option_parser.rb +9 -5
- data/lib/gli/version.rb +1 -1
- data/test/tc_command.rb +86 -9
- data/test/tc_gli.rb +33 -1
- data/test/tc_subcommands.rb +13 -1
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Git-Like Interface Command Line Parser
|
2
2
|
|
3
|
-
<b>
|
3
|
+
<b>GLI2 is currently in Release Candidate, so this might change before the official release</b>
|
4
4
|
|
5
5
|
The best way to make a "command-suite" command-line application (for the best way to make a
|
6
6
|
simpler command-line application, check out methadone[http://www.github.com/davetron5000/methadone]).
|
@@ -10,7 +10,7 @@ of syntax, but without restricting you in any way from the power of +OptionParse
|
|
10
10
|
|
11
11
|
* {Overview}[http://davetron5000.github.com/gli]
|
12
12
|
* {Source on Github}[http://github.com/davetron5000/gli]
|
13
|
-
* RDoc[http://davetron5000.github.com/gli]
|
13
|
+
* RDoc[http://davetron5000.github.com/gli/rdoc/index.html]
|
14
14
|
|
15
15
|
== Use
|
16
16
|
|
data/lib/gli/app.rb
CHANGED
@@ -58,6 +58,13 @@ module GLI
|
|
58
58
|
@skips_post = true
|
59
59
|
end
|
60
60
|
|
61
|
+
# Use this if the following command should not have the around block executed.
|
62
|
+
# By default, the around block is executed, but for commands that might not want the
|
63
|
+
# setup to happen, this can be handy
|
64
|
+
def skips_around
|
65
|
+
@skips_around = true
|
66
|
+
end
|
67
|
+
|
61
68
|
# Sets that this app uses a config file as well as the name of the config file.
|
62
69
|
#
|
63
70
|
# +filename+:: A String representing the path to the file to use for the config file. If it's an absolute
|
@@ -90,6 +97,25 @@ module GLI
|
|
90
97
|
@post_block = a_proc
|
91
98
|
end
|
92
99
|
|
100
|
+
# This inverts the pre/post concept. This is useful when you have a global shared resource that is governed by a block
|
101
|
+
# instead of separate open/close methods. The block you pass here will be given four parameters:
|
102
|
+
#
|
103
|
+
# global options:: the parsed global options
|
104
|
+
# command:: The GLI::Command that the user is going to invoke
|
105
|
+
# options:: the command specific options
|
106
|
+
# args:: unparsed command-line args
|
107
|
+
# code:: a block that you must +call+ to execute the command.
|
108
|
+
#
|
109
|
+
# #help_now! and #exit_now! work as expected; you can abort the command call by simply not calling it
|
110
|
+
#
|
111
|
+
# Note that if you declare an #around block, #pre and #post blocks will still work. The #pre is called first, followed by
|
112
|
+
# the around, followed by the #post.
|
113
|
+
#
|
114
|
+
# Call #skips_around before a command that should not have this hook fired
|
115
|
+
def around(&a_proc)
|
116
|
+
@around_block = a_proc
|
117
|
+
end
|
118
|
+
|
93
119
|
# Define a block to run if an error occurs.
|
94
120
|
# The block will receive any Exception that was caught.
|
95
121
|
# It should evaluate to false to avoid the built-in error handling (which basically just
|
data/lib/gli/app_support.rb
CHANGED
@@ -14,7 +14,7 @@ module GLI
|
|
14
14
|
def reset # :nodoc:
|
15
15
|
switches.clear
|
16
16
|
flags.clear
|
17
|
-
commands
|
17
|
+
@commands = nil
|
18
18
|
@version = nil
|
19
19
|
@config_file = nil
|
20
20
|
@use_openstruct = false
|
@@ -23,6 +23,7 @@ module GLI
|
|
23
23
|
@pre_block = false
|
24
24
|
@post_block = false
|
25
25
|
@default_command = :help
|
26
|
+
@around_block = nil
|
26
27
|
clear_nexts
|
27
28
|
end
|
28
29
|
|
@@ -43,7 +44,7 @@ module GLI
|
|
43
44
|
|
44
45
|
add_help_switch_if_needed(switches)
|
45
46
|
|
46
|
-
global_options,command,options,arguments = GLIOptionParser.new(commands,flags,switches,accepts).parse_options(args)
|
47
|
+
global_options,command,options,arguments = GLIOptionParser.new(commands,flags,switches,accepts,@default_command).parse_options(args)
|
47
48
|
|
48
49
|
copy_options_to_aliased_versions(global_options,command,options)
|
49
50
|
|
@@ -51,7 +52,6 @@ module GLI
|
|
51
52
|
options = convert_to_openstruct_if_needed(options)
|
52
53
|
|
53
54
|
if proceed?(global_options,command,options,arguments)
|
54
|
-
command ||= commands[:help]
|
55
55
|
call_command(command,global_options,options,arguments)
|
56
56
|
end
|
57
57
|
0
|
@@ -93,6 +93,7 @@ module GLI
|
|
93
93
|
super
|
94
94
|
@skips_post = false
|
95
95
|
@skips_pre = false
|
96
|
+
@skips_around = false
|
96
97
|
end
|
97
98
|
|
98
99
|
def stderr
|
@@ -126,6 +127,12 @@ module GLI
|
|
126
127
|
end
|
127
128
|
end
|
128
129
|
|
130
|
+
def around_block
|
131
|
+
@around_block ||= Proc.new do |global,command,options,args,code|
|
132
|
+
code.call
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
129
136
|
# Sets the default values for flags based on the configuration
|
130
137
|
def override_defaults_based_on_config(config)
|
131
138
|
override_default(flags,config)
|
@@ -216,7 +223,12 @@ module GLI
|
|
216
223
|
|
217
224
|
def call_command(command,global_options,options,arguments)
|
218
225
|
arguments = arguments.map { |arg| arg.dup } # unfreeze
|
219
|
-
command.execute(global_options,options,arguments)
|
226
|
+
code = lambda { command.execute(global_options,options,arguments) }
|
227
|
+
if command.skips_around
|
228
|
+
code.call
|
229
|
+
else
|
230
|
+
around_block.call(global_options,command,options,arguments,code)
|
231
|
+
end
|
220
232
|
unless command.skips_post
|
221
233
|
post_block.call(global_options,command,options,arguments)
|
222
234
|
end
|
data/lib/gli/command.rb
CHANGED
@@ -42,11 +42,13 @@ module GLI
|
|
42
42
|
# as a paragraph break. No other formatting is respected, though inner whitespace is maintained.
|
43
43
|
# +skips_pre+:: if true, this command advertises that it doesn't want the pre block called first
|
44
44
|
# +skips_post+:: if true, this command advertises that it doesn't want the post block called after it
|
45
|
+
# +skips_around+:: if true, this command advertises that it doesn't want the around block called
|
45
46
|
def initialize(options)
|
46
47
|
super(options[:names],options[:description],options[:long_desc])
|
47
48
|
@arguments_description = options[:arguments_name] || ''
|
48
49
|
@skips_pre = options[:skips_pre]
|
49
50
|
@skips_post = options[:skips_post]
|
51
|
+
@skips_around = options[:skips_around]
|
50
52
|
clear_nexts
|
51
53
|
end
|
52
54
|
|
data/lib/gli/command_support.rb
CHANGED
data/lib/gli/commands/help.rb
CHANGED
@@ -17,7 +17,8 @@ module GLI
|
|
17
17
|
:arguments_name => 'command',
|
18
18
|
:long_desc => 'Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function',
|
19
19
|
:skips_pre => true,
|
20
|
-
:skips_post => true
|
20
|
+
:skips_post => true,
|
21
|
+
:skips_around => true)
|
21
22
|
@app = app
|
22
23
|
action do |global_options,options,arguments|
|
23
24
|
show_help(global_options,options,arguments,output,error)
|
data/lib/gli/dsl.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module GLI
|
2
2
|
# Parses the command-line options using an actual +OptionParser+
|
3
3
|
class GLIOptionParser
|
4
|
-
def initialize(commands,flags,switches,accepts)
|
4
|
+
def initialize(commands,flags,switches,accepts,default_command = nil)
|
5
5
|
@commands = commands
|
6
6
|
@flags = flags
|
7
7
|
@switches = switches
|
8
8
|
@accepts = accepts
|
9
|
+
@default_command = default_command
|
9
10
|
end
|
10
11
|
|
11
12
|
# Given the command-line argument array, returns and array of size 4:
|
@@ -87,10 +88,13 @@ module GLI
|
|
87
88
|
names_to_commands[command_alias.to_s] = command
|
88
89
|
end
|
89
90
|
end
|
90
|
-
name
|
91
|
-
|
92
|
-
|
93
|
-
|
91
|
+
names_to_commands.fetch(name.to_s) do |command_to_match|
|
92
|
+
find_command_by_partial_name(names_to_commands, command_to_match)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def find_command_by_partial_name(names_to_commands, command_to_match)
|
97
|
+
partial_matches = names_to_commands.keys.select { |command_name| command_name =~ /^#{command_to_match}/ }
|
94
98
|
return names_to_commands[partial_matches[0]] if partial_matches.size == 1
|
95
99
|
partial_matches
|
96
100
|
end
|
data/lib/gli/version.rb
CHANGED
data/test/tc_command.rb
CHANGED
@@ -4,7 +4,15 @@ require 'tempfile'
|
|
4
4
|
class TC_testCommand < Clean::Test::TestCase
|
5
5
|
include TestHelper
|
6
6
|
def setup
|
7
|
+
@fake_stdout = FakeStdOut.new
|
8
|
+
@fake_stderr = FakeStdOut.new
|
9
|
+
@original_stdout = $stdout
|
10
|
+
$stdout = @fake_stdout
|
11
|
+
@original_stderr = $stderr
|
12
|
+
$stderr = @fake_stderr
|
7
13
|
@app = CLIApp.new
|
14
|
+
@app.error_device=@fake_stderr
|
15
|
+
ENV.delete('GLI_DEBUG')
|
8
16
|
@app.reset
|
9
17
|
@app.program_desc 'A super awesome program'
|
10
18
|
@app.desc 'Some Global Option'
|
@@ -50,17 +58,9 @@ class TC_testCommand < Clean::Test::TestCase
|
|
50
58
|
@app.command [:test_wrap] do |c|
|
51
59
|
c.action {}
|
52
60
|
end
|
53
|
-
@fake_stdout = FakeStdOut.new
|
54
|
-
@fake_stderr = FakeStdOut.new
|
55
|
-
@app.error_device=@fake_stderr
|
56
|
-
ENV.delete('GLI_DEBUG')
|
57
|
-
@original_stdout = $stdout
|
58
|
-
$stdout = @fake_stdout
|
59
|
-
@original_stderr = $stderr
|
60
|
-
$stderr = @fake_stderr
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
63
|
+
def teardown
|
64
64
|
$stdout = @original_stdout
|
65
65
|
$stderr = @original_stderr
|
66
66
|
FileUtils.rm_f "cruddo.rdoc"
|
@@ -100,6 +100,83 @@ class TC_testCommand < Clean::Test::TestCase
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
+
def test_around_filter
|
104
|
+
@around_block_called = false
|
105
|
+
@app.around do |global_options, command, options, arguments, code|
|
106
|
+
@around_block_called = true
|
107
|
+
code.call
|
108
|
+
end
|
109
|
+
@app.run(['bs'])
|
110
|
+
assert(@around_block_called, "Wrapper block should have been called")
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_around_filter_can_be_skipped
|
114
|
+
# Given
|
115
|
+
@around_block_called = false
|
116
|
+
@action_called = false
|
117
|
+
@app.skips_around
|
118
|
+
@app.command :skips_around_filter do |c|
|
119
|
+
c.action do |g,o,a|
|
120
|
+
@action_called = true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
@app.command :uses_around_filter do |c|
|
125
|
+
c.action do |g,o,a|
|
126
|
+
@action_called = true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
@app.around do |global_options, command, options, arguments, code|
|
131
|
+
@around_block_called = true
|
132
|
+
code.call
|
133
|
+
end
|
134
|
+
|
135
|
+
# When
|
136
|
+
exit_status = @app.run(['skips_around_filter'])
|
137
|
+
|
138
|
+
# Then
|
139
|
+
assert_equal 0,exit_status
|
140
|
+
assert(!@around_block_called, "Wrapper block should have been skipped")
|
141
|
+
assert(@action_called,"Action should have been called")
|
142
|
+
|
143
|
+
# When
|
144
|
+
@around_block_called = false
|
145
|
+
@action_called = false
|
146
|
+
exit_status = @app.run(['uses_around_filter'])
|
147
|
+
|
148
|
+
# Then
|
149
|
+
assert_equal 0,exit_status
|
150
|
+
assert(@around_block_called, "Wrapper block should have been called")
|
151
|
+
assert(@action_called,"Action should have been called")
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_around_filter_handles_exit_now
|
155
|
+
@around_block_called = false
|
156
|
+
@error_message = "OH NOES"
|
157
|
+
@app.around do |global_options, command, options, arguments, code|
|
158
|
+
@app.exit_now! @error_message
|
159
|
+
code.call
|
160
|
+
end
|
161
|
+
exit_code = @app.run(['bs'])
|
162
|
+
assert exit_code != 0
|
163
|
+
assert_contained(@fake_stderr,/#{@error_message}/)
|
164
|
+
assert_not_contained(@fake_stdout,/SYNOPSIS/)
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_around_filter_handles_help_now
|
168
|
+
@around_block_called = false
|
169
|
+
@error_message = "OH NOES"
|
170
|
+
@app.around do |global_options, command, options, arguments, code|
|
171
|
+
@app.help_now! @error_message
|
172
|
+
code.call
|
173
|
+
end
|
174
|
+
exit_code = @app.run(['bs'])
|
175
|
+
assert exit_code != 0
|
176
|
+
assert_contained(@fake_stderr,/#{@error_message}/)
|
177
|
+
assert_contained(@fake_stdout,/SYNOPSIS/)
|
178
|
+
end
|
179
|
+
|
103
180
|
def test_command_skips_pre
|
104
181
|
@app.skips_pre
|
105
182
|
@app.skips_post
|
data/test/tc_gli.rb
CHANGED
@@ -17,10 +17,15 @@ class TC_testGLI < Clean::Test::TestCase
|
|
17
17
|
include GLI
|
18
18
|
|
19
19
|
def setup
|
20
|
+
@fake_stdout = FakeStdOut.new
|
21
|
+
@fake_stderr = FakeStdOut.new
|
22
|
+
@original_stdout = $stdout
|
23
|
+
$stdout = @fake_stdout
|
24
|
+
@original_stderr = $stderr
|
25
|
+
$stderr = @fake_stderr
|
20
26
|
@app = CLIApp.new
|
21
27
|
@config_file = File.expand_path(File.dirname(File.realpath(__FILE__)) + '/new_config.yaml')
|
22
28
|
@gli_debug = ENV['GLI_DEBUG']
|
23
|
-
@fake_stderr = FakeStdOut.new
|
24
29
|
@app.error_device=@fake_stderr
|
25
30
|
ENV.delete('GLI_DEBUG')
|
26
31
|
end
|
@@ -29,6 +34,8 @@ class TC_testGLI < Clean::Test::TestCase
|
|
29
34
|
File.delete(@config_file) if File.exist?(@config_file)
|
30
35
|
ENV['GLI_DEBUG'] = @gli_debug
|
31
36
|
@app.error_device=$stderr
|
37
|
+
$stdout = @original_stdout
|
38
|
+
$stderr = @original_stderr
|
32
39
|
end
|
33
40
|
|
34
41
|
def test_flag_create
|
@@ -51,6 +58,19 @@ class TC_testGLI < Clean::Test::TestCase
|
|
51
58
|
assert @app.switches[:s].aliases.include? :'some-switch'
|
52
59
|
end
|
53
60
|
|
61
|
+
def test_default_command
|
62
|
+
@app.reset
|
63
|
+
@called = false
|
64
|
+
@app.command :foo do |c|
|
65
|
+
c.action do |global, options, arguments|
|
66
|
+
@called = true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
@app.default_command(:foo)
|
70
|
+
assert_equal 0, @app.run([]), "Expected exit status to be 0"
|
71
|
+
assert @called, "Expected default command to be executed"
|
72
|
+
end
|
73
|
+
|
54
74
|
def test_flag_with_space_barfs
|
55
75
|
@app.reset
|
56
76
|
assert_raises(ArgumentError) { @app.flag ['some flag'] }
|
@@ -214,6 +234,18 @@ class TC_testGLI < Clean::Test::TestCase
|
|
214
234
|
do_test_switch_create_twice(@app)
|
215
235
|
do_test_switch_create_twice(Command.new(:names => :f))
|
216
236
|
end
|
237
|
+
|
238
|
+
def test_non_negatable_negative_switch
|
239
|
+
@app.reset
|
240
|
+
@app.on_error { |ex| raise ex }
|
241
|
+
@app.switch 'no-color', :negatable => false
|
242
|
+
@app.command :smth do |c|
|
243
|
+
c.action do |global, *args|
|
244
|
+
assert global[:"no-color"], "Expected :'no-color' switch to be true"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
@app.run(%w(--no-color smth))
|
248
|
+
end
|
217
249
|
|
218
250
|
def test_all_aliases_in_options
|
219
251
|
@app.reset
|
data/test/tc_subcommands.rb
CHANGED
@@ -4,13 +4,25 @@ class TC_testSubCommand < Clean::Test::TestCase
|
|
4
4
|
include TestHelper
|
5
5
|
|
6
6
|
def setup
|
7
|
+
@fake_stdout = FakeStdOut.new
|
8
|
+
@fake_stderr = FakeStdOut.new
|
9
|
+
|
10
|
+
@original_stdout = $stdout
|
11
|
+
$stdout = @fake_stdout
|
12
|
+
@original_stderr = $stderr
|
13
|
+
$stderr = @fake_stderr
|
14
|
+
|
7
15
|
@app = CLIApp.new
|
8
16
|
@app.reset
|
9
|
-
@fake_stderr = FakeStdOut.new
|
10
17
|
@app.error_device=@fake_stderr
|
11
18
|
ENV.delete('GLI_DEBUG')
|
12
19
|
end
|
13
20
|
|
21
|
+
def teardown
|
22
|
+
$stdout = @original_stdout
|
23
|
+
$stderr = @original_stderr
|
24
|
+
end
|
25
|
+
|
14
26
|
['add','new'].each do |name|
|
15
27
|
test_that "We run the 'add' subcommand using '#{name}'" do
|
16
28
|
Given we_have_a_command_with_two_subcommands
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.rc4
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-06-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|