Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > [ANN] main-2.2.0

Reply
Thread Tools

[ANN] main-2.2.0

 
 
ara.t.howard
Guest
Posts: n/a
 
      10-24-2007

NAME
main.rb

SYNOPSIS
a class factory and dsl for generating command line programs real
quick

URI
http://rubyforge.org/projects/codeforpeople/
http://codeforpeople.com/lib/ruby/

INSTALL
gem install main

HISTORY
2.2.0
- added ability for parameter dsl error handlers to accept an
argument,
this will be passed the current error. for example

argument() do
arity 42

error do |e|
case e
when Parameter::Arity
...
end
end

- refined the mode parsing a bit: modes can now be abbreviated
to uniqness
and, when the mode is ambiuous, a nice error message is
printed, for
example:

ambiguous mode: in = (inflate or install)?

DESCRIPTION
main.rb features the following:

- unification of option, argument, keyword, and environment
parameter
parsing
- auto generation of usage and help messages
- support for mode/sub-commands
- io redirection support
- logging hooks using ruby's built-in logging mechanism
- intelligent error handling and exit codes
- use as dsl or library for building Main objects
- parsing user defined ARGV and ENV
- zero requirements for understanding the obtuse apis of *any*
command
line option parsers

in short main.rb aims to drastically lower the barrier to writing
uniform
command line applications.

for instance, this program

require 'main'

Main {
argument 'foo'
option 'bar'

def run
p params['foo']
p params['bar']
exit_success!
end
}

sets up a program which requires one argument, 'bar', and which
may accept one
command line switch, '--foo' in addition to the single option/mode
which is always
accepted and handled appropriately: 'help', '--help', '-h'. for
the most
part main.rb stays out of your command line namespace but insists
that your
application has at least a help mode/option.

main.rb supports sub-commands in a very simple way

require 'main'

Main {
mode 'install' do
def run() puts 'installing...' end
end

mode 'uninstall' do
def run() puts 'uninstalling...' end
end
}

which allows you a program called 'a.rb' to be invoked as

ruby a.rb install

and

ruby a.rb uninstall

for simple programs main.rb is a real time saver but it's for more
complex
applications where main.rb's unification of parameter parsing, class
configuration dsl, and auto-generation of usage messages can
really streamline
command line application development. for example the following
'a.rb'
program:

require 'main'

Main {
argument('foo'){
cast :int
}
keyword('bar'){
arity 2
cast :float
defaults 0.0, 1.0
}
option('foobar'){
argument ptional
description 'the foobar option is very handy'
}
environment('BARFOO'){
cast :list_of_bool
synopsis 'export barfoo=value'
}

def run
p params['foo'].value
p params['bar'].values
p params['foobar'].value
p params['BARFOO'].value
end
}

when run with a command line of

BARFOO=true,false,false ruby a.rb 42 bar=40 bar=2 --foobar=a

will produce

42
[40.0, 2.0]
"a"
[true, false, false]

while a command line of

ruby a.rb --help

will produce

NAME
a.rb

SYNOPSIS
a.rb foo [bar=bar] [options]+

PARAMETERS
* foo [ 1 -> int(foo) ]

* bar=bar [ 2 ~> float(bar=0.0,1.0) ]

* --foobar=[foobar] [ 1 ~> foobar ]
the foobar option is very handy

* --help, -h

* export barfoo=value

and this shows how all of argument, keyword, option, and
environment parsing
can be declartively dealt with in a unified fashion - the dsl for all
parameter types is the same - and how auto synopsis and usage
generation saves
keystrokes. the parameter synopsis is compact and can be read as

* foo [ 1 -> int(foo) ]

'one argument will get processed via int(argument_name)'

1 : one argument
-> : will get processed (the argument is required)
int(foo) : the cast is int, the arg name is foo

* bar=bar [ 2 ~> float(bar=0.0,1.0) ]

'two keyword arguments might be processed via float
(bar=0.0,1.0)'

2 : two arguments
~> : might be processed (the argument is
optional)
float(bar=0.0,1.0) : the cast will be float, the default
values are
0.0 and 1.0

* --foobar=[foobar] [ 1 ~> foobar ]

'one option with optional argument may be given directly'

* --help, -h

no synopsis, simple switch takes no args and is not required

* export barfoo=value

a user defined synopsis

SAMPLES

<========< samples/a.rb >========>

~ > cat samples/a.rb

require 'main'

ARGV.replace %w( 42 ) if ARGV.empty?

Main {
argument('foo'){
required # this is the default
cast :int # value cast to Fixnum
validate{|foo| foo == 42} # raises error in failure case
description 'the foo param' # shown in --help
}

def run
p params['foo'].given?
p params['foo'].value
end
}

