clio 0.0.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.
- data/COPYING +170 -0
- data/HISTORY +11 -0
- data/MANIFEST +181 -0
- data/METADATA +18 -0
- data/NEWS +10 -0
- data/README +52 -0
- data/admin/config/reap.yaml +30 -0
- data/admin/depot/commandline.rb +219 -0
- data/admin/depot/multicommand.rb +403 -0
- data/admin/depot/test_multicommand.rb +40 -0
- data/admin/log/notes.xml +28 -0
- data/admin/log/stats.html +25 -0
- data/admin/log/syntax.log +0 -0
- data/admin/log/testunit.log +16 -0
- data/admin/pack/clio-0.0.1.gem +0 -0
- data/admin/share/reap/example.rb +7 -0
- data/admin/temps/lib/clio/about.rb.erb +4 -0
- data/lib/clio/ansicode.rb +319 -0
- data/lib/clio/command.rb +296 -0
- data/lib/clio/commandable.rb +195 -0
- data/lib/clio/commandline.rb +275 -0
- data/lib/clio/consoleutils.rb +117 -0
- data/lib/clio/errors.rb +16 -0
- data/lib/clio/option.rb +36 -0
- data/lib/clio/progressbar.rb +253 -0
- data/lib/clio/runmode.rb +126 -0
- data/lib/clio/string.rb +147 -0
- data/test/test_command.rb +42 -0
- data/test/test_commandline.rb +83 -0
- data/vendor/Console/Console.cpp +1203 -0
- data/vendor/Console/Console.rdoc +690 -0
- data/vendor/Console/Console_ANSI.rdoc +302 -0
- data/vendor/Console/HISTORY.txt +7 -0
- data/vendor/Console/INSTALL.txt +18 -0
- data/vendor/Console/Makefile +162 -0
- data/vendor/Console/README.txt +26 -0
- data/vendor/Console/doc/classes/Win32.html +115 -0
- data/vendor/Console/doc/classes/Win32/Console.html +650 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000001.html +31 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000002.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000003.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000004.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000005.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000006.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000007.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000008.html +24 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000009.html +44 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000010.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000011.html +33 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000012.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000013.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000014.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000015.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000016.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000017.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000018.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000019.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000020.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000021.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000022.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000023.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000024.html +35 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000025.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000026.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000027.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000028.html +31 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000029.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000030.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000031.html +23 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000032.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000033.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console.src/M000034.html +25 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI.html +103 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.html +220 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000035.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000036.html +205 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000037.html +40 -0
- data/vendor/Console/doc/classes/Win32/Console/ANSI/IO.src/M000038.html +25 -0
- data/vendor/Console/doc/classes/Win32/Console/API.html +758 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000039.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000040.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000041.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000042.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000043.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000044.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000045.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000046.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000047.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000048.html +30 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000049.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000050.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000051.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000052.html +30 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000053.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000054.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000055.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000056.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000057.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000058.html +47 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000059.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000060.html +47 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000061.html +34 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000062.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000063.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000064.html +35 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000065.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000066.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000067.html +29 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000068.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000069.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000070.html +28 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000071.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000072.html +26 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000073.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000074.html +31 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000075.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000076.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000077.html +27 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000078.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000079.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/API.src/M000080.html +32 -0
- data/vendor/Console/doc/classes/Win32/Console/Constants.html +360 -0
- data/vendor/Console/doc/created.rid +1 -0
- data/vendor/Console/doc/files/Console_ANSI_rdoc.html +407 -0
- data/vendor/Console/doc/files/Console_cpp.html +104 -0
- data/vendor/Console/doc/files/Console_rdoc.html +964 -0
- data/vendor/Console/doc/files/lib/Win32/Console/ANSI_rb.html +123 -0
- data/vendor/Console/doc/files/lib/Win32/Console_rb.html +297 -0
- data/vendor/Console/doc/fr_class_index.html +32 -0
- data/vendor/Console/doc/fr_file_index.html +31 -0
- data/vendor/Console/doc/fr_method_index.html +106 -0
- data/vendor/Console/doc/index.html +24 -0
- data/vendor/Console/doc/rdoc-style.css +172 -0
- data/vendor/Console/extconf.rb +18 -0
- data/vendor/Console/lib/Term/ansicolor.rb +76 -0
- data/vendor/Console/lib/Win32/Console.rb +970 -0
- data/vendor/Console/lib/Win32/Console/ANSI.rb +305 -0
- data/vendor/Console/test/test_cursor.rb +9 -0
- data/vendor/Console/test/test_mouse.rb +6 -0
- data/vendor/Console/test/test_readinput.rb +62 -0
- data/vendor/Console/test/test_readoutput.rb +52 -0
- data/vendor/Console/test/test_sendevent.rb +17 -0
- data/vendor/Console/test/test_title.rb +14 -0
- data/vendor/Console/test/test_write.rb +36 -0
- metadata +253 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
syntax: {}
|
|
4
|
+
|
|
5
|
+
autotools: ~
|
|
6
|
+
|
|
7
|
+
testunit: {}
|
|
8
|
+
|
|
9
|
+
basicstats: {}
|
|
10
|
+
|
|
11
|
+
notes: {}
|
|
12
|
+
|
|
13
|
+
rdoc:
|
|
14
|
+
template: jamis
|
|
15
|
+
inline: true
|
|
16
|
+
|
|
17
|
+
ridoc: {}
|
|
18
|
+
|
|
19
|
+
gem:
|
|
20
|
+
exclude:
|
|
21
|
+
- doc/rdoc
|
|
22
|
+
- doc/ri
|
|
23
|
+
|
|
24
|
+
rubyforge:
|
|
25
|
+
unixname: clio
|
|
26
|
+
groupid: 6913
|
|
27
|
+
# publishmap:
|
|
28
|
+
# admin/web: .
|
|
29
|
+
# doc/rdoc: rdoc
|
|
30
|
+
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
require 'shellwords'
|
|
2
|
+
require 'facets/kernel/object_class'
|
|
3
|
+
|
|
4
|
+
module Clio
|
|
5
|
+
### = Commandline
|
|
6
|
+
###
|
|
7
|
+
### What a strange thing is the Clio Commandline.
|
|
8
|
+
### An entity unknown until put upon.
|
|
9
|
+
class Commandline
|
|
10
|
+
instance_methods.each{ |m| private m if m !~ /^(__|instance_|object_|send$|inspect$)/ }
|
|
11
|
+
|
|
12
|
+
### Splits a raw command line into two smaller
|
|
13
|
+
### ones. The first including only options
|
|
14
|
+
### upto the first non-option argument. This
|
|
15
|
+
### makes quick work of separating subcommands
|
|
16
|
+
### from a main command.
|
|
17
|
+
# TODO: Rename this method.
|
|
18
|
+
def self.gerrymander(argv=ARGV)
|
|
19
|
+
if String===argv
|
|
20
|
+
argv = Shellwords.shellwords(argv)
|
|
21
|
+
end
|
|
22
|
+
sub = argv.find{ |x| x !~ /^[-]/ }
|
|
23
|
+
idx = argv.index(sub)
|
|
24
|
+
opts = argv[0...idx]
|
|
25
|
+
scmd = argv[idx..-1]
|
|
26
|
+
return new(opts), new(scmd)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
### Define an option attibute.
|
|
30
|
+
### While commandline can be used without
|
|
31
|
+
### pre-declartion of support options
|
|
32
|
+
### doding so allows for creating option
|
|
33
|
+
### aliases. Eg. --quiet and -q.
|
|
34
|
+
def self.attr(name, *aliases)
|
|
35
|
+
name = name.to_s
|
|
36
|
+
if name =~ /\?$/
|
|
37
|
+
name = name.chomp('?')
|
|
38
|
+
#attr_writer name
|
|
39
|
+
module_eval "def #{name}?; self[:#{name}] ; end"
|
|
40
|
+
aliases.each do |alt|
|
|
41
|
+
alt = alt.to_s.chomp('?')
|
|
42
|
+
alias_method("#{alt}?", "#{name}?")
|
|
43
|
+
#alias_method("#{alt}=", "#{name}=")
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
attr_reader(name)
|
|
47
|
+
aliases.each do |alt|
|
|
48
|
+
#alt = alt.to_s.chomp('?') # TODO: raise error ?
|
|
49
|
+
alias_method("#{alt}" , "#{name}")
|
|
50
|
+
#alias_method("#{alt}=", "#{name}=")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
### Returns a list of all pre-defined options.
|
|
56
|
+
### It does this by seaching class ancestry
|
|
57
|
+
### for instance_methods until it reaches the
|
|
58
|
+
### Commandline base class.
|
|
59
|
+
### TODO: Rename #runmodes method.
|
|
60
|
+
### TODO: Robust enough? Use an Inheritor instead?
|
|
61
|
+
def self.runmodes
|
|
62
|
+
index = ancestors.index(::Clio::Commandline)
|
|
63
|
+
options = ancestors[0...index].inject([]) do |opts, ancestor|
|
|
64
|
+
opts | ancestor.instance_methods(false)
|
|
65
|
+
end
|
|
66
|
+
options.reject{ |o| o =~ /\=$/ }
|
|
67
|
+
options.collect{ |o| o.to_sym }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
public
|
|
71
|
+
|
|
72
|
+
### This method provides the centralized means of accessing
|
|
73
|
+
### the options and arguments of the commandline.
|
|
74
|
+
def [](index)
|
|
75
|
+
case index
|
|
76
|
+
when Integer
|
|
77
|
+
@arguments[index] ||= (
|
|
78
|
+
args = @argv.select{ |e| e !~ /^-/ }
|
|
79
|
+
val = args[index]
|
|
80
|
+
@argv.delete(args[index])
|
|
81
|
+
val
|
|
82
|
+
)
|
|
83
|
+
else
|
|
84
|
+
index = index.to_s
|
|
85
|
+
name = index.chomp('?')
|
|
86
|
+
key = name.to_sym
|
|
87
|
+
return @options[key] if @options.key?(key)
|
|
88
|
+
|
|
89
|
+
@options[key] = if index =~ /\?$/
|
|
90
|
+
name.size == 1 ? option_letter_flag(name) : option_flag(name)
|
|
91
|
+
else
|
|
92
|
+
name.size == 1 ? option_letter_value(name) : option_value(name)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
### Access to the underlying commandline "ARGV".
|
|
98
|
+
### This will show what is yet to be processed.
|
|
99
|
+
def instance_delegate ; @argv ; end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
### New Commandline. Takse a single argument
|
|
104
|
+
### which can be a "shell" string, or an array
|
|
105
|
+
### of shell arguments, like ARGV. If none
|
|
106
|
+
### is given it defaults to ARGV.
|
|
107
|
+
def initialize(argv=ARGV)
|
|
108
|
+
case argv
|
|
109
|
+
when String
|
|
110
|
+
@argv = Shellwords.shellwords(argv)
|
|
111
|
+
else
|
|
112
|
+
@argv = argv.dup
|
|
113
|
+
end
|
|
114
|
+
@arguments = []
|
|
115
|
+
@options = {}
|
|
116
|
+
|
|
117
|
+
object_class.runmodes.each do |name|
|
|
118
|
+
self[name]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
###
|
|
123
|
+
#def initialize(argv=ARGV)
|
|
124
|
+
# if Hash===argv
|
|
125
|
+
# argv.each{ |k,v| send("#{k}=", v) }
|
|
126
|
+
# else
|
|
127
|
+
# self.class.runmodes.each do |name|
|
|
128
|
+
# option(name)
|
|
129
|
+
# end
|
|
130
|
+
# end
|
|
131
|
+
#end
|
|
132
|
+
|
|
133
|
+
### Routes to #[].
|
|
134
|
+
def method_missing(name, *args)
|
|
135
|
+
super unless args.empty?
|
|
136
|
+
case name.to_s
|
|
137
|
+
when /\=$/
|
|
138
|
+
super
|
|
139
|
+
else
|
|
140
|
+
self[name]
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
### Helper method. This can be replaceb by
|
|
145
|
+
### String#index when it supports blocks.
|
|
146
|
+
### Err.. when will that be Matz?
|
|
147
|
+
def index(array, &block)
|
|
148
|
+
find = array.find(&block)
|
|
149
|
+
array.index(find)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
### Parse a flag option.
|
|
153
|
+
def option_flag(name)
|
|
154
|
+
p name
|
|
155
|
+
o = "--#{name}"
|
|
156
|
+
i = index(@argv){ |e| e =~ /^#{o}[=]?/ }
|
|
157
|
+
return false unless i
|
|
158
|
+
raise ArgumentError if @argv[i] =~ /=/
|
|
159
|
+
@argv[i] = nil
|
|
160
|
+
return true
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
### Parse a value option.
|
|
164
|
+
def option_value(name)
|
|
165
|
+
o = "--#{name}"
|
|
166
|
+
i = index(@argv){ |e| e =~ /^#{o}[=]?/ }
|
|
167
|
+
return false unless i
|
|
168
|
+
|
|
169
|
+
if @argv[i] =~ /=/
|
|
170
|
+
key, val = *@argv[i].split('=')
|
|
171
|
+
argv[i] = nil
|
|
172
|
+
else
|
|
173
|
+
case @argv[i+1]
|
|
174
|
+
when nil, /^-/
|
|
175
|
+
raise ArgumentError
|
|
176
|
+
else
|
|
177
|
+
key = @argv[i]
|
|
178
|
+
val = @argv[i+1]
|
|
179
|
+
@argv[i,2] = nil
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
return val
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
### Parse a single letter flag option.
|
|
186
|
+
def option_letter_flag(letter)
|
|
187
|
+
o = letter
|
|
188
|
+
i = index(@argv){ |e| e =~ /[-]\w+(#{o})\w*$/ }
|
|
189
|
+
if i
|
|
190
|
+
@argv[i] = @argv[i].gsub(o,'')
|
|
191
|
+
true
|
|
192
|
+
end
|
|
193
|
+
false
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
### Parse a single letter value option.
|
|
197
|
+
def option_letter_value(letter)
|
|
198
|
+
o = letter
|
|
199
|
+
i = index(@argv){ |e| e =~ /#{o}(\=|$)/ }
|
|
200
|
+
return nil unless i
|
|
201
|
+
if @argv[i] =~ /=/
|
|
202
|
+
rest, val = argv[i].split('=')
|
|
203
|
+
@argv[i] = rest
|
|
204
|
+
else
|
|
205
|
+
case @argv[i+1]
|
|
206
|
+
when nil, /^-/
|
|
207
|
+
raise ArgumentError
|
|
208
|
+
else
|
|
209
|
+
val = argv[i+1]
|
|
210
|
+
@argv[i] = argv[i].gsub(o,'')
|
|
211
|
+
@argv[i+1] = nil
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
return val
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
warn "MultiCommand is under construction!"
|
|
2
|
+
exit 0
|
|
3
|
+
|
|
4
|
+
# = Clio::MultiCommand
|
|
5
|
+
#
|
|
6
|
+
# Base class with metaclass DSL for creating
|
|
7
|
+
# well defined, static command line interfaces.
|
|
8
|
+
#
|
|
9
|
+
# == Authors
|
|
10
|
+
#
|
|
11
|
+
# * Trans
|
|
12
|
+
#
|
|
13
|
+
# == Copying
|
|
14
|
+
#
|
|
15
|
+
# Copyright (c) 2005,2008 Thomas Sawyer
|
|
16
|
+
# Lesser GNU Public License
|
|
17
|
+
#
|
|
18
|
+
# This module is free software. You may use, modify, and/or
|
|
19
|
+
# redistribute this software under the same terms as Ruby.
|
|
20
|
+
#
|
|
21
|
+
# This program is distributed in the hope that it will be
|
|
22
|
+
# useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
23
|
+
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
24
|
+
# PURPOSE.
|
|
25
|
+
|
|
26
|
+
begin
|
|
27
|
+
require 'annotatable'
|
|
28
|
+
rescue LoadError => e
|
|
29
|
+
$stderr << "ERROR: Clio Multicommand depends on 'annotatable'."
|
|
30
|
+
exit 0
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
#require 'facets/argvector'
|
|
34
|
+
require 'clio/errors'
|
|
35
|
+
|
|
36
|
+
module Clio
|
|
37
|
+
|
|
38
|
+
# Here is an example of usage:
|
|
39
|
+
#
|
|
40
|
+
# # General Options
|
|
41
|
+
#
|
|
42
|
+
# # Build Subcommand
|
|
43
|
+
#
|
|
44
|
+
# class MyMultiCommand < Clio::MultiCommand
|
|
45
|
+
#
|
|
46
|
+
# cmd "save to file"
|
|
47
|
+
#
|
|
48
|
+
# opt :file do
|
|
49
|
+
# desc "very useful option"
|
|
50
|
+
# type :path,
|
|
51
|
+
# flag :f,
|
|
52
|
+
# end
|
|
53
|
+
#
|
|
54
|
+
# def save(args, opts)
|
|
55
|
+
#
|
|
56
|
+
# end
|
|
57
|
+
#
|
|
58
|
+
# def fallback(opts)
|
|
59
|
+
# # if no subcommand
|
|
60
|
+
# end
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
# MyMultiCommand.start
|
|
64
|
+
#
|
|
65
|
+
class MultiCommand
|
|
66
|
+
|
|
67
|
+
include Annotatable
|
|
68
|
+
|
|
69
|
+
annotation :cmd
|
|
70
|
+
|
|
71
|
+
annotation :opt
|
|
72
|
+
|
|
73
|
+
def self.method_added(name)
|
|
74
|
+
anns = self.class.annotations(name)
|
|
75
|
+
desc = anns[:cmd]
|
|
76
|
+
@options << Option.new(name, @_desc, @_opts)
|
|
77
|
+
@_opt = nil
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Include this in your dispatch command if you want
|
|
86
|
+
# all options to be traeted the same.
|
|
87
|
+
|
|
88
|
+
module UniversalOptions
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
#
|
|
92
|
+
|
|
93
|
+
def self.option_arity(arity_hash=nil)
|
|
94
|
+
if arity_hash
|
|
95
|
+
(@option_arity ||= {}).merge!(arity_hash)
|
|
96
|
+
end
|
|
97
|
+
@option_arity
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
#
|
|
101
|
+
|
|
102
|
+
def self.start(line=nil)
|
|
103
|
+
cargs = Argvector.new(line || ARGV, option_arity)
|
|
104
|
+
pre = cargs.preoptions
|
|
105
|
+
|
|
106
|
+
if instance_method(:call).arity == 0 #is_a?(SingleCommand)
|
|
107
|
+
args, opts = *cargs.parameters
|
|
108
|
+
new(args, opts).call
|
|
109
|
+
else
|
|
110
|
+
subc, args, opts = *cargs.subcommand
|
|
111
|
+
if self < UniversalOptions
|
|
112
|
+
new(pre, opts).call(subc, args, opts)
|
|
113
|
+
else
|
|
114
|
+
new(pre).call(subc, args, opts)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Command Arguments (for single commands).
|
|
120
|
+
|
|
121
|
+
attr :arguments
|
|
122
|
+
|
|
123
|
+
# Command options. For dispatch commands these are the pre-options.
|
|
124
|
+
|
|
125
|
+
attr :options
|
|
126
|
+
|
|
127
|
+
# For dispatchers, this is a convenience method for creating subcommands.
|
|
128
|
+
|
|
129
|
+
def self.subcommand(name, command_class, options=nil)
|
|
130
|
+
options ||= {}
|
|
131
|
+
if options[:no_merge]
|
|
132
|
+
file, line = __FILE__, __LINE__+1
|
|
133
|
+
code = %{
|
|
134
|
+
def #{name}(args, opts)
|
|
135
|
+
#{command_class}.new(args, opts).call
|
|
136
|
+
end
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
file, line = __FILE__, __LINE__+1
|
|
140
|
+
code = %{
|
|
141
|
+
def #{name}(args, opts)
|
|
142
|
+
opts.merge(options)
|
|
143
|
+
#{command_class}.new(args, opts).call
|
|
144
|
+
end
|
|
145
|
+
}
|
|
146
|
+
end
|
|
147
|
+
class_eval(code, file, line)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
private
|
|
151
|
+
|
|
152
|
+
#
|
|
153
|
+
|
|
154
|
+
def initialize(*args)
|
|
155
|
+
@arguments = []
|
|
156
|
+
@options = {}
|
|
157
|
+
|
|
158
|
+
opts, args = *args.partition{ |e| Hash===e }
|
|
159
|
+
#TEST("options should all be hashes"){ ! opts.all?{ |e| Hash===e }
|
|
160
|
+
initialize_arguments(*args)
|
|
161
|
+
initialize_options(*opts)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
#
|
|
165
|
+
|
|
166
|
+
def initialize_arguments(*arguments)
|
|
167
|
+
@arguments.concat(arguments)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
#
|
|
171
|
+
|
|
172
|
+
def initialize_options(*options)
|
|
173
|
+
options = options.inject{ |h,o| h.merge(o) }
|
|
174
|
+
begin
|
|
175
|
+
opt, val = nil, nil
|
|
176
|
+
options.each do |opt, val|
|
|
177
|
+
opt = opt.gsub('-','_')
|
|
178
|
+
send("#{opt}=", val)
|
|
179
|
+
end
|
|
180
|
+
rescue NoMethodError
|
|
181
|
+
option_missing(opt, val)
|
|
182
|
+
end
|
|
183
|
+
@options.update(options)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
public
|
|
187
|
+
|
|
188
|
+
# For a single command (ie. a subcommand) override #call with arity=0.
|
|
189
|
+
|
|
190
|
+
def call(cmd=nil, *args)
|
|
191
|
+
opts = Hash==args.last ? args.pop : {}
|
|
192
|
+
#TEST("options should all be hashes"){ ! opts.all?{ |e| Hash===e }
|
|
193
|
+
#cmd = :default if cmd.nil?
|
|
194
|
+
if cmd.nil?
|
|
195
|
+
default
|
|
196
|
+
else
|
|
197
|
+
begin
|
|
198
|
+
# FIXME: rename call to [] ?
|
|
199
|
+
raise NameError if cmd == 'call'
|
|
200
|
+
raise NameError unless commands.include?(cmd.to_sym)
|
|
201
|
+
subcommand = method(cmd)
|
|
202
|
+
parameters = [args, opts]
|
|
203
|
+
rescue NameError
|
|
204
|
+
subcommand = method(:command_missing)
|
|
205
|
+
parameters = [cmd, args, opts]
|
|
206
|
+
end
|
|
207
|
+
if subcommand.arity < 0
|
|
208
|
+
subcommand.call(*parameters[0..subcommand.arity])
|
|
209
|
+
else
|
|
210
|
+
subcommand.call(*parameters[0,subcommand.arity])
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Display help message.
|
|
216
|
+
# The one provided is just a very limited dummy routine.
|
|
217
|
+
|
|
218
|
+
# def help
|
|
219
|
+
# puts "USAGE #{File.basename($0)} [options]"
|
|
220
|
+
# puts "\nOptions:"
|
|
221
|
+
# options = self.class.instance_methods(false)
|
|
222
|
+
# options = options - Command.instance_methods(true)
|
|
223
|
+
# options = options.select{ |m| m.to_s =~ /=$/ }
|
|
224
|
+
# options.each do |opt|
|
|
225
|
+
# puts " --#{opt.to_s.chomp('=')}"
|
|
226
|
+
# end
|
|
227
|
+
# end
|
|
228
|
+
|
|
229
|
+
private
|
|
230
|
+
|
|
231
|
+
# Override default to provide non-subcommand functionality.
|
|
232
|
+
|
|
233
|
+
def default; end
|
|
234
|
+
|
|
235
|
+
# TODO: Add "if no setter method".
|
|
236
|
+
def commands
|
|
237
|
+
@_commands ||= (
|
|
238
|
+
cmds = self.class.instance_methods(true) - Command.instance_methods(true)
|
|
239
|
+
cmds.select{ |c| c !~ /\W/ }
|
|
240
|
+
cmds.collect{ |c| c.to_sym }
|
|
241
|
+
)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
#
|
|
245
|
+
|
|
246
|
+
def command_missing(cmd, args, opt)
|
|
247
|
+
raise NoCommandError.new(cmd, args << opt)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
#
|
|
251
|
+
|
|
252
|
+
def option_missing(opt, arg=nil)
|
|
253
|
+
raise NoOptionError.new(opt)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Temporary backward compatability.
|
|
259
|
+
MasterCommand = Command
|
|
260
|
+
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
module Console #:nodoc:
|
|
265
|
+
# For backward compatibility.
|
|
266
|
+
Command = CLI::Command
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
# SCRAP CODE FOR REFERENCE TO POSSIBLE ADD FUTURE FEATURES
|
|
271
|
+
|
|
272
|
+
=begin
|
|
273
|
+
|
|
274
|
+
# We include a module here so you can define your own help
|
|
275
|
+
# command and call #super to utilize this one.
|
|
276
|
+
|
|
277
|
+
module Help
|
|
278
|
+
|
|
279
|
+
def help
|
|
280
|
+
opts = help_options
|
|
281
|
+
s = ""
|
|
282
|
+
s << "#{File.basename($0)}\n\n"
|
|
283
|
+
unless opts.empty?
|
|
284
|
+
s << "OPTIONS\n"
|
|
285
|
+
s << help_options
|
|
286
|
+
s << "\n"
|
|
287
|
+
end
|
|
288
|
+
s << "COMMANDS\n"
|
|
289
|
+
s << help_commands
|
|
290
|
+
puts s
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
private
|
|
294
|
+
|
|
295
|
+
def help_commands
|
|
296
|
+
help = self.class.help
|
|
297
|
+
bufs = help.keys.collect{ |a| a.to_s.size }.max + 3
|
|
298
|
+
lines = []
|
|
299
|
+
help.each { |cmd, str|
|
|
300
|
+
cmd = cmd.to_s
|
|
301
|
+
if cmd !~ /^_/
|
|
302
|
+
lines << " " + cmd + (" " * (bufs - cmd.size)) + str
|
|
303
|
+
end
|
|
304
|
+
}
|
|
305
|
+
lines.join("\n")
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def help_options
|
|
309
|
+
help = self.class.help
|
|
310
|
+
bufs = help.keys.collect{ |a| a.to_s.size }.max + 3
|
|
311
|
+
lines = []
|
|
312
|
+
help.each { |cmd, str|
|
|
313
|
+
cmd = cmd.to_s
|
|
314
|
+
if cmd =~ /^_/
|
|
315
|
+
lines << " " + cmd.gsub(/_/,'-') + (" " * (bufs - cmd.size)) + str
|
|
316
|
+
end
|
|
317
|
+
}
|
|
318
|
+
lines.join("\n")
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
module ClassMethods
|
|
322
|
+
|
|
323
|
+
def help( str=nil )
|
|
324
|
+
return (@help ||= {}) unless str
|
|
325
|
+
@current_help = str
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def method_added( meth )
|
|
329
|
+
if @current_help
|
|
330
|
+
@help ||= {}
|
|
331
|
+
@help[meth] = @current_help
|
|
332
|
+
@current_help = nil
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
include Help
|
|
341
|
+
extend Help::ClassMethods
|
|
342
|
+
|
|
343
|
+
=end
|
|
344
|
+
|
|
345
|
+
=begin
|
|
346
|
+
|
|
347
|
+
# Provides a very basic usage help string.
|
|
348
|
+
#
|
|
349
|
+
# TODO Add support for __options.
|
|
350
|
+
def usage
|
|
351
|
+
str = []
|
|
352
|
+
public_methods(false).sort.each do |meth|
|
|
353
|
+
meth = meth.to_s
|
|
354
|
+
case meth
|
|
355
|
+
when /^_/
|
|
356
|
+
opt = meth.sub(/^_+/, '')
|
|
357
|
+
meth = method(meth)
|
|
358
|
+
if meth.arity == 0
|
|
359
|
+
str << (opt.size > 1 ? "[--#{opt}]" : "[-#{opt}]")
|
|
360
|
+
elsif meth.arity == 1
|
|
361
|
+
str << (opt.size > 1 ? "[--#{opt} value]" : "[-#{opt} value]")
|
|
362
|
+
elsif meth.arity > 0
|
|
363
|
+
v = []; meth.arity.times{ |i| v << 'value' + (i + 1).to_s }
|
|
364
|
+
str << (opt.size > 1 ? "[--#{opt} #{v.join(' ')}]" : "[-#{opt} #{v.join(' ')}]")
|
|
365
|
+
else
|
|
366
|
+
str << (opt.size > 1 ? "[--#{opt} *values]" : "[-#{opt} *values]")
|
|
367
|
+
end
|
|
368
|
+
when /=$/
|
|
369
|
+
opt = meth.chomp('=')
|
|
370
|
+
str << (opt.size > 1 ? "[--#{opt} value]" : "[-#{opt} value]")
|
|
371
|
+
when /!$/
|
|
372
|
+
opt = meth.chomp('!')
|
|
373
|
+
str << (opt.size > 1 ? "[--#{opt}]" : "[-#{opt}]")
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
return str.join(" ")
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
#
|
|
380
|
+
|
|
381
|
+
def self.usage_class(usage)
|
|
382
|
+
c = Class.new(self)
|
|
383
|
+
argv = Shellwords.shellwords(usage)
|
|
384
|
+
argv.each_with_index do |name, i|
|
|
385
|
+
if name =~ /^-/
|
|
386
|
+
if argv[i+1] =~ /^[(.*?)]/
|
|
387
|
+
c.class_eval %{
|
|
388
|
+
attr_accessor :#{name}
|
|
389
|
+
}
|
|
390
|
+
else
|
|
391
|
+
c.class_eval %{
|
|
392
|
+
attr_reader :#{name}
|
|
393
|
+
def #{name}! ; @#{name} = true ; end
|
|
394
|
+
}
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
return c
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
=end
|