clio 0.0.1 → 0.2.0
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.
- data/CHANGES +66 -0
- data/MANIFEST +48 -169
- data/README +13 -17
- data/RELEASE +8 -0
- data/VERSION +1 -0
- data/lib/clio/buffer.rb +93 -0
- data/lib/clio/commandable.rb +71 -69
- data/lib/clio/commandline.rb +429 -230
- data/lib/clio/consoleutils.rb +0 -16
- data/lib/clio/facets/kernel.rb +13 -0
- data/lib/clio/facets/string.rb +25 -0
- data/lib/clio/layout.rb +14 -0
- data/lib/clio/layout/flow.rb +1 -0
- data/lib/clio/layout/line.rb +23 -0
- data/lib/clio/layout/list.rb +33 -0
- data/lib/clio/layout/split.rb +122 -0
- data/lib/clio/layout/stack.rb +17 -0
- data/lib/clio/layout/table.rb +46 -0
- data/lib/clio/progressbar.rb +189 -210
- data/lib/clio/string.rb +116 -109
- data/lib/clio/usage.rb +184 -0
- data/lib/clio/usage/argument.rb +84 -0
- data/lib/clio/usage/command.rb +440 -0
- data/lib/clio/usage/interface.rb +122 -0
- data/lib/clio/usage/main.rb +165 -0
- data/lib/clio/usage/option.rb +251 -0
- data/lib/clio/usage/parser.rb +191 -0
- data/lib/clio/usage/signature.rb +55 -0
- data/meta/abstract +3 -0
- data/meta/authors +1 -0
- data/meta/created +1 -0
- data/meta/homepage +1 -0
- data/meta/license +1 -0
- data/meta/repository +1 -0
- data/meta/summary +1 -0
- data/spec/commandline/autousage.rd +56 -0
- data/spec/commandline/bracket.rd +64 -0
- data/spec/commandline/completion.rd +38 -0
- data/spec/commandline/define.rd +44 -0
- data/spec/commandline/method.rd +60 -0
- data/spec/commandline/parse.rd +72 -0
- data/spec/commandline/scenario.rd +81 -0
- data/spec/commandline/subclass.rd +54 -0
- data/spec/string/unit.rd +58 -0
- data/spec/usage/define.rd +44 -0
- data/spec/usage/parse.rd +47 -0
- metadata +65 -196
- data/HISTORY +0 -11
- data/METADATA +0 -18
- data/NEWS +0 -10
- data/admin/config/reap.yaml +0 -30
- data/admin/depot/commandline.rb +0 -219
- data/admin/depot/multicommand.rb +0 -403
- data/admin/depot/test_multicommand.rb +0 -40
- data/admin/log/notes.xml +0 -28
- data/admin/log/stats.html +0 -25
- data/admin/log/syntax.log +0 -0
- data/admin/log/testunit.log +0 -16
- data/admin/pack/clio-0.0.1.gem +0 -0
- data/admin/share/reap/example.rb +0 -7
- data/admin/temps/lib/clio/about.rb.erb +0 -4
- data/lib/clio/command.rb +0 -296
- data/lib/clio/option.rb +0 -36
- data/lib/clio/runmode.rb +0 -126
- data/test/test_command.rb +0 -42
- data/test/test_commandline.rb +0 -83
- data/vendor/Console/Console.cpp +0 -1203
- data/vendor/Console/Console.rdoc +0 -690
- data/vendor/Console/Console_ANSI.rdoc +0 -302
- data/vendor/Console/HISTORY.txt +0 -7
- data/vendor/Console/INSTALL.txt +0 -18
- data/vendor/Console/Makefile +0 -162
- data/vendor/Console/README.txt +0 -26
- data/vendor/Console/doc/classes/Win32.html +0 -115
- data/vendor/Console/doc/classes/Win32/Console.html +0 -650
- data/vendor/Console/doc/classes/Win32/Console.src/M000001.html +0 -31
- data/vendor/Console/doc/classes/Win32/Console.src/M000002.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000003.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000004.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console.src/M000005.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000006.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console.src/M000007.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000008.html +0 -24
- data/vendor/Console/doc/classes/Win32/Console.src/M000009.html +0 -44
- data/vendor/Console/doc/classes/Win32/Console.src/M000010.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000011.html +0 -33
- data/vendor/Console/doc/classes/Win32/Console.src/M000012.html +0 -26
- data/vendor/Console/doc/classes/Win32/Console.src/M000013.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console.src/M000014.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console.src/M000015.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000016.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000017.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000018.html +0 -29
- data/vendor/Console/doc/classes/Win32/Console.src/M000019.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000020.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000021.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console.src/M000022.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000023.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console.src/M000024.html +0 -35
- data/vendor/Console/doc/classes/Win32/Console.src/M000025.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console.src/M000026.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console.src/M000027.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console.src/M000028.html +0 -31
- data/vendor/Console/doc/classes/Win32/Console.src/M000029.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000030.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000031.html +0 -23
- data/vendor/Console/doc/classes/Win32/Console.src/M000032.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console.src/M000033.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console.src/M000034.html +0 -25
- data/vendor/Console/doc/classes/Win32/Console/ANSI.html +0 -103
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.html +0 -220
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000035.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000036.html +0 -205
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000037.html +0 -40
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000038.html +0 -25
- data/vendor/Console/doc/classes/Win32/Console/API.html +0 -758
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000039.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000040.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000041.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000042.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000043.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000044.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000045.html +0 -26
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000046.html +0 -26
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000047.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000048.html +0 -30
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000049.html +0 -29
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000050.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000051.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000052.html +0 -30
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000053.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000054.html +0 -29
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000055.html +0 -29
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000056.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000057.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000058.html +0 -47
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000059.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000060.html +0 -47
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000061.html +0 -34
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000062.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000063.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000064.html +0 -35
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000065.html +0 -26
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000066.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000067.html +0 -29
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000068.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000069.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000070.html +0 -28
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000071.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000072.html +0 -26
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000073.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000074.html +0 -31
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000075.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000076.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000077.html +0 -27
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000078.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000079.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000080.html +0 -32
- data/vendor/Console/doc/classes/Win32/Console/Constants.html +0 -360
- data/vendor/Console/doc/created.rid +0 -1
- data/vendor/Console/doc/files/Console_ANSI_rdoc.html +0 -407
- data/vendor/Console/doc/files/Console_cpp.html +0 -104
- data/vendor/Console/doc/files/Console_rdoc.html +0 -964
- data/vendor/Console/doc/files/lib/Win32/Console/ANSI_rb.html +0 -123
- data/vendor/Console/doc/files/lib/Win32/Console_rb.html +0 -297
- data/vendor/Console/doc/fr_class_index.html +0 -32
- data/vendor/Console/doc/fr_file_index.html +0 -31
- data/vendor/Console/doc/fr_method_index.html +0 -106
- data/vendor/Console/doc/index.html +0 -24
- data/vendor/Console/doc/rdoc-style.css +0 -172
- data/vendor/Console/extconf.rb +0 -18
- data/vendor/Console/lib/Term/ansicolor.rb +0 -76
- data/vendor/Console/lib/Win32/Console.rb +0 -970
- data/vendor/Console/lib/Win32/Console/ANSI.rb +0 -305
- data/vendor/Console/test/test_cursor.rb +0 -9
- data/vendor/Console/test/test_mouse.rb +0 -6
- data/vendor/Console/test/test_readinput.rb +0 -62
- data/vendor/Console/test/test_readoutput.rb +0 -52
- data/vendor/Console/test/test_sendevent.rb +0 -17
- data/vendor/Console/test/test_title.rb +0 -14
- data/vendor/Console/test/test_write.rb +0 -36
data/lib/clio/commandable.rb
CHANGED
|
@@ -2,7 +2,7 @@ require 'clio/errors'
|
|
|
2
2
|
|
|
3
3
|
module Clio
|
|
4
4
|
|
|
5
|
-
# = Commandable
|
|
5
|
+
# = Commandable Mixin
|
|
6
6
|
#
|
|
7
7
|
# The Commandable mixin is a very quick and and easy
|
|
8
8
|
# way to make almost any class usable via a command
|
|
@@ -40,6 +40,8 @@ module Clio
|
|
|
40
40
|
# Commandable also defines #command_missing and #option_missing,
|
|
41
41
|
# which you can override to provide suitable results.
|
|
42
42
|
#
|
|
43
|
+
# TODO: Maybe command_missing is redundant, and method_missing would suffice?
|
|
44
|
+
#
|
|
43
45
|
module Commandable
|
|
44
46
|
|
|
45
47
|
# Used to invoke the command.
|
|
@@ -50,7 +52,7 @@ module Clio
|
|
|
50
52
|
# This is the fallback subcommand. Override this to provide
|
|
51
53
|
# a fallback when no command is given on the commandline.
|
|
52
54
|
def command_missing
|
|
53
|
-
raise
|
|
55
|
+
raise NoCommandError
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
# Override option_missing if needed.
|
|
@@ -62,88 +64,88 @@ module Clio
|
|
|
62
64
|
raise NoOptionError, opt
|
|
63
65
|
end
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
class << self
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
def run(obj, argv=ARGV)
|
|
70
|
+
args = parse(obj, argv)
|
|
71
|
+
subcmd = args.shift
|
|
72
|
+
if subcmd && !obj.respond_to?("#{subcmd}=")
|
|
73
|
+
obj.send(subcmd, *args)
|
|
74
|
+
else
|
|
75
|
+
obj.command_missing
|
|
76
|
+
end
|
|
74
77
|
end
|
|
75
|
-
end
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
#def run(obj)
|
|
80
|
+
# methname, args = *parse(obj)
|
|
81
|
+
# meth = obj.method(methname)
|
|
82
|
+
# meth.call(*args)
|
|
83
|
+
#end
|
|
84
|
+
|
|
85
|
+
#
|
|
86
|
+
def parse(obj, argv)
|
|
87
|
+
case argv
|
|
88
|
+
when String
|
|
89
|
+
require 'shellwords'
|
|
90
|
+
argv = Shellwords.shellwords(argv)
|
|
91
|
+
else
|
|
92
|
+
argv = argv.dup
|
|
93
|
+
end
|
|
82
94
|
|
|
83
|
-
#
|
|
84
|
-
def parse(obj, argv)
|
|
85
|
-
case argv
|
|
86
|
-
when String
|
|
87
|
-
require 'shellwords'
|
|
88
|
-
argv = Shellwords.shellwords(argv)
|
|
89
|
-
else
|
|
90
95
|
argv = argv.dup
|
|
96
|
+
args, opts, i = [], {}, 0
|
|
97
|
+
while argv.size > 0
|
|
98
|
+
case opt = argv.shift
|
|
99
|
+
when /=/
|
|
100
|
+
parse_equal(obj, opt, argv)
|
|
101
|
+
when /^--/
|
|
102
|
+
parse_option(obj, opt, argv)
|
|
103
|
+
when /^-/
|
|
104
|
+
parse_flags(obj, opt, argv)
|
|
105
|
+
else
|
|
106
|
+
args << opt
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
return args
|
|
91
110
|
end
|
|
92
111
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
when /=/
|
|
98
|
-
parse_equal(obj, opt, argv)
|
|
99
|
-
when /^--/
|
|
100
|
-
parse_option(obj, opt, argv)
|
|
101
|
-
when /^-/
|
|
102
|
-
parse_flags(obj, opt, argv)
|
|
112
|
+
#
|
|
113
|
+
def parse_equal(obj, opt, argv)
|
|
114
|
+
if md = /^[-]*(.*?)=(.*?)$/.match(opt)
|
|
115
|
+
x, v = md[1], md[2]
|
|
103
116
|
else
|
|
104
|
-
|
|
117
|
+
raise ArgumentError, "#{x}"
|
|
118
|
+
end
|
|
119
|
+
if obj.respond_to?("#{x}=")
|
|
120
|
+
# TODO: to_b if 'true' or 'false' ?
|
|
121
|
+
obj.send("#{x}=",v)
|
|
122
|
+
else
|
|
123
|
+
obj.option_missing(x, v) # argv?
|
|
105
124
|
end
|
|
106
125
|
end
|
|
107
|
-
return args
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
#
|
|
111
|
-
def parse_equal(obj, opt, argv)
|
|
112
|
-
if md = /^[-]*(.*?)=(.*?)$/.match(opt)
|
|
113
|
-
x, v = md[1], md[2]
|
|
114
|
-
else
|
|
115
|
-
raise ArgumentError, "#{x}"
|
|
116
|
-
end
|
|
117
|
-
if obj.respond_to?("#{x}=")
|
|
118
|
-
# TODO: to_b if 'true' or 'false' ?
|
|
119
|
-
obj.send("#{x}=",v)
|
|
120
|
-
else
|
|
121
|
-
obj.option_missing(x, v) # argv?
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
#
|
|
126
|
-
def parse_option(obj, opt, argv)
|
|
127
|
-
x = opt[2..-1]
|
|
128
|
-
if obj.respond_to?("#{x}=")
|
|
129
|
-
obj.send("#{x}=",true)
|
|
130
|
-
else
|
|
131
|
-
obj.option_missing(x, argv)
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
126
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if obj.respond_to?("#{k}=")
|
|
141
|
-
obj.send("#{k}=",true)
|
|
127
|
+
#
|
|
128
|
+
def parse_option(obj, opt, argv)
|
|
129
|
+
x = opt[2..-1]
|
|
130
|
+
if obj.respond_to?("#{x}=")
|
|
131
|
+
obj.send("#{x}=",true)
|
|
142
132
|
else
|
|
143
133
|
obj.option_missing(x, argv)
|
|
144
134
|
end
|
|
145
135
|
end
|
|
146
|
-
|
|
136
|
+
|
|
137
|
+
#
|
|
138
|
+
def parse_flags(obj, opt, args)
|
|
139
|
+
x = opt[1..-1]
|
|
140
|
+
c = 0
|
|
141
|
+
x.split(//).each do |k|
|
|
142
|
+
if obj.respond_to?("#{k}=")
|
|
143
|
+
obj.send("#{k}=",true)
|
|
144
|
+
else
|
|
145
|
+
obj.option_missing(x, argv)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
147
149
|
|
|
148
150
|
end #class << self
|
|
149
151
|
|
data/lib/clio/commandline.rb
CHANGED
|
@@ -1,275 +1,474 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
3
|
-
require '
|
|
1
|
+
require 'clio/facets/kernel' # for deep_copy
|
|
2
|
+
require 'clio/usage'
|
|
3
|
+
#require 'shellwords'
|
|
4
4
|
|
|
5
5
|
module Clio
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
6
|
+
|
|
7
|
+
# = Commandline
|
|
8
|
+
#
|
|
9
|
+
# Clio's Commandline class is a very versitile command line parser.
|
|
10
|
+
# A Command can be used either declaritively, defining usage
|
|
11
|
+
# and help information upfront; or lazily, whereby information
|
|
12
|
+
# about usage is built-up as the commandline actually gets use in
|
|
13
|
+
# one's program; or you can use a mixture of the two.
|
|
14
|
+
#
|
|
15
|
+
# = Underlying Notation
|
|
16
|
+
#
|
|
17
|
+
# As you might expect the fluent notation can be broken down into
|
|
18
|
+
# block notation.
|
|
19
|
+
#
|
|
20
|
+
# cli = Clio::Command.new
|
|
21
|
+
# cli.usage do
|
|
22
|
+
# option(:verbose, :v) do
|
|
23
|
+
# help('verbose output')
|
|
24
|
+
# end
|
|
25
|
+
# option(:quiet, :q) do
|
|
26
|
+
# help('run silently')
|
|
27
|
+
# xor(:V)
|
|
28
|
+
# end
|
|
29
|
+
# command(:document) do
|
|
30
|
+
# help('generate documentation')
|
|
31
|
+
# option(:output, :o) do
|
|
32
|
+
# type('FILE')
|
|
33
|
+
# help('output directory')
|
|
34
|
+
# end
|
|
35
|
+
# argument('files') do
|
|
36
|
+
# multiple
|
|
37
|
+
# end
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# Clearly block notation is DRY and easier to read, but fluent
|
|
42
|
+
# notation is important to have because it allows the Commandline
|
|
43
|
+
# object to be passed around as an argument and modified easily.
|
|
44
|
+
#
|
|
45
|
+
# == Method Notation
|
|
46
|
+
#
|
|
47
|
+
# This notation is very elegant, but slightly more limited in scope.
|
|
48
|
+
# For instance, subcommands that use non-letter characters, such as ':',
|
|
49
|
+
# can not be described with this notation.
|
|
50
|
+
#
|
|
51
|
+
# cli.usage.document('*files', '--output=FILE -o')
|
|
52
|
+
# cli.usage('--verbose -V','--quiet -q')
|
|
53
|
+
#
|
|
54
|
+
# cli.usage.help(
|
|
55
|
+
# 'document' , 'generate documentation',
|
|
56
|
+
# 'validate' , 'run tests or specifications',
|
|
57
|
+
# '--verbose' , 'verbose output',
|
|
58
|
+
# '--quiet' , 'run siltently'
|
|
59
|
+
# )
|
|
60
|
+
#
|
|
61
|
+
# cli.usage.document.help(
|
|
62
|
+
# '--output', 'output directory'
|
|
63
|
+
# 'file*', 'files to document'
|
|
64
|
+
# )
|
|
65
|
+
#
|
|
66
|
+
# This notation is slightly more limited in scope... so...
|
|
67
|
+
#
|
|
68
|
+
# cli.usage.command(:document, '--output=FILE -o', 'files*')
|
|
69
|
+
#
|
|
70
|
+
# == Bracket Shorthand Notation
|
|
71
|
+
#
|
|
72
|
+
# The core notation can be somewhat verbose. As a further convenience
|
|
73
|
+
# commandline usage can be defined with a brief <i>bracket shorthand</i>.
|
|
74
|
+
# This is especailly useful when the usage is simple and statically defined.
|
|
75
|
+
#
|
|
76
|
+
# cli.usage['document']['--output=FILE -o']['FILE*']
|
|
77
|
+
#
|
|
78
|
+
# Using a little creativity to improve readabilty we can convert the
|
|
79
|
+
# whole example from above using this notation.
|
|
80
|
+
#
|
|
81
|
+
# cli.usage['--verbose -V', 'verbose output' ] \
|
|
82
|
+
# ['--quiet -q', 'run silently' ] \
|
|
83
|
+
# ['document', 'generate documention' ] \
|
|
84
|
+
# [ '--output=FILE -o', 'output directory' ] \
|
|
85
|
+
# [ 'FILE*', 'files to document' ]
|
|
86
|
+
#
|
|
87
|
+
# Alternately the help information can be left out and defined in
|
|
88
|
+
# a seprate set of usage calls.
|
|
89
|
+
#
|
|
90
|
+
# cli.usage['--verbose -V']['--quiet -q'] \
|
|
91
|
+
# ['document']['--output=FILE -o']['FILE*']
|
|
92
|
+
#
|
|
93
|
+
# cli.usage.help(
|
|
94
|
+
# 'document' , 'generate documentation',
|
|
95
|
+
# 'validate' , 'run tests or specifications',
|
|
96
|
+
# '--verbose' , 'verbose output',
|
|
97
|
+
# '--quiet' , 'run siltently'
|
|
98
|
+
# )
|
|
99
|
+
#
|
|
100
|
+
# cli.usage['document'].help(
|
|
101
|
+
# '--output', 'output directory'
|
|
102
|
+
# 'FILE', 'files to docment'
|
|
103
|
+
# )
|
|
104
|
+
#
|
|
105
|
+
# A little more verbose, but a bit more intutive.
|
|
106
|
+
#
|
|
107
|
+
# == Combining Notations
|
|
108
|
+
#
|
|
109
|
+
# Since the various notations all translate to same underlying
|
|
110
|
+
# structures, they can be mixed and matched as suites ones taste.
|
|
111
|
+
# For example we could mix Method Notation and Bracket Notation.
|
|
112
|
+
#
|
|
113
|
+
# cli.usage.document['--output=FILE -o']['file*']
|
|
114
|
+
# cli.usage['--verbose -V']['--quiet -q']
|
|
115
|
+
#
|
|
116
|
+
# The important thing to keep in mind when doing this is what is
|
|
117
|
+
# returned by each type of usage call.
|
|
118
|
+
#
|
|
119
|
+
# == Commandline Parsing
|
|
120
|
+
#
|
|
121
|
+
# With usage in place, call the +parse+ method to process the
|
|
122
|
+
# actual commandline.
|
|
123
|
+
#
|
|
124
|
+
# cli.parse
|
|
125
|
+
#
|
|
126
|
+
# If no command arguments are passed to +parse+, ARGV is used.
|
|
127
|
+
#
|
|
128
|
+
#--
|
|
129
|
+
# == Passive Parsing
|
|
130
|
+
#
|
|
131
|
+
# The Command class allows you to declare as little or as
|
|
132
|
+
# much of the commandline interface upfront as is suitable to
|
|
133
|
+
# your application. When using the commandline object, if not
|
|
134
|
+
# already defined, options will be lazily created. For example:
|
|
135
|
+
#
|
|
136
|
+
# cli = Clio::Commandline.new('--force')
|
|
137
|
+
# cli.force? #=> true
|
|
138
|
+
#
|
|
139
|
+
# Commandline sees that you expect a '--force' flag to be an
|
|
140
|
+
# acceptable option. So it will call cli.usage.option('force')
|
|
141
|
+
# behind the scenes before trying to determine the actual value
|
|
142
|
+
# per the content of the command line. You can add aliases as
|
|
143
|
+
# parameters to this call as well.
|
|
144
|
+
#
|
|
145
|
+
# cli = Clio::Commandline.new('-f')
|
|
146
|
+
# cli.force?(:f) #=> true
|
|
147
|
+
#
|
|
148
|
+
# Once set, you do not need to specify the alias again:
|
|
149
|
+
#
|
|
150
|
+
# cli.force? #=> true
|
|
151
|
+
#
|
|
152
|
+
# With the exception of help information, this means you can
|
|
153
|
+
# generally just use a commandline as needed without having
|
|
154
|
+
# to declare anything upfront.
|
|
155
|
+
#++
|
|
156
|
+
#
|
|
157
|
+
# == Usage Cache
|
|
158
|
+
#
|
|
159
|
+
# Lastly, Commandline provides a simple means to cache usage
|
|
160
|
+
# information to a configuration file, which then can be used
|
|
161
|
+
# again the next time the same command is used. This allows
|
|
162
|
+
# Commandline to provide high-performane tab completion.
|
|
163
|
+
#
|
|
164
|
+
#--
|
|
165
|
+
# == Coming Soon
|
|
166
|
+
#
|
|
167
|
+
# In the future Commandline will be able to generate Manpage
|
|
168
|
+
# templates.
|
|
169
|
+
#
|
|
170
|
+
# TODO: Allow option setter methods (?)
|
|
171
|
+
# TODO: Allow a hash as argument to initialize (?)
|
|
172
|
+
#++
|
|
173
|
+
|
|
22
174
|
class Commandline
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
### upto the first non-option argument. This
|
|
28
|
-
### makes quick work of separating a subcommand
|
|
29
|
-
### from the options for a main command.
|
|
30
|
-
# TODO: Rename this method.
|
|
31
|
-
def self.gerrymander(argv=ARGV)
|
|
32
|
-
if String===argv
|
|
33
|
-
argv = Shellwords.shellwords(argv)
|
|
34
|
-
end
|
|
35
|
-
sub = argv.find{ |x| x !~ /^[-]/ }
|
|
36
|
-
idx = argv.index(sub)
|
|
37
|
-
opts = argv[0...idx]
|
|
38
|
-
scmd = argv[idx..-1]
|
|
39
|
-
return opts, scmd
|
|
175
|
+
|
|
176
|
+
#
|
|
177
|
+
instance_methods.each do |m|
|
|
178
|
+
private m if m !~ /^(__|instance_|object_|send$|class$|inspect$|respond_to\?$)/
|
|
40
179
|
end
|
|
41
180
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
181
|
+
class << self
|
|
182
|
+
|
|
183
|
+
#def inherited(subclass)
|
|
184
|
+
#p usage.to_s
|
|
185
|
+
#p subclass.usage.to_s
|
|
186
|
+
# subclass.usage = self.usage.clone #deep_copy
|
|
187
|
+
#p subclass.usage.to_s
|
|
188
|
+
# end
|
|
189
|
+
|
|
190
|
+
# Command usage.
|
|
191
|
+
def usage
|
|
192
|
+
@usage ||= (
|
|
193
|
+
if ancestors[1] < Commandline
|
|
194
|
+
ancestors[1].usage.dup
|
|
195
|
+
else
|
|
196
|
+
Usage.new
|
|
197
|
+
end
|
|
198
|
+
)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def usage=(u)
|
|
202
|
+
raise ArgumentError unless u <= Usage
|
|
203
|
+
@usage = u
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# if ancestors[1] < Command
|
|
207
|
+
# @usage = ancestors[0].usage.deep_copy
|
|
208
|
+
# else
|
|
209
|
+
# @usage = Usage.new
|
|
210
|
+
# end
|
|
211
|
+
|
|
212
|
+
#
|
|
213
|
+
def subcommand(name, help=nil, &block)
|
|
214
|
+
usage.subcommand(name, help, &block)
|
|
215
|
+
end
|
|
216
|
+
alias_method :command, :subcommand
|
|
217
|
+
alias_method :cmd, :subcommand
|
|
218
|
+
|
|
219
|
+
#
|
|
220
|
+
def option(name, *aliases, &block)
|
|
221
|
+
usage.option(name, *aliases, &block)
|
|
222
|
+
end
|
|
223
|
+
alias_method :switch, :option
|
|
224
|
+
|
|
225
|
+
#
|
|
226
|
+
def opt(label, help, &block)
|
|
227
|
+
usage.opt(label, help, &block)
|
|
228
|
+
end
|
|
229
|
+
alias_method :swt, :opt
|
|
230
|
+
|
|
231
|
+
#
|
|
232
|
+
def argument(*n_type, &block)
|
|
233
|
+
usage.argument(*n_type, &block)
|
|
68
234
|
end
|
|
235
|
+
|
|
236
|
+
#
|
|
237
|
+
def help(string=nil)
|
|
238
|
+
usage.help(string)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
#
|
|
242
|
+
#def arg(label, help, &block)
|
|
243
|
+
# usage.arg(label, help, &block)
|
|
244
|
+
#end
|
|
245
|
+
|
|
69
246
|
end
|
|
70
247
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
@predefined_options
|
|
248
|
+
# New Command.
|
|
249
|
+
def initialize(argv=nil, opts={}, &block)
|
|
250
|
+
argv_set(argv || ARGV)
|
|
251
|
+
#if opts[:usage]
|
|
252
|
+
# @usage = opts[:usage]
|
|
253
|
+
#else
|
|
254
|
+
# #@usage = load_cache
|
|
255
|
+
#end
|
|
256
|
+
if self.class == Commandline
|
|
257
|
+
@usage = Usage.new
|
|
82
258
|
else
|
|
83
|
-
@
|
|
259
|
+
@usage = self.class.usage #|| Usage.new #.deep_copy
|
|
84
260
|
end
|
|
261
|
+
@usage.instance_eval(&block) if block
|
|
85
262
|
end
|
|
86
263
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
when Integer
|
|
95
|
-
@arguments[index] ||= (
|
|
96
|
-
args = @argv.select{ |e| e !~ /^-/ }
|
|
97
|
-
val = args[index]
|
|
98
|
-
@argv.delete(args[index])
|
|
99
|
-
val
|
|
100
|
-
)
|
|
101
|
-
else
|
|
102
|
-
return send(index) if respond_to?(index)
|
|
103
|
-
key = index.to_s.chomp('?')
|
|
104
|
-
val = option_parse(index)
|
|
105
|
-
instance_variable_set("@#{key}", val)
|
|
106
|
-
(class << self; self; end).class_eval %{
|
|
107
|
-
def #{index}; @#{key}; end
|
|
108
|
-
}
|
|
109
|
-
return val
|
|
264
|
+
#
|
|
265
|
+
def argv_set(argv)
|
|
266
|
+
# reset parser
|
|
267
|
+
@parser = nil
|
|
268
|
+
# convert to array if string
|
|
269
|
+
if String===argv
|
|
270
|
+
argv = Shellwords.shellwords(argv)
|
|
110
271
|
end
|
|
272
|
+
# remove anything subsequent to '--'
|
|
273
|
+
if index = argv.index('--')
|
|
274
|
+
argv = argv[0...index]
|
|
275
|
+
end
|
|
276
|
+
@argv = argv
|
|
111
277
|
end
|
|
112
278
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
@
|
|
117
|
-
val
|
|
279
|
+
#
|
|
280
|
+
def cli
|
|
281
|
+
#parse unless @cli
|
|
282
|
+
@cli
|
|
118
283
|
end
|
|
119
284
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
(class << self; self; end).class_eval do
|
|
130
|
-
alias_method new, old
|
|
131
|
-
end
|
|
285
|
+
#
|
|
286
|
+
#def usage(name=nil, &block)
|
|
287
|
+
# @usage ||= Usage.new(name)
|
|
288
|
+
# @usage.instance_eval(&block) if block
|
|
289
|
+
# @usage
|
|
290
|
+
#end
|
|
291
|
+
|
|
292
|
+
def usage
|
|
293
|
+
@usage
|
|
132
294
|
end
|
|
133
295
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
### Returns a hash of all options parsed.
|
|
139
|
-
def instance_options
|
|
140
|
-
h = {}
|
|
141
|
-
ivs = instance_variables - ['@arguments','@argv']
|
|
142
|
-
ivs.each do |iv|
|
|
143
|
-
val = instance_variable_get(iv)
|
|
144
|
-
h[iv.sub('@','').to_sym] = val if val
|
|
145
|
-
end
|
|
146
|
-
h
|
|
296
|
+
#
|
|
297
|
+
def to_s
|
|
298
|
+
usage.to_s
|
|
147
299
|
end
|
|
148
300
|
|
|
149
|
-
|
|
150
|
-
def
|
|
151
|
-
|
|
301
|
+
#
|
|
302
|
+
def to_s_help
|
|
303
|
+
usage.to_s_help
|
|
152
304
|
end
|
|
153
305
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
### of shell arguments, like ARGV. If none
|
|
159
|
-
### is given it defaults to ARGV.
|
|
160
|
-
def initialize(argv=ARGV)
|
|
161
|
-
case argv
|
|
162
|
-
when String
|
|
163
|
-
@argv = Shellwords.shellwords(argv)
|
|
164
|
-
#when Hash
|
|
165
|
-
# argv.each{ |k,v| send("#{k}=", v) }
|
|
166
|
-
else
|
|
167
|
-
@argv = argv.dup
|
|
168
|
-
end
|
|
169
|
-
@arguments = []
|
|
170
|
-
|
|
171
|
-
# parse predefined options attributes.
|
|
172
|
-
object_class.predefined_options.each do |modes|
|
|
173
|
-
key = modes.first.to_s.chomp('?')
|
|
174
|
-
modes.reverse.each do |i|
|
|
175
|
-
val = option_parse(i)
|
|
176
|
-
instance_variable_set("@#{key}", val) if val
|
|
177
|
-
end
|
|
178
|
-
end
|
|
306
|
+
#
|
|
307
|
+
def parse(argv=nil)
|
|
308
|
+
argv_set(argv) if argv
|
|
309
|
+
@cli = parser.parse
|
|
179
310
|
end
|
|
180
311
|
|
|
181
|
-
|
|
182
|
-
def
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
end
|
|
312
|
+
#
|
|
313
|
+
def parser
|
|
314
|
+
@parser ||= Usage::Parser.new(usage, @argv)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
#
|
|
318
|
+
def [](i)
|
|
319
|
+
@cli[i]
|
|
190
320
|
end
|
|
191
321
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
322
|
+
#
|
|
323
|
+
def command ; cli.command ; end
|
|
324
|
+
|
|
325
|
+
#
|
|
326
|
+
def commands ; cli.commands ; end
|
|
327
|
+
|
|
328
|
+
#
|
|
329
|
+
def arguments ; cli.arguments ; end
|
|
330
|
+
|
|
331
|
+
#
|
|
332
|
+
def switches ; cli.options ; end
|
|
333
|
+
|
|
334
|
+
#
|
|
335
|
+
alias_method :options, :switches
|
|
336
|
+
|
|
337
|
+
# Parameters
|
|
338
|
+
#
|
|
339
|
+
def parameters ; cli.parameters ; end
|
|
196
340
|
|
|
197
|
-
|
|
198
|
-
|
|
341
|
+
#
|
|
342
|
+
def to_a
|
|
343
|
+
cli.to_a
|
|
344
|
+
end
|
|
199
345
|
|
|
200
|
-
|
|
346
|
+
# Commandline fully valid?
|
|
347
|
+
#
|
|
348
|
+
def valid?
|
|
349
|
+
@cli.valid?
|
|
201
350
|
end
|
|
202
351
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
@argv.
|
|
210
|
-
|
|
352
|
+
# TODO: adding '-' is best idea?
|
|
353
|
+
#
|
|
354
|
+
def completion(argv=nil)
|
|
355
|
+
argv_set(argv) if argv
|
|
356
|
+
@argv << "\t"
|
|
357
|
+
parse
|
|
358
|
+
@argv.pop
|
|
359
|
+
parser.errors[0][1].completion.collect{ |s| s.to_s }
|
|
360
|
+
#@argv.pop if @argv.last == '?'
|
|
361
|
+
#load_cache
|
|
362
|
+
#parse
|
|
211
363
|
end
|
|
212
364
|
|
|
213
|
-
|
|
214
|
-
def
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
365
|
+
#
|
|
366
|
+
#def load_cache
|
|
367
|
+
# if usage = Usage.load_cache
|
|
368
|
+
# @usage = usage
|
|
369
|
+
# end
|
|
370
|
+
#end
|
|
218
371
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
372
|
+
# Method missing provide passive usage and parsing.
|
|
373
|
+
#
|
|
374
|
+
# TODO: This reparses the commandline after every query.
|
|
375
|
+
# Need only parse if usage has change.
|
|
376
|
+
def method_missing(s, *a)
|
|
377
|
+
begin
|
|
378
|
+
s = s.to_s
|
|
379
|
+
case s
|
|
380
|
+
when /[=]$/
|
|
381
|
+
n = s.chomp('=')
|
|
382
|
+
usage.option(n).type(*a)
|
|
383
|
+
parse
|
|
384
|
+
res = @cli.options[n.to_sym]
|
|
385
|
+
when /[!]$/
|
|
386
|
+
n = s.chomp('!')
|
|
387
|
+
cmd = usage.commands[n.to_sym] || usage.command(n, *a)
|
|
388
|
+
res = parse
|
|
389
|
+
when /[?]$/
|
|
390
|
+
n = s.chomp('?')
|
|
391
|
+
u = usage.option(n, *a)
|
|
392
|
+
parse
|
|
393
|
+
res = @cli.options[u.key]
|
|
226
394
|
else
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
@
|
|
230
|
-
@argv.delete_at(i)
|
|
395
|
+
usage.option(s, *a)
|
|
396
|
+
parse
|
|
397
|
+
res = @cli.options[s.to_sym]
|
|
231
398
|
end
|
|
399
|
+
rescue Usage::ParseError => e
|
|
400
|
+
res = nil
|
|
232
401
|
end
|
|
233
|
-
return
|
|
402
|
+
return res
|
|
234
403
|
end
|
|
235
404
|
|
|
236
|
-
|
|
237
|
-
def option_letter_flag(letter)
|
|
238
|
-
o = letter
|
|
239
|
-
i = @argv.index_of{ |e| e =~ /[-][^-]\w*(#{o})\w*$/ }
|
|
240
|
-
if i
|
|
241
|
-
@argv[i] = @argv[i].gsub(o.to_s,'')
|
|
242
|
-
true
|
|
243
|
-
end
|
|
244
|
-
false
|
|
245
|
-
end
|
|
405
|
+
end # class Commandline
|
|
246
406
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
new = @argv[i].gsub(o.to_s,'')
|
|
262
|
-
if new == '-'
|
|
263
|
-
@argv.delete_at(i)
|
|
264
|
-
else
|
|
265
|
-
@argv[i] = new
|
|
266
|
-
end
|
|
267
|
-
@argv.delete_at(i+1)
|
|
268
|
-
end
|
|
407
|
+
end # module Clio
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
=begin demo 1
|
|
412
|
+
|
|
413
|
+
cli = Clio::Commandline.new
|
|
414
|
+
|
|
415
|
+
cli.usage do
|
|
416
|
+
command(:document) do
|
|
417
|
+
help('generate documentation')
|
|
418
|
+
option(:output, :o) do
|
|
419
|
+
type('FILE')
|
|
420
|
+
help('output directory')
|
|
269
421
|
end
|
|
270
|
-
return val
|
|
271
422
|
end
|
|
423
|
+
option(:verbose, :V) do
|
|
424
|
+
help('verbose output')
|
|
425
|
+
end
|
|
426
|
+
option(:quiet, :q) do
|
|
427
|
+
help('run silently')
|
|
428
|
+
xor(:verbose)
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
#p cli
|
|
433
|
+
puts
|
|
434
|
+
puts cli.to_s_help
|
|
435
|
+
|
|
436
|
+
=end
|
|
437
|
+
|
|
438
|
+
=begin demo 2
|
|
272
439
|
|
|
440
|
+
cli = Clio::Commandline.new('--verbose')
|
|
441
|
+
|
|
442
|
+
cli.usage do
|
|
443
|
+
cmd(:document, 'generate documentation') do
|
|
444
|
+
opt('--output=FILE -o', 'output directory')
|
|
445
|
+
end
|
|
446
|
+
opt('--verbose -V', 'verbose output')
|
|
447
|
+
opt('--quiet -q', 'run silently')
|
|
273
448
|
end
|
|
274
|
-
|
|
449
|
+
|
|
450
|
+
=end
|
|
451
|
+
|
|
452
|
+
=begin demo 3
|
|
453
|
+
|
|
454
|
+
# cli.usage %{
|
|
455
|
+
# document generate documentation
|
|
456
|
+
# -o --output=FILE output directory
|
|
457
|
+
# -V --verbose verbose output
|
|
458
|
+
# -q --quiet run silently
|
|
459
|
+
# }
|
|
460
|
+
|
|
461
|
+
#p cline.verbose?(:V)
|
|
462
|
+
#p cline.force?(:f)
|
|
463
|
+
#p cline.document.output='FILE'
|
|
464
|
+
|
|
465
|
+
p cli
|
|
466
|
+
puts
|
|
467
|
+
puts cli.to_s_help
|
|
468
|
+
|
|
469
|
+
=end
|
|
470
|
+
|
|
471
|
+
#cli[['--verbose', '-V'],['--quiet', '-q']] \
|
|
472
|
+
# ['--force'] \
|
|
473
|
+
# ['document']['--output=FILE', '-o']
|
|
275
474
|
|