package Term::ShellKit::Dev;
require Term::ShellKit;
######################################################################
sub require {
die "No module name provided" unless ( scalar @_ );
map { Term::ShellKit::require_package( $_ ) } @_;
}
######################################################################
use vars '%LibLastLoaded';
sub reload {
if ( scalar @_ ) {
my $lib = shift;
( $lib .= '.pm' ) =~ s|::|/|go unless ( $lib =~ /\.\w{2,3}$/ );
delete $INC{$lib};
CORE::require( $lib );
return "Reloaded $lib";
}
my @libs;
while(my($lib, $file) = each %INC) {
local $^W = 0;
my $mtime = (stat $file)[9];
# warn and skip the files with relative paths which
# can't be locate by applying @INC;
unless ( defined $mtime and $mtime ) {
warn "Can't locate $file" ;
next;
}
# Assume all files loaded at startup
$LibLastLoaded{$file} ||= $^T;
if($mtime > $LibLastLoaded{$file}) {
delete $INC{$lib};
eval {
local $SIG{__DIE__};
CORE::require( $lib );
};
if ( $@ ) {
$INC{$lib} = $file;
die $@;
}
push @libs, $lib;
$LibLastLoaded{$file} = $mtime;
}
}
scalar(@libs) ? "Reloaded " . join(', ', @libs ) : 'No changes found in @INC';
}
######################################################################
sub current_package () {
$Term::ShellKit::CurrentPackage;
}
sub package ($) {
Term::ShellKit::package( @_ );
}
sub show_package (;$) {
my $package = shift || $Term::ShellKit::CurrentPackage;
no strict;
my $symbols = \%{ $package . '::' };
my (%scalars, %arrays, %hashes, %codes, %packages);
foreach my $name ( sort keys %$symbols ) {
local *symbol = $symbols->{ $name };
if ( defined $symbol ) {
$scalars{ $name } = "$symbol";
}
if ( defined @symbol ) {
$arrays{ $name } = join(', ', @symbol);
}
if ( my $coderef = *Term::ShellKit::Dev::symbol{CODE} ) {
my $prototype = prototype($coderef);
$codes{ $name } = "$coderef" . ( $prototype ? " ($prototype)" : '' );
}
if ( defined %symbol ) {
if ( $name =~ /(.*)\:\:\Z/ ) {
$packages{ $1 } = "PACKAGE";
} else {
$hashes{ $name } = join(', ', map { "$_ => $symbol{$_}" } (sort
keys %symbol));
}
}
}
my @out = ( "Package Stash for $package" );
foreach my $output (
[ 'Scalars', \%scalars, '$', ],
["Arrays", \%arrays, '@'],
[ 'Hashes', \%hashes, '%'],
['Subs', \%codes, 'sub '],
['Packages', \%packages, '::']
) {
next unless scalar keys %{$output->[1]};
push @out, "$output->[0]:";
foreach ( sort keys %{$output->[1]} ) {
push @out, " $output->[2]$_ = \"$output->[1]{$_}\"";
}
}
join "\n", @out;
}
######################################################################
1;
######################################################################
=head1 NAME
Term::ShellKit::Commands - Basic shell functions
=head1 SYNOPSIS
> perl -Iblib/lib -MTerm::ShellKit -eshell "kit Dev"
Term::ShellKit: Starting interactive shell; commands include help, exit.
Activating Term::ShellKit::Commands
Activating Term::ShellKit::Dev
Term::ShellKit> require MyClass
MyClass
Term::ShellKit> show_package MyClass
Package Stash for MyClass
Subs:
sub smee = "CODE(0x73fa4)"
sub twiddle = "CODE(0xc8530)"
=head1 COMMANDS
The following commands are available.
=head2 require
Load a Perl module or library.
=over 4
=item *
require I<module>
=back
=head2 reload
Reload any Perl modules which have changed since they were last loaded.
=over 4
=item *
reload
=back
You can use the shell reload command to read in changes to your modules while continuing to work in the same environment.
Start with the following code in MyObject.pm:
package MyObject;
sub new {
my $class = shift;
bless { }, $class;
}
1;
Then start your shell and load your module:
~> perl lib/Shell/Shell.pm
Term::ShellKit: Starting interactive shell
Term::ShellKit> require MyObject
You can now start creating instances of your class:
Term::ShellKit> $example = MyObject->new()
$example = MyObject->new(): MyObject=HASH(0x1e5118)
Your class doesn't do anything else yet, so trying to call other methods on your new object will result in an error:
Term::ShellKit> $example->twiddle
$example->twiddle: Failed.
shell_cmd_method:
shell_cmd_eval: Can't locate object method "twiddle" via package "MyObject" at (eval 12) line 1.
Let's define that method -- leave the shell running, and add the following method to your package:
sub twiddle {
my $self = shift;
return "Song and dance goes here...";
}
Then return to the shell and run the "reload" command to load your changes. You can now start calling your new method, even on objects that were created earlier:
Term::ShellKit> reload
Term::ShellKit: reload MyObject.pm
Term::ShellKit> $example->twiddle
$example->twiddle: Song and dance goes here...
Subsequent additions or revisions to the module will be available the next time you run the "reload" command. (Note that if you remove a method from your module code, it will not be deleted from the live workspace; you'll need to quit and restart the shell to achieve this.)
If there's an error in your code, you'll get a message similar to this when you try to reload:
Term::ShellKit> reload
reload: Failed.
shell_cmd_method: Type of arg 1 to shift must be array (not return)
at /tmp/MyObject.pm line 10, near ""Song and dance goes here...";"
To view the problematic line, you can copy and paste in the file and line number, taking advantage of the default alias that maps "at" to "show_file":
Term::ShellKit> at /tmp/MyObject.pm line 10
> show_file /tmp/MyObject.pm line 10
> show_file /tmp/MyObject.pm window 2 line 10
my $self = shift
return "Song and dance goes here...";
}
If you need to see more of the code you can re-run the show_file command with a window argument that's larger than the default of 2, but that's generally enough to spot errors like semicolon missing from the above.
=head2 show_package
=over 4
=item *
show_package I<package_name>
=back
> perl -Iblib/lib -MTerm::ShellKit -eshell "kit Dev"
Term::ShellKit: Starting interactive shell; commands include help, exit.
Activating Term::ShellKit::Commands
Activating Term::ShellKit::Dev
Term::ShellKit> show_package Carp
Package Stash for Carp
Scalars:
$CarpLevel = "0"
$MaxArgLen = "64"
$MaxArgNums = "8"
$MaxEvalLen = "0"
$Verbose = "0"
Arrays:
@EXPORT = "confess, croak, carp"
@EXPORT_FAIL = "verbose"
@EXPORT_OK = "cluck, verbose"
@ISA = "Exporter"
Hashes:
%EXPORT = "carp => 1, cluck => 1, confess => 1, croak => 1, verbose => 1"
%EXPORT_FAIL = "&verbose => 1, verbose => 1"
Subs:
sub carp = "CODEREF"
sub cluck = "CODEREF"
sub confess = "CODEREF"
sub croak = "CODEREF"
sub export_fail = "CODEREF"
sub longmess = "CODEREF"
sub shortmess = "CODEREF"
Term::ShellKit> exit
=head1 SEE ALSO
L<Term::ShellKit>
=cut