Writing Modular Command-line Apps with App::Cmd

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    4 Favorites & 1 Group

    Writing Modular Command-line Apps with App::Cmd - Presentation Transcript

    1. App::Cmd Writing Maintainable Commands
    2. TMTOWTDI
    3. There’s More Than One Way To Do It
    4. Web
    5. Web • Mason
    6. Web • Mason • Catalyst
    7. Web • Mason • Catalyst • CGI::Application
    8. Web • Mason • Catalyst • CGI::Application • Maypole
    9. Web • Mason • Catalyst • CGI::Application • Maypole • Continuity
    10. Web • Mason • RayApp • Catalyst • CGI::Application • Maypole • Continuity
    11. Web • Mason • RayApp • Catalyst • Gantry • CGI::Application • Maypole • Continuity
    12. Web • Mason • RayApp • Catalyst • Gantry • CGI::Application • Tripletail • Maypole • Continuity
    13. Web • Mason • RayApp • Catalyst • Gantry • CGI::Application • Tripletail • Maypole • CGI::ExApp • Continuity
    14. Web • Mason • RayApp • Catalyst • Gantry • CGI::Application • Tripletail • Maypole • CGI::ExApp • Continuity • OpenInteract
    15. Daemons
    16. Daemons • POE
    17. Daemons • POE • Danga
    18. Daemons • POE • Danga • Net::Server
    19. Daemons • POE • Danga • Net::Server • Daemon::Generic
    20. Daemons • POE • Proc::Daemon • Danga • Net::Server • Daemon::Generic
    21. Daemons • POE • Proc::Daemon • Danga • Net::Daemon • Net::Server • Daemon::Generic
    22. Daemons • POE • Proc::Daemon • Danga • Net::Daemon • Net::Server • MooseX::Daemonize • Daemon::Generic
    23. Daemons • POE • Proc::Daemon • Danga • Net::Daemon • Net::Server • MooseX::Daemonize • Daemon::Generic • Event
    24. TMTOWTDI
    25. TMTOWTDI • All the big problem sets have a few solutions!
    26. TMTOWTDI • All the big problem sets have a few solutions! • So, when I needed to write a CLI app, I checked CPAN...
    27. Command-Line Apps
    28. Command-Line Apps • App::CLI
    29. Command-Line Apps
    30. Command-Line Apps :-(
    31. Everybody writes command-line apps!
    32. Why are there no good tools?
    33. Second-Class Citizens
    34. Second-Class Citizens • That’s how we view them.
    35. Second-Class Citizens • That’s how we view them. • They’re
    36. Second-Class Citizens • That’s how we view them. • They’re • hard to test
    37. Second-Class Citizens • That’s how we view them. • They’re • hard to test • not reusable components
    38. Second-Class Citizens • That’s how we view them. • They’re • hard to test • not reusable components • hard to add more behavior later
    39. Here’s an Example
    40. Example Script $ sink 30min “server mx-pa-1 crashed!”
    41. Example Script $ sink --list who | time | event ------+-------+---------------------------- rjbs | 30min | server mx-pa-1 crashed!
    42. Example Script GetOptions(\\%opt, ...); if ($opt{list}) { die if @ARGV; @events = Events->get_all; } else { my ($duration, $desc) = @ARGV; Event->new($duration, $desc); }
    43. Example Script $ sink --list --user jcap who | time | event ------+-------+---------------------------- jcap | 2hr | redeploy exigency subsystem
    44. Example Script GetOptions(\\%opt, ...); if ($opt{list}) { die if @ARGV; @events = $opt{user} ? Events->get(user => $opt{user}) : Events->get_all; } else { my ($duration, $desc) = @ARGV; Event->new($duration, $desc); }
    45. Example Script GetOptions(\\%opt, ...); if ($opt{list}) { die if @ARGV; @events = $opt{user} ? Events->get(user => $opt{user}) : Events->get_all; } else { my ($duration, $desc) = @ARGV; die if $opt{user}; Event->new($duration, $desc); }
    46. Example Script $ sink --start ‘putting out oil fire‘ Event begun! use --finish to finish event $ sink --list --open 18. putting out oil fire $ sink --finish 18 Event finished! Total time taken: 23 min
    47. Insult to Injury
    48. Insult to Injury • ...well, that’s going to take a lot of testing.
    49. Insult to Injury • ...well, that’s going to take a lot of testing. • How can we test it?
    50. Insult to Injury • ...well, that’s going to take a lot of testing. • How can we test it? • my $output = `sink @args`;
    51. Insult to Injury • ...well, that’s going to take a lot of testing. • How can we test it? • my $output = `sink @args`; • IPC::Run3 (or one of those)
    52. Here’s a Solution
    53. Command Breakdown $ sink do --for 1hr --ago 1d ‘rebuild raid’
    54. Command Breakdown $ sink do --for 1hr --ago 1d ‘rebuild raid’ App
    55. Command Breakdown $ sink do --for 1hr --ago 1d ‘rebuild raid’ Command
    56. Command Breakdown $ sink do --for 1hr --ago 1d ‘rebuild raid’ Options
    57. Command Breakdown $ sink do --for 1hr --ago 1d ‘rebuild raid’ Args
    58. Command Breakdown $ sink do --for 1hr --ago 1d ‘rebuild raid’
    59. “do” command
    60. “do” command sub run {
    61. “do” command sub run { my ($self, $opt, $args) = @_;
    62. “do” command sub run { my ($self, $opt, $args) = @_;
    63. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago});
    64. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for});
    65. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0];
    66. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0];
    67. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create(
    68. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create( start => $start,
    69. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create( start => $start, finish => $start + $length,
    70. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create( start => $start, finish => $start + $length, desc => $desc;
    71. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create( start => $start, finish => $start + $length, desc => $desc; );
    72. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create( start => $start, finish => $start + $length, desc => $desc; );
    73. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create( start => $start, finish => $start + $length, desc => $desc; ); print “event created!”;
    74. “do” command sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create( start => $start, finish => $start + $length, desc => $desc; ); print “event created!”; }
    75. “do” command
    76. “do” command sub opt_desc {
    77. “do” command sub opt_desc { [ “start=s”, “when you started doing this” ],
    78. “do” command sub opt_desc { [ “start=s”, “when you started doing this” ], [ “for=s”, “how long you did this for”,
    79. “do” command sub opt_desc { [ “start=s”, “when you started doing this” ], [ “for=s”, “how long you did this for”, { required => 1} ],
    80. “do” command sub opt_desc { [ “start=s”, “when you started doing this” ], [ “for=s”, “how long you did this for”, { required => 1} ], }
    81. “do” command
    82. “do” command sub validate_args {
    83. “do” command sub validate_args { my ($self, $opt, $args) = @_;
    84. “do” command sub validate_args { my ($self, $opt, $args) = @_;
    85. “do” command sub validate_args { my ($self, $opt, $args) = @_; if (@$args != 1) {
    86. “do” command sub validate_args { my ($self, $opt, $args) = @_; if (@$args != 1) { $self->usage_error(“provide one argument”);
    87. “do” command sub validate_args { my ($self, $opt, $args) = @_; if (@$args != 1) { $self->usage_error(“provide one argument”); }
    88. “do” command sub validate_args { my ($self, $opt, $args) = @_; if (@$args != 1) { $self->usage_error(“provide one argument”); } }
    89. package Sink::Command::Do; use base ‘App::Cmd::Command’; sub opt_desc { [ “start=s”, “when you started doing this” ], [ “for=s”, “how long you did this for”, { required => 1} ], } sub validate_args { my ($self, $opt, $args) = @_; if (@$args != 1) { $self->usage_error(“provide one argument”); } } sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create( start => $start, finish => $start + $length, desc => $desc; ); print “event created!”; } 1;
    90. package Sink::Command::Do; use base ‘App::Cmd::Command’; sub opt_desc { [ “start=s”, “when you started doing this” ], [ “for=s”, “how long you did this for”, { required => 1} ], } sub validate_args { my ($self, $opt, $args) = @_; if (@$args != 1) { $self->usage_error(“provide one argument”); } } sub run { my ($self, $opt, $args) = @_; my $start = parse_ago($opt->{ago}); my $length = parse_duration($opt->{for}); my $desc = $args->[0]; Sink::Event->create( start => $start, finish => $start + $length, desc => $desc; ); print “event created!”; } 1;
    91. Extra Scaffolding
    92. Extra Scaffolding package Sink;
    93. Extra Scaffolding package Sink; use base ‘App::Cmd’;
    94. Extra Scaffolding package Sink; use base ‘App::Cmd’; 1;
    95. Extra Scaffolding use Sink; Sink->run;
    96. Testing Your App
    97. Testing App::Cmd
    98. Testing App::Cmd use Test::More tests => 3;
    99. Testing App::Cmd use Test::More tests => 3; use Test::Output;
    100. Testing App::Cmd use Test::More tests => 3; use Test::Output;
    101. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error;
    102. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do {
    103. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do { local @ARGV = qw(do --for 8hr ‘sleeping’);
    104. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do { local @ARGV = qw(do --for 8hr ‘sleeping’); stdout_from(sub {
    105. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do { local @ARGV = qw(do --for 8hr ‘sleeping’); stdout_from(sub { eval { Sink->run; 1 } or $error = $@;
    106. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do { local @ARGV = qw(do --for 8hr ‘sleeping’); stdout_from(sub { eval { Sink->run; 1 } or $error = $@; });
    107. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do { local @ARGV = qw(do --for 8hr ‘sleeping’); stdout_from(sub { eval { Sink->run; 1 } or $error = $@; }); }
    108. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do { local @ARGV = qw(do --for 8hr ‘sleeping’); stdout_from(sub { eval { Sink->run; 1 } or $error = $@; }); }
    109. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do { local @ARGV = qw(do --for 8hr ‘sleeping’); stdout_from(sub { eval { Sink->run; 1 } or $error = $@; }); } like $stdout, qr/^event created!$/;
    110. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do { local @ARGV = qw(do --for 8hr ‘sleeping’); stdout_from(sub { eval { Sink->run; 1 } or $error = $@; }); } like $stdout, qr/^event created!$/; is Sink::Event->get_count, 1;
    111. Testing App::Cmd use Test::More tests => 3; use Test::Output; my $error; my $stdout = do { local @ARGV = qw(do --for 8hr ‘sleeping’); stdout_from(sub { eval { Sink->run; 1 } or $error = $@; }); } like $stdout, qr/^event created!$/; is Sink::Event->get_count, 1; ok ! $error;
    112. Testing App::Cmd use Test::More tests => 3; use Test::App::Cmd; use Sink; my ($stdout, $error) = test_app( Sink => qw(do --for 8hr ‘sleeping’) ); like $stdout, qr/^event created!$/; is Sink::Event->get_count, 1; ok ! $error;
    113. Testing App::Cmd use Test::More tests => π; use Sink::Command::Do; eval { Sink::Command::Do->validate_args( { for => ‘1hr’ }, [ 1, 2, 3 ], ); }; like $@, qr/one arg/;
    114. Growing Your App
    115. Command Breakdown $ sink do --for 1hr --ago 1d ‘rebuild raid’
    116. Command Breakdown $ sink do --for 1hr --ago 1d ‘rebuild raid’ Command
    117. package Sink::Command::List; use base ‘App::Cmd::Command’; sub opt_desc { ... } sub validate_args { ... } sub run { ... } 1;
    118. package Sink::Command::List; use base ‘App::Cmd::Command’; sub opt_desc { [ “open”, “only unfinished events” ], [ “user|u=s”, “only events for this user” ], } sub validate_args { shift->usage_error(’no args allowed’) if @{ $_[1] } } sub run { ... } 1;
    119. package Sink::Command::Start; use base ‘App::Cmd::Command’; sub opt_desc { ... } sub validate_args { ... } sub run { ... } 1;
    120. package Sink::Command::Start; use base ‘App::Cmd::Command’; sub opt_desc { return } sub validate_args { shift->usage_error(’one args required’) if @{ $_[1] } != 1 } sub run { ... } 1;
    121. More Commands! $ sink do --for 1hr --ago 1d ‘rebuild raid’ $ sink list --open $ sink start ‘porting PHP to ASP.NET’
    122. More Commands! $ sink sink help <command> Available commands: commands: list the application’s commands help: display a command’s help screen do: (unknown) list: (unknown) start: (unknown)
    123. Command Listing package Sink::Command::Start; =head1 NAME Sink::Command::Start - start a new task =cut
    124. Command Listing package Sink::Command::Start; sub abstract { ‘start a new task’; }
    125. Command Listing $ sink commands Available commands: commands: list the application’s commands help: display a command’s help screen do: record that you did something list: list existing events start: start a new task
    126. Command Listing
    127. Command Listing $ sink help list
    128. Command Listing $ sink help list
    129. Command Listing $ sink help list sink list [long options...]
    130. Command Listing $ sink help list sink list [long options...] -u --user only events for this user
    131. Command Listing $ sink help list sink list [long options...] -u --user only events for this user --open only unfinished events
    132. Core Commands
    133. Core Commands • Where did “help” and “commands” come from?
    134. Core Commands • Where did “help” and “commands” come from? • App::Cmd::Command::help
    135. Core Commands • Where did “help” and “commands” come from? • App::Cmd::Command::help • App::Cmd::Command::commands
    136. Default Command package Sink; use base ‘App::Cmd’; 1;
    137. Default Command package Sink; use base ‘App::Cmd’; sub default_command { ‘help’ } # default 1;
    138. Default Command package Sink; use base ‘App::Cmd’; sub default_command { ‘list’ } 1;
    139. One-Command Applications
    140. Simple Example $ ckmail check -a work -a home No new mail.
    141. Simple Example $ ckmail -a work -a home No new mail.
    142. The Lousy Way
    143. The Lousy Way • create Ckmail::Command::Check
    144. The Lousy Way • create Ckmail::Command::Check • make empty Ckmail.pm
    145. The Lousy Way • create Ckmail::Command::Check • make empty Ckmail.pm • make “check” the default command
    146. The Simple Way package Ckmail::Command::Check; use base ‘App::Cmd::Command’; sub opt_desc { ... } sub run { ... } 1;
    147. The Simple Way package Ckmail::Command::Check; use base ‘App::Cmd::Simple’; sub opt_desc { ... } sub run { ... } 1;
    148. The Simple Way use Ckmail; Ckmail->run;
    149. The Simple Way use Ckmail::Command::Check; Ckmail::Command::Check->run;
    150. App::Cmd::Simple
    151. App::Cmd::Simple • You write a command...
    152. App::Cmd::Simple • You write a command... • ...but you use it like an App::Cmd.
    153. App::Cmd::Simple • You write a command... • ...but you use it like an App::Cmd. • Later, you can just demote it.
    154. Any Questions?
    155. Thank You!

    + Ricardo SignesRicardo Signes, 3 years ago

    custom

    8127 views, 4 favs, 1 embeds more stats

    It's easy to write command-line programs in Perl. T more

    More info about this document

    © All Rights Reserved

    Go to text version

    • Total Views 8127
      • 8126 on SlideShare
      • 1 from embeds
    • Comments 0
    • Favorites 4
    • Downloads 93
    Most viewed embeds
    • 1 views on http://192.168.10.100

    more

    All embeds
    • 1 views on http://192.168.10.100

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories

    Groups / Events