~ > ruby samples/a.rb

true
42

~ > ruby samples/a.rb --help

NAME
a.rb

SYNOPSIS
a.rb foo [options]+

PARAMETERS
foo (1 -> int(foo))
the foo param
--help, -h



<========< samples/b.rb >========>

~ > cat samples/b.rb

require 'main'

ARGV.replace %w( 40 1 1 ) if ARGV.empty?

Main {
argument('foo'){
arity 3 # foo will given three
times
cast :int # value cast to Fixnum
validate{|foo| [40,1].include? foo} # raises error in
failure case
description 'the foo param' # shown in --help
}

def run
p params['foo'].given?
p params['foo'].values
end
}

~ > ruby samples/b.rb

true
[40, 1, 1]

~ > ruby samples/b.rb --help

NAME
b.rb

SYNOPSIS
b.rb foo [options]+

PARAMETERS
foo (3 -> int(foo))
the foo param
--help, -h



<========< samples/c.rb >========>

~ > cat samples/c.rb

require 'main'

ARGV.replace %w( foo=40 foo=2 bar=false ) if ARGV.empty?

Main {
keyword('foo'){
required # by default keywords are not required
arity 2
cast :float
}
keyword('bar'){
cast :bool
}

def run
p params['foo'].given?
p params['foo'].values
p params['bar'].given?
p params['bar'].value
end
}

~ > ruby samples/c.rb

true
[40.0, 2.0]
true
false

~ > ruby samples/c.rb --help

NAME
c.rb

SYNOPSIS
c.rb foo=foo [bar=bar] [options]+

PARAMETERS
foo=foo (2 -> float(foo))
bar=bar (1 ~> bool(bar))
--help, -h



<========< samples/d.rb >========>

~ > cat samples/d.rb

require 'main'

ARGV.replace %w( --foo=40 -f2 ) if ARGV.empty?

Main {
option('foo', 'f'){
required # by default options are not required, we could
use 'foo=foo'
# above as a shortcut
argument_required
arity 2
cast :float
}

option('bar=[bar]', 'b'){ # note shortcut syntax for optional
args
# argument_optional # we could also use this method
cast :bool
default false
}

def run
p params['foo'].given?
p params['foo'].values
p params['bar'].given?
p params['bar'].value
end
}

~ > ruby samples/d.rb

true
[40.0, 2.0]
nil
false

~ > ruby samples/d.rb --help

NAME
d.rb

SYNOPSIS
d.rb --foo=foo [options]+

PARAMETERS
--foo=foo, -f (2 -> float(foo))
--bar=[bar], -b (1 ~> bool(bar=false))
--help, -h



