clio 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|