mascip has asked for the wisdom of the Perl Monks concerning the following question:

App::Rad is in the same vein as App::Cmd but much simpler to use for some applications, as all your commands will fit in a single file.

But I'm hitting a wall, I need help. Here is an example of some commands I want to run (my application is called aid):

### Create some entries in various CSV tables # Maria is a person aid add person maria # HSBC is a bank aid add bank hsbc # Perlmonk is a website aid add website perlmonks ### List them all, and check for existence aid list person aid listed person maria ### Operations # There is £100 in maria's bank account aid set hsbc maria 100 # How much is there, again? aid get hsbc maria # £100 # Maria wrote 25 posts on Perlmonks aid set maria perlmonks 25 # How much? aid get maria perlmonks ### More to come...
It's pretty simple, right?

All of my commands are processing the two same arguments:

Because I need them for almost all commands, in all functions I keep on writing:

use Function::Parameters; # works with App::Rad, while Kavorka doesn't fun add ($c) { my ($data, $data_type) = @{_process_data($c)}; my ($name) = @{$c->argv}; $data->add($name); } # $data is a wrapper object to a csv file: person.csv / bank.csv / etc # This way I can delegate the work to these different entities # $data_type is not used here, but could be used to display a message
which is a pain (especially when most of my functions just call a method on an object), and not the ideal of maintainability. Any idea to relieve my pain?

I tried to use an around modifier, but it doesn't seem compatible:

use Class::Method::Modifiers; around 'add' => sub { my $orig = shift; my $c = shift; my ($data, $data_type) = @{_process_data($c)}; my ($name) = @{$c->argv}; $orig->($data, $data_type, $name, @_); }; sub add ($data, $data_type, $name) { $data->add($name); }
Notice how the add() code is much simplified. But it throws:
Can't locate object method "add" via package "App::Rad" at bin/arb-aid +.pl line 58. main::add('App::Rad=HASH(0xa20010)') called at /home/user/.per +lbrew/libs/perl-5.16.3@devel/lib/perl5/App/Rad/Command.pm line 251 App::Rad::Command::run('App::Rad::Command=HASH(0x1ac72c0)', 'A +pp::Rad=HASH(0xa20010)') called at /home/user/.perlbrew/libs/perl-5.1 +6.3@devel/lib/perl5/App/Rad.pm line 598 App::Rad::__ANON__('App::Rad=HASH(0xa20010)') called at /home/ +user/.perlbrew/libs/perl-5.16.3@devel/lib/perl5/App/Rad.pm line 321 App::Rad::_run_full_round('App::Rad=HASH(0xa20010)', 'CODE(0xa +34138)') called at /home/user/.perlbrew/libs/perl-5.16.3@devel/lib/pe +rl5/App/Rad.pm line 602 App::Rad::run('App::Rad') called at bin/arb-aid.pl line 2

So, for the maintainer of the quite awesome App::Rad, here is a feature request: Do you think you could make it compatible with method modifiers?

Otherwise, if anyone has an idea of something smarter I could do, please tell. I'm also eager for some honest critique, if my approach to this problem is wrong.

Replies are listed 'Best First'.
Re: App::Rad is pretty rad for creating command line apps, but... (need help!)
by kcott (Archbishop) on Mar 29, 2014 at 09:44 UTC

    G'day mascip,

    Firstly, I'm unfamiliar with the App::Rad module so I can't provide any direct help.

    If the author given in the documentation, garu, is the same as the garu on PerlMonks, he hasn't been here for 5 years. Your feature request will probably fall on deaf ears.

    You could use the View/Report Bugs link from the documentation and add your feature request with Severity=Wishlist.

    -- Ken

      Cheers for the advice :)
      I have written to the author's email address (the @cpan.org one indicated in the documentation). If he doesn't answer within a week, I will try to reach him through RT.

        Wow, it's been a while - both here and in App::Rad!

        First of all, thanks for using App::Rad. I've been meaning to rewrite it from scratch in a much saner/cleaner/expandable way but never got around to it. Still, I'm glad you found it useful!

        As for your question, if you're doing the same thing in all your commands, you should know that, as documented, App::Rad will optionally read a "setup" sub every time your app starts. People have used this to setup DBI handles, reading configuration files and whatnot.

        There is also the "pre_process" function, which is like a "before" hook in Moose (but for all commands), and you can use it to parse $c->argv *after* Rad knows the command it's going to run, and put some things on the stash (like $data and $name).

        With the code you shared here, I think you could try something like:

        fun pre_process ($c) { my ($data, $data_type) = @{_process_data($c)}; $c->stash->{data} = $data; $c->stash->{data_type} = $data_type; $c->stash->{name} = $c->argv->[0]; } fun add ($c) { $c->stash->{data}->add( $c->stash->{name} ); }

        Not as pretty as you might have hoped, but still pretty straightforward, I think :)

        Note that, if you need to behave differently on some commands, you can check $c->cmd to see the name of the current command from within pre_process().

        I'll keep method modifiers (as well as subcommands, which is what you seem to be doing) in mind if I ever get to that rewrite. Meanwhile, I hope this helps!

        Cheers!