From ec069554b263d4d3a60a315089e170b92badd1d8 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Mon, 3 Jun 2013 15:18:17 -0500 Subject: start on some docs --- bin/reply | 16 +++++ lib/Reply.pm | 122 +++++++++++++++++++++++++++++++++ lib/Reply/Plugin.pm | 117 +++++++++++++++++++++++++++++++ lib/Reply/Plugin/Colors.pm | 20 ++++++ lib/Reply/Plugin/DataDump.pm | 12 ++++ lib/Reply/Plugin/DataDumper.pm | 12 ++++ lib/Reply/Plugin/Editor.pm | 20 ++++++ lib/Reply/Plugin/FancyPrompt.pm | 14 ++++ lib/Reply/Plugin/Hints.pm | 15 ++++ lib/Reply/Plugin/Interrupt.pm | 13 ++++ lib/Reply/Plugin/LexicalPersistence.pm | 14 ++++ lib/Reply/Plugin/LoadClass.pm | 16 +++++ lib/Reply/Plugin/Nopaste.pm | 22 +++++- lib/Reply/Plugin/Packages.pm | 16 +++++ lib/Reply/Plugin/ReadLine.pm | 23 +++++++ lib/Reply/Plugin/ResultCache.pm | 17 +++++ 16 files changed, 467 insertions(+), 2 deletions(-) diff --git a/bin/reply b/bin/reply index 6703852..3de86de 100644 --- a/bin/reply +++ b/bin/reply @@ -2,12 +2,28 @@ use strict; use warnings; # PODNAME: reply +# ABSTRACT: read, eval, print, loop, yay! use File::HomeDir; use File::Spec; use Reply; +=head1 SYNOPSIS + + reply + +=head1 DESCRIPTION + +This script runs the L shell. It looks for a configuration file in +C<.replyrc> in your home directory, and will generate a basic configuration for +you if that file does not exist. + +See the L documentation for more information about using and configuring +this program. + +=cut + my $cfg = File::Spec->catfile(File::HomeDir->my_home, ".replyrc"); my %args = (config => $cfg); diff --git a/lib/Reply.pm b/lib/Reply.pm index d32ff05..53808bc 100644 --- a/lib/Reply.pm +++ b/lib/Reply.pm @@ -8,6 +8,71 @@ use Module::Runtime qw(compose_module_name use_package_optimistically); use Scalar::Util qw(blessed); use Try::Tiny; +=head1 SYNOPSIS + + use Reply; + + Reply->new(config => "$ENV{HOME}/.replyrc")->run; + +=head1 DESCRIPTION + +Reply is a lightweight, extensible REPL for Perl. It is plugin-based (see +L), and through plugins supports many advanced features such as +coloring and pretty printing, readline support, and pluggable commands. + +=head1 CONFIGURATION + +Configuration uses an INI-style format similar to the configuration format of +L. Section names are used as the names of plugins, and any options +within a section are passed as arguments to that plugin. Plugins are loaded in +order as they are listed in the configuration file, which can affect the +results in some cases where multiple plugins are hooking into a single callback +(see L for more information). + +In addition to plugin configuration, there are some additional options +recognized. These must be specified at the top of the file, before any section +headers. + +=over 4 + +=item script_file + +This contains a filename whose contents will be evaluated as perl code once the +configuration is done being loaded. + +=item script_lineI + +Any options that start with C will be sorted by their key and then +each value will be evaluated individually once the configuration is done being +loaded. + +NOTE: this is currently a hack due to the fact that L doesn't +support multiple keys with the same name in a section. This may be fixed in the +future to just allow specifying C multiple times. + +=back + +=cut + +=method new(%opts) + +Creates a new Reply instance. Valid options are: + +=over 4 + +=item config + +Name of a configuration file to load. This should contain INI-style +configuration for plugins as described above. + +=item plugins + +An arrayref of additional plugins to load. + +=back + +=cut + sub new { my $class = shift; my %opts = @_; @@ -29,6 +94,14 @@ sub new { return $self; } +=method load_plugin($plugin, $opts) + +Loads the specified plugin. C<$plugin> corresponds to the class +C, which will be loaded and instantiated. If C<$opts> +is given, it should be a hashref of options to pass to the plugin constructor. + +=cut + sub load_plugin { my $self = shift; my ($plugin, $opts) = @_; @@ -44,6 +117,13 @@ sub load_plugin { push @{ $self->{plugins} }, $plugin; } +=method run + +Runs the repl. Will continue looping until the C callback returns +undef. + +=cut + sub run { my $self = shift; @@ -176,4 +256,46 @@ sub _chained_plugin { return @args; } +=head1 BUGS + +No known bugs. + +Please report any bugs through RT: email +C, or browse to +L. + +=head1 SEE ALSO + +L + +=head1 SUPPORT + +You can find this documentation for this module with the perldoc command. + + perldoc Reply + +You can also look for information at: + +=over 4 + +=item * AnnoCPAN: Annotated CPAN documentation + +L + +=item * CPAN Ratings + +L + +=item * RT: CPAN's request tracker + +L + +=item * Search CPAN + +L + +=back + +=cut + 1; diff --git a/lib/Reply/Plugin.pm b/lib/Reply/Plugin.pm index 0858758..ec16ddf 100644 --- a/lib/Reply/Plugin.pm +++ b/lib/Reply/Plugin.pm @@ -1,6 +1,123 @@ package Reply::Plugin; use strict; use warnings; +# ABSTRACT: base class for Reply plugins + +=head1 SYNOPSIS + + package Reply::Plugin::Foo; + use strict; + use warnings; + + use base 'Reply::Plugin'; + + # ... + +=head1 DESCRIPTION + +A L plugin is an object which adds some functionality to a Reply +instance by implementing some specific methods which the Reply object will call +at various points during execution. Plugins may implement as many callback +methods as necessary to implement their functionality (although the more +callbacks a given plugin implements, the more likely it is that the plugin may +be more useful as multiple independent plugins). + +Callback methods have two potential calling conventions: + +=over 4 + +=item wrapped + +Wrapped plugins receive a coderef as their first argument (before any arguments +to the callback itself), and that coderef can be used to call the next callback +in the list (if more than one plugin implements a given callback). In +particular, this allows calling the next plugin multiple times, or not at all +if necessary. Wrapped plugins should always call their coderef in list context. +All plugins listed below are wrapped plugins unless indicated otherwise. + +=item chained + +Chained plugins receive a list of arguments, and return a new list of arguments +which will be passed to the next plugin in the chain. This allows each plugin a +chance to modify a value before it's actually used by the repl. + +=back + +=head2 CALLBACKS + +=over 4 + +=item prompt + +Called to determine the prompt to use when reading the next line. Takes no +arguments, and returns a single string to use as the prompt. The default +implementation returns C<< ">" >> + +=item read_line + +Called to actually read a line from the user. Takes no arguments, and returns a +single string. The default implementation uses the C<< <> >> operator to read a +single line from the user. + +=item command_C<$name> (chained) + +If the line read from the user is of the form "#foo args...", then plugins will +be searched for a callback method named C. This callback takes a +single string containing the provided arguments, and returns a new line to +evaluate instead, if any. + +=item mangle_line (chained) + +Modifies the line read from the user before it's evaluated. Takes the line as a +string and returns the modified line. + +=item compile + +Compiles the string of Perl code into a coderef. Takes the line of code as a +string and a hash of extra parameters, and returns the coderef to be executed. +The default implementation uses L to compile the given string. +The extra parameters are passed directly to the C call. + +=item execute + +Executes the coderef which has just been compiled. Takes the coderef and a list +of parameters to pass to it, and returns the list of results returned by +calling the coderef. The default implementation just calls the coderef +directly. + +=item mangle_error (chained) + +If the C or C callbacks throw an exception, this callback +will be called to modify the exception before it is passed to C. +It receives the exception and returns the modified exception. + +=item print_error + +If the C or C callbacks throw an exception, this callback +will be called to display it to the user. It receives the exception and returns +nothing. The default implementation just uses C to print it to the +screen. + +=item mangle_result (chained) + +This callback is used to modify the result of evaluating the line of code +before it is displayed. It receives the list of results and returns a modified +list of results. + +=item print_result + +This callback displays to the user the results of evaluating the given line of +code. It receives the list of results, and returns nothing. The default +implementation just uses C to print them to the screen. + +=item loop (chained) + +This callback is called at the end of each evaluation. It receives no +parameters and returns nothing. + +=back + +=cut sub new { bless {}, shift } diff --git a/lib/Reply/Plugin/Colors.pm b/lib/Reply/Plugin/Colors.pm index 8b68119..839654d 100644 --- a/lib/Reply/Plugin/Colors.pm +++ b/lib/Reply/Plugin/Colors.pm @@ -1,11 +1,31 @@ package Reply::Plugin::Colors; use strict; use warnings; +# ABSTRACT: colorize output use base 'Reply::Plugin'; use Term::ANSIColor; +=head1 SYNOPSIS + + ; .replyrc + [Colors] + error = bright red + warning = bright yellow + result = bright green + +=head1 DESCRIPTION + +This plugin adds coloring to the results when they are printed to the screen. +By default, errors are C, warnings are C, and normal results are +C, although this can be overridden through configuration as shown in the +synopsis. L is used to generate the colors, so any value that +is accepted by that module is a valid value for the C, C, and +C options. + +=cut + sub new { my $class = shift; my %opts = @_; diff --git a/lib/Reply/Plugin/DataDump.pm b/lib/Reply/Plugin/DataDump.pm index e80deab..64d756e 100644 --- a/lib/Reply/Plugin/DataDump.pm +++ b/lib/Reply/Plugin/DataDump.pm @@ -1,11 +1,23 @@ package Reply::Plugin::DataDump; use strict; use warnings; +# ABSTRACT: format results using Data::Dump use base 'Reply::Plugin'; use Data::Dump 'pp'; +=head1 SYNOPSIS + + ; .replyrc + [DataDumper] + +=head1 DESCRIPTION + +This plugin uses L to format results. + +=cut + sub mangle_result { my $self = shift; my (@result) = @_; diff --git a/lib/Reply/Plugin/DataDumper.pm b/lib/Reply/Plugin/DataDumper.pm index 676344a..7491c60 100644 --- a/lib/Reply/Plugin/DataDumper.pm +++ b/lib/Reply/Plugin/DataDumper.pm @@ -1,11 +1,23 @@ package Reply::Plugin::DataDumper; use strict; use warnings; +# ABSTRACT: format results using Data::Dumper use base 'Reply::Plugin'; use Data::Dumper; +=head1 SYNOPSIS + + ; .replyrc + [DataDumper] + +=head1 DESCRIPTION + +This plugin uses L to format results. + +=cut + sub mangle_result { my $self = shift; my (@result) = @_; diff --git a/lib/Reply/Plugin/Editor.pm b/lib/Reply/Plugin/Editor.pm index fe5c23e..e7ff91d 100644 --- a/lib/Reply/Plugin/Editor.pm +++ b/lib/Reply/Plugin/Editor.pm @@ -1,6 +1,7 @@ package Reply::Plugin::Editor; use strict; use warnings; +# ABSTRACT: command to edit the current line in a text editor use base 'Reply::Plugin'; @@ -8,6 +9,25 @@ use File::HomeDir; use File::Spec; use Proc::InvokeEditor; +=head1 SYNOPSIS + + ; .replyrc + [Editor] + editor = emacs + +=head1 DESCRIPTION + +This plugin provides the C<#e> command. It will launch your editor, and allow +you to edit bits of code in your editor, which will then be evaluated all at +once. The text you entered will be saved, and restored the next time you enter +the command. Alternatively, you can pass a filename to the C<#e> command, and +the contents of that file will be preloaded instead. + +The C option can be specified to provide a different editor to use, +otherwise it will use the value of C<$ENV{VISUAL}> or C<$ENV{EDITOR}>. + +=cut + sub new { my $class = shift; my %opts = @_; diff --git a/lib/Reply/Plugin/FancyPrompt.pm b/lib/Reply/Plugin/FancyPrompt.pm index d998763..d0afc38 100644 --- a/lib/Reply/Plugin/FancyPrompt.pm +++ b/lib/Reply/Plugin/FancyPrompt.pm @@ -1,9 +1,23 @@ package Reply::Plugin::FancyPrompt; use strict; use warnings; +# ABSTRACT: provides a more informative prompt use base 'Reply::Plugin'; +=head1 SYNOPSIS + + ; .replyrc + [FancyPrompt] + +=head1 DESCRIPTION + +This plugin enhances the default Reply prompt. Currently, the only difference +is that it includes a counter of the number of lines evaluated so far in the +current session. + +=cut + sub new { my $class = shift; my $self = $class->SUPER::new(@_); diff --git a/lib/Reply/Plugin/Hints.pm b/lib/Reply/Plugin/Hints.pm index ac31ceb..d36d14a 100644 --- a/lib/Reply/Plugin/Hints.pm +++ b/lib/Reply/Plugin/Hints.pm @@ -11,9 +11,24 @@ BEGIN { use strict; use warnings; +# ABSTRACT: persists lexical hints across input lines use base 'Reply::Plugin'; +=head1 SYNOPSIS + + ; .replyrc + [Hints] + +=head1 DESCRIPTION + +This plugin persists the values of various compile time lexical hints between +evaluated lines. This means, for instance, that entering a line like C at the Reply prompt will cause C to be enabled for all future +lines (at least until C is given). + +=cut + sub new { my $class = shift; diff --git a/lib/Reply/Plugin/Interrupt.pm b/lib/Reply/Plugin/Interrupt.pm index a5c2b37..1aab2b8 100644 --- a/lib/Reply/Plugin/Interrupt.pm +++ b/lib/Reply/Plugin/Interrupt.pm @@ -1,9 +1,22 @@ package Reply::Plugin::Interrupt; use strict; use warnings; +# ABSTRACT: allows using Ctrl+C to interrupt long-running lines use base 'Reply::Plugin'; +=head1 SYNOPSIS + + ; .replyrc + [Interrupt] + +=head1 DESCRIPTION + +This plugin allows you to use Ctrl+C to interrupt long running commands without +exiting the Reply shell entirely. + +=cut + sub compile { my $self = shift; my ($next, @args) = @_; diff --git a/lib/Reply/Plugin/LexicalPersistence.pm b/lib/Reply/Plugin/LexicalPersistence.pm index deb8d94..3b1f3f5 100644 --- a/lib/Reply/Plugin/LexicalPersistence.pm +++ b/lib/Reply/Plugin/LexicalPersistence.pm @@ -1,11 +1,25 @@ package Reply::Plugin::LexicalPersistence; use strict; use warnings; +# ABSTRACT: persists lexical variables between lines use base 'Reply::Plugin'; use Lexical::Persistence; +=head1 SYNOPSIS + + ; .replyrc + [LexicalPersistence] + +=head1 DESCRIPTION + +This plugin persists the values of lexical variables between input lines. For +instance, with this plugin you can enter C into the Reply shell, and +then use C<$x> as expected in subsequent lines. + +=cut + sub new { my $class = shift; my $self = $class->SUPER::new(@_); diff --git a/lib/Reply/Plugin/LoadClass.pm b/lib/Reply/Plugin/LoadClass.pm index 8bba634..a225cdd 100644 --- a/lib/Reply/Plugin/LoadClass.pm +++ b/lib/Reply/Plugin/LoadClass.pm @@ -1,12 +1,28 @@ package Reply::Plugin::LoadClass; use strict; use warnings; +# ABSTRACT: attempts to load classes implicitly if possible use base 'Reply::Plugin'; use Module::Runtime 'use_package_optimistically'; use Try::Tiny; +=head1 SYNOPSIS + + ; .replyrc + [LoadClass] + +=head1 DESCRIPTION + +If executing a line of code fails due to a method not being defined on a +package, this plugin will load the corresponding module and then try executing +the line again. This simplifies common cases like running C<< DateTime->now >> +at the prompt before loading L - this plugin will cause DateTime to +be loaded implicitly. + +=cut + sub execute { my $self = shift; my ($next, @args) = @_; diff --git a/lib/Reply/Plugin/Nopaste.pm b/lib/Reply/Plugin/Nopaste.pm index 1f64f85..085e478 100644 --- a/lib/Reply/Plugin/Nopaste.pm +++ b/lib/Reply/Plugin/Nopaste.pm @@ -1,13 +1,31 @@ package Reply::Plugin::Nopaste; use strict; use warnings; +# ABSTRACT: command to nopaste a transcript of the current session use base 'Reply::Plugin'; use App::Nopaste; -# XXX note that this has to be loaded early, in order to catch all of the -# appropriate manipulations that plugins do ([DataDump], etc) +=head1 SYNOPSIS + + ; .replyrc + [Nopaste] + service = Gist + +=head1 DESCRIPTION + +This plugin provides a C<#nopaste> command, which will use L to +nopaste a transcript of the current Reply session. The C option can be +used to choose an alternate service to use, rather than using the one that +App::Nopaste chooses on its own. If arguments are passed to the C<#nopaste> +command, they will be used as the title of the paste. + +Note that this plugin should be loaded early in your configuration file, in +order to ensure that it sees all modifications to the result (due to plugins +like [DataDump], etc). + +=cut sub new { my $class = shift; diff --git a/lib/Reply/Plugin/Packages.pm b/lib/Reply/Plugin/Packages.pm index fd48737..eb69f96 100644 --- a/lib/Reply/Plugin/Packages.pm +++ b/lib/Reply/Plugin/Packages.pm @@ -1,9 +1,25 @@ package Reply::Plugin::Packages; use strict; use warnings; +# ABSTRACT: persist the current package between lines use base 'Reply::Plugin'; +=head1 SYNOPSIS + + ; .replyrc + [Packages] + default_package = My::Scratchpad + +=head1 DESCRIPTION + +This plugin persists the state of the current package between lines. This +allows lines such as C in the Reply shell to do what you'd +expect. The C configuration option can also be used to set the +initial package to use when Reply starts up. + +=cut + sub new { my $class = shift; my %opts = @_; diff --git a/lib/Reply/Plugin/ReadLine.pm b/lib/Reply/Plugin/ReadLine.pm index 39126d1..6fcb0ad 100644 --- a/lib/Reply/Plugin/ReadLine.pm +++ b/lib/Reply/Plugin/ReadLine.pm @@ -1,6 +1,7 @@ package Reply::Plugin::ReadLine; use strict; use warnings; +# ABSTRACT: use Term::ReadLine for user input use base 'Reply::Plugin'; @@ -8,6 +9,28 @@ use File::HomeDir; use File::Spec; use Term::ReadLine; +=head1 SYNOPSIS + + ; .replyrc + [ReadLine] + history_file = '.hist' + history_length = 100 + +=head1 DESCRIPTION + +This plugin uses L to read lines from the user. This enables +useful features such as line editing and command history. The history will be +persisted between runs, by default in C<~/.reply_history>, although this is +changeable with the C option. To limit the number of lines +written to this file, you can use the C option. Setting a +C of C<0> will disable writing history to a file entirely. + +NOTE: you probably want to install a reasonable L backend in +order for this plugin to be very useful. L is highly +recommended if possible. + +=cut + sub new { my $class = shift; my %opts = @_; diff --git a/lib/Reply/Plugin/ResultCache.pm b/lib/Reply/Plugin/ResultCache.pm index fc260f5..a973d05 100644 --- a/lib/Reply/Plugin/ResultCache.pm +++ b/lib/Reply/Plugin/ResultCache.pm @@ -1,9 +1,26 @@ package Reply::Plugin::ResultCache; use strict; use warnings; +# ABSTRACT: retain previous results to be able to refer to them later use base 'Reply::Plugin'; +=head1 SYNOPSIS + + ; .replyrc + [ResultCache] + variable = r + +=head1 DESCRIPTION + +This plugin caches the results of successful evaluations, and provides them in +a lexical array (by default C<@res>, although this can be changed via the +C option). This means that you can, for instance, access the value +returned by the previous line with C<$res[-1]>. It also modifies the output to +include an indication of where the value is stored, for later reference. + +=cut + sub new { my $class = shift; my %opts = @_; -- cgit v1.2.3-54-g00ecf