This file is indexed.

/usr/share/perl5/App/Rad.pm is in libapp-rad-perl 1.05-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 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
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
package App::Rad;
use 5.006;
use App::Rad::Help;
use Carp                ();
use warnings;
use strict;

our $VERSION = '1.05';
{

#========================#
#   INTERNAL FUNCTIONS   #
#========================#

my @OPTIONS = ();

sub _init {
    my $c = shift;

    # instantiate references for the first time
    $c->{'_ARGV'    } = [];
    $c->{'_options'} = {};
    $c->{'_stash'  } = {};
    $c->{'_config' } = {};
    $c->{'_plugins'} = [];

    # this internal variable holds
    # references to all special
    # pre-defined control functions
    $c->{'_functions'} = {
        'setup'        => \&setup,
        'pre_process'  => \&pre_process,
        'post_process' => \&post_process,
        'default'      => \&default,
        'invalid'      => \&invalid,
        'teardown'     => \&teardown,
    };
    
    #load extensions
    App::Rad::Help->load($c);
    foreach (@OPTIONS) {
        if ($_ eq 'include') {
            eval 'use App::Rad::Include; App::Rad::Include->load($c)';
            Carp::croak 'error loading "include" extension.' if ($@);
        }
        elsif ($_ eq 'exclude') {
            eval 'use App::Rad::Exclude; App::Rad::Exclude->load($c)';
            Carp::croak 'error loading "exclude" extension.' if ($@);
        }
        elsif ($_ eq 'debug') {
            $c->{'debug'} = 1;
        }
        else {
        	$c->load_plugin($_);
        }
	}
	
    # tiny cheat to avoid doing a lot of processing
    # when not in debug mode. If needed, I'll create
    # an actual is_debugging() method or something
    if ($c->{'debug'}) {
        $c->debug('initializing: default commands are: '
            . join ( ', ', $c->commands() )
        );
    }
}

sub import {
    my $class = shift;
    @OPTIONS  = @_;
}

sub load_plugin {
    my $c      = shift;
    my $plugin = shift;
	my $class  = ref $c;

    my $plugin_fullname = '';    
	if ($plugin =~ s{^\+}{} ) {
		$plugin_fullname = $plugin;
	}
    else {
        $plugin_fullname = "App::Rad::Plugin::$plugin";
    }
	eval "use $plugin_fullname ()";
    Carp::croak "error loading plugin '$plugin_fullname': $@\n"
        if $@;
    my %methods = _get_subs_from($plugin_fullname);

    Carp::croak "No methods found for plugin '$plugin_fullname'\n"
		unless keys %methods > 0;

	no strict 'refs';
	foreach my $method (keys %methods) {
        # don't add plugin's internal methods
        next if substr ($method, 0, 1) eq '_';

		*{"$class\::$method"} = $methods{$method};
		$c->debug("-- method '$method' added [$plugin_fullname]");

        # fill $c->plugins()
        push @{ $c->{'_plugins'} }, $plugin;
	}
}

# this function browses a file's
# symbol table (usually 'main') and maps
# each function to a hash
#
# FIXME: if I create a sub here (Rad.pm) and
# there is a global variable with that same name
# inside the user's program (e.g.: sub ARGV {}),
# the name will appear here as a command. It really 
# shouldn't...
sub _get_subs_from {
    my $package = shift || 'main';
    $package .= '::';
    
    my %subs = ();

    no strict 'refs';
    while (my ($key, $value) = ( each %{*{$package}} )) {
        local (*SYMBOL) = $value;
        if ( defined $value && defined *SYMBOL{CODE} ) {
            $subs{$key} = $value;
        }
    }
    return %subs;
}


# overrides our pre-defined control
# functions with any available
# user-defined ones
sub _register_functions {
    my $c = shift;
    my %subs = _get_subs_from('main');

    # replaces only if the function is
    # in 'default', 'pre_process' or 'post_process'
    foreach ( keys %{$c->{'_functions'}} ) {
        if ( defined $subs{$_} ) {
            $c->debug("overriding $_ with user-defined function.");
            $c->{'_functions'}->{$_} = $subs{$_};
        }
    }
}

# retrieves command line arguments
# to be executed by the main program
sub _get_input {
    my $c = shift;

    my $cmd = (defined ($ARGV[0]) and substr($ARGV[0], 0, 1) ne '-')
            ? shift @ARGV
            : ''
            ;

    @{$c->argv} = @ARGV;
    $c->{'cmd'} = $cmd;

    $c->debug('received command: ' . $c->{'cmd'});
    $c->debug('received parameters: ' . join (' ', @{$c->argv} ));

    $c->_tinygetopt();
}

# stores arguments passed to a
# command via --param[=value] or -p
sub _tinygetopt {
    my $c = shift;

    my @argv = ();
    foreach ( @{$c->argv} ) {

        # single option (could be grouped)
        if ( m/^\-([^\-\=]+)$/o) {
            my @args = split //, $1;
            foreach (@args) {
				if ($c->options->{$_}) {
					$c->options->{$_}++;
				}
				else {
					$c->options->{$_} = 1;
				}
            }
        }
        # long option: --name or --name=value
        elsif (m/^\-\-([^\-\=]+)(?:\=(.+))?$/o) {
            $c->options->{$1} = defined $2 ? $2 
			                  : 1
                              ;
        }
        else {
            push @argv, $_;
        }
    }
    @{$c->argv} = @argv;
}


#========================#
#     PUBLIC METHODS     #
#========================#

sub load_config {
    require App::Rad::Config;
    App::Rad::Config::load_config(@_);
}


#TODO: this code probably could use some optimization
sub register_commands {
    my $c = shift;
    my %help_for_sub = ();
    my %rules = ();

    # process parameters
    foreach my $item (@_) {
        if ( ref ($item) ) {
            Carp::croak '"register_commands" may receive only HASH references'
                unless ref ($item) eq 'HASH';
            foreach my $params (keys %{$item}) {
                if ($params eq '-ignore_prefix'
                 or $params eq '-ignore_suffix'
                 or $params eq '-ignore_regexp'
                ) {
                    $rules{$params} = $item->{$params};
                }
                else {
                    $help_for_sub{$params} = $item->{$params};
                }
            }
        }
        else {
            $help_for_sub{$item} = undef; # no help text
        }
    }

    my %subs = _get_subs_from('main');

    foreach (keys %help_for_sub) {

        # we only add the sub to the commands
        # list if it's *not* a control function
        if ( not defined $c->{'_functions'}->{$_} ) {

            # user want to register a valid (existant) sub
            if ( exists $subs{$_} ) {
                $c->debug("registering $_ as a command.");
                $c->{'_commands'}->{$_}->{'code'} = $subs{$_};
                App::Rad::Help->register_help($c, $_, $help_for_sub{$_});
            }
            else {
                Carp::croak "'$_' does not appear to be a valid sub. Registering seems impossible.\n";
            }
        }
    }

    # no parameters, or params+rules: try to register everything
    if ((!%help_for_sub) or %rules) {
        foreach my $subname (keys %subs) {

            # we only add the sub to the commands
            # list if it's *not* a control function
            if ( not defined $c->{'_functions'}->{$subname} ) {

                if ( $rules{'-ignore_prefix'} ) {  
                    next if ( substr ($subname, 0, length($rules{'-ignore_prefix'}))
                           eq $rules{'-ignore_prefix'}
                            );
                }
                if ( $rules{'-ignore_suffix'} ) {
                    next if ( substr ($subname, 
                                      length($subname) - length($rules{'-ignore_suffix'}),
                                      length($rules{'-ignore_suffix'})
                                     )
                              eq $rules{'-ignore_suffix'}
                            );
                }
                if ( $rules{'-ignore_regexp'} ) {
                    my $re = $rules{'-ignore_regexp'};
                    next if $subname =~ m/$re/o;
                }

                # avoid duplicate registration
                if ( !exists $help_for_sub{$subname} ) {
                    $c->{'_commands'}->{$subname}->{'code'} = $subs{$subname};
                    App::Rad::Help->register_help($c, $subname, undef);
                }
            }
        }
    }
}


sub register_command { return register(@_) }
sub register {
    my ($c, $command_name, $coderef, $helptext) = @_;
    $c->debug("got: " . ref $coderef);
    return undef
        unless ( (ref $coderef) eq 'CODE' );

    $c->debug("registering $command_name as a command.");
    $c->{'_commands'}->{$command_name}->{'code'} = $coderef;
    App::Rad::Help->register_help($c, $command_name, $helptext);
    return $command_name;
}

sub unregister_command { return unregister(@_) }
sub unregister {
    my ($c, $command_name) = @_;

    if ( $c->{'_commands'}->{$command_name} ) {
        delete $c->{'_commands'}->{$command_name};
    }
    else {
        return undef;
    }
}


sub create_command_name {
    my $id = 0;
    foreach (commands()) {
        if ( m/^cmd(\d+)$/ ) {
            $id = $1 if ($1 > $id);
        }
    }
    return 'cmd' . ($id + 1);
}


sub commands {
    return ( keys %{$_[0]->{'_commands'}} );
}


sub is_command {
    my ($c, $cmd) = @_;
    return (defined $c->{'_commands'}->{$cmd}
            ? 1
            : 0
           );
}

sub command :lvalue { cmd(@_) }
sub cmd :lvalue {
    $_[0]->{'cmd'};
}


sub run {
    my $class = shift;
    my $c = {};
    bless $c, $class;

    $c->_init();

    # first we update the control functions
    # with any overriden value
    $c->_register_functions();

    # then we run the setup to register
    # some commands
    $c->{'_functions'}->{'setup'}->($c);

    # now we get the actual input from
    # the command line (someone using the app!)
    $c->_get_input();

    # run the specified command
    $c->execute();

    # that's it. Tear down everything and go home :)
    $c->{'_functions'}->{'teardown'}->($c);

    return 0;
}

# run operations 
# in a shell-like environment
#sub shell {
#    my $class = shift;
#    App::Rad::Shell::shell($class);
#}

sub execute {
    my ($c, $cmd) = @_;

    # given command has precedence
    if ($cmd) {
        $c->{'cmd'} = $cmd;
    }
    else {
        $cmd = $c->{'cmd'};  # now $cmd always has the called cmd
    }

    $c->debug('calling pre_process function...');
    $c->{'_functions'}->{'pre_process'}->($c);

    $c->debug("executing '$cmd'...");

    # valid command, run it
    if ($c->is_command($c->{'cmd'}) ) {
        $c->{'output'} = $c->{'_commands'}->{$cmd}->{'code'}->($c);
    }
    # no command, run default()
    elsif ( $cmd eq '' ) {
        $c->debug('no command detected. Falling to default');
        $c->{'output'} = $c->{'_functions'}->{'default'}->($c);
    }
    # invalid command, run invalid()
    else {
        $c->debug("'$cmd' is not a valid command. Falling to invalid.");
        $c->{'output'} = $c->{'_functions'}->{'invalid'}->($c);
    }

    # 3: post-process the result
    # from the command
    $c->debug('calling post_process function...');
    $c->{'_functions'}->{'post_process'}->($c);

    $c->debug('reseting output');
    $c->{'output'} = undef;
}

sub argv    { return $_[0]->{'_ARGV'}     }
sub options { return $_[0]->{'_options'} }
sub stash   { return $_[0]->{'_stash'}   }   
sub config  { return $_[0]->{'_config'}  }

# $c->plugins is sort of "read-only" externally
sub plugins { 
    my @plugins = @{$_[0]->{'_plugins'}};
    return @plugins;
}


sub getopt {
    require Getopt::Long;
    Carp::croak "Getopt::Long needs to be version 2.36 or above"
        unless $Getopt::Long::VERSION >= 2.36;

    my ($c, @options) = @_;

    # reset values from tinygetopt
    $c->{'_options'} = {};

    my $parser = new Getopt::Long::Parser;
    $parser->configure( qw(bundling) );

    my @tARGV = @ARGV; # we gotta stick to our API
    my $ret = $parser->getoptions($c->{'_options'}, @options);
    @{$c->argv} = @ARGV;
    @ARGV = @tARGV;

    return $ret;
}

sub debug {
    if (shift->{'debug'}) {
        print "[debug]   @_\n";
    }
}

# gets/sets the output (returned value)
# of a command, to be post processed
sub output {
    my ($c, @msg) = @_;
    if (@msg) {
        $c->{'output'} = join(' ', @msg);
    }
    else {
        return $c->{'output'};
    }
}


#=========================#
#     CONTROL FUNCTIONS   #
#=========================#

sub setup { $_[0]->register_commands( {-ignore_prefix => '_'} ) }

sub teardown {}

sub pre_process {}

sub post_process {
    my $c = shift;

    if ($c->output()) {
        print $c->output() . $/;
    }
}


sub default {
    my $c = shift;
    return $c->{'_commands'}->{'help'}->{'code'}->($c);
}


sub invalid {
    my $c = shift;
    return $c->{'_functions'}->{'default'}->($c);
}


}
42; # ...and thus ends thy module  ;)
__END__

