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.
249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/clio/commandline.rb', line 249 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 #.deep_copy 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.
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/clio/commandline.rb', line 376 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
232 233 234 |
# File 'lib/clio/commandline.rb', line 232 def argument(*n_type, &block) usage.argument(*n_type, &block) end |
.help(string = nil) ⇒ Object
237 238 239 |
# File 'lib/clio/commandline.rb', line 237 def help(string=nil) usage.help(string) end |
.opt(label, help, &block) ⇒ Object Also known as: swt
226 227 228 |
# File 'lib/clio/commandline.rb', line 226 def opt(label, help, &block) usage.opt(label, help, &block) end |
.option(name, *aliases, &block) ⇒ Object Also known as: switch
220 221 222 |
# File 'lib/clio/commandline.rb', line 220 def option(name, *aliases, &block) usage.option(name, *aliases, &block) end |
.subcommand(name, help = nil, &block) ⇒ Object Also known as: command, cmd
213 214 215 |
# File 'lib/clio/commandline.rb', line 213 def subcommand(name, help=nil, &block) usage.subcommand(name, help, &block) end |
.usage ⇒ Object
Command usage.
191 192 193 194 195 196 197 198 199 |
# File 'lib/clio/commandline.rb', line 191 def usage @usage ||= ( if ancestors[1] < Commandline ancestors[1].usage.dup else Usage.new end ) end |
.usage=(u) ⇒ Object
201 202 203 204 |
# File 'lib/clio/commandline.rb', line 201 def usage=(u) raise ArgumentError unless u <= Usage @usage = u end |
Instance Method Details
#[](i) ⇒ Object
318 319 320 |
# File 'lib/clio/commandline.rb', line 318 def [](i) @cli[i] end |
#arguments ⇒ Object
329 |
# File 'lib/clio/commandline.rb', line 329 def arguments ; cli.arguments ; end |
#argv_set(argv) ⇒ Object
265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/clio/commandline.rb', line 265 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
280 281 282 283 |
# File 'lib/clio/commandline.rb', line 280 def cli #parse unless @cli @cli end |
#command ⇒ Object
323 |
# File 'lib/clio/commandline.rb', line 323 def command ; cli.command ; end |
#commands ⇒ Object
326 |
# File 'lib/clio/commandline.rb', line 326 def commands ; cli.commands ; end |
#completion(argv = nil) ⇒ Object
TODO: adding ‘-’ is best idea?
354 355 356 357 358 359 360 361 362 363 |
# File 'lib/clio/commandline.rb', line 354 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
339 |
# File 'lib/clio/commandline.rb', line 339 def parameters ; cli.parameters ; end |
#parse(argv = nil) ⇒ Object
307 308 309 310 |
# File 'lib/clio/commandline.rb', line 307 def parse(argv=nil) argv_set(argv) if argv @cli = parser.parse end |
#parser ⇒ Object
313 314 315 |
# File 'lib/clio/commandline.rb', line 313 def parser @parser ||= Usage::Parser.new(usage, @argv) end |
#switches ⇒ Object Also known as: options
332 |
# File 'lib/clio/commandline.rb', line 332 def switches ; cli. ; end |
#to_a ⇒ Object
342 343 344 |
# File 'lib/clio/commandline.rb', line 342 def to_a cli.to_a end |
#to_s ⇒ Object
297 298 299 |
# File 'lib/clio/commandline.rb', line 297 def to_s usage.to_s end |
#to_s_help ⇒ Object
302 303 304 |
# File 'lib/clio/commandline.rb', line 302 def to_s_help usage.to_s_help end |
#usage ⇒ Object
292 293 294 |
# File 'lib/clio/commandline.rb', line 292 def usage @usage end |
#valid? ⇒ Boolean
Commandline fully valid?
348 349 350 |
# File 'lib/clio/commandline.rb', line 348 def valid? @cli.valid? end |