[ANN] main-0.0.1 - command line apps for the truly lazy

A

Ara.T.Howard

NAME
main.rb

SYNOPSIS
a class factory and dsl for generating real main programs real quick

URI

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

INSTALL

$sudo gem install main

DESCRIPTION
main.rb is a library which simplifies and unifies the details of creating
command line programs. 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 which is always
accepted and handled appropriately: '--help', '-h'.

for simple programs this 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 :eek: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]
true
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
find lib|xargs -n1 vi -R

HISTORY
0.0.1

initial version. this version extracts much of the functionality of alib's
(gen install alib) Alib.script main program generator and also some of jim's
freeze's excellent CommandLine::Aplication into what i hope is a simpler and
more unified interface

-a
 
A

ara.t.howard

Excellent looking library. I have some fascination with writing CLI apps,
and have used the commandline gem extensively. My one complaint with that
library was the obscure syntax used for parsing command line options.

Can the options parsed be used outside the Main { } declaration? For small
scripts, the "run" method is fine, but for larger apps it becomes necessary
to break things up. Specifically, I am thinking of some highline driven apps
I have which would benefit from the option parsing provided here.

harp:~ > cat a.rb
require 'rubygems'
require 'main'

ARGV.replace %w( --foo=42 42.0 )

main = Main.new{
option('foo='){ cast :int }
option('bar='){ cast :float }
}


p main.param['foo']
p main.param['bar']


harp:~ > ruby a.rb
#<Main::parameter::Option:0xb74ce1b4 @given=true, @validate=nil, @cast=:int, @names=["foo"], @arity=1, @argument=:required, @required=false, @values=[42], @type=:eek:ption, @defaults=[]>
#<Main::parameter::Option:0xb74cdd18 @given=nil, @cast=:float, @names=["bar"], @argument=:required, @required=false, @type=:eek:ption, @defaults=[]>


i realize, as i post this, that i'd meant for one to be able to say

argv = %w( --foo=42 42.0 )

env = {'PATH' => '/usr/loca/bin'}

main = Main.new(argv, env){
option('foo='){ cast :int }
option('bar='){ cast :float }
}

but neglected to pass some information through. i'll think about it and ensure
that's possible v.s directly manipulating ARGV

this is quite a re-work of what's in alib, so i'm all ears for
comments/feature-requests. btw. the usage can be overridden simply with

Main{
usage 'my usage string'
}

regards.

-a
 
A

ara.t.howard

Could you make the main gem dependent on other gems? I had to manually
install attributes and arrayfields to get main to run. Otherwise, this is a
cool little library, thanks!

will do. it was an oversight. i'm making a few mods today and should release
later in the aftertoon.

cheers.

-a
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

ANN main-4.4.0 0
[ANN] main-4.0.0 (for avdi) 0
[ANN] main-2.8.3 2
[ANN] main-3.0.1 0
[ANN] main-0.0.2 5
[ANN] main-2.1.0 6
[ANN] main-2.6.0 0
[ANN] main-2.5.0 4

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top