=head1 NAME

App::Rad - Rapid (and easy!) creation of command line applications

=head1 VERSION

Version 1.04

=head1 SYNOPSIS

This is your smallest working application (let's call it I<myapp.pl>)

    use App::Rad;
    App::Rad->run();

That's it, your program already works and you can use it directly via the command line (try it!)

    [user@host]$ ./myapp.pl
    Usage: myapp.pl command [arguments]
    
    Available Commands:
        help    show syntax and available commands

Next, start creating your own functions (e.g.) inside I<myapp.pl>:

    sub hello {
        return "Hello, World!";
    }

And now your simple command line program I<myapp.pl> has a 'hello' command!

    [user@host]$ ./myapp.pl
    Usage: myapp.pl command [arguments]
    
    Available Commands:
        hello
        help    show syntax and available commands


   [user@host]$ ./myapp.pl hello
   Hello, World!

You could easily add a customized help message for your command through the 'Help()' attribute:

    sub hello 
    :Help(give a nice compliment)
    {
        return "Hello, World!";
    }

And then, as expected:

    [user@host]$ ./myapp.pl
    Usage: myapp.pl command [arguments]
    
    Available Commands:
        hello   give a nice compliment
        help    show syntax and available commands


App::Rad also lets you expand your applications, providing a lot of flexibility for every command, with embedded help, argument and options parsing, configuration file, default behavior, and much more:

    use App::Rad;
    App::Rad->run();

    sub setup {
        my $c = shift;

        $c->register_commands( {
                foo => 'expand your foo!',
                bar => 'have a drink! arguments: --drink=DRINK',
            });
    }

    sub foo {
        my $c = shift;
        $c->load_config('myapp.conf');

        return 'foo expanded to ' . baz() * $c->config->{'myfoo'};
    }

    # note that 'baz' was not registered as a command,
    # so it can't be called from the outside.
    sub baz { rand(10) }

    sub bar {
        my $c = shift;
        if ( $c->options->{'drink'} ) {
            return 'you asked for a ' . $c->options->{'drink'};
        }
        else {
            return 'you need to ask for a drink';
        }
    }

        

You can try on the command line:

   [user@host]$ ./myapp.pl
    Usage: myapp.pl command [arguments]
    
    Available Commands:
        bar 	have a drink! arguments: --drink=DRINK
        foo 	expand your foo!
        help	show syntax and available commands


   [user@host]$ ./myapp.pl bar --drink=martini
    you asked for a martini


=head1 WARNING

This module is very young, likely to change in strange ways and to have some bugs (please report if you find any!). I will try to keep the API stable, but even that is subject to change (let me know if you find anything annoying or have a wishlist). You have been warned!


=head1 DESCRIPTION

App::Rad aims to be a simple yet powerful framework for developing your command-line applications. It can easily transform your Perl I<one-liners> into reusable subroutines than can be called directly by the user of your program.

It also tries to provide a handy interface for your common command-line tasks. B<If you have a feature request to easen out your tasks even more, please drop me an email or a RT feature request.>

=head2 Extending App::Rad - Plugins!

App::Rad plugins can be loaded by naming them as arguments to the C<< use App::Rad >> statement. Just ommit the C<< App::Rad::Plugin >> prefix from the plugin name. For example:

   use App::Rad  qw(My::Module);

will load the C<< App::Rad::Plugin::My::Module >> plugin for you!

Developers are B<strongly> encouraged to publish their App::Rad plugins under the C<< App::Rad::Plugin >> namespace. But, if your plugin start with a name other than that, you can fully qualify the name by using an unary plus sign:

  use App::Rad  qw(
          My::Module
          +Fully::Qualified::Plugin::Name
  );

Note that plugins are loaded in the order in which they appear.

B<Please refer to the actual plugin documentation for specific usage>. And check out L<< App::Rad::Plugin >> if you want to create your own plugins.


=head1 INSTANTIATION

These are the main execution calls for the application. In your App::Rad programs, the B<*ONLY*> thing your script needs to actually (and actively) call is one of the instantiation (or dispatcher) methods. Leave all the rest to your subs. Currently, the only available dispatcher is run():

=head2 run()

You'll be able to access all of your program's commands directly through the command line, as shown in the synopsis.


=head1 BUILT-IN COMMANDS

This module comes with the following default commands. You are free to override them as you see fit.


=head2 help

Shows help information for your program. This built-in function displays the program name and all available commands (including the ones you added yourself) if a user types the 'help' command, or no command at all, or any command that does not exist (as they'd fall into the 'default' control function which (by default) calls 'help').

You can also display specific embedded help for your commands, either explicitly registering them with C<< $c->register() >> or C<< $c->register_commands() >> inside C<< $c->setup() >> (see respective sections below) or with the Help() attribute:

    use App::Rad;
    App::Rad->run();
    
    sub mycmd 
    :Help(display a nice welcome message) 
    {
        return "Welcome!";
    }

the associated help text would go like this:

    [user@host]$ ./myapp.pl
    Usage: myapp.pl command [arguments]

    Available Commands:
        help 	show syntax and available commands
        mycmd	display a nice welcome message
    

=head1 OTHER BUILT IN COMMANDS (OPT-IN)

The 'include' and 'exclude' commands below let the user include and exclude commands to your program and, as this might be dangerous when the user is not yourself, you have to opt-in on them:

   use App::Rad qw(include);  # add the 'include' command
   use App::Rad qw(exclude);  # add the 'exclude' command

though you'll probably want to set them both:

   use App::Rad qw(include exclude);


=head2 include I<[command_name]> I<-perl_params> I<'your subroutine code'>

Includes the given subroutine into your program on-the-fly, just as you would writing it directly into your program.

Let's say you have your simple I<'myapp.pl'> program that uses App::Rad sitting on your system quietly. One day, perhaps during your sysadmin's tasks, you create a really amazing one-liner to solve a really hairy problem, and want to keep it for posterity (reusability is always a good thing!). 

For instance, to change a CSV file in place, adding a column on position #2 containing the line number, you might do something like this (this is merely illustrative, it's not actually the best way to do it):

    $ perl -i -paF, -le 'splice @F,1,0,$.; $_=join ",",@F' somesheet.csv

And you just found out that you might use this other times. What do you do? App::Rad to the rescue!

In the one-liner above, just switch I<'perl'> to I<'myapp.pl include SUBNAME'> and remove the trailing parameters (I<somesheet.csv>):

    $ myapp.pl include addcsvcol -i -paF, -le 'splice @F,1,0,$.; $_=join ",",@F'

That's it! Now myapp.pl has the 'addcsvcol' command (granted, not the best name) and you can call it directly whenever you want:

    $ myapp.pl addcsvcol somesheet.csv

App::Rad not only transforms and adjusts your one-liner so it can be used inside your program, but also automatically formats it with Perl::Tidy (if you have it). This is what the one-liner above would look like inside your program:

    sub addcsvcol {
        my $c = shift;
    
        local ($^I) = "";
        local ($/)  = "\n";
        local ($\)  = "\n";
      LINE: while ( defined( $_ = <ARGV> ) ) {
            chomp $_;
            our (@F) = split( /,/, $_, 0 );
            splice @F, 1, 0, $.;
            $_ = join( ',', @F );
        }
        continue {
            die "-p destination: $!\n" unless print $_;
        }
    }

With so many arguments (-i, -p, -a -F,, -l -e), this is about as bad as it gets. And still one might find this way easier to document and mantain than a crude one-liner stored in your ~/.bash_history or similar.

B<Note:> If you don't supply a name for your command, App::Rad will make one up for you (cmd1, cmd2, ...). But don't do that, as you'll have a hard time figuring out what that specific command does.

B<Another Note: App::Rad tries to adjust the command to its interface, but please keep in mind this module is still in its early stages so it's not guaranteed to work every time. *PLEASE* let me know via email or RT bug request if your one-liner was not correctly translated into an App::Rad command. Thanks!>


=head2 exclude I<command_name>

Removes the requested function from your program. Note that this will delete the actual code from your program, so be *extra* careful. It is strongly recommended that you do not use this command and either remove the subroutine yourself or add the function to your excluded list inside I<setup()>.

Note that built-in commands such as 'help' cannot be removed via I<exclude>. They have to be added to your excluded list inside I<setup()>.



=head1 ROLLING YOUR OWN COMMANDS

Creating a new command is as easy as writing any sub inside your program. Some names ("setup", "default", "invalid", "pre_process", "post_process" and "teardown") are reserved for special purposes (see the I<Control Functions> section of this document). App::Rad provides a nice interface for reading command line input and writing formatted output:


=head2 The Controller

Every command (sub) you create receives the controller object "C<< $c >>" (sometimes referred as "C<< $self >>" in other projects) as an argument. The controller is the main interface to App::Rad and has several methods to easen your command manipulation and execution tasks.


=head2 Reading arguments

When someone types in a command, she may pass some arguments to it. Those arguments can be accessed in four different ways, depending on what you want. This way it's up to you to control which and how many arguments (if at all) you want to receive and/or use. They are:

=head3 @ARGV

Perl's @ARGV array has all the arguments passed to your command, without the command name (use C<< $c->cmd >> for this) and without any processing (even if you explicitly use C<< $c->getopt >>, which will change $c->argv instead, see below). Since the command itself won't be in the @ARGV parameters, you can use it in each command as if they were stand-alone programs.

=head3 $c->options

App::Rad lets you automatically retrieve any POSIX syntax command line options (I<getopt-style>) passed to your command via the $c->options method. This method returns a hash reference with keys as given parameters and values as... well... values. The 'options' method automatically supports two simple argument structures:

Extended (long) option. Translates C<< --parameter or --parameter=value >> into C<< $c->options->{parameter} >>. If no value is supplied, it will be set to 1.

Single-letter option. Translates C<< -p >> into C<< $c->options->{p} >>.

Single-letter options can be nested together, so C<-abc> will be parsed into C<< $c->options->{a} >>, C<< $c->options->{b} >> and C<< $c->options{c} >>, while C<--abc> will be parsed into C<< $c->options->{abc} >>. We could, for instance, create a dice-rolling command like this:

    sub roll {
        my $c = shift;

        my $value = 0;
        for ( 1..$c->options->{'times'} ) {
            $value += ( int(rand ($c->options->{'faces'}) + 1));
        }
        return $value;
    }

And now you can call your 'roll' command like:

    [user@host]$ ./myapp.pl roll --faces=6 --times=2

Note that App::Rad does not control which arguments can or cannot be passed: they are all parsed into C<< $c->options >> and it's up to you to use whichever you want. For a more advanced use and control, see the C<< $c->getopt >> method below.

Also note that single-letter options will be set to 1. However, if a user types them more than once, the value will be incremented accordingly. For example, if a user calls your program like so:

   [user@host]$ ./myapp.pl some_command -vvv

or

   [user@host]$ ./myapp.pl some_command -v -v -v

then, in both cases, C<< $c->options->{v} >> will be set to 3. This will let you easily keep track of how many times any given option was chosen, and still let you just check for definedness if you don't care about that. 


=head3 $c->argv

The array reference C<< $c->argv >> contains every argument passed to your command that have B<not> been parsed into C<< $c->options >>. This is usually a list of every provided argument that didn't start with a dash (-), unless you've called C<< $c->getopt >> and used something like 'param=s' (again, see below).

=head3 $c->getopt (Advanced Getopt usage)

App::Rad is also smoothly integrated with Getopt::Long, so you can have even more flexibility and power while parsing your command's arguments, such as aliases and types. Call the C<< $c->getopt() >> method anytime inside your commands (or just once in your "pre_process" function to always have the same interface) passing a simple array with your options, and refer back to $c->options to see them. For instance: 

    sub roll {
        my $c = shift;

        $c->getopt( 'faces|f=i', 'times|t=i' )
            or $c->execute('usage') and return undef;

        # and now you have $c->options->{'faces'} 
        # and $c->options->{'times'} just like above.
    }

This becomes very handy for complex or feature-rich commands. Please refer to the Getopt::Long module for more usage examples.


B<< So, in order to manipulate and use any arguments, remember: >>

=over 6

=item * The given command name does not appear in the argument list;

=item * All given arguments are in C<< @ARGV >>

=item * Automatically processed arguments are in C<< $c->options >>

=item * Non-processed arguments (the ones C<< $c->options >> didn't catch) are in $c->argv

=item * You can use C<< $c->getopt >> to have C<< Getopt::Long >> parse your arguments (it will B<not> change C<< @ARGV >>)

=back


=head2 Sharing Data: C<< $c->stash >>

The "stash" is a universal hash for storing data among your Commands:

    $c->stash->{foo} = 'bar';
    $c->stash->{herculoids} = [ qw(igoo tundro zok gloop gleep) ];
    $c->stash->{application} = { name => 'My Application' };

You can use it for more granularity and control over your program. For instance, you can email the output of a command if (and only if) something happened:

    sub command {
        my $c = shift;
        my $ret = do_something();

        if ( $ret =~ /critical error/ ) {
            $c->stash->{mail} = 1;
        }
        return $ret;
    }

    sub post_process {
        my $c = shift;

        if ( $c->stash->{mail} ) {
            # send email alert...
        }
        else {
            print $c->output . "\n";
        }
    }



=head2 Returning output

Once you're through, return whatever you want to give as output for your command:

    my $ret = "Here's the list: ";
    $ret .= join ', ', 1..5;
    return $ret;
    
    # this prints "Here's the list: 1, 2, 3, 4, 5"

App::Rad lets you post-process the returned value of every command, so refrain from printing to STDOUT directly whenever possible as it will give much more power to your programs. See the I<post_process()> control function further below in this document.


=head1 HELPER METHODS

App::Rad's controller comes with several methods to help you manage your application easily. B<If you can think of any other useful command that is not here, please drop me a line or RT request>.


=head2 $c->execute( I<COMMAND_NAME> )

Runs the given command. If no command is given, runs the one stored in C<< $c->cmd >>. If the command does not exist, the 'default' command is ran instead. Each I<execute()> call also invokes pre_process and post_process, so you can easily manipulate income and outcome of every command.


=head2 $c->cmd

Returns a string containing the name of the command (that is, the first argument of your program), that will be called right after pre_process.


=head3 $c->command

Alias for C<< $c->cmd >>. This longer form is discouraged and may be removed in future versions, as one may confuse it with the C<< $c->commands() >> method, explained below. You have been warned.


=head2 $c->commands()

Returns a list of available commands (I<functions>) inside your program


=head2 $c->is_command ( I<COMMAND_NAME> )

Returns 1 (true) if the given I<COMMAND_NAME> is available, 0 (false) otherwise.


=head2 $c->create_command_name()

Returns a valid name for a command (i.e. a name slot that's not been used by your program). This goes in the form of 'cmd1', 'cmd2', etc., so don't use unless you absolutely have to. App::Rad, for instance, uses this whenever you try to I<include> (see below) a new command but do not supply a name for it.


=head2 $c->load_config( I<< FILE (FILE2, FILE3, ...) >> )

This method lets you easily load into your program one or more configuration files written like this:

    # comments and blank lines are discarded
    key1 value1
    key2:value2
    key3=value3
    key5           # stand-alone attribute (and inline-comment)


=head2 $c->config

Returns a hash reference with any loaded config values (see C<< $c->load_config() >> above).


=head2 $c->register ( I<NAME>, I<CODEREF> [, I<INLINE_HELP> ])

Registers a coderef as a callable command. Note that you don't have to call this in order to register a sub inside your program as a command, run() will already do this for you - and if you don't want some subroutines to be issued as commands you can always use C<< $c->register_commands() >> (note the plural) inside setup(). This is just an interface to dinamically include commands in your programs. The function returns the command name in case of success, undef otherwise.

It is also very useful for creating aliases for your commands:

    sub setup {
        my $c = shift;
        $c->register_commands();

        $c->register('myalias', \&command);
    }

    sub command { return "Hi!" }

and, on the command line:

    [user@host]$ ./myapp.pl command
    Hi!

    [user@host]@ ./myapp.pl myalias
    Hi!

The last parameter is optional and lets you add inline help to your command:

    $c->register('cmd_name', \&cmd_func, 'display secret of life');

=head3 $c->register_command ( I<NAME>, I<CODEREF> [, I<INLINE_HELP> ] )

Longer alias for C<< $c->register() >>. It's use is disencouraged as one may confuse it with C<register_commands> (note the plural) below. Plus you type more :)
As such, this method may be removed in future versions. You have been warned!

=head2 $c->register_commands()

This method, usually called during setup(), tells App::Rad to register subroutines as valid commands. If called without any parameters, it will register B<all> subroutines in your main program as valid commands (note that the default behavior of App::Rad is to ignore subroutines starting with an underscore '_'). You can easily change this behavior using some of the options below:

=head3 Adding single commands

    $c->register_commands( qw/foo bar baz/ );

The code above will register B<only> the subs C<foo>, C<bar> and C<baz> as commands. Other subroutines will B<not> be valid commands, so they can be used as internal subs for your program. You can change this behavior with the bundled options - see 'Adding several commands' and 'Putting it all together' below.

=head3 Adding single commands (with inline help)

    $c->register_commands(
            {
                dos2unix => 'convert text files from DOS to Unix format',
                unix2dos => 'convert text files from Unix to DOS format',
            }
    );

You can pass a hash reference containing commands as keys and a small help string as their values. The code above will register B<only> the subs C<dos2unix> and C<unix2dos>, and the default help for your program will become something like this:

    [user@host]$ ./myapp.pl
    Usage: myapp.pl command [arguments]
    
    Available Commands:
        dos2unix    convert text files from DOS to Unix format
        help        show syntax and available commands
        unix2dos    convert text files from Unix to DOS format


=head3 Adding several commands

You can pass a hash reference as an argument, letting you choose which subroutines to add as commands. The following keys may be used (note the dash preceding each key):

=over 4

=item * C<< -ignore_prefix >>: subroutine names starting with the given string won't be added as commands

=item * C<< -ignore_suffix >>: subroutine names ending with the given string won't be added as commands

=item * C<< -ignore_regexp >>: subroutine names matching the given regular expression (as a string) won't be added as commands

=back

For example:

    use App::Rad;
    App::Rad->run();

    sub setup { 
        my $c = shift; 
        $c->register_commands( { -ignore_prefix => '_' } );
    }

    sub foo  {}  # will become a command
    sub bar  {}  # will become a command
    sub _baz {}  # will *NOT* become a command

This way you can easily segregate between commands and helper functions, making your code even more reusable without jeopardizing the command line interface (As of version 1.04, ignoring commands with underscore '_' prefixes is also the default App::Rad behavior).


=head3 Putting it all together

You can combine some of the options above to have even more flexibility:

    $c->register_commands(
            'foo',
            { -ignore_suffix => 'foo' },
            { bar => 'all your command line are belong to us' },
    );

The code above will register as commands all subs with names B<not> ending in 'foo', but it B<will> register the 'foo' sub as well. It will also give the 'bar' command the help string. This behavior is handy for registering several commands and having a few exceptions, or to add your commands and only have inline help for a few of them (as you see fit).

You don't have to worry about the order of your elements passed, App::Rad will figure them out for you in a DWIM fashion.

    # this does the same as the code above
    $c->register_commands(
            { bar => 'all your command line are belong to us' },
            'foo',
            { -ignore_suffix => 'foo' },
    );

You can even bundle the hash reference to include your C<< cmd => help >> and special keys:

    # this behaves the same way as the code above:
    $c->register_commands(
        'foo',
        { 
            -ignore_suffix => 'foo',
            bar => 'all your command line are belong to us',
        }
    );


=head2 $c->unregister_command ( I<NAME> )

Longer alias for C<< $c->unregister() >>. The use of the shorter form is encouraged, and this alias may be removed in future versions. You have been warned.


=head3 $c->unregister ( I<NAME> )

Unregisters a given command name so it's not available anymore. Note that the subroutine will still be there to be called from inside your program - it just won't be accessible via command line anymore.


=head2 $c->debug( I<MESSAGE> )

Will print the given message on screen only if the debug flag is enabled:

    use App::Rad  qw( debug );

Note that, if debug is enabled, App::Rad itself will print several debug messages stating its current flow, so you can easily find out where everything is happening.


=head2 $c->plugins()

Returns a list of all loaded plugins, in the order in which they were loaded.


=head2 $c->load_plugin( I<PLUGIN NAME> )

This method will dinamically load the given plugin. The plugin needs to be under the C<< App::Rad::Plugin >> namespace, and the name should be relative to this path (i.e. $c->load_plugin('MyPlugin') will try to load 'App::Rad::Plugin::MyPlugin'). If you want to load a plugin by its fully qualified name, you need to prepend a plus sign to the name ('+Fully::Qualified::Plugin::Name'). B<This is an internal method> and you really should refrain from using it. Instead, plugins should be loaded as parameters to the C<< use App::Rad >> statement, as explained above.


=head1 CONTROL FUNCTIONS (to possibly override)

App::Rad implements some control functions which are expected to be overridden by implementing them in your program. They are as follows:

=head2 setup()

This function is responsible for setting up what your program can and cannot do, plus everything you need to set before actually running any command (connecting to a database or host, check and validate things, download a document, whatever). Note that, if you override setup(), you B<< *must* >> call C<< $c->register_commands() >> or at least C<< $c->register() >> so your subs are classified as valid commands (check $c->register_commands() above for more information).

Another interesting thing you can do with setup is to manipulate the command list. For instance, you may want to be able to use the C<include> and C<exclude> commands, but not let them available for all users. So instead of writing:

    use App::Rad qw(include exclude);
    App::Rad->run();

you can write something like this:

    use App::Rad;
    App::Rad->run();

    sub setup {
        my $c = shift;
        $c->register_commands();

        # EUID is 'root'
        if ( $> == 0 ) {
            $c->register('include', \&App::Rad::include);
            $c->register('exclude', \&App::Rad::exclude);
        }
    }

to get something like this:

    [user@host]$ myapp.pl help
    Usage: myapp.pl command [arguments]

    Available Commands:
       help

    [user@host]$ sudo myapp.pl help
    Usage: myapp.pl command [arguments]

    Available Commands:
       exclude
       help
       include



=head2 default()

If no command is given to your application, it will fall in here. Please note that invalid (non-existant) command will fall here too, but you can change this behavior with the invalid() function below (although usually you don't want to).

Default's default (grin) is just an alias for the help command.

    sub default {
        my $c = shift;

        # will fall here if the given
        # command isn't valid.
    }

You are free (and encouraged) to change the default behavior to whatever you want. This is rather useful for when your program will only do one thing, and as such it receives only parameters instead of command names. In those cases, use the "C<< default() >>" sub as your main program's sub and parse the parameters with C<< $c->argv >> and C<< $c->getopt >> as you would in any other command.

=head2 invalid()

This is a special function to provide even more flexibility while creating your command line applications. This is called when the user requests a command that does not exist. The built-in C<< invalid() >> will simply redirect itself to C<< default() >> (see above), so usually you just have to worry about this when you want to differentiate between "no command given" (with or without getopt-like arguments) and "invalid command given" (with or without getopt-like arguments).

=head2 teardown()

If implemented, this function is called automatically after your application runs. It can be used to clean up after your operations, removing temporary files, disconnecting a database connection established in the setup function, logging, sending data over a network, or even storing state information via Storable or whatever.


=head2 pre_process()

If implemented, this function is called automatically right before the actual wanted command is called. This way you have an optional pre-run hook, which permits functionality to be added, such as preventing some commands to be run from a specific uid (e.g. I<root>): 

    sub pre_process {
        my $c = shift;

        if ( $c->cmd eq 'some_command' and $> != 0 ) {
            $c->cmd = 'default'; # or some standard error message
        }
    }
    

=head2 post_process()

If implemented, this function is called automatically right after the requested function returned. It receives the Controller object right after a given command has been executed (and hopefully with some output returned), so you can manipulate it at will. In fact, the default "post_process" function is as goes:

    sub post_process {
        my $c = shift;

        if ( $c->output() ) {
            print $c->output() . "\n";
        }
    }

You can override this function to include a default header/footer for your programs (either a label or perhaps a "Content-type: " string), parse the output in any ways you see fit (CPAN is your friend, as usual), etc.



=head1 IMPORTANT NOTE ON PRINTING INSIDE YOUR COMMANDS

B<The post_process() function above is why your application should *NEVER* print to STDOUT>. Using I<print> (or I<say>, in 5.10) to send output to STDOUT is exclusively the domain of the post_process() function. Breaking this rule is a common source of errors. If you want your functions to be interactive (for instance) and print everything themselves, you should disable post-processing in setup(), or create an empty post_process function or make your functions return I<undef> (so I<post_process()> will only add a blank line to the output).


=head1 DIAGNOSTICS

If you see a '1' printed on the screen after a command is issued, it's probably because that command is returning a "true" value instead of an output string. If you don't want to return the command output for post processing(you'll loose some nice features, though) you can return undef or make post_process() empty.


=head1 CONFIGURATION AND ENVIRONMENT

App::Rad requires no configuration files or environment variables.


=head1 DEPENDENCIES

App::Rad depends only on 5.8 core modules (Carp for errors, Getopt::Long for "$c->getopt", Attribute::Handlers for "help" and O/B::Deparse for the "include" command).

If you have Perl::Tidy installed, the "include" command will tidy up your code before inclusion.

The test suite depends on Test::More, FindBin and File::Temp, also core modules.


=head1 INCOMPATIBILITIES

None reported.


=head1 BUGS AND LIMITATIONS

Please report any bugs or feature requests to 
C<bug-app-easy at rt.cpan.org>, or through the web interface at 
L<http://rt.cpan.org/garu/ReportBug.html?Queue=App-Rad>.  I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.


=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc App::Rad

Although this Module comes without any warraties whatsoever (see DISCLAIMER below), I try really hard to provide some quality assurance for the users. This means I not only try to close all reported bugs in the minimum amount of time but I also try to find some on my own.

This version of App::Rad comes with 183 tests and I keep my eye constantly on CPAN Testers L<http://www.cpantesters.org/show/App-Rad.html> to ensure it passes all of them, in all platforms. You can send me your own App::Rad tests if you feel I'm missing something and I'll hapilly add them to the distribution.

Since I take user's feedback very seriously, I really hope you send me any wishlist/TODO you'd like App::Rad to have (please try to send them via RT so other people can give their own suggestions).


You can also look for information at:

=over 4

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/garu/Bugs.html?Dist=App-Rad>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/App-Rad>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/App-Rad>

=item * Search CPAN

L<http://search.cpan.org/dist/App-Rad>

=back

=head2 IRC

   #app-rad  on irc.perl.org


=head1 TODO

This is a small list of features I plan to add in the near future (in no particular order). Feel free to contribute with your wishlist and comentaries!

=over 4

=item * Shell-like environment

=item * Loadable commands (in an external container file)

=item * Modularized commands (similar to App::Cmd::Commands ?)

=item * app-starter

=item * command inclusion by prefix, suffix and regexp (feature request by fco)

=item * command inclusion and exclusion also by attributes

=item * some extra integration, maybe IPC::Cmd and IO::Prompt

=back


=head1 AUTHOR

Breno G. de Oliveira, C<< <garu at cpan.org> >>


=head1 CONTRIBUTORS

(in alphabetical order)

Ben Hengst

Fernando Correa

Flavio Glock

Thanks to everyone for contributing! Please let me know if I've skipped your name by accident.


=head1 ACKNOWLEDGEMENTS

This module was inspired by Kenichi Ishigaki's presentation I<"Web is not the only one that requires frameworks"> during YAPC::Asia::2008 and the modules it exposed (mainly App::Cmd and App::CLI).

Also, many thanks to CGI::App(now Titanium)'s Mark Stosberg and all the Catalyst developers, as some of App::Rad's functionality was taken from those (web) frameworks.


=head1 LICENSE AND COPYRIGHT

Copyright 2008 Breno G. de Oliveira C<< <garu at cpan.org> >>. All rights reserved.

This module is free software; you can redistribute it and/or modify it
under the same terms as Perl itself. See L<perlartistic>.



=head1 DISCLAIMER OF WARRANTY

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
NECESSARY SERVICING, REPAIR, OR CORRECTION.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.