DOCS
test/main.rb
vim -o lib/main.rb lib/main/*

API

Main {


################################################## ######################
###
# CLASS LEVEL
API #

################################################## ######################
###
#
# the name of the program, auto-set and used in usage
#
program 'foo.rb'
#
# a short description of program functionality, auto-set and used
in usage
#
synopsis "foo.rb arg [options]+"
#
# long description of program functionality, used in usage iff set
#
description <<-hdoc
this text will automatically be indented to the right level.

it should describe how the program works in detail
hdoc
#
# used in usage iff set
#
author '(E-Mail Removed)'
#
# used in usage
#
version '0.0.42'
#
# stdin/out/err can be anthing which responds to read/write or a
string
# which will be opened as in the appropriate mode
#
stdin '/dev/null'
stdout '/dev/null'
stderr open('/dev/null', 'w')
#
# the logger should be a Logger object, something 'write'-able, or
a string
# which will be used to open the logger. the logger_level
specifies the
# initalize verbosity setting, the default is Logger::INFO
#
logger(( program + '.log' ))
logger_level Logger:EBUG
#
# you can configure exit codes. the defaults are shown
#
exit_success # 0
exit_failure # 1
exit_warn # 42
#
# the usage object is rather complex. by default it's an object
which can
# be built up in sections using the
#
# usage["BUGS"] = "something about bugs'
#
# syntax to append sections onto the already pre-built usage
message which
# contains program, synopsis, parameter descriptions and the like
#
# however, you always replace the usage object wholesale with one
of your
# chosing like so
#
usage <<-txt
my own usage message
txt


################################################## ######################
###
# MODE
API #

################################################## ######################
###
#
# modes are class factories that inherit from their parent class.
they can
# be nested *arbitrarily* deep. usage messages are tailored for
each mode.
# modes are, for the most part, independant classes but parameters
are
# always a superset of the parent class - a mode accepts all of
it's parents
# paramters *plus* and additional ones
#
option 'inherited-option'
argument 'inherited-argument'

mode 'install' do
option 'force' do
description 'clobber existing installation'
end

def run
inherited_method()
puts 'installing...'
end

mode 'docs' do
description 'installs the docs'

def run
puts 'installing docs...'
end
end
end

mode 'un-install' do
option 'force' do
description 'remove even if dependancies exist'
end

def run
inherited_method()
puts 'un-installing...'
end
end

def run
puts 'no mode yo?'
end

def inherited_method
puts 'superclass_method...'
end



################################################## ######################
###
# PARAMETER
API #

################################################## ######################
###
#
# all the parameter types of argument|keyword|option|environment
share this
# api. you must specify the type when the parameter method is used.
# alternatively used one of the shortcut methods
# argument|keyword|option|environment. in otherwords
#
# parameter('foo'){ type ption }
#
# is synonymous with
#
# option('foo'){ }
#
option 'foo' {
#
# required - whether this paramter must by supplied on the
command line.
# note that you can create 'required' options with this keyword
#
required # or required true
#
# argument_required - applies only to options.
#
argument_required # argument :required
#
# argument_optional - applies only to options.
#
argument_optional # argument ptional
#
# cast - should be either a lambda taking one argument, or a symbol
# designation one of the built in casts defined in Main::Cast.
supported
# types are :boolean|:integer|:float|:numeric|:string|:uri.
built-in
# casts can be abbreviated
#
cast :int
#
# validate - should be a lambda taking one argument and returning
# true|false
#
validate{|int| int == 42}
#
# synopsis - should be a concise characterization of the
paramter. a
# default synopsis is built automatically from the parameter. this
# information is displayed in the usage message
#
synopsis '--foo'
#
# description - a longer description of the paramter. it
appears in the
# usage also.
#
description 'a long description of foo'
#
# arity - indicates how many times the parameter should appear
on the
# command line. the default is one. negative arities are
supported and
# follow the same rules as ruby methods/procs.
#
arity 2
#
# default - you can provide a default value in case none is
given. the
# alias 'defaults' reads a bit nicer when you are giving a list of
# defaults for paramters of > 1 arity
#
defaults 40, 2
#
# you can add custom per-parameter error handlers using the
following
#
error :before do
puts 'this fires *before* normal error handling using
#instance_eval...'
end

error do
puts 'this fires *instead of* normal error handling using
#instance_eval...'
end

error :after do
puts 'this fires *after* normal error handling using
#instance_eval...'
end
}


################################################## ######################
###
# INSTANCE LEVEL
API #

################################################## ######################
###
#
# you must define a run method. it is the only method you must
define.
#
def run
#
# all parameters are available in the 'params' hash and via
the alias
# 'param'. it can be indexed via string or symbol. the
values are all
# Main:arameter objects
#
foo = params['foo']
#
# the given? method indicates whether or not the parameter was
given on
# the commandline/environment, etc. in particular this will
not be true
# when a default value was specified but no parameter was given
#
foo.given?
#
# the list of all values can be retrieved via 'values'. note
that this
# is always an array.
#
p foo.values
#
# the __first__ value can be retrieved via 'value'. note that
this
# never an array.
#
p foo.value
#
# the methods debug|info|warn|error|fatal are delegated to the
logger
# object
#
info{ "this goes to the log" }
#
# you can set the exit_status at anytime. this status is used
when
# exiting the program. exceptions cause this to be
ext_failure if, and
# only if, the current value was exit_success. in otherwords an
# un-caught exception always results in a failing exit_status
#
exit_status exit_failure
#
# a few shortcuts both set the exit_status and exit the program.
#
exit_success!
exit_failure!
exit_warn!
end

}


enjoy.

a @ http://codeforpeople.com/
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama




 
Reply With Quote
 
 
 
 
Benjohn Barnes
Guest
Posts: n/a
 
      10-24-2007

On 24 Oct 2007, at 16:55, ara.t.howard wrote:

>
> NAME
> main.rb
>
> SYNOPSIS
> a class factory and dsl for generating command line programs real
> quick


*snip*

> DESCRIPTION
> main.rb features the following:
>
> - unification of option, argument, keyword, and environment
> parameter
> parsing


*snip*

Splendid! I'll take it, thanks

 
Reply With Quote
 
 
 
 
Jesús Gabriel y Galán
Guest
Posts: n/a
 
      10-25-2007
On 10/24/07, ara.t.howard <(E-Mail Removed)> wrote:
>
> NAME
> main.rb
>
> SYNOPSIS
> a class factory and dsl for generating command line programs real
> quick


I am using this for every medium to complex command line script I
write. Thanks and keep up the good work !!!!

Jesus.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off




Advertisments