[Bps-public-commit] Prophet branch, master, updated. ddbb5e2ee35db219af36b563c36cec216543d866

spang at bestpractical.com spang at bestpractical.com
Wed Aug 5 11:25:45 EDT 2009


The branch, master has been updated
       via  ddbb5e2ee35db219af36b563c36cec216543d866 (commit)
       via  e6f73f6cd6e60400b483e6eb2e02aaa1699b0db2 (commit)
       via  d7701002bc7b8fb9e953126f8791c76d0597a845 (commit)
       via  87f41ace2a245c21f166d3263155dc23033da647 (commit)
       via  97cafcc09a58ad4163abfad4bee9ecc5c69b2e2a (commit)
       via  a09bdebc1a1bac201c474bb30b8daf7dfc616247 (commit)
       via  c6b01984879bbd8c7ddcc47bb9f8173906966f1f (commit)
       via  f68bea3a3efd08e9cda398c63081454432709ff9 (commit)
       via  31833bf5532fdef4a4092df8ccb916fa1cca8f06 (commit)
       via  c4873def259f0d733dee0f27300d48ed6b78c95f (commit)
       via  f4f22144baf55254776f436c429b1b6df818b079 (commit)
       via  fbadf391b7f070f854f830c3df3376d5bab27b73 (commit)
       via  313085d3a04eb0fef4e08a1489379550ffc5a080 (commit)
       via  c77f2b1f8a7b100fe7febecbfe13376cf6823ac1 (commit)
       via  9aff1bb1e126f2b34e6a99366a7bf520de23a03f (commit)
       via  b6898a1a8aefdd2cd14bb47f28a2066c8ff2b237 (commit)
       via  d0d7154fec0242b6b961128c25cb8d3ce26a0beb (commit)
       via  33795a9f7dac0ba82dca05a6d692934c7435f904 (commit)
       via  e0241859429011bf0f248d7081e086a1f82c5f5f (commit)
      from  c283d43c20650cbbf3b09eabe22ac46f04179a2a (commit)

Summary of changes:
 .gitignore                          |    1 +
 lib/Prophet/CLI.pm                  |   57 ++++++--
 lib/Prophet/CLI/Command.pm          |   68 +++++++++-
 lib/Prophet/CLI/Command/Aliases.pm  |   74 ++++------
 lib/Prophet/CLI/Command/Clone.pm    |   18 ++-
 lib/Prophet/CLI/Command/Config.pm   |  217 +++++++++++++++++++++++++++-
 lib/Prophet/CLI/Command/Create.pm   |   12 ++
 lib/Prophet/CLI/Command/Delete.pm   |   13 ++-
 lib/Prophet/CLI/Command/Export.pm   |   18 ++-
 lib/Prophet/CLI/Command/History.pm  |   13 ++-
 lib/Prophet/CLI/Command/Info.pm     |   12 ++
 lib/Prophet/CLI/Command/Init.pm     |   13 ++
 lib/Prophet/CLI/Command/Log.pm      |   28 +++-
 lib/Prophet/CLI/Command/Merge.pm    |   16 ++
 lib/Prophet/CLI/Command/Mirror.pm   |   18 ++-
 lib/Prophet/CLI/Command/Publish.pm  |   17 ++-
 lib/Prophet/CLI/Command/Pull.pm     |   23 +++-
 lib/Prophet/CLI/Command/Push.pm     |   17 ++-
 lib/Prophet/CLI/Command/Search.pm   |   11 ++
 lib/Prophet/CLI/Command/Server.pm   |   20 ++-
 lib/Prophet/CLI/Command/Settings.pm |   14 ++
 lib/Prophet/CLI/Command/Shell.pm    |   22 ++--
 lib/Prophet/CLI/Command/Show.pm     |   13 ++-
 lib/Prophet/CLI/Command/Update.pm   |   14 ++-
 lib/Prophet/CLI/Dispatcher.pm       |   14 ++-
 lib/Prophet/CLIContext.pm           |   18 +---
 lib/Prophet/Test.pm                 |   12 +-
 t/aliases.t                         |  125 +++++++++--------
 t/export.t                          |   51 ++++---
 t/init.t                            |   11 +-
 t/log.t                             |    9 +-
 t/non-conflicting-merge.t           |   54 +++++---
 t/publish-html.t                    |   27 +++--
 t/publish-pull.t                    |   76 +++++++----
 t/scripts/aliases-editor.pl         |    3 +-
 t/search.t                          |  119 +++++++++-------
 t/simple-push.t                     |   53 +++-----
 t/usage.t                           |  267 +++++++++++++++++++++++++++++++++++
 38 files changed, 1203 insertions(+), 365 deletions(-)
 create mode 100644 t/usage.t

- Log -----------------------------------------------------------------
commit e0241859429011bf0f248d7081e086a1f82c5f5f
Author: Christine Spang <spang at mit.edu>
Date:   Mon Jul 20 19:42:34 2009 +0100

    Use Text::ParseWords' shellwords routine to tokenize input from the shell
    
    Is probably better/less buggy than my quick hack.

diff --git a/lib/Prophet/CLI/Command/Shell.pm b/lib/Prophet/CLI/Command/Shell.pm
index ff48e12..95a7de8 100644
--- a/lib/Prophet/CLI/Command/Shell.pm
+++ b/lib/Prophet/CLI/Command/Shell.pm
@@ -4,6 +4,7 @@ use Any::Moose;
 extends 'Prophet::CLI::Command';
 use File::Spec;
 use Prophet::Util;
+use Text::ParseWords qw(shellwords);
 
 has name => (
     is => 'ro',
@@ -52,16 +53,7 @@ sub eval {
 
     eval {
         local $SIG{__DIE__} = 'DEFAULT';
-        my @args;
-        while ($line) {
-            $line =~ s/^\s*//;
-            if ( $line =~ s/^["'](.+)["']// ) {
-                push @args, $1;
-            }
-            else {
-                push @args, $1 if $line =~ s/(\S+)//;
-            }
-        }
+        my @args = shellwords($line);
         $self->cli->run_one_command(@args);
     };
     warn $@ if $@;

commit 33795a9f7dac0ba82dca05a6d692934c7435f904
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jul 21 11:42:27 2009 +0100

    Both aliases and config use this cli parsing code, and aliases extends config, so the method really belongs in the Config module.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 51f922a..43be982 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -144,53 +144,6 @@ sub process_template {
     }
 }
 
-sub parse_cli_arg {
-    my $self = shift;
-    my ($cmd, $arg) = @_;
-
-    if ( $arg =~ /^show\b/ ) {
-        $self->context->set_arg(show => 1);
-    }
-    elsif ( $arg =~ /^edit\b/ ) {
-        $self->context->set_arg(edit => 1);
-    }
-    # arg *might* be quoted
-    elsif ( $arg =~ /^delete\s+"?([^"]+)"?/ ) {
-        $self->context->set_arg(delete => $1);
-    }
-    # prophet alias "foo bar" = "foo baz"
-    # prophet alias foo = bar
-    # prophet alias add foo bar = "bar baz"
-    # prophet alias add foo bar = bar baz
-    elsif ( $arg =~ 
-        /^(?:add |set )?\s*(?:(?:"([^"]+)"|([^"]+))\s+=\s+(?:"([^"]+)"|([^"]+)))$/ ) {
-        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
-        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
-        $self->context->set_arg(set => "$orig=$new");
-    }
-    # prophet alias "foo = bar"
-    # prophet alias "foo bar = foo baz"
-    elsif ( $arg =~ /^(?:add |set )?\s*"([^"]+=[^"]+)"$/ ) {
-        $self->context->set_arg(set => $1);
-    }
-    # alternate syntax (preferred):
-    # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
-    # prophet alias foo bar, etc.
-    elsif ( $arg =~ /^(?:add |set )?\s*(?:"([^"]+)"|([^"\s]+))(?:\s+(?:"([^"]+)"|([^"\s]+)))?/ ) {
-        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
-        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
-        if ( $new ) {
-            $self->context->set_arg(set => "$orig=$new");
-        }
-        else {
-            $self->context->set_arg(set => $orig);
-        }
-    }
-    else {
-        die 'no idea what you mean, sorry';
-    }
-}
-
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;
 
diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index 253070d..40fc31f 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -146,6 +146,53 @@ sub no_config_files {
          "' or set the PROPHET_APP_CONFIG environment variable.\n\n";
 }
 
+sub parse_cli_arg {
+    my $self = shift;
+    my ($cmd, $arg) = @_;
+
+    if ( $arg =~ /^show\b/ ) {
+        $self->context->set_arg(show => 1);
+    }
+    elsif ( $arg =~ /^edit\b/ ) {
+        $self->context->set_arg(edit => 1);
+    }
+    # arg *might* be quoted
+    elsif ( $arg =~ /^delete\s+"?([^"]+)"?/ ) {
+        $self->context->set_arg(delete => $1);
+    }
+    # prophet alias "foo bar" = "foo baz"
+    # prophet alias foo = bar
+    # prophet alias add foo bar = "bar baz"
+    # prophet alias add foo bar = bar baz
+    elsif ( $arg =~
+        /^(?:add |set )?\s*(?:(?:"([^"]+)"|([^"]+))\s+=\s+(?:"([^"]+)"|([^"]+)))$/ ) {
+        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
+        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
+        $self->context->set_arg(set => "$orig=$new");
+    }
+    # prophet alias "foo = bar"
+    # prophet alias "foo bar = foo baz"
+    elsif ( $arg =~ /^(?:add |set )?\s*"([^"]+=[^"]+)"$/ ) {
+        $self->context->set_arg(set => $1);
+    }
+    # alternate syntax (preferred):
+    # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
+    # prophet alias foo bar, etc.
+    elsif ( $arg =~ /^(?:add |set )?\s*(?:"([^"]+)"|([^"\s]+))(?:\s+(?:"([^"]+)"|([^"\s]+)))?/ ) {
+        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
+        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
+        if ( $new ) {
+            $self->context->set_arg(set => "$orig=$new");
+        }
+        else {
+            $self->context->set_arg(set => $orig);
+        }
+    }
+    else {
+        die 'no idea what you mean, sorry';
+    }
+}
+
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;
 
diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index d79065a..470803a 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -75,7 +75,9 @@ on qr/^(alias(?:es)?|config)?\s+(.*)/ => sub {
     my $cmd = $1;
     my $arg = $2;
 
-    my @classes = $self->class_names('Aliases');
+    # Load Config command class so we can run
+    # its arg-parsing sub (the syntax is complex)
+    my @classes = $self->class_names('Config');
     for my $class (@classes) {
         Prophet::App->try_to_require($class) or next;
         my $aliases_cmd = $class->new(
@@ -85,7 +87,8 @@ on qr/^(alias(?:es)?|config)?\s+(.*)/ => sub {
         return run( $cmd, $self, @_ );
     }
 
-    die "Could not find 'Aliases' command class";
+    # Something is wrong with the app layout...
+    die "Could not find 'Config' command class";
 };
 
 sub run_command {

commit d0d7154fec0242b6b961128c25cb8d3ce26a0beb
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jul 21 12:53:09 2009 +0100

    Warn about possible mis-preparsed args in config/aliases commands.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 43be982..2d13ffe 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -15,7 +15,7 @@ sub run {
 
     # alias.pull --from http://foo-bar.com/
     # add is the same as set
-    if ( $self->context->has_arg('add') ) {
+    if ( $self->context->has_arg('add') && !$self->has_arg('set') ) {
         $self->context->set_arg('set', $self->arg('add') )
     }
 
diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index 40fc31f..8232a77 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -35,7 +35,7 @@ sub run {
     }
 
     # add is the same as set
-    if ( $self->context->has_arg('add') ) {
+    if ( $self->context->has_arg('add') && !$self->has_arg('set') ) {
         $self->context->set_arg('set', $self->arg('add') )
     }
 
@@ -44,20 +44,31 @@ sub run {
         if ( $self->has_arg('set') ) {
             my $value = $self->arg('set');
             if ( $value =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ ) {
+                my ($key, $value) = ($1, $2);
+                $self->_warn_unknown_args( $key, $value );
                 $config->set(
-                    key => $1,
-                    value => $2,
+                    key => $key,
+                    value => $value,
                     filename => $self->config_filename,
                 );
             }
             # no value given, just print the current value
             else {
-                print $config->get( key => $self->arg('set') ) . "\n";
+                $self->_warn_unknown_args( $self->arg('set') );
+                my $value = $config->get( key => $self->arg('set') );
+                if ( defined $value ) {
+                    print $config->get( key => $self->arg('set') ) . "\n";
+                }
+                else {
+                    print "Key " . $self->arg('set') . " is not set.\n";
+                }
             }
         }
         elsif ( $self->has_arg('delete') ) {
             my $key = $self->arg('delete');
 
+            $self->_warn_unknown_args( $key );
+
             $config->set(
                 key => $key,
                 filename => $self->config_filename,
@@ -94,6 +105,28 @@ sub run {
     }
 }
 
+sub _warn_unknown_args {
+    my $self = shift;
+    my $key = shift;
+    my $value = shift;
+
+    # help users avoid frustration if they accidentally do something
+    # like config add aliases.foo = push --to foo at bar.com
+    my %args = %{$self->args};
+    for my $arg ( qw(show edit add delete set) ) {
+        delete $args{$arg};
+    }
+    if ( keys %args != 0 ) {
+        my $args_str = join(q{ }, keys %args);
+        print "W: You have args set that aren't used by this command! Quote your\n"
+            . "W: key/value if this was accidental.\n"
+            . "W: - offending args: ${args_str}\n"
+            . "W: - running command with key '$key'";
+        print ", value '$value'" if defined $value;
+        print "\n";
+    }
+}
+
 sub make_template {
     my $self = shift;
 

commit b6898a1a8aefdd2cd14bb47f28a2066c8ff2b237
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jul 21 13:28:58 2009 +0100

    This is a user-facing error msg, not a debug msg--don't display line #.

diff --git a/lib/Prophet/CLIContext.pm b/lib/Prophet/CLIContext.pm
index 244e02c..23a1aa4 100644
--- a/lib/Prophet/CLIContext.pm
+++ b/lib/Prophet/CLIContext.pm
@@ -203,7 +203,7 @@ sub parse_args {
     my $cmp_re = $self->cmp_regex;
 
     while ( my $name = shift @args ) {
-        die "$name doesn't look like --argument"
+        die "$name doesn't look like --argument\n"
             if !$collecting_props && $name !~ /^-/;
 
         if ( $name eq '--' || $name eq '--props' ) {

commit 9aff1bb1e126f2b34e6a99366a7bf520de23a03f
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jul 21 18:53:24 2009 +0100

    Some cleanup + escape double quotes rather than hackishly converting them to single quotes.

diff --git a/lib/Prophet/CLI.pm b/lib/Prophet/CLI.pm
index 210e700..4061b44 100644
--- a/lib/Prophet/CLI.pm
+++ b/lib/Prophet/CLI.pm
@@ -90,22 +90,26 @@ sub run_one_command {
     my $ori_cmd = join ' ', @args;
     my $aliases = $self->app_handle->config->aliases;
     for my $alias ( keys %$aliases ) {
-   
-            my $command = $self->_command_matches_alias($ori_cmd, $alias, $aliases->{$alias}) || next; 
-        
-            # we don't want to recursively call if people stupidly write
-            # alias pull --local = pull --local
-            next if ( $command eq $ori_cmd );
-            return $self->run_one_command( split /\s+/, $command );
+        my $command = $self->_command_matches_alias($ori_cmd, $alias, $aliases->{$alias}) || next;
+
+        # we don't want to recursively call if people stupidly write
+        # alias pull --local = pull --local
+        next if ( $command eq $ori_cmd );
+        return $self->run_one_command( split /\s+/, $command );
     }
 
     #  really, we shouldn't be doing this stuff from the command dispatcher
     $self->context( Prophet::CLIContext->new( app_handle => $self->app_handle ) );
     $self->context->setup_from_args(@args);
     my $dispatcher = $self->dispatcher_class->new( cli => $self );
-    # XXX this means you can't have a literal " in CLI commands...
-    my $dispatch_command_string = join(' ', map { s/"/'/g; /\s/ ? qq{"$_"} : $_ }
-        @{ $self->context->primary_commands });
+
+    # Path::Dispatcher is string-based, so we need to join the args
+    # hash with spaces before passing off (args with whitespace in
+    # them are quoted, double quotes are escaped)
+    my $dispatch_command_string = join(' ', map {
+            s/"/\\"/g;  # escape double quotes
+            /\s/ ? qq{"$_"} : $_;
+        } @{ $self->context->primary_commands });
     my $dispatch = $dispatcher->dispatch( $dispatch_command_string );
     $self->start_pager();
     $dispatch->run($dispatcher);

commit c77f2b1f8a7b100fe7febecbfe13376cf6823ac1
Author: Christine Spang <spang at mit.edu>
Date:   Tue Jul 21 18:53:41 2009 +0100

    Config/Aliases improvements.
    
    Don't use crazy homebrew regexes to parse, use Text::QuoteWords'
    shellwords. Hopefully this makes things more robust.
    
    Add in usage messages for subcommands with non-trivial syntax.

diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 2d13ffe..5339c60 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -144,6 +144,16 @@ sub process_template {
     }
 }
 
+# override the messages from Config module with messages w/better context for
+# Aliases
+override delete_usage_msg => sub {
+    qq{usage: $_[1] delete "alias text"\n};
+};
+
+override add_usage_msg => sub {
+    qq{usage: $_[1] $_[2]"alias text" "cmd to translate to"\n};
+};
+
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;
 
diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index 8232a77..685e35b 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -183,36 +183,119 @@ sub parse_cli_arg {
     my $self = shift;
     my ($cmd, $arg) = @_;
 
-    if ( $arg =~ /^show\b/ ) {
+    use Text::ParseWords qw(shellwords);
+    my @args = shellwords($arg);
+
+    if ( $args[0] eq 'show' ) {
         $self->context->set_arg(show => 1);
     }
-    elsif ( $arg =~ /^edit\b/ ) {
+    elsif ( $args[0] eq 'edit' ) {
         $self->context->set_arg(edit => 1);
     }
-    # arg *might* be quoted
-    elsif ( $arg =~ /^delete\s+"?([^"]+)"?/ ) {
-        $self->context->set_arg(delete => $1);
+    elsif ( $args[0] eq 'delete' ) {
+        $self->_run_delete_subcmd( $cmd, @args[1..$#args] );
     }
+    # all of these may also contain add|set after alias
     # prophet alias "foo bar" = "foo baz"
     # prophet alias foo = bar
-    # prophet alias add foo bar = "bar baz"
-    # prophet alias add foo bar = bar baz
-    elsif ( $arg =~
-        /^(?:add |set )?\s*(?:(?:"([^"]+)"|([^"]+))\s+=\s+(?:"([^"]+)"|([^"]+)))$/ ) {
-        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
-        $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
+    # prophet alias foo bar = bar baz
+    # prophet alias foo bar = "bar baz"
+    elsif ( $args[0] =~ /^(add|set)$/
+        || (@args >= 3 && grep { m/(^=|=$)/ } @args)
+        || (@args == 2 && $args[1] =~ /=/) ) {
+        my $subcmd = $1;
+        shift @args if $args[0] =~ /^(?:add|set)$/;
+
+        $self->_run_old_syntax_add_subcmd( $cmd, $subcmd, @args );
+    }
+    # alternate syntax (preferred):
+    # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
+    # prophet alias foo bar, etc.
+    # (can still have add|set at the beginning)
+    else {
+        my $subcmd = q{};
+        if ( $args[0] =~ /^(add|set)$/ ) {
+            shift @args;
+            $subcmd = $1;
+        }
+
+        $self->_run_new_syntax_add_subcmd( $cmd, $subcmd, @args );
+    }
+}
+
+sub _run_delete_subcmd {
+    my $self = shift;
+    my $cmd = shift;
+    my @args = @_;
+
+    if ( @args ) {
+        my $remainder = join(' ', @args);
+        $self->context->set_arg(delete => $remainder);
+    }
+    else {
+        $self->_prompt_delete_usage( $cmd );
+        if ( $cmd =~ /^alias/ ) {
+            die qq{usage: $cmd delete "alias text"\n};
+        }
+    }
+}
+
+sub _run_old_syntax_add_subcmd {
+    my $self = shift;
+    my $cmd = shift;
+    my $subcmd = shift;
+    my @args = @_;
+
+    if ( @args > 1 ) {
+        # divide words up into two groups split on =
+        my (@orig_words, @new_words);
+        my $seen_equals = 0;
+        for my $word (@args) {
+            if ( $seen_equals ) {
+                push @new_words, $word;
+            }
+            else {
+                if ( $word =~ s/=$// ) {
+                    $seen_equals = 1;
+                    # allows syntax like alias add foo bar= bar baz
+                    push @orig_words, $word if $word;
+                    next;
+                }
+                elsif ( $word =~ s/^=// ) {
+                    $seen_equals = 1;
+                    # allows syntax like alias add foo bar =bar baz
+                    push @new_words, $word if $word;
+                    next;
+                }
+                push @orig_words, $word;
+            }
+        }
+        # join each group together to get what we're setting
+        my $orig = join( q{ }, @orig_words );
+        my $new  = join( q{ }, @new_words );
+
+        $orig = "'$orig'" if $cmd =~ /^alias/ && $orig =~ /\./;
         $self->context->set_arg(set => "$orig=$new");
     }
+    # all of these may also contain add|set after alias
     # prophet alias "foo = bar"
     # prophet alias "foo bar = foo baz"
-    elsif ( $arg =~ /^(?:add |set )?\s*"([^"]+=[^"]+)"$/ ) {
-        $self->context->set_arg(set => $1);
+    elsif ( defined $args[0] && $args[0] =~ /=/ ) {
+        $self->context->set_arg(set => $args[0]);
     }
-    # alternate syntax (preferred):
-    # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
-    # prophet alias foo bar, etc.
-    elsif ( $arg =~ /^(?:add |set )?\s*(?:"([^"]+)"|([^"\s]+))(?:\s+(?:"([^"]+)"|([^"\s]+)))?/ ) {
-        my ($orig, $new) = grep { defined } ($1, $2, $3, $4);
+    else {
+        $self->_prompt_add_usage( $cmd, $subcmd );
+    }
+}
+
+sub _run_new_syntax_add_subcmd {
+    my $self = shift;
+    my $cmd = shift;
+    my $subcmd = shift;
+    my @args = @_;
+
+    if ( @args <= 2 ) {
+        my ($orig, $new) = ($args[0], $args[1]);
         $orig = "'$orig'" if $cmd =~ /alias/ && $orig =~ /\./;
         if ( $new ) {
             $self->context->set_arg(set => "$orig=$new");
@@ -222,10 +305,36 @@ sub parse_cli_arg {
         }
     }
     else {
-        die 'no idea what you mean, sorry';
+        $self->_prompt_add_usage( $cmd, $subcmd );
     }
 }
 
+sub _prompt_delete_usage {
+    my $self = shift;
+    my $cmd = shift;
+
+    die $self->delete_usage_msg( $cmd );
+}
+
+sub delete_usage_msg {
+    die qq{usage: $_[1] delete section.subsection.var\n};
+}
+
+sub _prompt_add_usage {
+    my $self = shift;
+    my $cmd = shift;
+    my $subcmd = shift || '';
+
+    $subcmd .= q{ } if $subcmd;
+
+    # prompt user with the preferred syntax
+    die $self->add_usage_msg( $cmd, $subcmd );
+}
+
+sub add_usage_msg {
+    qq{usage: $_[1] $_[2]section.subsection.var ["key value"]\n};
+}
+
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;
 
diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index 470803a..b769566 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -75,9 +75,11 @@ on qr/^(alias(?:es)?|config)?\s+(.*)/ => sub {
     my $cmd = $1;
     my $arg = $2;
 
-    # Load Config command class so we can run
+    my $class = $cmd =~ /^alias/ ? 'Aliases' : 'Config';
+
+    # Load command class so we can run
     # its arg-parsing sub (the syntax is complex)
-    my @classes = $self->class_names('Config');
+    my @classes = $self->class_names($class);
     for my $class (@classes) {
         Prophet::App->try_to_require($class) or next;
         my $aliases_cmd = $class->new(
@@ -88,7 +90,7 @@ on qr/^(alias(?:es)?|config)?\s+(.*)/ => sub {
     }
 
     # Something is wrong with the app layout...
-    die "Could not find 'Config' command class";
+    die "Could not find '$class' command class";
 };
 
 sub run_command {

commit 313085d3a04eb0fef4e08a1489379550ffc5a080
Author: Christine Spang <spang at mit.edu>
Date:   Wed Jul 22 13:41:26 2009 +0100

    Update aliases tests.
    
    Use is_script_output instead of run_command so we can
    compare STDERR as well as STDOUT.
    
    Add tests for triggering usage messages on bad syntax.

diff --git a/t/aliases.t b/t/aliases.t
index d15edf4..e8ddffc 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -2,14 +2,13 @@
 #
 use warnings;
 use strict;
-use Prophet::Test tests => 33;
+use Prophet::Test tests => 38;
 use File::Temp qw/tempfile/;
+use Test::Script::Run;
 
 $ENV{'PROPHET_APP_CONFIG'} = (tempfile(UNLINK => !$ENV{PROPHET_DEBUG}))[1];
 diag("Using config file $ENV{PROPHET_APP_CONFIG}");
 
-# since we don't initialize the db for these tests, make the repo dir
-
 use_ok('Prophet::CLI');
 
 my $a = Prophet::CLI->new();
@@ -23,143 +22,164 @@ is_deeply( scalar $config->aliases, {}, 'initial alias is empty' );
 # no news is good news
 my @cmds = (
     {
-        cmd => [ 'show' ],
-        output  => qr/^No aliases for the current repository.\n$/,
+        cmd     => [ 'show' ],
+        output  => [qr/^No aliases for the current repository.$/, ''],
         comment => 'show empty aliases',
     },
-
     {
-        cmd => [ 'add', 'pull -a=pull --all' ],
-        output  => qr//,
+        cmd     => [ 'add', 'pull -a=pull --all' ],
         comment => 'add a new alias',
+        # no output specified = no output expected
     },
     {
-        cmd => [ 'pull -a' ],
-        output  => qr/pull --all/,
+        cmd     => [ 'pull -a' ],
+        output  => [qr/pull --all/],
         comment => 'new alias set correctly',
     },
     {
         # this alias is bad, please don't use it in real life
-        cmd => [ 'set', 'pull -a=pull --local' ],
-        output => qr//,
+        cmd     => [ 'set', 'pull -a=pull --local' ],
         comment =>
           q{changed alias 'pull -a' from 'pull --all' to 'pull --local'},
     },
     {
-        cmd => [ 'pull -a' ],
-        output  => qr/pull --local/,
+        cmd     => [ 'pull -a' ],
+        output  => [qr/pull --local/],
         comment => 'alias changed correctly',
     },
     {
         cmd     => [ 'delete', 'pull -a' ],
-        output  => qr//,
         comment => q{deleted alias 'pull -a = pull --local'},
     },
     {
         cmd     => [ 'delete', 'pull -a' ],
-        output  => qr//,
+        error   => [ qr/No occurrence of alias.pull -a found to unset/ ],
         comment => q{delete an alias that doesn't exist any more},
     },
     {
-        cmd => [ 'add', 'pull -a=pull --all' ],
-        output  => qr//,
+        cmd     => [ 'add', 'pull -a=pull --all' ],
         comment => 'add a new alias',
     },
     {
-        cmd => [ 'pull -a' ],
-        output  => qr/pull --all/,
+        cmd     => [ 'pull -a' ],
+        output  => [qr/pull --all/],
         comment => 'alias is set correctly',
     },
     {
-        cmd => [ 'add', 'pull -l=pull --local' ],
-        output  => qr//,
+        cmd     => [ 'add', 'pull -l=pull --local' ],
         comment => 'add a new alias',
     },
     {
-        cmd => [ 'pull -l' ],
-        output  => qr/pull --local/,
+        cmd     => [ 'pull -l' ],
+        output  => [qr/pull --local/],
         comment => 'alias is set correctly',
     },
     {
-        cmd => [ 'show' ],
-        output  => qr/Active aliases for the current repository \(including user-wide and global\naliases if not overridden\):\n\npull -l = pull --local\npull -a = pull --all/s,
+        cmd     => [ 'show' ],
+        output  => [
+            qr/Active aliases for the current repository \(including user-wide and global/,
+            qr/aliases if not overridden\):/, '',
+            'pull -l = pull --local', 'pull -a = pull --all', '',
+            ],
         comment => 'show',
     },
     {
-        cmd => [ 'add', 'foo', 'bar', '=', 'bar',  'baz' ],
-        output  => qr//,
+        cmd     => [ 'add', 'foo', 'bar', '=', 'bar',  'baz' ],
         comment => 'added alias foo bar',
     },
     {
-        cmd => [ 'foo bar' ],
-        output  => qr/bar baz/,
+        cmd     => [ 'foo bar' ],
+        output  => [qr/bar baz/],
         comment => 'alias is set correctly',
     },
     {
-        cmd => [ 'foo', 'bar', '=bar',  'baz' ],
-        output  => qr//,
-        comment => 'read alias foo bar',
+        cmd     => [ 'foo', 'bar', '=bar',  'baz' ],
+        comment => 'set alias foo bar again',
+    },
+    {
+        cmd     => [ 'foo', 'bar=', 'bar',  'baz' ],
+        comment => 'set alias with tail =',
     },
     {
         cmd => [ 'foo bar' ],
-        output  => qr/bar baz/,
+        output  => [qr/bar baz/],
         comment => 'alias foo bar still the same',
     },
     {
-        cmd => [ 'delete', 'foo', 'bar' ],
-        output  => qr//,
+        cmd     => [ 'delete', 'foo', 'bar' ],
         comment => 'deleted alias foo bar',
     },
     {
-        cmd => [ 'foo', 'bar' ],
-        output  => qr//,
+        cmd     => [ 'foo', 'bar' ],
         comment => 'deleted alias no longer exists',
     },
     {
-        cmd => [ 'set', 'foo bar', '=', 'bar baz'],
-        output => qr//,
+        cmd     => [ 'set', 'foo bar', '=', 'bar baz'],
         comment => 'set alias again with different syntax',
     },
     # tests for alternate syntax
     {
-        cmd => [ 'foo bar', 'bar baz'],
-        output  => qr//,
+        cmd     => [ 'foo bar', 'bar baz'],
         comment => 'alias foo bar = bar baz didn\'t change',
     },
     {
-        cmd => [ 'foo', 'bar baz'],
-        output  => qr//,
+        cmd     => [ 'foo', 'bar baz'],
         comment => 'added alias foo',
     },
     {
-        cmd => [ 'foo' ],
-        output  => qr/bar baz/,
+        cmd     => [ 'foo' ],
+        output  => [qr/bar baz/],
         comment => 'alias foo set correctly',
     },
     {
-        cmd => [ 'foo bar', 'bar'],
-        output => qr//,
+        cmd     => [ 'foo bar', 'bar'],
         comment => 'changed alias foo bar',
     },
     {
-        cmd => [ 'pull --from http://www.example.com/', 'pfe'],
-        output => qr//,
+        cmd     => [ 'pull --from http://www.example.com/', 'pfe'],
         comment => 'added alias with weird characters',
     },
     {
-        cmd => [ 'pull --from http://www.example.com/'],
-        output => qr/pfe/,
+        cmd     => [ 'pull --from http://www.example.com/'],
+        output  => [qr/pfe/],
         comment => 'alias with weird chars is correct',
     },
-,
+    # test cases for syntax error messages
+    {
+        cmd     => [ 'add' ],
+        error   => [qr/^usage: aliases add "alias text" "cmd to translate to"$/],
+        comment => 'add usage msg is correct',
+    },
+    {
+        cmd     => [ 'delete' ],
+        error   => [qr/^usage: aliases delete "alias text"$/],
+        comment => 'delete usage msg is correct',
+    },
+    # test warning when accidentally setting args
+    {
+        cmd     => [ 'pt', '=', 'push', '--to', 'foo at example.com' ],
+        output  => [
+            q{W: You have args set that aren't used by this command! Quote your},
+            q{W: key/value if this was accidental.},
+            q{W: - offending args: to},
+            q{W: - running command with key 'alias.pt', value 'push'},
+            ],
+        comment => 'warning when setting accidental arg',
+    },
+    {
+        cmd     => [ 'delete', 'pt' ],
+        comment => 'delete previous bad alias',
+    },
 );
 
 for my $item ( @cmds ) {
-    my $out = run_command( 'aliases', @{$item->{cmd}} );
-    like( $out, $item->{output}, $item->{comment} );
+    my $output = defined $item->{output} ? $item->{output} : [undef];
+    my $error = defined $item->{error} ? $item->{error} : [undef];
+    is_script_output( 'prophet', ['aliases', @{$item->{cmd}} ],
+        $output, $error, $item->{comment},
+    );
 }
 
-
 # check aliases in config
 my $aliases = Prophet::Config->new(
     app_handle => Prophet::CLI->new->app_handle,

commit fbadf391b7f070f854f830c3df3376d5bab27b73
Author: Christine Spang <spang at mit.edu>
Date:   Wed Jul 22 16:47:03 2009 +0100

    Initial brief usage messages for commands.

diff --git a/lib/Prophet/CLI/Command.pm b/lib/Prophet/CLI/Command.pm
index 714d532..f55e2b6 100644
--- a/lib/Prophet/CLI/Command.pm
+++ b/lib/Prophet/CLI/Command.pm
@@ -284,6 +284,69 @@ sub record_replica_in_config {
     }
 }
 
+=head2 print_usage
+
+Print the command's usage message to STDERR and die. Commands should
+implement C<usage_msg>, which returns the usage message.
+
+If the usage message method needs arguments passed in, use a closure.
+
+=cut
+
+sub print_usage {
+    my $self = shift;
+    my %args = (
+        usage_method      => sub { $self->usage_msg },
+        @_,
+    );
+
+    die $args{usage_method}();
+}
+
+=head2 get_cmd_name
+
+Return the name of the script that was run. This is the empty string
+if we're in a shell, otherwise the script name concatenated with
+a space character. This is so you can just use this for e.g.
+printing usage messages or help docs that might be run from either
+a shell or the command line.
+
+=cut
+
+sub get_cmd_name {
+    my $self = shift;
+    return '' if $self->cli->interactive_shell;
+    my ${cmd}= $0;
+    ${cmd}=~ s{^(.*)/}{}g;
+    return $cmd.' ';
+}
+
+=head2 get_cmd_and_subcmd_names [no_type => 1]
+
+Gets the name of the script that was run and the primary commands that were
+specified on the command-line. If a true boolean is passed in as C<no_type>,
+won't add '<record-type>' to the subcmd if no type was passed in via the
+primary commands.
+
+=cut
+
+sub get_cmd_and_subcmd_names {
+    my $self = shift;
+    my %args = @_;
+
+    my $cmd = $self->get_cmd_name;
+    my @primary_commands = @{ $self->context->primary_commands };
+
+    # if primary commands was only length 1, the type was not specified
+    # and we should indicate that a type is expected
+    push @primary_commands, '<record-type>'
+        if @primary_commands <= 1 && !$args{no_type};
+
+    my $type_and_subcmd = join( q{ }, @primary_commands );
+
+    return ($cmd, $type_and_subcmd);
+}
+
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;
 
diff --git a/lib/Prophet/CLI/Command/Aliases.pm b/lib/Prophet/CLI/Command/Aliases.pm
index 5339c60..bfb1c06 100644
--- a/lib/Prophet/CLI/Command/Aliases.pm
+++ b/lib/Prophet/CLI/Command/Aliases.pm
@@ -6,9 +6,22 @@ extends 'Prophet::CLI::Command::Config';
 
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(), s => 'show' };
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}aliases [show]
+       ${cmd}aliases edit [--global|--user]
+       ${cmd}alias <alias text> [<text to translate to>]
+END_USAGE
+}
+
 sub run {
     my $self     = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     my $config = $self->config;
 
     my $template = $self->make_template;
@@ -147,11 +160,19 @@ sub process_template {
 # override the messages from Config module with messages w/better context for
 # Aliases
 override delete_usage_msg => sub {
-    qq{usage: $_[1] delete "alias text"\n};
+    my $self = shift;
+    my $app_cmd = $self->get_cmd_name;
+    my $cmd = shift;
+
+    qq{usage: ${app_cmd}${cmd} "alias text"\n};
 };
 
 override add_usage_msg => sub {
-    qq{usage: $_[1] $_[2]"alias text" "cmd to translate to"\n};
+    my $self = shift;
+    my $app_cmd = $self->get_cmd_name;
+    my ($cmd, $subcmd) = @_;
+
+    qq{usage: ${app_cmd}$cmd $subcmd "alias text" "cmd to translate to"\n};
 };
 
 __PACKAGE__->meta->make_immutable;
diff --git a/lib/Prophet/CLI/Command/Clone.pm b/lib/Prophet/CLI/Command/Clone.pm
index 285cf2c..3258775 100644
--- a/lib/Prophet/CLI/Command/Clone.pm
+++ b/lib/Prophet/CLI/Command/Clone.pm
@@ -2,9 +2,20 @@ package Prophet::CLI::Command::Clone;
 use Any::Moose;
 extends 'Prophet::CLI::Command::Merge';
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}clone --from <url>
+END_USAGE
+}
+
 sub run {
     my $self = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     $self->validate_args();
 
     $self->set_arg( 'to' => $self->app_handle->handle->url() );
@@ -72,8 +83,11 @@ sub run {
 
 sub validate_args {
     my $self = shift;
-    die "Please specify a --from.\n"
-        unless $self->has_arg('from');
+
+    unless ( $self->has_arg('from') ) {
+        warn "No --from specified!\n";
+        die $self->print_usage;
+    }
 }
 
 # When we clone from another replica, we ALWAYS want to take their way forward,
diff --git a/lib/Prophet/CLI/Command/Config.pm b/lib/Prophet/CLI/Command/Config.pm
index 685e35b..7b56ffd 100644
--- a/lib/Prophet/CLI/Command/Config.pm
+++ b/lib/Prophet/CLI/Command/Config.pm
@@ -22,9 +22,22 @@ has old_errors => (
 
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  a => 'add', d => 'delete', s => 'show' };
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}config [show]
+       ${cmd}config edit [--global|--user]
+       ${cmd}config <section.subsection.var> [<value>]
+END_USAGE
+}
+
 sub run {
     my $self = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     my $config = $self->config;
 
     if ($self->has_arg('global')) {
@@ -40,15 +53,14 @@ sub run {
     }
 
     if ( $self->has_arg('set') || $self->has_arg('delete') ) {
-
         if ( $self->has_arg('set') ) {
             my $value = $self->arg('set');
             if ( $value =~ /^\s*(.+?)\s*=\s*(.+?)\s*$/ ) {
                 my ($key, $value) = ($1, $2);
                 $self->_warn_unknown_args( $key, $value );
                 $config->set(
-                    key => $key,
-                    value => $value,
+                    key      => $key,
+                    value    => $value,
                     filename => $self->config_filename,
                 );
             }
@@ -90,6 +102,8 @@ sub run {
         }
     }
     else {
+        # if no args are given, print out the contents of the currently loaded
+        # config files
         print "Configuration:\n\n";
         my @files =@{$config->config_files};
         if (!scalar @files) {
@@ -193,7 +207,7 @@ sub parse_cli_arg {
         $self->context->set_arg(edit => 1);
     }
     elsif ( $args[0] eq 'delete' ) {
-        $self->_run_delete_subcmd( $cmd, @args[1..$#args] );
+        $self->_setup_delete_subcmd( "$cmd delete", @args[1..$#args] );
     }
     # all of these may also contain add|set after alias
     # prophet alias "foo bar" = "foo baz"
@@ -206,7 +220,7 @@ sub parse_cli_arg {
         my $subcmd = $1;
         shift @args if $args[0] =~ /^(?:add|set)$/;
 
-        $self->_run_old_syntax_add_subcmd( $cmd, $subcmd, @args );
+        $self->_setup_old_syntax_add_subcmd( $cmd, $subcmd, @args );
     }
     # alternate syntax (preferred):
     # prophet alias "foo bar" "bar baz", prophet alias foo "bar baz",
@@ -219,28 +233,34 @@ sub parse_cli_arg {
             $subcmd = $1;
         }
 
-        $self->_run_new_syntax_add_subcmd( $cmd, $subcmd, @args );
+        $self->_setup_new_syntax_add_subcmd( $cmd, $subcmd, @args );
     }
 }
 
-sub _run_delete_subcmd {
+sub _setup_delete_subcmd {
     my $self = shift;
     my $cmd = shift;
     my @args = @_;
 
     if ( @args ) {
-        my $remainder = join(' ', @args);
+        my $remainder = join(q{ }, @args);
         $self->context->set_arg(delete => $remainder);
     }
     else {
-        $self->_prompt_delete_usage( $cmd );
-        if ( $cmd =~ /^alias/ ) {
-            die qq{usage: $cmd delete "alias text"\n};
+        if ( $cmd =~ /delete/ ) {
+            $self->print_usage(
+                usage_method => sub {
+                    $self->delete_usage_msg( $cmd );
+                },
+            );
+        }
+        else {
+            $self->print_usage;
         }
     }
 }
 
-sub _run_old_syntax_add_subcmd {
+sub _setup_old_syntax_add_subcmd {
     my $self = shift;
     my $cmd = shift;
     my $subcmd = shift;
@@ -284,15 +304,19 @@ sub _run_old_syntax_add_subcmd {
         $self->context->set_arg(set => $args[0]);
     }
     else {
-        $self->_prompt_add_usage( $cmd, $subcmd );
+        $self->print_usage(
+            usage_method      => sub {
+                $self->add_usage_msg($cmd, $subcmd);
+            },
+        );
     }
 }
 
-sub _run_new_syntax_add_subcmd {
-    my $self = shift;
-    my $cmd = shift;
+sub _setup_new_syntax_add_subcmd {
+    my $self   = shift;
+    my $cmd    = shift;
     my $subcmd = shift;
-    my @args = @_;
+    my @args   = @_;
 
     if ( @args <= 2 ) {
         my ($orig, $new) = ($args[0], $args[1]);
@@ -305,34 +329,28 @@ sub _run_new_syntax_add_subcmd {
         }
     }
     else {
-        $self->_prompt_add_usage( $cmd, $subcmd );
+        $self->print_usage(
+            usage_method      => sub {
+                $self->add_usage_msg($cmd, $subcmd);
+            },
+        );
     }
 }
 
-sub _prompt_delete_usage {
-    my $self = shift;
-    my $cmd = shift;
-
-    die $self->delete_usage_msg( $cmd );
-}
-
 sub delete_usage_msg {
-    die qq{usage: $_[1] delete section.subsection.var\n};
-}
-
-sub _prompt_add_usage {
     my $self = shift;
+    my $app_cmd = $self->get_cmd_name;
     my $cmd = shift;
-    my $subcmd = shift || '';
 
-    $subcmd .= q{ } if $subcmd;
-
-    # prompt user with the preferred syntax
-    die $self->add_usage_msg( $cmd, $subcmd );
+    qq{usage: ${app_cmd}${cmd} section.subsection.var\n};
 }
 
 sub add_usage_msg {
-    qq{usage: $_[1] $_[2]section.subsection.var ["key value"]\n};
+    my $self = shift;
+    my $app_cmd = $self->get_cmd_name;
+    my ($cmd, $subcmd) = @_;
+
+    qq{usage: ${app_cmd}${cmd} ${subcmd} section.subsection.var ["key value"]\n};
 }
 
 __PACKAGE__->meta->make_immutable;
diff --git a/lib/Prophet/CLI/Command/Create.pm b/lib/Prophet/CLI/Command/Create.pm
index 8fe3d63..1e88c44 100644
--- a/lib/Prophet/CLI/Command/Create.pm
+++ b/lib/Prophet/CLI/Command/Create.pm
@@ -10,8 +10,20 @@ has record => (
     documentation => 'The record object of the created record.',
 );
 
+sub usage_msg {
+    my $self = shift;
+    my ($cmd, $type_and_subcmd) = $self->get_cmd_and_subcmd_names;
+
+    return <<"END_USAGE";
+usage: ${cmd}${type_and_subcmd} -- prop1=foo prop2=bar
+END_USAGE
+}
+
 sub run {
     my $self   = shift;
+
+    $self->print_usage if $self->has_arg('h');
+
     my $record = $self->_get_record_object;
     my ($val, $msg) = $record->create( props => $self->edit_props );
     if (!$val) {
diff --git a/lib/Prophet/CLI/Command/Delete.pm b/lib/Prophet/CLI/Command/Delete.pm
index 6c498c9..779d405 100644
--- a/lib/Prophet/CLI/Command/Delete.pm
+++ b/lib/Prophet/CLI/Command/Delete.pm
@@ -3,9 +3,20 @@ use Any::Moose;
 extends 'Prophet::CLI::Command';
 with 'Prophet::CLI::RecordCommand';
 
+sub usage_msg {
+    my $self = shift;
+    my ($cmd, $type_and_subcmd) = $self->get_cmd_and_subcmd_names;
+
+    return <<"END_USAGE";
+usage: ${cmd}${type_and_subcmd} <id>
+END_USAGE
+}
+
 sub run {
     my $self = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     $self->context->require_uuid;
     my $record = $self->_load_record;
 
diff --git a/lib/Prophet/CLI/Command/Export.pm b/lib/Prophet/CLI/Command/Export.pm
index 20ae1fb..d24b11f 100644
--- a/lib/Prophet/CLI/Command/Export.pm
+++ b/lib/Prophet/CLI/Command/Export.pm
@@ -2,18 +2,30 @@ package Prophet::CLI::Command::Export;
 use Any::Moose;
 extends 'Prophet::CLI::Command';
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}export --path <path> [--format feed]
+END_USAGE
+}
+
 sub run {
     my $self = shift;
     my $class;
 
+    $self->print_usage if $self->has_arg('h');
+
     unless ($self->context->has_arg('path')) {
-        die "You need to specify a --path argument to the 'export' command"."\n";
+        warn "No --path argument specified!\n";
+        $self->print_usage;
     }
 
-    
     if ($self->context->has_arg('format') && ($self->context->arg('format') eq 'feed') ){
         $class = 'Prophet::ReplicaFeedExporter';
-    } else {
+    }
+    else {
         $class = 'Prophet::ReplicaExporter';
     }
 
diff --git a/lib/Prophet/CLI/Command/History.pm b/lib/Prophet/CLI/Command/History.pm
index 8a196a4..6dc41e2 100644
--- a/lib/Prophet/CLI/Command/History.pm
+++ b/lib/Prophet/CLI/Command/History.pm
@@ -3,9 +3,20 @@ use Any::Moose;
 extends 'Prophet::CLI::Command';
 with 'Prophet::CLI::RecordCommand';
 
+sub usage_msg {
+    my $self = shift;
+    my ($cmd, $type_and_subcmd) = $self->get_cmd_and_subcmd_names;
+
+    return <<"END_USAGE";
+usage: ${cmd}${type_and_subcmd} <record>
+END_USAGE
+}
+
 sub run {
     my $self = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     $self->context->require_uuid;
     my $record = $self->_load_record;
 
diff --git a/lib/Prophet/CLI/Command/Info.pm b/lib/Prophet/CLI/Command/Info.pm
index a8b7f37..b3960f0 100644
--- a/lib/Prophet/CLI/Command/Info.pm
+++ b/lib/Prophet/CLI/Command/Info.pm
@@ -4,8 +4,20 @@ extends 'Prophet::CLI::Command';
 
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  l => 'local' };
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}info
+END_USAGE
+}
+
 sub run {
     my $self = shift;
+
+    $self->print_usage if $self->has_arg('h');
+
     print "Working on prophet database: ".$self->handle->url." (@{[ref($self->handle)]})".$/;
     print "DB UUID: ".$self->handle->db_uuid.$/;
     print "Changets: ".$self->handle->latest_sequence_no.$/;
diff --git a/lib/Prophet/CLI/Command/Init.pm b/lib/Prophet/CLI/Command/Init.pm
index cba2f59..2322400 100644
--- a/lib/Prophet/CLI/Command/Init.pm
+++ b/lib/Prophet/CLI/Command/Init.pm
@@ -2,9 +2,22 @@ package Prophet::CLI::Command::Init;
 use Any::Moose;
 extends 'Prophet::CLI::Command';
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+    my $env_var = uc $cmd . '_REPO';
+    $env_var =~ s/ //;
+
+    return <<"END_USAGE";
+usage: [${env_var}=/path/to/new/repo] ${cmd}init
+END_USAGE
+}
+
 sub run {
     my $self = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     if ($self->app_handle->handle->replica_exists) {
         print "Your Prophet database already exists.\n";
         return;
diff --git a/lib/Prophet/CLI/Command/Log.pm b/lib/Prophet/CLI/Command/Log.pm
index 89e5075..78725ad 100644
--- a/lib/Prophet/CLI/Command/Log.pm
+++ b/lib/Prophet/CLI/Command/Log.pm
@@ -2,6 +2,18 @@ package Prophet::CLI::Command::Log;
 use Any::Moose;
 extends 'Prophet::CLI::Command';
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}log --all              Show all entries
+       ${cmd}log 0..LATEST~5        Show first entry up until the latest
+       ${cmd}log LATEST~10          Show last ten entries
+       ${cmd}log LATEST             Show last entry
+END_USAGE
+}
+
 # Default: last 20 entries.
 # sd log --all                    # show it all (overrides everything else)
 # sd log --range 0..LATEST~5      # shows the first until 5 from the latest
@@ -16,6 +28,8 @@ sub run {
     my $self   = shift;
     my $handle = $self->handle;
 
+    $self->print_usage if $self->has_arg('h');
+
     # --all overrides any other args
     if ($self->has_arg('all')) {
         $self->set_arg('range', '0..'.$handle->latest_sequence_no);
diff --git a/lib/Prophet/CLI/Command/Merge.pm b/lib/Prophet/CLI/Command/Merge.pm
index 1d23276..5a0af36 100644
--- a/lib/Prophet/CLI/Command/Merge.pm
+++ b/lib/Prophet/CLI/Command/Merge.pm
@@ -11,9 +11,25 @@ sub ARG_TRANSLATIONS {
     shift->SUPER::ARG_TRANSLATIONS(),  f => 'force' , n => 'dry-run',
 };
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}merge --from <replica> --to <replica> [options]
+
+Options are:
+    -v|--verbose            Be verbose
+    -f|--force              Do merge even if replica UUIDs differ
+    -n|--dry-run            Don't actually import changesets
+END_USAGE
+}
+
 sub run {
     my $self = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     Prophet::CLI->end_pager();
 
     $self->source( Prophet::Replica->get_handle(
diff --git a/lib/Prophet/CLI/Command/Mirror.pm b/lib/Prophet/CLI/Command/Mirror.pm
index c954271..d7ad37f 100644
--- a/lib/Prophet/CLI/Command/Mirror.pm
+++ b/lib/Prophet/CLI/Command/Mirror.pm
@@ -10,12 +10,22 @@ has target => ( isa => 'Prophet::Replica', is => 'rw');
 
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  f => 'force' };
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}mirror --from <url>
+END_USAGE
+}
+
 sub run {
     my $self = shift;
     Prophet::CLI->end_pager();
 
-    $self->validate_args();
+    $self->print_usage if $self->has_arg('h');
 
+    $self->validate_args();
 
     my $source = Prophet::Replica->get_handle( url        => $self->arg('from'), app_handle => $self->app_handle,);
     unless ( $source->replica_exists ) {
@@ -31,8 +41,10 @@ sub run {
 
 sub validate_args {
     my $self = shift;
-    die "Please specify a --from.\n"
-        unless $self->has_arg('from');
+    unless ( $self->has_arg('from') ) {
+        warn "No --from specified!\n";
+        $self->print_usage;
+    }
 }
 
 __PACKAGE__->meta->make_immutable;
diff --git a/lib/Prophet/CLI/Command/Publish.pm b/lib/Prophet/CLI/Command/Publish.pm
index 4bf2526..84d7181 100644
--- a/lib/Prophet/CLI/Command/Publish.pm
+++ b/lib/Prophet/CLI/Command/Publish.pm
@@ -7,9 +7,24 @@ with 'Prophet::CLI::CollectionCommand';
 use File::Path;
 use File::Spec;
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}publish --to <location|name> [--html] [--replica]
+END_USAGE
+}
+
 sub run {
     my $self = shift;
-    die "Please specify a --to.\n" unless $self->has_arg('to');
+
+    $self->print_usage if $self->has_arg('h');
+
+    unless ($self->has_arg('to')) {
+        warn "No --to specified!\n";
+        $self->print_usage;
+    }
 
     # substitute publish-url config variable for to arg if possible
     my %previous_sources_by_name
diff --git a/lib/Prophet/CLI/Command/Pull.pm b/lib/Prophet/CLI/Command/Pull.pm
index 1649795..6940804 100644
--- a/lib/Prophet/CLI/Command/Pull.pm
+++ b/lib/Prophet/CLI/Command/Pull.pm
@@ -4,10 +4,23 @@ extends 'Prophet::CLI::Command::Merge';
 
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  l => 'local' };
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}pull --from <url|name>
+       ${cmd}pull --all
+       ${cmd}pull --local
+END_USAGE
+}
+
 sub run {
     my $self = shift;
     my @from;
 
+    $self->print_usage if $self->has_arg('h');
+
     Prophet::CLI->end_pager();
 
     # prefer replica.name.pull-url if it exists, otherwise use
@@ -56,10 +69,12 @@ sub run {
 
 sub validate_args {
     my $self = shift;
-    die "Please specify a --from, --local or --all.\n"
-        unless ( $self->has_arg('from')
-        || $self->has_arg('local')
-        || $self->has_arg('all') );
+
+    unless ( $self->has_arg('from') || $self->has_arg('local') ||
+        $self->has_arg('all') ) {
+        warn "No --from, --local, or --all specified!\n";
+        $self->print_usage;
+    }
 }
 
 =head2 find_bonjour_sources
diff --git a/lib/Prophet/CLI/Command/Push.pm b/lib/Prophet/CLI/Command/Push.pm
index dbe7bd4..c224377 100644
--- a/lib/Prophet/CLI/Command/Push.pm
+++ b/lib/Prophet/CLI/Command/Push.pm
@@ -2,11 +2,22 @@ package Prophet::CLI::Command::Push;
 use Any::Moose;
 extends 'Prophet::CLI::Command::Merge';
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}push --to <url|name> [--force]
+END_USAGE
+}
+
 sub run {
     my $self = shift;
 
     Prophet::CLI->end_pager();
 
+    $self->print_usage if $self->has_arg('h');
+
     $self->validate_args;
 
     # sub out friendly names for replica URLs if possible
@@ -57,9 +68,13 @@ sub run {
 sub validate_args {
     my $self = shift;
 
-    die "Please specify a --to.\n" unless $self->context->has_arg('to');
+    unless ( $self->context->has_arg('to') ) {
+       warn "No --to specified!\n";
+       $self->print_usage;
+    }
 }
 
+
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;
 
diff --git a/lib/Prophet/CLI/Command/Search.pm b/lib/Prophet/CLI/Command/Search.pm
index 3d5f6e0..4eff68c 100644
--- a/lib/Prophet/CLI/Command/Search.pm
+++ b/lib/Prophet/CLI/Command/Search.pm
@@ -35,6 +35,15 @@ has group_routine => (
         'A subroutine which takes an arrayref to a list of records and returns an array of hashrefs  { label => $label, records => \@array}'
 );
 
+sub usage_msg {
+    my $self = shift;
+    my ($cmd, $type_and_subcmd) = $self->get_cmd_and_subcmd_names;
+
+    return <<"END_USAGE";
+usage: ${cmd}${type_and_subcmd}
+       ${cmd}${type_and_subcmd} -- prop1=~foo prop2!~bar|baz
+END_USAGE
+}
 
 sub default_match { 1 }
 
@@ -112,6 +121,8 @@ sub _compare {
 sub run {
     my $self = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     my $records = $self->get_collection_object();
     my $search_cb = $self->get_search_callback();
     $records->matching($search_cb);
diff --git a/lib/Prophet/CLI/Command/Server.pm b/lib/Prophet/CLI/Command/Server.pm
index 5b0ff2a..bb6c4eb 100644
--- a/lib/Prophet/CLI/Command/Server.pm
+++ b/lib/Prophet/CLI/Command/Server.pm
@@ -6,6 +6,15 @@ sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  p => 'port', w => 'wri
 
 use Prophet::Server;
 
+sub usage_msg {
+    my $self = shift;
+    my ($cmd, $subcmd) = $self->get_cmd_and_subcmd_names( no_type => 1 );
+
+    return <<"END_USAGE";
+usage: ${cmd}${subcmd} [--port <number>]
+END_USAGE
+}
+
 sub run {
     my $self = shift;
     my $server = $self->setup_server();
@@ -15,10 +24,13 @@ sub run {
 
 sub setup_server {
     my $self = shift;
-     my $server_class = ref($self->app_handle) . "::Server";
-     if (!$self->app_handle->try_to_require($server_class)) {
-         $server_class = "Prophet::Server";
-     }
+
+    $self->print_usage if $self->has_arg('h');
+
+    my $server_class = ref($self->app_handle) . "::Server";
+    if (!$self->app_handle->try_to_require($server_class)) {
+        $server_class = "Prophet::Server";
+    }
     my $server = $server_class->new( $self->arg('port') || 8080 );
     $server->app_handle( $self->app_handle );
     return $server;
diff --git a/lib/Prophet/CLI/Command/Settings.pm b/lib/Prophet/CLI/Command/Settings.pm
index efc854a..fe7a0e6 100644
--- a/lib/Prophet/CLI/Command/Settings.pm
+++ b/lib/Prophet/CLI/Command/Settings.pm
@@ -8,8 +8,22 @@ with 'Prophet::CLI::TextEditorCommand';
 
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  s => 'show' };
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
+
+    return <<"END_USAGE";
+usage: ${cmd}settings [--show]
+       ${cmd}settings [--edit]
+       ${cmd}settings --set -- setting "new value"
+END_USAGE
+}
+
 sub run {
     my $self     = shift;
+
+    $self->print_usage if $self->has_arg('h');
+
     my $template = $self->make_template;
 
     if ( $self->context->has_arg('show') ) {
diff --git a/lib/Prophet/CLI/Command/Shell.pm b/lib/Prophet/CLI/Command/Shell.pm
index 95a7de8..a1ced58 100644
--- a/lib/Prophet/CLI/Command/Shell.pm
+++ b/lib/Prophet/CLI/Command/Shell.pm
@@ -28,7 +28,14 @@ has term => (
     our $LEN = $ENV{PROPHET_HISTLEN} || 500;
 
 
+sub usage_msg {
+    my $self = shift;
+    my $cmd = $self->get_cmd_name;
 
+    return <<"END_USAGE";
+usage: ${cmd}\[shell]
+END_USAGE
+}
 
 sub prompt {
     my $self = shift;
@@ -82,6 +89,9 @@ sub _run {
 # make the REPL history persistent
 sub run{
     my $self = shift;
+
+    $self->print_usage if $self->has_arg('h');
+
     $self->_read_repl_history();
     $self->_run(@_);
     $self->_write_repl_history();
diff --git a/lib/Prophet/CLI/Command/Show.pm b/lib/Prophet/CLI/Command/Show.pm
index 9cef523..dc51063 100644
--- a/lib/Prophet/CLI/Command/Show.pm
+++ b/lib/Prophet/CLI/Command/Show.pm
@@ -6,9 +6,20 @@ with 'Prophet::CLI::RecordCommand';
 
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  'b' => 'batch' };
 
+sub usage_msg {
+    my $self = shift;
+    my ($cmd, $type_and_subcmd) = $self->get_cmd_and_subcmd_names;
+
+    return <<"END_USAGE";
+usage: ${cmd}$type_and_subcmd <record-id> [--batch] [--verbose]
+END_USAGE
+}
+
 sub run {
     my $self = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     $self->context->require_uuid;
     my $record = $self->_load_record;
 
diff --git a/lib/Prophet/CLI/Command/Update.pm b/lib/Prophet/CLI/Command/Update.pm
index aa4425e..b9786b6 100644
--- a/lib/Prophet/CLI/Command/Update.pm
+++ b/lib/Prophet/CLI/Command/Update.pm
@@ -5,6 +5,16 @@ with 'Prophet::CLI::RecordCommand';
 
 sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(), e => 'edit' };
 
+sub usage_msg {
+    my $self = shift;
+    my ($cmd, $type_and_subcmd) = $self->get_cmd_and_subcmd_names;
+
+    return <<"END_USAGE";
+usage: ${cmd}${type_and_subcmd} <record-id> --edit
+       ${cmd}${type_and_subcmd} <record-id> -- prop1="new value"
+END_USAGE
+}
+
 sub edit_record {
     my $self   = shift;
     my $record = shift;
@@ -28,6 +38,8 @@ sub edit_record {
 sub run {
     my $self = shift;
 
+    $self->print_usage if $self->has_arg('h');
+
     $self->context->require_uuid;
     my $record = $self->_load_record;
 
diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index b769566..e11aea2 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -82,10 +82,11 @@ on qr/^(alias(?:es)?|config)?\s+(.*)/ => sub {
     my @classes = $self->class_names($class);
     for my $class (@classes) {
         Prophet::App->try_to_require($class) or next;
-        my $aliases_cmd = $class->new(
+        my $cmd_obj = $class->new(
             context => $self->context,
+            cli     => $self->cli,
         );
-        $aliases_cmd->parse_cli_arg($cmd, $arg);
+        $cmd_obj->parse_cli_arg($cmd, $arg);
         return run( $cmd, $self, @_ );
     }
 
diff --git a/t/aliases.t b/t/aliases.t
index e8ddffc..44de815 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -147,12 +147,12 @@ my @cmds = (
     # test cases for syntax error messages
     {
         cmd     => [ 'add' ],
-        error   => [qr/^usage: aliases add "alias text" "cmd to translate to"$/],
+        error   => [qr/^usage: prophet aliases add "alias text" "cmd to translate to"$/],
         comment => 'add usage msg is correct',
     },
     {
         cmd     => [ 'delete' ],
-        error   => [qr/^usage: aliases delete "alias text"$/],
+        error   => [qr/^usage: prophet aliases delete "alias text"$/],
         comment => 'delete usage msg is correct',
     },
     # test warning when accidentally setting args

commit f4f22144baf55254776f436c429b1b6df818b079
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Aug 3 19:37:00 2009 +0100

    Commands require UUIDs, not the context. (bad factoring)
    
    We can just kill the require_uuid method in CLIContext and
    use the one in Command instead.

diff --git a/lib/Prophet/CLI/Command/Delete.pm b/lib/Prophet/CLI/Command/Delete.pm
index 779d405..97bf64a 100644
--- a/lib/Prophet/CLI/Command/Delete.pm
+++ b/lib/Prophet/CLI/Command/Delete.pm
@@ -17,7 +17,7 @@ sub run {
 
     $self->print_usage if $self->has_arg('h');
 
-    $self->context->require_uuid;
+    $self->require_uuid;
     my $record = $self->_load_record;
 
     if ( $record->delete ) {
diff --git a/lib/Prophet/CLI/Command/History.pm b/lib/Prophet/CLI/Command/History.pm
index 6dc41e2..cf8dff9 100644
--- a/lib/Prophet/CLI/Command/History.pm
+++ b/lib/Prophet/CLI/Command/History.pm
@@ -17,7 +17,7 @@ sub run {
 
     $self->print_usage if $self->has_arg('h');
 
-    $self->context->require_uuid;
+    $self->require_uuid;
     my $record = $self->_load_record;
 
     print $record->history_as_string;
diff --git a/lib/Prophet/CLI/Command/Show.pm b/lib/Prophet/CLI/Command/Show.pm
index dc51063..d471f5f 100644
--- a/lib/Prophet/CLI/Command/Show.pm
+++ b/lib/Prophet/CLI/Command/Show.pm
@@ -20,7 +20,7 @@ sub run {
 
     $self->print_usage if $self->has_arg('h');
 
-    $self->context->require_uuid;
+    $self->require_uuid;
     my $record = $self->_load_record;
 
     print $self->stringify_props(
diff --git a/lib/Prophet/CLI/Command/Update.pm b/lib/Prophet/CLI/Command/Update.pm
index b9786b6..5937ba4 100644
--- a/lib/Prophet/CLI/Command/Update.pm
+++ b/lib/Prophet/CLI/Command/Update.pm
@@ -40,7 +40,7 @@ sub run {
 
     $self->print_usage if $self->has_arg('h');
 
-    $self->context->require_uuid;
+    $self->require_uuid;
     my $record = $self->_load_record;
 
     my $new_props = $self->edit_record($record);
diff --git a/lib/Prophet/CLIContext.pm b/lib/Prophet/CLIContext.pm
index 23a1aa4..861e87d 100644
--- a/lib/Prophet/CLIContext.pm
+++ b/lib/Prophet/CLIContext.pm
@@ -160,22 +160,6 @@ sub setup_from_args {
 
 }
 
-
-=head2 require_uuid
-
-Checks to make sure the uuid attribute is set. Prints an error and dies
-if it is not set.
-
-=cut
-
-sub require_uuid {
-    my $self    = shift;
-
-    if (!$self->has_uuid) {
-        die "This command requires a luid or uuid (use --id to specify).\n";
-    }
-}
-
 =head2 parse_args @args
 
 This routine pulls arguments (specified by --key=value or --key

commit c4873def259f0d733dee0f27300d48ed6b78c95f
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Aug 3 19:38:16 2009 +0100

    Don't talk about sd in prophet.

diff --git a/lib/Prophet/CLI/Command/Log.pm b/lib/Prophet/CLI/Command/Log.pm
index 78725ad..34a6a45 100644
--- a/lib/Prophet/CLI/Command/Log.pm
+++ b/lib/Prophet/CLI/Command/Log.pm
@@ -15,14 +15,14 @@ END_USAGE
 }
 
 # Default: last 20 entries.
-# sd log --all                    # show it all (overrides everything else)
-# sd log --range 0..LATEST~5      # shows the first until 5 from the latest
-# sd log --range LATEST~10        # shows last 10 entries
-# sd log --range LATEST           # shows the latest entry
+# prophet log --all                    # show it all (overrides everything else)
+# prophet log --range 0..LATEST~5      # shows the first until 5 from the latest
+# prophet log --range LATEST~10        # shows last 10 entries
+# prophet log --range LATEST           # shows the latest entry
 
 # syntactic sugar in dispatcher:
-#  sd log 0..LATEST~5 => sd log --range 0..LATEST~5
-#  sd log LATEST~10   => sd log --range LATEST~10
+#  prophet log 0..LATEST~5 => prophet log --range 0..LATEST~5
+#  prophet log LATEST~10   => prophet log --range LATEST~10
 
 sub run {
     my $self   = shift;

commit 31833bf5532fdef4a4092df8ccb916fa1cca8f06
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Aug 3 20:13:02 2009 +0100

    Error out with the command's usage when ID is missing.

diff --git a/lib/Prophet/CLI/Command.pm b/lib/Prophet/CLI/Command.pm
index f55e2b6..90c306c 100644
--- a/lib/Prophet/CLI/Command.pm
+++ b/lib/Prophet/CLI/Command.pm
@@ -81,7 +81,7 @@ sub fatal_error {
 =head2 require_uuid
 
 Checks to make sure the uuid attribute is set. Prints an error and dies
-if it is not set.
+with the command's usage string if it is not set.
 
 =cut
 
@@ -91,7 +91,8 @@ sub require_uuid {
     if (!$self->has_uuid) {
         my $type = $self->type;
         my $name = (split /::/, $self->meta->name)[-1];
-        die "\u$type \l$name requires a luid or uuid (use --id to specify).\n";
+        warn "No UUID or LUID given!\n";
+        $self->print_usage;
     }
 }
 

commit f68bea3a3efd08e9cda398c63081454432709ff9
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Aug 4 15:33:33 2009 +0100

    gitignore Devel::Cover output

diff --git a/.gitignore b/.gitignore
index 5f68f73..66d08f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ Makefile.old
 .prove
 MANIFEST*
 META.yml
+cover_db

commit c6b01984879bbd8c7ddcc47bb9f8173906966f1f
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Aug 4 20:06:31 2009 +0100

    Make Prophet::Test->run_command support returning STDERR
    
    Using run_command as opposed to Test::Script::Run::is_script_output
    and friends is some 7x faster--so let's use it while we can.

diff --git a/lib/Prophet/CLI.pm b/lib/Prophet/CLI.pm
index 4061b44..5579f96 100644
--- a/lib/Prophet/CLI.pm
+++ b/lib/Prophet/CLI.pm
@@ -142,24 +142,45 @@ sub tokenize {
 }
 
 
-=head2 invoke outhandle, ARGV_COMPATIBLE_ARRAY
+=head2 invoke outhandle, error_variable_ref, ARGV_COMPATIBLE_ARRAY
 
 Run the given command. If outhandle is true, select that as the file handle
-for the duration of the command.
+for the duration of the command. If error_variable_ref is true, STDERR
+will be redirected to that scalar for the duration of the command.
 
 =cut
 
 sub invoke {
-    my ($self, $output, @args) = @_;
-    my $ofh;
+    my ($self, $output, $error, @args) = @_;
+
+    # save old STDOUT for later restoration and select $output as the
+    # new default filehandle
+    my $original_stdout;
+    $original_stdout = select $output if $output;
+
+    my $original_stderr;
+    # save STDERR away for later restoration
+    if ( ref $error ) {
+        open $original_stderr, '>&', \*STDERR;
+        close STDERR;
+        open STDERR, ">", $error or die "Couldn't open STDERR for redirection: $!";
+    }
 
-    $ofh = select $output if $output;
     my $ret = eval {
         local $SIG{__DIE__} = 'DEFAULT';
         $self->run_one_command(@args);
     };
     warn $@ if $@;
-    select $ofh if $ofh;
+
+    if ( ref $error ) {
+        # restore STDERR
+        close STDERR;
+        open STDERR, ">", $original_stderr;
+    }
+
+    # restore STDOUT
+    select $original_stdout if $original_stdout;
+
     return $ret;
 }
 
diff --git a/lib/Prophet/Test.pm b/lib/Prophet/Test.pm
index 78a45ec..43c4d76 100644
--- a/lib/Prophet/Test.pm
+++ b/lib/Prophet/Test.pm
@@ -309,7 +309,8 @@ sub serialize_changeset {
 
 Run the given command with (optionally) the given args using a new
 L<Prophet::CLI> object. Returns the standard output of that command
-in scalar form.
+in scalar form or, in array context, the STDOUT in scalar form
+*and* the STDERR in scalar form.
 
 Examples:
 
@@ -319,9 +320,12 @@ Examples:
 
 sub run_command {
     my $output = '';
-    open my $handle, '>', \$output;
-    Prophet::CLI->new->invoke($handle, @_);
-    return $output;
+    my $error  = '';
+    open my $out_handle, '>', \$output;
+
+    Prophet::CLI->new->invoke($out_handle, \$error, @_);
+
+    return wantarray ? ($output, $error) : $output;
 }
 
 {
diff --git a/t/aliases.t b/t/aliases.t
index 44de815..f97354e 100644
--- a/t/aliases.t
+++ b/t/aliases.t
@@ -2,9 +2,8 @@
 #
 use warnings;
 use strict;
-use Prophet::Test tests => 38;
+use Prophet::Test tests => 68;
 use File::Temp qw/tempfile/;
-use Test::Script::Run;
 
 $ENV{'PROPHET_APP_CONFIG'} = (tempfile(UNLINK => !$ENV{PROPHET_DEBUG}))[1];
 diag("Using config file $ENV{PROPHET_APP_CONFIG}");
@@ -23,7 +22,7 @@ is_deeply( scalar $config->aliases, {}, 'initial alias is empty' );
 my @cmds = (
     {
         cmd     => [ 'show' ],
-        output  => [qr/^No aliases for the current repository.$/, ''],
+        output  => qr/No aliases for the current repository/,
         comment => 'show empty aliases',
     },
     {
@@ -33,7 +32,7 @@ my @cmds = (
     },
     {
         cmd     => [ 'pull -a' ],
-        output  => [qr/pull --all/],
+        output  => qr/pull --all/,
         comment => 'new alias set correctly',
     },
     {
@@ -44,7 +43,7 @@ my @cmds = (
     },
     {
         cmd     => [ 'pull -a' ],
-        output  => [qr/pull --local/],
+        output  => qr/pull --local/,
         comment => 'alias changed correctly',
     },
     {
@@ -53,7 +52,7 @@ my @cmds = (
     },
     {
         cmd     => [ 'delete', 'pull -a' ],
-        error   => [ qr/No occurrence of alias.pull -a found to unset/ ],
+        error   => qr/No occurrence of alias.pull -a found to unset/,
         comment => q{delete an alias that doesn't exist any more},
     },
     {
@@ -62,7 +61,7 @@ my @cmds = (
     },
     {
         cmd     => [ 'pull -a' ],
-        output  => [qr/pull --all/],
+        output  => qr/pull --all/,
         comment => 'alias is set correctly',
     },
     {
@@ -71,16 +70,13 @@ my @cmds = (
     },
     {
         cmd     => [ 'pull -l' ],
-        output  => [qr/pull --local/],
+        output  => qr/pull --local/,
         comment => 'alias is set correctly',
     },
     {
         cmd     => [ 'show' ],
-        output  => [
-            qr/Active aliases for the current repository \(including user-wide and global/,
-            qr/aliases if not overridden\):/, '',
-            'pull -l = pull --local', 'pull -a = pull --all', '',
-            ],
+        output  => 
+            qr/Active aliases for the current repository \(including user-wide and global\naliases if not overridden\):\n\npull -l = pull --local\npull -a = pull --all/,
         comment => 'show',
     },
     {
@@ -89,7 +85,7 @@ my @cmds = (
     },
     {
         cmd     => [ 'foo bar' ],
-        output  => [qr/bar baz/],
+        output  => qr/bar baz/,
         comment => 'alias is set correctly',
     },
     {
@@ -102,7 +98,7 @@ my @cmds = (
     },
     {
         cmd => [ 'foo bar' ],
-        output  => [qr/bar baz/],
+        output  => qr/bar baz/,
         comment => 'alias foo bar still the same',
     },
     {
@@ -128,7 +124,7 @@ my @cmds = (
     },
     {
         cmd     => [ 'foo' ],
-        output  => [qr/bar baz/],
+        output  => qr/bar baz/,
         comment => 'alias foo set correctly',
     },
     {
@@ -141,30 +137,26 @@ my @cmds = (
     },
     {
         cmd     => [ 'pull --from http://www.example.com/'],
-        output  => [qr/pfe/],
+        output  => qr/pfe/,
         comment => 'alias with weird chars is correct',
     },
     # test cases for syntax error messages
     {
         cmd     => [ 'add' ],
-        error   => [qr/^usage: prophet aliases add "alias text" "cmd to translate to"$/],
+        error   => qr/^usage: aliases.t aliases add "alias text" "cmd to translate to"$/,
         comment => 'add usage msg is correct',
     },
     {
         cmd     => [ 'delete' ],
-        error   => [qr/^usage: prophet aliases delete "alias text"$/],
+        error   => qr/^usage: aliases.t aliases delete "alias text"$/,
         comment => 'delete usage msg is correct',
     },
     # test warning when accidentally setting args
     {
         cmd     => [ 'pt', '=', 'push', '--to', 'foo at example.com' ],
-        output  => [
-            q{W: You have args set that aren't used by this command! Quote your},
-            q{W: key/value if this was accidental.},
-            q{W: - offending args: to},
-            q{W: - running command with key 'alias.pt', value 'push'},
-            ],
-        comment => 'warning when setting accidental arg',
+        output  =>
+            qr|W: You have args set that aren't used by this command! Quote your\nW: key/value if this was accidental.\nW: - offending args: to\nW: - running command with key 'alias.pt', value 'push'|,
+            comment => 'warning when setting accidental arg',
     },
     {
         cmd     => [ 'delete', 'pt' ],
@@ -173,11 +165,14 @@ my @cmds = (
 );
 
 for my $item ( @cmds ) {
-    my $output = defined $item->{output} ? $item->{output} : [undef];
-    my $error = defined $item->{error} ? $item->{error} : [undef];
-    is_script_output( 'prophet', ['aliases', @{$item->{cmd}} ],
-        $output, $error, $item->{comment},
-    );
+    my $exp_output = defined $item->{output} ? $item->{output} : qr/^$/;
+    my $exp_error = defined $item->{error} ? $item->{error} : qr/^$/;
+
+    my ($got_output, $got_error)
+        = run_command( 'aliases', @{$item->{cmd}} );
+
+    like( $got_output, $exp_output, $item->{comment} . ' (STDOUT)' );
+    like( $got_error, $exp_error, $item->{comment} . ' (STDERR)' );
 }
 
 # check aliases in config
@@ -220,6 +215,8 @@ my $filename = File::Temp->new(
 diag ("interactive template status will be found in $filename");
 Prophet::Test->set_editor_script("aliases-editor.pl --first $filename");
 
+# can't run this with run_command because STDOUT redirection will
+# screw up piping to the script
 run_output_matches( 'prophet', [ 'aliases', 'edit' ],
     [
         "Added alias 'something different' = 'pull --local'",
@@ -229,11 +226,7 @@ run_output_matches( 'prophet', [ 'aliases', 'edit' ],
 );
 
 # check with alias show
-my @valid_settings_output = Prophet::Util->slurp('t/data/aliases.tmpl');
-chomp (@valid_settings_output);
+my $valid_settings_output = Prophet::Util->slurp('t/data/aliases.tmpl');
 
-run_output_matches(
-    'prophet',
-    [ qw/alias show/ ],
-    [ @valid_settings_output ], [], "changed alias output matches"
-);
+my $got_output = run_command( 'alias', 'show' );
+is( $got_output, $valid_settings_output, 'changed alias output matches' );

commit a09bdebc1a1bac201c474bb30b8daf7dfc616247
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Aug 4 15:32:14 2009 +0100

    Test cases for usage messages.

diff --git a/t/usage.t b/t/usage.t
new file mode 100644
index 0000000..7a63f3c
--- /dev/null
+++ b/t/usage.t
@@ -0,0 +1,275 @@
+#!/usr/bin/perl 
+#
+use warnings;
+use strict;
+use Prophet::Test tests => 38;
+use File::Temp qw/tempdir/;
+use Test::Script::Run;
+
+$ENV{'PROPHET_REPO'} = tempdir( CLEANUP => ! $ENV{PROPHET_DEBUG}  ) . '/repo-' . $$;
+
+# command usage messages
+
+use_ok('Prophet::CLI');
+
+my $cli = Prophet::CLI->new();
+my $cxn = $cli->handle;
+isa_ok($cxn, 'Prophet::Replica');
+
+$cxn->initialize;
+
+my @cmds = (
+    {
+        cmd     => [ 'config', '-h' ],
+        error   => [
+            'usage: usage.t config [show]',
+            '       usage.t config edit [--global|--user]',
+            '       usage.t config <section.subsection.var> [<value>]',
+            ],
+        comment => 'config usage',
+    },
+    {
+        cmd     => [ 'config', 'add' ],
+        error   => ['usage: usage.t config add section.subsection.var ["key value"]'],
+        comment => 'config add usage',
+    },
+    {
+        cmd     => [ 'config', 'delete' ],
+        error   => [ 'usage: usage.t config delete section.subsection.var' ],
+        comment => 'config delete usage',
+    },
+    {
+        cmd     => [ 'alias', '-h' ],
+        error   => [
+            'usage: usage.t aliases [show]',
+            '       usage.t aliases edit [--global|--user]',
+            '       usage.t alias <alias text> [<text to translate to>]',
+                ],
+        comment => 'alias usage',
+    },
+    {
+        cmd     => [ 'alias', 'add'  ],
+        error   => [ 'usage: usage.t alias add "alias text" "cmd to translate to"' ],
+        comment => 'alias add usage',
+    },
+    {
+        cmd     => [ 'aliases', 'add' ],
+        error   => [ 'usage: usage.t aliases add "alias text" "cmd to translate to"' ],
+        comment => 'aliases add usage',
+    },
+    {
+        cmd     => [ 'alias', 'delete' ],
+        error   => [ 'usage: usage.t alias delete "alias text"' ],
+        comment => 'alias delete usage',
+    },
+    {
+        cmd     => [ 'clone', '-h' ],
+        error   => [ 'usage: usage.t clone --from <url>' ],
+        comment => 'clone usage',
+    },
+    {
+        cmd     => [ 'create', '-h' ],
+        error   => [ 'usage: usage.t create <record-type> -- prop1=foo prop2=bar' ],
+        comment => 'create usage',
+    },
+    {
+        cmd     => [ 'new', '-h' ],
+        error   => [ 'usage: usage.t new <record-type> -- prop1=foo prop2=bar' ],
+        comment => 'new usage (alias of create)',
+    },
+    {
+        cmd     => [ 'delete', '-h' ],
+        error   => [ 'usage: usage.t delete <record-type> <id>' ],
+        comment => 'delete usage',
+    },
+    {
+        cmd     => [ 'rm', '-h' ],
+        error   => [ 'usage: usage.t rm <record-type> <id>' ],
+        comment => 'rm usage (alias of delete)',
+    },
+    {
+        cmd     => [ 'export', '-h' ],
+        error   => [ 'usage: usage.t export --path <path> [--format feed]' ],
+        comment => 'export usage',
+    },
+    {
+        cmd     => [ 'export' ],
+        error   => [
+            'No --path argument specified!',
+            'usage: usage.t export --path <path> [--format feed]'
+        ],
+        comment => 'export usage with error',
+    },
+    {
+        cmd     => [ 'history', '-h' ],
+        error   => [ 'usage: usage.t history <record-type> <record>' ],
+        comment => 'history usage',
+    },
+    {
+        cmd     => [ 'info', '-h' ],
+        error   => [ 'usage: usage.t info' ],
+        comment => 'info usage',
+    },
+    {
+        cmd     => [ 'init', '-h' ],
+        error   => [ 'usage: [USAGE.T_REPO=/path/to/new/repo] usage.t init' ],
+        comment => 'init usage',
+    },
+    {
+        cmd     => [ 'log', '-h' ],
+        error   => [
+            'usage: usage.t log --all              Show all entries',
+            '       usage.t log 0..LATEST~5        Show first entry up until the latest',
+            '       usage.t log LATEST~10          Show last ten entries',
+            '       usage.t log LATEST             Show last entry',
+        ],
+        comment => 'log usage',
+    },
+    {
+        cmd     => [ 'merge', '-h' ],
+        error   => [
+            'usage: usage.t merge --from <replica> --to <replica> [options]',
+            '',
+            'Options are:',
+            '    -v|--verbose            Be verbose',
+            '    -f|--force              Do merge even if replica UUIDs differ',
+            "    -n|--dry-run            Don't actually import changesets",
+        ],
+        comment => 'merge usage',
+    },
+    {
+        cmd     => [ 'mirror', '-h' ],
+        error   => [ 'usage: usage.t mirror --from <url>' ],
+        comment => 'mirror usage',
+    },
+    {
+        cmd     => [ 'mirror' ],
+        error   => [
+            'No --from specified!',
+            'usage: usage.t mirror --from <url>',
+        ],
+        comment => 'mirror usage with error',
+    },
+    {
+        cmd     => [ 'publish', '-h' ],
+        error   => [ 'usage: usage.t publish --to <location|name> [--html] [--replica]' ],
+        comment => 'publish usage',
+    },
+    {
+        cmd     => [ 'publish' ],
+        error   => [
+            'No --to specified!',
+            'usage: usage.t publish --to <location|name> [--html] [--replica]',
+        ],
+        comment => 'publish usage with error',
+    },
+    {
+        cmd     => [ 'pull', '-h' ],
+        error   => [
+            'usage: usage.t pull --from <url|name>',
+            '       usage.t pull --all',
+            '       usage.t pull --local',
+        ],
+        comment => 'pull usage',
+    },
+    {
+        cmd     => [ 'pull' ],
+        error   => [
+            'No --from, --local, or --all specified!',
+            'usage: usage.t pull --from <url|name>',
+            '       usage.t pull --all',
+            '       usage.t pull --local',
+        ],
+        comment => 'pull usage with error',
+    },
+    {
+        cmd     => [ 'push', '-h' ],
+        error   => [ 'usage: usage.t push --to <url|name> [--force]' ],
+        comment => 'push usage',
+    },
+    {
+        cmd     => [ 'push' ],
+        error   => [
+            'No --to specified!',
+            'usage: usage.t push --to <url|name> [--force]',
+        ],
+        comment => 'push usage with error',
+    },
+    {
+        cmd     => [ 'search', '-h' ],
+        error   => [
+            'usage: usage.t search <record-type>',
+            '       usage.t search <record-type> -- prop1=~foo prop2!~bar|baz',
+        ],
+        comment => 'search usage',
+    },
+    {
+        cmd     => [ 'list', '-h' ],
+        error   => [
+            'usage: usage.t list <record-type>',
+            '       usage.t list <record-type> -- prop1=~foo prop2!~bar|baz',
+        ],
+        comment => 'list usage',
+    },
+    {
+        cmd     => [ 'settings', '-h' ],
+        error   => [
+            'usage: usage.t settings [--show]',
+            '       usage.t settings [--edit]',
+            '       usage.t settings --set -- setting "new value"',
+        ],
+        comment => 'settings usage',
+    },
+    {
+        cmd     => [ 'shell', '-h' ],
+        error   => [ 'usage: usage.t [shell]' ],
+        comment => 'shell usage',
+    },
+    {
+        cmd     => [ 'show', '-h' ],
+        error   => [ 'usage: usage.t show <record-type> <record-id> [--batch] [--verbose]' ],
+        comment => 'show usage',
+    },
+    {
+        cmd     => [ 'show' ],
+        error   => [
+            'No UUID or LUID given!',
+            'usage: usage.t show <record-type> <record-id> [--batch] [--verbose]',
+        ],
+        comment => 'show usage with error',
+    },
+    {
+        cmd     => [ 'update', '-h' ],
+        error   => [
+            'usage: usage.t update <record-type> <record-id> --edit',
+            '       usage.t update <record-type> <record-id> -- prop1="new value"',
+        ],
+        comment => 'update usage',
+    },
+    {
+        cmd     => [ 'edit', '-h' ],
+        error   => [
+            'usage: usage.t edit <record-type> <record-id> --edit',
+            '       usage.t edit <record-type> <record-id> -- prop1="new value"',
+        ],
+        comment => 'edit usage',
+    },
+    {
+        cmd     => [ 'update' ],
+        error   => [
+            'No UUID or LUID given!',
+            'usage: usage.t update <record-type> <record-id> --edit',
+            '       usage.t update <record-type> <record-id> -- prop1="new value"',
+        ],
+        comment => 'update usage with error',
+    },
+);
+
+for my $item ( @cmds ) {
+    my $exp_error
+        = defined $item->{error}
+        ? (join "\n", @{$item->{error}}) . "\n"
+        : '';
+    my ($got_output, $got_error) = run_command( @{$item->{cmd}} );
+    is( $got_error, $exp_error, $item->{comment} );
+}

commit 97cafcc09a58ad4163abfad4bee9ecc5c69b2e2a
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Aug 4 21:35:29 2009 +0100

    minor cleanup

diff --git a/t/scripts/aliases-editor.pl b/t/scripts/aliases-editor.pl
index c7aaea5..d75719b 100755
--- a/t/scripts/aliases-editor.pl
+++ b/t/scripts/aliases-editor.pl
@@ -5,8 +5,7 @@ use Prophet::Test::Editor;
 
 # perl script to trick Proc::InvokeEditor with for the settings command
 
-my %tmpl_files = ( '--first' => 'aliases.tmpl',
-);
+my %tmpl_files = ( '--first' => 'aliases.tmpl' );
 
 Prophet::Test::Editor::edit(
     tmpl_files => \%tmpl_files,

commit 87f41ace2a245c21f166d3263155dc23033da647
Author: Christine Spang <spang at bestpractical.com>
Date:   Wed Aug 5 00:18:39 2009 +0100

    Make some tests faster by avoiding Test::Script::Run when possible.

diff --git a/t/export.t b/t/export.t
index 9ef799a..de81a3f 100644
--- a/t/export.t
+++ b/t/export.t
@@ -5,10 +5,13 @@ use strict;
 
 use Prophet::Test tests => 17;
 use Test::Exception;
+
 as_alice {
-    run_ok('prophet', [qw(init)]);
-    run_ok( 'prophet', [qw(create --type Bug -- --status new --from alice )], "Created a record as alice" );
-    run_output_matches( 'prophet', [qw(search --type Bug --regex .)], [qr/new/], [], " Found our record" );
+    ok( run_command( qw(init) ), 'alice replica init' );
+    ok( run_command( qw(create --type Bug -- --status new --from alice )),
+        'Created a record as alice' );
+    my $out = run_command( qw(search --type Bug --regex .));
+    like( $out, qr/new/, 'Found our record' );
 };
 
 diag('Bob syncs from alice');
@@ -18,39 +21,43 @@ my $record_id;
 use File::Temp 'tempdir';
 
 as_bob {
-
-
     diag repo_uri_for('bob');
     diag repo_uri_for('alice');
 
-    run_ok( 'prophet', [ 'clone', '--from', repo_uri_for('alice'), '--force' ], "Sync ran ok!" );
-    run_ok( 'prophet', [qw(create --type Dummy -- --ignore yes)], "Created a dummy record" );
+    ok( run_command( 'clone', '--from', repo_uri_for('alice'), '--force' ),
+        'Sync ran ok!' );
+    ok( run_command(qw(create --type Dummy -- --ignore yes)),
+        'Created a dummy record' );
 
     # check our local replicas
-    my ( $ret, $out, $err ) = run_script( 'prophet', [qw(search --type Bug --regex .)] );
-    like( $out, qr/new/, "We have the one record from alice" );
+    my $out = run_command( qw(search --type Bug --regex .) );
+    like( $out, qr/new/, 'We have the one record from alice' );
     if ( $out =~ /'uuid': '(.*?)'\s./ ) {
         $record_id = $1;
     }
     diag($record_id);
 
-    run_ok( 'prophet', [ 'update', '--type', 'Bug', '--uuid', $record_id, '--', '--status' => 'stalled' ] );
-    run_output_matches(
-        'prophet',
-        ['show', '--type', 'Bug', '--uuid', $record_id, '--batch'],
-        [
-            qr/id: (\d+) \($record_id\)/,
-              'creator: alice at example.com',
-              'from: alice',
-              'original_replica: ' . replica_uuid_for('alice'),
-              'status: stalled',
-        ], [],
-        'content is correct'
+    ok( run_command(
+            'update', '--type', 'Bug', '--uuid', $record_id,
+            '--', '--status' => 'stalled'),
+        'update record'
     );
+    $out = run_command(
+        'show', '--type', 'Bug', '--uuid', $record_id, '--batch'
+    );
+    my $alice_uuid = replica_uuid_for('alice');
+    my $expected = qr/id: (\d+) \($record_id\)
+creator: alice\@example.com
+from: alice
+original_replica: $alice_uuid
+status: stalled
+/;
+    like( $out, $expected, 'content is correct' );
 
     my $path = tempdir( CLEANUP => ! $ENV{PROPHET_DEBUG} ) ;
 
-    run_ok( 'prophet', [ 'export', '--path', $path ] );
+    ok( run_command( 'export', '--path', $path ), 'export ok' );
+
     my $cli = Prophet::CLI->new;
     ok( -d $path,                       'found db-uuid root ' . $path );
     ok( -e File::Spec->catdir($path => 'replica-uuid'), 'found replica uuid file' );
diff --git a/t/log.t b/t/log.t
index cefe815..a280cb5 100644
--- a/t/log.t
+++ b/t/log.t
@@ -97,9 +97,10 @@ $out = run_command('log', 'LATEST~2');
 like($out, qr{$third\n\n$second\n\n}, "syntactic sugar doesn't change output");
 
 # error -- invalid input
-run_output_matches('prophet', [ 'log', 'invalid' ],
-    [], [ "Invalid range specified.\n" ], "invalid input caught correctly" );
+(undef, my $error) = run_command( 'log', '--range', 'invalid' );
+is( $error, "Invalid range specified.\n", "invalid input caught correctly" );
 
 # error -- end is before start
-run_output_matches('prophet', [ 'log', '10..5' ],
-    [], [ "START must be before END in START..END." ], "caught START before END correctly" );
+(undef, $error) = run_command( 'log', '10..5' );
+is( $error, "START must be before END in START..END.\n",
+    "caught START before END correctly" );
diff --git a/t/search.t b/t/search.t
index 6d47185..a58b202 100644
--- a/t/search.t
+++ b/t/search.t
@@ -1,74 +1,85 @@
 #!/usr/bin/env perl
 use strict;
 use warnings;
-use Prophet::Test tests => 16;
+use Prophet::Test tests => 17;
+
+use_ok('Prophet::CLI::Command::Search');
 
 as_alice {
-    run_ok('prophet', [qw(init)], "created a db as alice");
-    run_ok('prophet', [qw(create --type=Bug --), 'summary=first ticket summary', 'status=new'], "created a record as alice");
-    run_ok('prophet', [qw(create --type=Bug --), 'summary=other ticket summary', 'status=open'], "created a record as alice");
-    run_ok('prophet', [qw(create --type=Bug --), 'summary=bad ticket summary', 'status=stalled', 'cmp=ne'], "created a record as alice");
+    ok( run_command( 'init' ), 'created a db as alice' );
+    ok( run_command(
+            qw(create --type=Bug --),
+            'summary=first ticket summary',
+            'status=new',
+        ), 'created a record as alice');
+    ok( run_command(
+            qw(create --type=Bug --),
+            'summary=other ticket summary',
+            'status=open'),
+        'created a record as alice' );
+    ok( run_command(
+            qw(create --type=Bug --), 'summary=bad ticket summary',
+            'status=stalled', 'cmp=ne',
+        ), 'created a record as alice');
 
-    run_output_matches('prophet', [qw(search --type Bug --regex .)],
-        [qr/first ticket summary/,
-         qr/other ticket summary/,
-         qr/bad ticket summary/], [],
-        "Found our records",
-    );
+    my $out = run_command( qw(search --type Bug --regex .));
+    my $expected = qr/.*first ticket summary.*
+.*other ticket summary.*
+.*bad ticket summary.*
+/;
+    like( $out, $expected, 'Found our records' );
 
-    run_output_matches('prophet', [qw(ls --type Bug -- status=new)],
-        [qr/first ticket summary/], [],
-        "found the only ticket with status=new",
-    );
+    $out = run_command( qw(ls --type Bug -- status=new));
+    $expected = qr/.*first ticket summary.*/;
+    like( $out, $expected, 'found the only ticket with status=new' );
+    $out = run_command(qw(search --type Bug -- status=open));
+    $expected = qr/.*other ticket summary.*/;
+    like( $out, $expected, 'found the only ticket with status=open' );
 
-    run_output_matches('prophet', [qw(search --type Bug -- status=open)],
-        [qr/other ticket summary/], [],
-        "found the only ticket with status=open",
-    );
+    $out = run_command( qw(search --type Bug -- status=closed));
+    $expected = '';
+    is( $out, $expected, 'found no tickets with status=closed' );
 
-    run_output_matches('prophet', [qw(search --type Bug -- status=closed)],
-        [], [],
-        "found no tickets with status=closed",
-    );
+    $out = run_command(qw(search --type Bug -- status=new status=open));
+    $expected = qr/.*first ticket summary.*
+.*other ticket summary.*
+/;
+    like( $out, $expected, 'found two tickets with status=new OR status=open' );
 
-    run_output_matches('prophet', [qw(search --type Bug -- status=new status=open)],
-        [qr/first ticket summary/, qr/other ticket summary/], [],
-        "found two tickets with status=new OR status=open",
-    );
+    $out = run_command(qw(search --type Bug -- status!=new));
+    $expected = qr/.*other ticket summary.*
+.*bad ticket summary.*
+/;
+    like( $out, $expected, 'found two tickets with status!=new' );
 
-    run_output_matches('prophet', [qw(search --type Bug -- status!=new)],
-        [qr/other ticket summary/, qr/bad ticket summary/], [],
-        "found two tickets with status!=new",
-    );
+    $out = run_command(qw(search --type Bug -- status=~n));
+    $expected = qr/.*first ticket summary.*
+.*other ticket summary.*
+/;
+    like( $out, $expected, 'found two tickets with status=~n' );
 
-    run_output_matches('prophet', [qw(search --type Bug -- status=~n)],
-        [qr/first ticket summary/, qr/other ticket summary/], [],
-        "found two tickets with status=~n",
-    );
-
-    run_output_matches('prophet', [qw(search --type Bug -- summary=~first|bad)],
-        [qr/first ticket summary/, qr/bad ticket summary/], [],
-        "found two tickets with status=~first|stalled",
-    );
+    $out = run_command(qw(search --type Bug -- summary=~first|bad));
+    $expected = qr/.*first ticket summary.*
+.*bad ticket summary.*
+/;
+    like( $out, $expected, 'found two tickets with status=~first|stalled' );
 
-    run_output_matches('prophet', [qw(search --type Bug -- status !=new summary=~first|bad)],
-        [qr/bad ticket summary/], [],
-        "found two tickets with status=~first|bad",
-    );
+    $out = run_command(qw(search --type Bug -- status !=new summary=~first|bad));
+    $expected = qr/bad ticket summary/;
+    like( $out, $expected, 'found two tickets with status=~first|bad' );
 
-    run_output_matches('prophet', [qw(search --type Bug -- status ne new summary =~ first|bad)],
-        [qr/bad ticket summary/], [],
-        "found two tickets with status=~first|bad",
-    );
+    $out = run_command(qw(search --type Bug -- status ne new summary =~ first|bad));
+    $expected = qr/bad ticket summary/;
+    like( $out, $expected, 'found two tickets with status=~first|bad' );
 
-    run_output_matches('prophet', [qw(search --type Bug -- cmp ne)],
-        [qr/bad ticket summary/], [],
+    $out = run_command(qw(search --type Bug -- cmp ne));
+    $expected = qr/bad ticket summary/;
+    like( $out, $expected,
         "found the ticket with cmp=ne (which didn't treat 'ne' as a comparator)",
     );
 
-    run_output_matches('prophet', [qw(search --type Bug --regex=new -- status=~n)],
-        [qr/first ticket summary/], [],
-        "found a ticket with regex and props working together",
-    );
+    $out = run_command(qw(search --type Bug --regex=new -- status=~n));
+    $expected = qr/first ticket summary/;
+    like( $out, $expected, 'found a ticket with regex and props working together' );
 };
 

commit d7701002bc7b8fb9e953126f8791c76d0597a845
Author: Christine Spang <spang at bestpractical.com>
Date:   Wed Aug 5 00:18:59 2009 +0100

    Avoid undef warnings when range is invalid.

diff --git a/lib/Prophet/CLI/Command/Log.pm b/lib/Prophet/CLI/Command/Log.pm
index 34a6a45..f2c949a 100644
--- a/lib/Prophet/CLI/Command/Log.pm
+++ b/lib/Prophet/CLI/Command/Log.pm
@@ -112,7 +112,7 @@ sub _parse_delimiter {
         my $offset;
         $offset = 0 if $delim eq 'LATEST';
         (undef, $offset) = split(/~/, $delim, 2) if $delim =~ m/^LATEST~/;
-        return undef unless $offset =~ m/^\d+$/;
+        return undef unless defined $offset && $offset =~ m/^\d+$/;
 
         return $self->handle->latest_sequence_no - $offset;
     }

commit e6f73f6cd6e60400b483e6eb2e02aaa1699b0db2
Author: Christine Spang <spang at bestpractical.com>
Date:   Wed Aug 5 15:01:06 2009 +0100

    Remove more unnecessary usage of Test::Script::Run

diff --git a/t/init.t b/t/init.t
index 3925e4b..3232df6 100644
--- a/t/init.t
+++ b/t/init.t
@@ -3,11 +3,10 @@ use strict;
 use Prophet::Test tests => 2;
 
 as_alice {
-    run_output_matches('prophet', [qw(init)],
-        [qr/Initialized your new Prophet database/]);
-    run_output_matches('prophet', [qw(init)],
-        [qr/Your Prophet database already exists/]);
+    my $output = run_command( qw(init) );
+    like( $output, qr/Initialized your new Prophet database/, 'init' );
+    $output = run_command( qw(init) );
+    like( $output, qr/Your Prophet database already exists/,
+        'init w/existing replica');
 };
 
-1;
-
diff --git a/t/non-conflicting-merge.t b/t/non-conflicting-merge.t
index ca47afa..acc16bd 100644
--- a/t/non-conflicting-merge.t
+++ b/t/non-conflicting-merge.t
@@ -6,9 +6,11 @@ use strict;
 use Prophet::Test tests => 24;
 
 as_alice {
-    run_ok( 'prophet', ['init']);
-    run_ok( 'prophet', [qw(create --type Bug -- --status new --from alice )], "Created a record as alice" );
-    run_output_matches( 'prophet', [qw(search --type Bug --regex .)], [qr/new/], [], "Found our record" );
+    ok( run_command( 'init' ), 'alice replica init' );
+    ok( run_command( qw(create --type Bug -- --status new --from alice )),
+        'Created a record as alice' );
+    my $output = run_command( qw(search --type Bug --regex .) );
+    like( $output, qr/new/, 'Found our record' );
 
     # update the record
     # show the record history
@@ -16,24 +18,27 @@ as_alice {
 };
 
 as_bob {
-    run_ok( 'prophet', [qw(init)]);
-    run_ok( 'prophet', [qw(create --type Bug -- --status open --from bob )], "Created a record as bob" );
-    run_output_matches( 'prophet', [qw(search --type Bug --regex .)], [qr/open/], [], "Found our record" );
+    ok( run_command( qw(init) ), 'bob replica init' );
+    ok( run_command( qw(create --type Bug -- --status open --from bob ) ),
+        'Created a record as bob' );
+    my $output = run_command( qw(search --type Bug --regex .) );
+    like( $output, qr/open/, 'Found our record' );
 
     # update the record
     # show the record history
     # show the record
-
 };
 
 as_alice {
-
     # sync from bob
     diag('Alice syncs from bob');
-    run_ok( 'prophet', [ 'merge',  '--from', repo_uri_for('bob'), '--to', repo_uri_for('alice'), '--force' ], "Sync ran ok!" );
+    ok( run_command(
+            'merge',  '--from', repo_uri_for('bob'),
+            '--to', repo_uri_for('alice'), '--force',
+        ), 'Sync ran ok!' );
 
     # check our local replicas
-    my ( $ret, $out, $err ) = run_script( 'prophet', [qw(search --type Bug --regex .)] );
+    my $out = run_command( qw(search --type Bug --regex .) );
     like( $out, qr/open/ );
     like( $out, qr/new/ );
     my @out = split( /\n/, $out );
@@ -44,10 +49,13 @@ as_alice {
     diag('Alice syncs from bob again. There will be no new changes from bob');
 
     # sync from bob
-    run_ok( 'prophet', [ 'merge',  '--from', repo_uri_for('bob'), '--to', repo_uri_for('alice'), '--force' ], "Sync ran ok!" );
+    ok( run_command(
+            'merge',  '--from', repo_uri_for('bob'),
+            '--to', repo_uri_for('alice'), '--force',
+        ), 'Sync ran ok!' );
 
     # check our local replicas
-    ( $ret, $out, $err ) = run_script( 'prophet', [qw(search --type Bug --regex .)] );
+    $out = run_command(  qw(search --type Bug --regex .) );
     like( $out, qr/open/ );
     like( $out, qr/new/ );
     @out = split( /\n/, $out );
@@ -62,15 +70,17 @@ diag('Bob syncs from alice');
 as_bob {
     my $last_rev = replica_last_rev();
 
-    my ( $ret, $out, $err ) = run_script( 'prophet', [qw(search --type Bug --regex .)] );
+    my $out = run_command( qw(search --type Bug --regex .) );
     unlike( $out, qr/new/, "bob doesn't have alice's yet" );
 
     # sync from alice
-
-    run_ok( 'prophet', [ 'merge',  '--to', repo_uri_for('bob'), '--from', repo_uri_for('alice'), '--force' ], "Sync ran ok!" );
+    ok( run_command(
+            'merge',  '--to', repo_uri_for('bob'),
+            '--from', repo_uri_for('alice'), '--force',
+        ), 'Sync ran ok!' );
 
     # check our local replicas
-    ( $ret, $out, $err ) = run_script( 'prophet', [qw(search --type Bug --regex .)] );
+    $out = run_command( qw(search --type Bug --regex .) );
     like( $out, qr/open/ );
     like( $out, qr/new/ );
     is( replica_last_rev, $last_rev + 1, "only one rev from alice is sycned" );
@@ -79,7 +89,10 @@ as_bob {
     $last_rev = replica_last_rev();
 
     diag('Sync from alice to bob again');
-    run_ok( 'prophet', [ 'merge',  '--to', repo_uri_for('bob'), '--from', repo_uri_for('alice'), '--force' ], "Sync ran ok!" );
+    ok( run_command(
+            'merge',  '--to', repo_uri_for('bob'),
+            '--from', repo_uri_for('alice'), '--force',
+        ), 'Sync ran ok!' );
 
     is( replica_last_rev(), $last_rev, "We have not recorded another transaction after a second sync" );
 
@@ -87,10 +100,13 @@ as_bob {
 
 as_alice {
     my $last_rev = replica_last_rev();
-    run_ok( 'prophet', [ 'merge',  '--to', repo_uri_for('alice'), '--from', repo_uri_for('bob'), '--force' ], "Sync ran ok!" );
+    ok( run_command(
+            'merge',  '--to', repo_uri_for('alice'),
+            '--from', repo_uri_for('bob'), '--force',
+        ), 'Sync ran ok!' );
+
     is( replica_last_rev(), $last_rev,
         "We have not recorded another transaction after bob had fully synced from alice" );
-
 }
 
 # create 1 record
diff --git a/t/publish-html.t b/t/publish-html.t
index dfa89c7..2d40a03 100644
--- a/t/publish-html.t
+++ b/t/publish-html.t
@@ -2,26 +2,33 @@
 use warnings;
 use strict;
 use Prophet::Test tests => 13;
-use Test::Exception;
 use File::Temp 'tempdir';
 use File::Spec;
-use Params::Validate;
 
 my ($bug_uuid, $pullall_uuid);
 
 my $alice_published = tempdir(CLEANUP => ! $ENV{PROPHET_DEBUG});
 
 as_alice {
-    run_ok('prophet', [qw(init)]);
-    run_output_matches( 'prophet',
-        [qw(create --type Bug -- --status new --from alice --summary), 'this is a template test'],
-        [qr/Created Bug \d+ \((\S+)\)(?{ $bug_uuid = $1 })/],
-        [],
-        "Created a Bug record as alice");
+    ok( run_command( qw(init) ), 'replica init' );
+    my $out = run_command(
+        qw(create --type Bug -- --status new --from alice --summary),
+        'this is a template test',
+    );
+    my $expected = qr/Created Bug \d+ \((\S+)\)(?{ $bug_uuid = $1 })/;
+    like( $out, $expected, 'Created a Bug record as alice');
+
     ok($bug_uuid, "got a uuid for the Bug record");
-    run_output_matches( 'prophet', [qw(search --type Bug --regex .)], [qr/new/], [], " Found our record" );
 
-    run_ok( 'prophet', [qw(publish --html --to), $alice_published] );
+    $out = run_command(
+        qw(search --type Bug --regex .)
+    );
+    $expected = qr/new/;
+    like( $out, $expected, 'Found our record' );
+
+    ok( run_command( qw(publish --html --to), $alice_published ),
+        'alice publish html',
+    );
 };
 
 my $dir = $alice_published;
diff --git a/t/publish-pull.t b/t/publish-pull.t
index 27c92ad..0488810 100644
--- a/t/publish-pull.t
+++ b/t/publish-pull.t
@@ -2,11 +2,9 @@
 use warnings;
 use strict;
 use Prophet::Test tests => 33;
-use Test::Exception;
 use File::Temp qw(tempdir tempfile);
 use Params::Validate;
 use Prophet::Util;
-# require Prophet::CLIContext;
 
 my ($bug_uuid, $pullall_uuid);
 
@@ -19,7 +17,7 @@ diag "Bob's config file is located at $bob_config";
 
 as_alice {
     $ENV{PROPHET_APP_CONFIG} = $alice_config;
-    run_ok('prophet', [qw(init)]);
+    ok( run_command( qw(init) ), 'replica init' );
 
     # check that new config section has been created with uuid variable
     my $config_contents = Prophet::Util->slurp($ENV{PROPHET_APP_CONFIG});
@@ -30,13 +28,21 @@ as_alice {
 	uuid = $Prophet::CLIContext::ID_REGEX
 /, 'replica section created in config file after init');
 
-    run_output_matches( 'prophet',
-        [qw(create --type Bug -- --status new --from alice )],
-        [qr/Created Bug \d+ \((\S+)\)(?{ $bug_uuid = $1 })/],
-        "Created a Bug record as alice");
+    my $output
+        = run_command( qw(create --type Bug -- --status new --from alice ) );
+    my $expected = qr/Created Bug \d+ \((\S+)\)(?{ $bug_uuid = $1 })/;
+    like( $output, $expected, 'Created a Bug record as alice' );
+
     ok($bug_uuid, "got a uuid for the Bug record");
-    run_output_matches( 'prophet', [qw(search --type Bug --regex .)], [qr/new/], [], " Found our record" );
-    run_ok( 'prophet', [qw(publish --to), $alice_published] );
+
+    $output
+        = run_command( qw(search --type Bug --regex .) );
+    $expected = qr/new/;
+    like( $output, $expected, 'Found our record' );
+
+    ok( run_command( qw(publish --to), $alice_published ),
+        'publish --to'
+    );
 
     # check that publish-url config key has been created correctly
     $config_contents = Prophet::Util->slurp($ENV{PROPHET_APP_CONFIG});
@@ -60,7 +66,9 @@ as_alice {
 
     # publish again to a different location
     my $new_published = tempdir( CLEANUP => ! $ENV{PROPHET_DEBUG} );
-    run_ok( 'prophet', [qw(publish --to), $new_published ] );
+    ok( run_command( qw(publish --to), $new_published ),
+        'publish again to different location',
+    );
     # make sure the subsection name was changed and the publish-url
     # was updated, rather than a new section being created
     $config_contents = Prophet::Util->slurp($ENV{PROPHET_APP_CONFIG});
@@ -88,7 +96,9 @@ EOF
     );
     # diag "publishing to $new_published";
     # diag "bogus name is $bogus_name";
-    run_ok( 'prophet', [qw(publish --to), $bogus_name ] );
+    ok( run_command( qw(publish --to), $bogus_name ),
+        'publish to bogus name',
+    );
     ok( ! -f File::Spec->catfile( $new_published, 'config' )
         && -f File::Spec->catfile( $bogus_name, 'replica-uuid' ),
         'did not fall back to url variable' );
@@ -98,7 +108,9 @@ my $path =$alice_published;
 
 as_bob {
     $ENV{PROPHET_APP_CONFIG} = $bob_config;
-    run_ok( 'prophet', ['clone', '--from', "file://$path"] );
+    ok( run_command( 'clone', '--from', "file://$path" ),
+        'clone as bob',
+    );
     my $config_contents = Prophet::Util->slurp($ENV{PROPHET_APP_CONFIG});
     like($config_contents, qr|
 \[core\]
@@ -108,18 +120,23 @@ as_bob {
 	uuid = $Prophet::CLIContext::ID_REGEX
 |, 'replica section created in config file after clone');
 
-    run_output_matches( 'prophet', [qw(search --type Bug --regex .)], [qr/new/], [], " Found our record" );
+    my $output = run_command( qw(search --type Bug --regex .));
+    my $expected = qr/new/;
+    like( $output, $expected, 'Found our record' );
 };
+
 as_alice {
     $ENV{PROPHET_APP_CONFIG} = $alice_config;
-    run_output_matches( 'prophet',
-        [qw(create --type Pullall -- --status new --from alice )],
-        [qr/Created Pullall \d+ \((\S+)\)(?{ $pullall_uuid = $1 })/],
-        [],
-        "Created a Pullall record as alice");
+    my $output
+        = run_command( qw(create --type Pullall -- --status new --from alice ));
+    my $expected = qr/Created Pullall \d+ \((\S+)\)(?{ $pullall_uuid = $1 })/;
+    like( $output, $expected, 'Created a Pullall record as alice' );
+
     ok($pullall_uuid, "got a uuid $pullall_uuid for the Pullall record");
 
-    run_ok( 'prophet', [qw(publish --to), $alice_published] );
+    ok( run_command( qw(publish --to), $alice_published ),
+        'publish as alice',
+    );
 };
 
 as_bob {
@@ -135,30 +152,37 @@ as_bob {
         file => $ENV{PROPHET_APP_CONFIG},
         content => $new_config_contents,
     );
-    run_ok( 'prophet', ['pull', '--from', 'new-name'], 'pull from name works');
-    run_output_matches( 'prophet', [qw(search --type Pullall --regex .)], [qr/new/], [], " Found our record" );
+    ok( run_command( 'pull', '--from', 'new-name' ), 'pull from name works');
+    my $output
+        = run_command( qw(search --type Pullall --regex .));
+    my $expected = qr/new/;
+    like( $output, $expected, 'Found our record' );
 
     $new_config_contents =~ s/url/pull-url/;
     Prophet::Util->write_file(
         file => $ENV{PROPHET_APP_CONFIG},
         content => $new_config_contents,
     );
-    run_ok( 'prophet', ['pull', '--from', 'new-name'],
-        'pull from name works with pull-url var');
+    ok( run_command( 'pull', '--from', 'new-name' ),
+        'pull from name works with pull-url var',
+    );
 
     $new_config_contents .= "\turl = don't-use-this";
     Prophet::Util->write_file(
         file => $ENV{PROPHET_APP_CONFIG},
         content => $new_config_contents,
     );
-    run_ok( 'prophet', ['pull', '--from', 'new-name'],
-        'pull-url is preferred over url');
+    ok( run_command( 'pull', '--from', 'new-name' ),
+        'pull-url is preferred over url',
+    );
 };
 
 
 as_charlie {
     (undef, $ENV{PROPHET_APP_CONFIG}) = tempfile( CLEANUP => ! $ENV{PROPHET_DEBUG} );
-    run_ok( 'prophet', ['clone', '--from', "file://$path"] );
+    ok( run_command( 'clone', '--from', "file://$path" ),
+        'clone as charlie',
+    );
 };
 
 is(database_uuid_for('alice'), database_uuid_for('charlie'), "pull propagated the database uuid properly");
diff --git a/t/simple-push.t b/t/simple-push.t
index 341dabd..a4c500e 100644
--- a/t/simple-push.t
+++ b/t/simple-push.t
@@ -6,16 +6,12 @@ use strict;
 use Prophet::Test tests => 17;
 
 as_alice {
-    run_ok( 'prophet', [qw(init)] );
-    run_ok(
-        'prophet',
-        [qw(create --type Bug -- --status new-alice --from alice )],
-        "Created a record as alice"
-    );
-    run_output_matches(
-        'prophet', [qw(search --type Bug --regex .)],
-        [qr/new/], [], "Found our record"
+    ok( run_command( qw(init) ), 'replica init' );
+    ok( run_command( qw(create --type Bug -- --status new-alice --from alice )),
+        'Created a record as alice'
     );
+    my $output = run_command( qw(search --type Bug --regex .) );
+    like( $output, qr/new/, 'Found our record' );
 
     # update the record
     # show the record history
@@ -24,25 +20,19 @@ as_alice {
 
 diag( repo_uri_for('alice') );
 as_bob {
-    run_ok( 'prophet', [ qw(clone --from), repo_uri_for('alice') ] );
-    run_ok(
-        'prophet',
-        [qw(create --type Bug -- --status open-bob --from bob )],
-        "Created a record as bob"
-    );
-    run_output_matches( 'prophet', [qw(search --type Bug --regex new-alice)],
-        [ qr/new-alice/ ], [], "Found our record"
-    );
-
-    run_output_matches( 'prophet', [qw(search --type Bug --regex open-bob)],
-        [ qr/open-bob/ ], [], "Found our record"
-    );
+    ok( run_command( qw(clone --from), repo_uri_for('alice') ),
+        'clone from alice' );
+    ok( run_command( qw(create --type Bug -- --status open-bob --from bob ) ),
+        'Created a record as bob' );
+    my $output = run_command( qw(search --type Bug --regex new-alice) );
+    like( $output, qr/new-alice/, 'Found our record' );
 
+    $output = run_command( qw(search --type Bug --regex open-bob) );
+    like( $output, qr/open-bob/, 'Found our record' );
 
     # update the record
     # show the record history
     # show the record
-
 };
 
 my ($alice, $alice_app);
@@ -57,8 +47,7 @@ is( $bob->db_uuid, $alice->db_uuid,
 
 my $openbug = '';
 as_bob {
-    my ( $ret, $stdout, $stderr )
-        = run_script( 'prophet', [qw(search --type Bug --regex open-bob)] );
+    my $stdout = run_command( qw(search --type Bug --regex open-bob) );
     if ( $stdout =~ /^'uuid': '(.*?)'\s/ ) {
         $openbug = $1;
     }
@@ -133,8 +122,8 @@ as_alice {
     # sync from bob
     diag('Alice syncs from bob');
     is( $alice->last_changeset_from_source( $bob->uuid ) => -1 );
-    run_ok( 'prophet', [ 'pull', '--from', repo_uri_for('bob') ],
-        "Sync ran ok!" );
+    ok( run_command( 'pull', '--from', repo_uri_for('bob') ),
+        'Sync ran ok!' );
     is( $alice->last_changeset_from_source( $bob->uuid ) =>
             $bob->latest_sequence_no );
 };
@@ -142,13 +131,9 @@ as_alice {
 my $last_id;
 
 as_bob {
-    run_ok(
-        'prophet',
-        [qw(create --type Bug -- --status new2-bob --from bob )],
-        "Created a record as bob"
-    );
-    my ( $ret, $stdout, $stderr )
-        = run_script( 'prophet', [qw(search --type Bug --regex new2)] );
+    ok( run_command( qw(create --type Bug -- --status new2-bob --from bob ) ),
+        'Created a record as bob');
+    my $stdout = run_command( qw(search --type Bug --regex new2) );
     if ( $stdout =~ /^'uuid': '(.*?)'\s/ ) {
         $last_id = $1;
     }

commit ddbb5e2ee35db219af36b563c36cec216543d866
Author: Christine Spang <spang at bestpractical.com>
Date:   Wed Aug 5 16:22:13 2009 +0100

    Don't do useless stuff at the top of t/usage.t/

diff --git a/t/usage.t b/t/usage.t
index 7a63f3c..9b5ff3e 100644
--- a/t/usage.t
+++ b/t/usage.t
@@ -1,23 +1,15 @@
-#!/usr/bin/perl 
-#
+#!/usr/bin/perl
 use warnings;
 use strict;
-use Prophet::Test tests => 38;
-use File::Temp qw/tempdir/;
-use Test::Script::Run;
+
+use Prophet::Test tests => 36;
+use File::Temp qw(tempdir);
 
 $ENV{'PROPHET_REPO'} = tempdir( CLEANUP => ! $ENV{PROPHET_DEBUG}  ) . '/repo-' . $$;
+diag "Replica is in $ENV{PROPHET_REPO}";
 
 # command usage messages
 
-use_ok('Prophet::CLI');
-
-my $cli = Prophet::CLI->new();
-my $cxn = $cli->handle;
-isa_ok($cxn, 'Prophet::Replica');
-
-$cxn->initialize;
-
 my @cmds = (
     {
         cmd     => [ 'config', '-h' ],

-----------------------------------------------------------------------



More information about the Bps-public-commit mailing list