Class: Clio::Commandline
- Inherits:
-
Object
- Object
- Clio::Commandline
- Defined in:
- lib/clio/commandline.rb
Overview
Commandline
Clio’s Commandline class is a very versitile command line parser. A Command can be used either declaritively, defining usage and help information upfront; or lazily, whereby information about usage is built-up as the commandline actually gets use in one’s program; or you can use a mixture of the two.
Underlying Notation
As you might expect the fluent notation can be broken down into block notation.
cli = Clio::Command.new
cli.usage do
option(:verbose, :v) do
help('verbose output')
end
option(:quiet, :q) do
help('run silently')
xor(:V)
end
command(:document) do
help('generate documentation')
option(:output, :o) do
type('FILE')
help('output directory')
end
argument('files') do
multiple
end
end
end
Clearly block notation is DRY and easier to read, but fluent notation is important to have because it allows the Commandline object to be passed around as an argument and modified easily.
Method Notation
This notation is very elegant, but slightly more limited in scope. For instance, subcommands that use non-letter characters, such as ‘:’, can not be described with this notation.
cli.usage.document('*files', '--output=FILE -o')
cli.usage('--verbose -V','--quiet -q')
cli.usage.help(
'document' , 'generate documentation',
'validate' , 'run tests or specifications',
'--verbose' , 'verbose output',
'--quiet' , 'run siltently'
)
cli.usage.document.help(
'--output', 'output directory'
'file*', 'files to document'
)
This notation is slightly more limited in scope… so…
cli.usage.command(:document, '--output=FILE -o', 'files*')
Bracket Shorthand Notation
The core notation can be somewhat verbose. As a further convenience commandline usage can be defined with a brief bracket shorthand. This is especailly useful when the usage is simple and statically defined.
cli.usage['document']['--output=FILE -o']['FILE*']
Using a little creativity to improve readabilty we can convert the whole example from above using this notation.
cli.usage['--verbose -V', 'verbose output' ] \
['--quiet -q', 'run silently' ] \
['document', 'generate documention' ] \
[ '--output=FILE -o', 'output directory' ] \
[ 'FILE*', 'files to document' ]
Alternately the help information can be left out and defined in a seprate set of usage calls.
cli.usage['--verbose -V']['--quiet -q'] \
['document']['--output=FILE -o']['FILE*']
cli.usage.help(
'document' , 'generate documentation',
'validate' , 'run tests or specifications',
'--verbose' , 'verbose output',
'--quiet' , 'run siltently'
)
cli.usage['document'].help(
'--output', 'output directory'
'FILE', 'files to docment'
)
A little more verbose, but a bit more intutive.
Combining Notations
Since the various notations all translate to same underlying structures, they can be mixed and matched as suites ones taste. For example we could mix Method Notation and Bracket Notation.
cli.usage.document['--output=FILE -o']['file*']
cli.usage['--verbose -V']['--quiet -q']
The important thing to keep in mind when doing this is what is returned by each type of usage call.
Commandline Parsing
With usage in place, call the parse
method to process the actual commandline.
cli.parse
If no command arguments are passed to parse
, ARGV is used.
–
Passive Parsing
The Command class allows you to declare as little or as much of the commandline interface upfront as is suitable to your application. When using the commandline object, if not already defined, options will be lazily created. For example:
cli = Clio::Commandline.new('--force')
cli.force? #=> true
Commandline sees that you expect a ‘–force’ flag to be an acceptable option. So it will call cli.usage.option(‘force’) behind the scenes before trying to determine the actual value per the content of the command line. You can add aliases as parameters to this call as well.
cli = Clio::Commandline.new('-f')
cli.force?(:f) #=> true
Once set, you do not need to specify the alias again:
cli.force? #=> true
With the exception of help information, this means you can generally just use a commandline as needed without having to declare anything upfront. ++
Usage Cache
Lastly, Commandline provides a simple means to cache usage information to a configuration file, which then can be used again the next time the same command is used. This allows Commandline to provide high-performane tab completion.
–
Coming Soon
In the future Commandline will be able to generate Manpage templates.
TODO: Allow option setter methods (?) TODO: Allow a hash as argument to initialize (?) ++
Class Method Summary collapse
- .argument(*n_type, &block) ⇒ Object
- .help(string = nil) ⇒ Object
- .opt(label, help, &block) ⇒ Object (also: swt)
- .option(name, *aliases, &block) ⇒ Object (also: switch)
- .subcommand(name, help = nil, &block) ⇒ Object (also: command, cmd)
-
.usage ⇒ Object
Command usage.
- .usage=(u) ⇒ Object
Instance Method Summary collapse
- #[](i) ⇒ Object
- #arguments ⇒ Object
- #argv_set(argv) ⇒ Object
- #cli ⇒ Object
- #command ⇒ Object
- #commands ⇒ Object
-
#completion(argv = nil) ⇒ Object
TODO: adding ‘-’ is best idea?.
-
#initialize(argv = nil, opts = {}, &block) ⇒ Commandline
constructor
New Command.
-
#method_missing(s, *a) ⇒ Object
Method missing provide passive usage and parsing.
-
#parameters ⇒ Object
Parameters.
- #parse(argv = nil) ⇒ Object
- #parser ⇒ Object
- #switches ⇒ Object (also: #options)
- #to_a ⇒ Object
- #to_s ⇒ Object
- #to_s_help ⇒ Object
-
#usage ⇒ Object
def usage(name=nil, &block) @usage ||= Usage.new(name) @usage.instance_eval(&block) if block @usage end.
-
#valid? ⇒ Boolean
Commandline fully valid?.
Constructor Details
#initialize(argv = nil, opts = {}, &block) ⇒ Commandline
New Command.
242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/clio/commandline.rb', line 242 def initialize(argv=nil, opts={}, &block) argv_set(argv || ARGV) #if opts[:usage] # @usage = opts[:usage] #else # #@usage = load_cache #end if self.class == Commandline @usage = Usage.new else @usage = self.class.usage #|| Usage.new end @usage.instance_eval(&block) if block end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(s, *a) ⇒ Object
Method missing provide passive usage and parsing.
TODO: This reparses the commandline after every query.
Need only parse if usage has change.
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
# File 'lib/clio/commandline.rb', line 369 def method_missing(s, *a) begin s = s.to_s case s when /[=]$/ n = s.chomp('=') usage.option(n).type(*a) parse res = @cli.[n.to_sym] when /[!]$/ n = s.chomp('!') cmd = usage.commands[n.to_sym] || usage.command(n, *a) res = parse when /[?]$/ n = s.chomp('?') u = usage.option(n, *a) parse res = @cli.[u.key] else usage.option(s, *a) parse res = @cli.[s.to_sym] end rescue Usage::ParseError => e res = nil end return res end |
Class Method Details
.argument(*n_type, &block) ⇒ Object
225 226 227 |
# File 'lib/clio/commandline.rb', line 225 def argument(*n_type, &block) usage.argument(*n_type, &block) end |
.help(string = nil) ⇒ Object
230 231 232 |
# File 'lib/clio/commandline.rb', line 230 def help(string=nil) usage.help(string) end |
.opt(label, help, &block) ⇒ Object Also known as: swt
219 220 221 |
# File 'lib/clio/commandline.rb', line 219 def opt(label, help, &block) usage.opt(label, help, &block) end |
.option(name, *aliases, &block) ⇒ Object Also known as: switch
213 214 215 |
# File 'lib/clio/commandline.rb', line 213 def option(name, *aliases, &block) usage.option(name, *aliases, &block) end |
.subcommand(name, help = nil, &block) ⇒ Object Also known as: command, cmd
206 207 208 |
# File 'lib/clio/commandline.rb', line 206 def subcommand(name, help=nil, &block) usage.subcommand(name, help, &block) end |
.usage ⇒ Object
Command usage.
190 191 192 193 194 195 196 197 198 |
# File 'lib/clio/commandline.rb', line 190 def usage @usage ||= ( if ancestors[1] < Commandline ancestors[1].usage.dup else Usage.new end ) end |
.usage=(u) ⇒ Object
200 201 202 203 |
# File 'lib/clio/commandline.rb', line 200 def usage=(u) raise ArgumentError unless u <= Usage @usage = u end |
Instance Method Details
#[](i) ⇒ Object
311 312 313 |
# File 'lib/clio/commandline.rb', line 311 def [](i) @cli[i] end |
#arguments ⇒ Object
322 |
# File 'lib/clio/commandline.rb', line 322 def arguments ; cli.arguments ; end |
#argv_set(argv) ⇒ Object
258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/clio/commandline.rb', line 258 def argv_set(argv) # reset parser @parser = nil # convert to array if string if String===argv argv = Shellwords.shellwords(argv) end # remove anything subsequent to '--' if index = argv.index('--') argv = argv[0...index] end @argv = argv end |
#cli ⇒ Object
273 274 275 276 |
# File 'lib/clio/commandline.rb', line 273 def cli #parse unless @cli @cli end |
#command ⇒ Object
316 |
# File 'lib/clio/commandline.rb', line 316 def command ; cli.command ; end |
#commands ⇒ Object
319 |
# File 'lib/clio/commandline.rb', line 319 def commands ; cli.commands ; end |
#completion(argv = nil) ⇒ Object
TODO: adding ‘-’ is best idea?
347 348 349 350 351 352 353 354 355 356 |
# File 'lib/clio/commandline.rb', line 347 def completion(argv=nil) argv_set(argv) if argv @argv << "\t" parse @argv.pop parser.errors[0][1].completion.collect{ |s| s.to_s } #@argv.pop if @argv.last == '?' #load_cache #parse end |
#parameters ⇒ Object
Parameters
332 |
# File 'lib/clio/commandline.rb', line 332 def parameters ; cli.parameters ; end |
#parse(argv = nil) ⇒ Object
300 301 302 303 |
# File 'lib/clio/commandline.rb', line 300 def parse(argv=nil) argv_set(argv) if argv @cli = parser.parse end |
#parser ⇒ Object
306 307 308 |
# File 'lib/clio/commandline.rb', line 306 def parser @parser ||= Usage::Parser.new(usage, @argv) end |
#switches ⇒ Object Also known as: options
325 |
# File 'lib/clio/commandline.rb', line 325 def switches ; cli. ; end |
#to_a ⇒ Object
335 336 337 |
# File 'lib/clio/commandline.rb', line 335 def to_a cli.to_a end |
#to_s ⇒ Object
290 291 292 |
# File 'lib/clio/commandline.rb', line 290 def to_s usage.to_s end |
#to_s_help ⇒ Object
295 296 297 |
# File 'lib/clio/commandline.rb', line 295 def to_s_help usage.to_s_help end |
#usage ⇒ Object
285 286 287 |
# File 'lib/clio/commandline.rb', line 285 def usage @usage end |
#valid? ⇒ Boolean
Commandline fully valid?
341 342 343 |
# File 'lib/clio/commandline.rb', line 341 def valid? @cli.valid? end |