[Bps-public-commit] SD branch, master, updated. da8b6b77990621edbac8ed91e11841d96b92c82e

jesse jesse at bestpractical.com
Tue Jul 7 17:35:33 EDT 2009


The branch, master has been updated
       via  da8b6b77990621edbac8ed91e11841d96b92c82e (commit)
       via  c5fb6bdee1001711f7114d908eff8363ab4f2639 (commit)
       via  d3ff5b53308276a700b6c74b910611c034df9c5e (commit)
       via  82861066066ac51d8c947a5c128b5fbe1982ec9e (commit)
       via  f48aca6dea4f753a89528ed122bfb1abda235fc0 (commit)
       via  42e88417114f719ddceb56b07cdd3b5029a33de9 (commit)
       via  72f08d65d88b800bca837c56f0061890f76d7af3 (commit)
       via  be94243d53c94600b8c334de940979a3b4957fbd (commit)
       via  789b06e45e18068cee1ebcdc25f45869a6cb7463 (commit)
       via  d3eca042c433951ec45fe89e893535a5902a6851 (commit)
       via  7300f47adf0fa6c709105a4e6a76e658180c4362 (commit)
       via  3554472eecd44ea3799b23dda09eb438ec81cca0 (commit)
       via  395333503b67d565e0f6611a2a61436b6723fe15 (commit)
       via  dc543e9025bd00ead02987240a4b82f384867bfd (commit)
       via  1bee4fd1068661c8364b1d5293ba62b12c67e161 (commit)
       via  fd8ecfbb884800ac8b8175c3ddbe789d1e277b76 (commit)
       via  0cf13ad324b39026f4273451f9ec93620a55dcca (commit)
       via  a82a51e31aaef6348a2f6fd74b19f1615cff8274 (commit)
       via  73763bcf220d16513fb3999110dec5545b89df6f (commit)
       via  12736f2bb1a27fdd5adeab7f3083e5900287e65c (commit)
       via  58868c32e5dc978c6014769c195f87599291247c (commit)
       via  355ed5828b3b5da51dd3cf9ee09434f18729a407 (commit)
       via  f55c04f93606dab43fd9dc9b99c695c2abbf651e (commit)
       via  bdfa44778b5ca8caf2c831900bff07f3f89f682a (commit)
       via  b579dd8a176e9a787707ec63df888b3c6ae88f38 (commit)
       via  eaf67947af885b073cc2c0ba01c1284ecd547ddc (commit)
       via  6929bf3b893c05b960ed9adac0b22b3a4ac1e15c (commit)
       via  48e062210fa330d834263cc99b905f10855b92d8 (commit)
       via  d8cf98d1fceda68f32a5dfbab9fb5a0ba4d60430 (commit)
       via  7ff676c9fe83a16b46f6dfd168482b38ed523776 (commit)
       via  a9e5b5957fe3c4b4526e825201dd2841c375f8e4 (commit)
       via  bf784c9f13886e60175e6dbf81402de3c05cc3f0 (commit)
      from  d9d5d205e9af1d2a01aa5e3c9dfa619d2f7ce20e (commit)

Summary of changes:
 bin/sd                                             |    5 +-
 inc/Module/AutoInstall.pm                          |   88 +++++++---
 inc/Module/Install.pm                              |    6 +-
 inc/Module/Install/AutoInstall.pm                  |    8 +-
 inc/Module/Install/Base.pm                         |   40 ++---
 inc/Module/Install/Can.pm                          |   14 +-
 inc/Module/Install/Fetch.pm                        |    8 +-
 inc/Module/Install/Include.pm                      |    8 +-
 inc/Module/Install/Makefile.pm                     |   10 +-
 inc/Module/Install/Metadata.pm                     |  104 ++++++++----
 inc/Module/Install/Scripts.pm                      |    8 +-
 inc/Module/Install/Share.pm                        |    8 +-
 inc/Module/Install/Win32.pm                        |    6 +-
 inc/Module/Install/WriteAll.pm                     |   18 ++-
 lib/App/SD.pm                                      |    9 +-
 lib/App/SD/CLI/Command/Help.pm                     |    2 +-
 lib/App/SD/CLI/Command/Help/Aliases.pm             |   44 ++++--
 lib/App/SD/CLI/Command/Help/Config.pm              |   77 ++++++---
 lib/App/SD/CLI/Command/Help/Environment.pm         |    2 +
 ...y_format_ticket.pm => ticket_summary_format.pm} |    9 +-
 lib/App/SD/CLI/Command/Log.pm                      |    2 +-
 lib/App/SD/CLI/Command/Ticket/Create.pm            |    3 +-
 lib/App/SD/CLI/Command/Ticket/Search.pm            |   10 +-
 lib/App/SD/CLI/Command/Ticket/Show.pm              |   14 +-
 lib/App/SD/CLI/Command/Ticket/Update.pm            |    4 +-
 lib/App/SD/CLI/Dispatcher.pm                       |   57 +++++--
 lib/App/SD/Config.pm                               |  181 ++++++++++++++++++--
 lib/App/SD/Model/Ticket.pm                         |    2 +-
 lib/App/SD/Replica/trac/PushEncoder.pm             |    1 -
 lib/App/SD/Server/Dispatcher.pm                    |    4 +-
 lib/App/SD/Server/View.pm                          |    6 +-
 lib/App/SD/Test.pm                                 |   33 ----
 t/02-create-with-editor.t                          |    8 +-
 t/03-update-ticket-with-editor.t                   |    8 +-
 t/04-update-ticket-comment-with-editor.t           |    2 +-
 t/05-config-file-loading.t                         |  115 -------------
 t/06-ticket-show.t                                 |   14 +-
 t/07-sort-group.t                                  |   25 ++-
 t/data/sd-settings-first.tmpl                      |   31 ----
 t/data/sd-settings-second.tmpl                     |   31 ----
 t/data/sd-settings-third.tmpl                      |   31 ----
 t/scripts/SDTestsEditor.pm                         |   95 ----------
 t/scripts/settings-editor.pl                       |   53 ------
 t/scripts/ticket-create-editor.pl                  |    8 +-
 t/scripts/ticket-update-editor.pl                  |    8 +-
 t/sd-settings.t                                    |   98 -----------
 46 files changed, 577 insertions(+), 741 deletions(-)
 rename lib/App/SD/CLI/Command/Help/{summary_format_ticket.pm => ticket_summary_format.pm} (83%)
 delete mode 100644 t/05-config-file-loading.t
 delete mode 100644 t/data/sd-settings-first.tmpl
 delete mode 100644 t/data/sd-settings-second.tmpl
 delete mode 100644 t/data/sd-settings-third.tmpl
 delete mode 100755 t/scripts/SDTestsEditor.pm
 delete mode 100755 t/scripts/settings-editor.pl
 delete mode 100644 t/sd-settings.t

- Log -----------------------------------------------------------------
commit bf784c9f13886e60175e6dbf81402de3c05cc3f0
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jun 16 12:11:32 2009 +0300

    Kill t/05-config-file-loading.t
    
    These tests don't cover anything that Config::GitLike's
    tests don't cover.

diff --git a/t/05-config-file-loading.t b/t/05-config-file-loading.t
deleted file mode 100644
index e40a7d3..0000000
--- a/t/05-config-file-loading.t
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-
-use Prophet::Test tests => 10;
-use App::SD::Test;
-use File::Temp qw/tempdir/;
-use Path::Class;
-
-
-no warnings 'once';
-
-BEGIN {
-    require File::Temp;
-    $ENV{'PROPHET_REPO'} = $ENV{'SD_REPO'} = $ENV{'HOME'} = File::Temp::tempdir( CLEANUP => 1 ) . '/_svb';
-    diag "export SD_REPO=".$ENV{'PROPHET_REPO'} ."\n";
-    diag "export HOME=".$ENV{'PROPHET_REPO'} ."\n";
-    delete $ENV{'PROPHET_APP_CONFIG'}; # clear this because Prophet::Test sets it
-}
-
-# Tests the config file order of preference laid out in App::SD::Config
-run_script( 'sd', [ 'init']);
-
-
-
-# create from sd
-my ($yatta_id, $yatta_uuid) = create_ticket_ok( '--summary', 'YATTA');
-
-# default config file
-diag("Testing default config file\n");
-
-run_output_matches( 'sd', [ 'ticket',
-    'list', '--regex', '.' ],
-    [ qr/(\d+) YATTA new/]
-);
-
-# override App::SD::Test
-delete $ENV{'SD_CONFIG'};
-delete $ENV{'PROPHET_APP_CONFIG'};
-
-ok( ! $ENV{'SD_CONFIG'}, "SD_CONFIG env var has been cleared" );
-ok( ! $ENV{'PROPHET_APP_CONFIG'}, "PROPHET_APP_CONFIG env var has been cleared" );
-
-# Test from least-preferred to most preferred, leaving the least-preferred
-# files in place to make sure the next-most-preferred file is preferred
-# over all the files beneath it.
-
-diag("Testing \$HOME/.prophetrc\n");
-
-my $config_filename = $ENV{'HOME'} . '/.prophetrc';
-
-App::SD::Test->write_to_file($config_filename,
-    "summary_format_ticket = %4s },\$luid | %-11.11s,status | %-60.60s,summary\n");
-
-run_output_matches( 'sd', [ 'ticket',
-    'list', '--regex', '.' ],
-    [ qr/\s+(\d+) } new         YATTA/]
-);
-
-diag("Testing PROPHET_APP_CONFIG\n");
-
-$config_filename = $ENV{'HOME'} . '/config-test';
-$ENV{'PROPHET_APP_CONFIG'} = $config_filename;
-
-App::SD::Test->write_to_file($config_filename,
-    "summary_format_ticket = %-9.9s,status | %-60.60s,summary\n");
-
-run_output_matches( 'sd', [ 'ticket',
-    'list', '--regex', '.' ],
-    [ qr/new       YATTA/]
-);
-
-diag("Testing \$HOME/.sdrc\n");
-
-$config_filename = $ENV{'HOME'} . '/.sdrc';
-
-App::SD::Test->write_to_file($config_filename,
-    "summary_format_ticket = %4s },\$luid | %-7.7s,status | %-60.60s,summary\n");
-
-run_output_matches( 'sd', [ 'ticket',
-    'list', '--regex', '.' ],
-    [ qr/\s+(\d+) } new     YATTA/]
-);
-
-diag("Testing fs_root/prophetrc\n");
-
-$config_filename = $ENV{'SD_REPO'} . '/prophetrc';
-
-App::SD::Test->write_to_file($config_filename,
-    "summary_format_ticket = %4s },\$luid | %-10.10s,status | %-60.60s,summary\n");
-
-run_output_matches( 'sd', [ 'ticket',
-    'list', '--regex', '.' ],
-    [ qr/\s+(\d+) } new        YATTA/]
-);
-
-diag("Testing fs_root/config\n");
-
-$config_filename = $ENV{'SD_REPO'} . '/config';
-
-App::SD::Test->write_to_file($config_filename,
-    "summary_format_ticket = %4s },\$luid | %-6.6s,status | %-60.60s,summary\n");
-
-run_output_matches( 'sd', [ 'ticket',
-    'list', '--regex', '.' ],
-    [ qr/\s+(\d+) } new    YATTA/]
-);
-
-diag("Testing SD_CONFIG\n");
-
-$ENV{'SD_CONFIG'} = 't/prophet_testing.conf';
-
-run_output_matches( 'sd', [ 'ticket',
-    'list', '--regex', '.' ],
-    [ qr/(\d+) } new    YATTA /]
-);

commit a9e5b5957fe3c4b4526e825201dd2841c375f8e4
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jun 16 13:40:00 2009 +0300

    Update core to use Config::GitLike-based Prophet::Config.

diff --git a/lib/App/SD.pm b/lib/App/SD.pm
index 7f8e47e..1b96c6f 100644
--- a/lib/App/SD.pm
+++ b/lib/App/SD.pm
@@ -1,6 +1,6 @@
 package App::SD;
 use Any::Moose;
-use App::SD::Config;
+use Prophet::Config;
 
 extends 'Prophet::App';
 
@@ -9,14 +9,15 @@ our $VERSION = '0.02';
 has '+config' => (
     default => sub {
         my $self = shift;
-        return App::SD::Config->new(app_handle => $self);
+        $ENV{PROPHET_APP_CONFIG} = $ENV{SD_CONFIG} if defined $ENV{SD_CONFIG};
+        return Prophet::Config->new( app_handle => $self, confname => 'sdrc' );
     }
 );
 
 sub database_settings {
 { 
         statuses            => ['24183C4D-EFD0-4B16-A207-ED7598E875E6' => qw/new open stalled closed rejected/],
-        active_statuses            => ['C879A68F-8CFE-44B5-9EDD-14E53933669E' => qw/new open/],
+        active_statuses     => ['C879A68F-8CFE-44B5-9EDD-14E53933669E' => qw/new open/],
         default_status      => ['2F9E6509-4468-438A-A733-246B3061003E' => 'new' ],
         components          => ['6CBD84A1-4568-48E7-B90C-F1A5B7BD8ECD' => qw/core ui docs tests/],
         default_component   => ['0AEC922F-57B1-44BE-9588-816E5841BB18' => 'core'],
@@ -24,8 +25,8 @@ sub database_settings {
         default_milestone   => ['BAB613BD-9E25-4612-8DE3-21E4572859EA' => 'alpha'],
 
         project_name        => ['3B4B297C-906F-4018-9829-F7CC672274C9' => 'Your SD Project'],
-        common_ticket_props    => ['3f0a074f-af13-406f-bf7b-d69bbf360720' => qw/id summary status milestone component owner created due creator reporter original_replica/],
-        prop_descriptions => ['c1bced3a-ad2c-42c4-a502-4149205060f1',
+        common_ticket_props => ['3f0a074f-af13-406f-bf7b-d69bbf360720' => qw/id summary status milestone component owner created due creator reporter original_replica/],
+        prop_descriptions   => ['c1bced3a-ad2c-42c4-a502-4149205060f1',
         {   summary =>
               "a one-line summary of what this ticket is about",
             owner =>
diff --git a/lib/App/SD/Config.pm b/lib/App/SD/Config.pm
deleted file mode 100644
index 8892c3e..0000000
--- a/lib/App/SD/Config.pm
+++ /dev/null
@@ -1,35 +0,0 @@
-package App::SD::Config;
-use Any::Moose;
-use File::Spec;
-
-extends 'Prophet::Config';
-
-# We can't just frob $ENV{PROPHET_APP_CONFIG} the way the sd script does
-# with $ENV{PROPHET_REPO} because we need to instantiate App::SD::CLI to
-# get the location of the repo root, and then Prophet would load its own
-# config file before we got around to messing with the env var
-sub app_config_file {
-    my $self = shift;
-
-    # The order of preference for config files is:
-    #   $ENV{SD_CONFIG} > fs_root/config > fs_root/prophetrc (for backcompat)
-    #   $HOME/.sdrc > $ENV{PROPHET_APP_CONFIG} > $HOME/.prophetrc
-
-    $ENV{'PROPHET_APP_CONFIG'}
-            =  $self->file_if_exists($ENV{'SD_CONFIG'})
-            || $self->file_if_exists(
-                File::Spec->catfile($self->app_handle->handle->fs_root => 'config'))
-            || $self->file_if_exists(
-                # backcompat
-                File::Spec->catfile($self->app_handle->handle->fs_root => 'prophetrc'))
-            || $self->file_if_exists(
-                File::Spec->catfile($ENV{'HOME'}.'/.sdrc'))
-            || $ENV{'PROPHET_APP_CONFIG'} # don't overwrite with nothing
-            || ''; # don't write undef
-        $self->SUPER::app_config_file(@_);
-}
-
-__PACKAGE__->meta->make_immutable;
-no Any::Moose;
-
-1;

commit 7ff676c9fe83a16b46f6dfd168482b38ed523776
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jun 16 13:41:09 2009 +0300

    Update ticket search command and related test to use new config API and names.

diff --git a/lib/App/SD/CLI/Command/Ticket/Search.pm b/lib/App/SD/CLI/Command/Ticket/Search.pm
index 87adc92..4444f00 100644
--- a/lib/App/SD/CLI/Command/Ticket/Search.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Search.pm
@@ -10,18 +10,20 @@ sub run {
     my $self = shift;
 
     if (  (!$self->has_arg('sort') || !$self->arg('sort'))
-        && $self->app_handle->config->get('default_sort_ticket_list') )
+        && $self->app_handle->config->get( key => 'ticket.list.default-sort') )
     {
         $self->set_arg(
-            'sort' => $self->app_handle->config->get('default_sort_ticket_list')
+            'sort' => $self->app_handle->config->get(
+                key => 'ticket.list.default-sort'
+            )
         );
     }
 
     if (  (!$self->has_arg('group') || !$self->arg('group'))
-        && $self->app_handle->config->get('default_group_ticket_list') )
+        && $self->app_handle->config->get( key => 'ticket.list.default-group') )
     {
         $self->set_arg( 'group' =>
-              $self->app_handle->config->get('default_group_ticket_list') );
+              $self->app_handle->config->get( key => 'ticket.list.default-group') );
     }
 
     # sort output by given prop if user specifies --sort
diff --git a/t/07-sort-group.t b/t/07-sort-group.t
index 952247e..8703bf3 100644
--- a/t/07-sort-group.t
+++ b/t/07-sort-group.t
@@ -40,11 +40,13 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--sort', 'owner' ],
 );
 
 my $config_filename = $ENV{'SD_REPO'} . '/config';
-App::SD::Test->write_to_file($config_filename,
-    "default_sort_ticket_list = owner\n");
+App::SD::Test->write_to_file($config_filename, '
+[ticket "list"]
+    default-sort = owner
+');
 $ENV{'SD_CONFIG'} = $config_filename;
 
-diag('using default_sort_ticket_list = owner');
+diag('using ticket.list.default-sort = owner');
 run_output_matches( 'sd', [ 'ticket', 'list' ],
     [ qr/(\d+) huzzah! new/,
       qr/(\d+) YATTA new/,
@@ -58,7 +60,7 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--sort' ],
     ]
 );
 
-diag('using default_sort_ticket_list = owner and --sort none');
+diag('using ticket.list.default-sort = owner and --sort none');
 run_output_matches( 'sd', [ 'ticket', 'list', '--sort', 'none' ],
     [ qr/(\d+) YATTA new/,
       qr/(\d+) huzzah! new/,
@@ -82,11 +84,11 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--group', 'owner' ],
     ]
 );
 
-diag('using default_group_ticket_list = owner');
-$config_filename = $ENV{'SD_REPO'} . '/config';
-App::SD::Test->write_to_file($config_filename,
-    "default_group_ticket_list = owner\n");
-$ENV{'SD_CONFIG'} = $config_filename;
+diag('using ticket.list.default-group = owner');
+App::SD::Test->write_to_file($config_filename, '
+[ticket "list"]
+    default-group = owner
+');
 
 run_output_matches( 'sd', [ 'ticket', 'list' ],
     [ '',
@@ -117,7 +119,7 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--group' ],
     ]
 );
 
-diag('using default_group_ticket_list = owner and --group none');
+diag('using ticket.list.default-group = owner and --group none');
 run_output_matches( 'sd', [ 'ticket', 'list', '--group', 'none' ],
     [ qr/(\d+) YATTA new/,
       qr/(\d+) huzzah! new/,

commit d8cf98d1fceda68f32a5dfbab9fb5a0ba4d60430
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jun 16 13:41:59 2009 +0300

    Update ticket show and related test to new config API.

diff --git a/lib/App/SD/CLI/Command/Ticket/Show.pm b/lib/App/SD/CLI/Command/Ticket/Show.pm
index b3f9090..a3f6078 100644
--- a/lib/App/SD/CLI/Command/Ticket/Show.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Show.pm
@@ -46,11 +46,13 @@ override run => sub {
     }
 
     # allow user to not display history by specifying the --skip-history
-    # arg or setting disable_ticket_show_history_by_default config item to a
+    # arg or setting ticket.show.disable-history config item to a
     # true value (can be overridden with --with-history)
     if (!$self->has_arg('skip-history')
-        && (  !$self->app_handle->config->get('disable_ticket_show_history_by_default')
-            || $self->has_arg('with-history') )
+        && (  !$self->app_handle->config->get(
+                key => 'ticket.show.disable-history',
+                as => 'bool',
+            ) || $self->has_arg('with-history') )
         )
     {
         print "\n= HISTORY\n\n";
diff --git a/t/06-ticket-show.t b/t/06-ticket-show.t
index fa3802d..00f30c8 100644
--- a/t/06-ticket-show.t
+++ b/t/06-ticket-show.t
@@ -93,22 +93,24 @@ diag("passing --skip history (doesn't show history)");
 check_output_without_history('--skip-history');
 
 my $config_filename = $ENV{'SD_REPO'} . '/config';
-App::SD::Test->write_to_file($config_filename,
-    "disable_ticket_show_history_by_default = 1\n");
+App::SD::Test->write_to_file($config_filename, '
+[ticket "show"]
+    disable-history = true
+');
 $ENV{'SD_CONFIG'} = $config_filename;
 
-diag("config option disable_ticket_show_history_by_default set");
+diag("config option ticket.show.disable-history set");
 diag("(shouldn't show history)");
 
 check_output_without_history();
 
-diag("config option disable_ticket_show_history_by_default set");
+diag("config option ticket.show.disable-history set");
 diag("and --skip-history passed (shouldn't show history)");
 
 check_output_without_history('--skip-history');
 
 # config option set and --with-history passed (should show history)
-diag('config option disable_ticket_show_history_by_default set');
+diag('config option ticket.show.disable-history set');
 diag('and --with-history passed (should show history)');
 
 check_output_with_history('--with-history');

commit 48e062210fa330d834263cc99b905f10855b92d8
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jun 16 13:42:43 2009 +0300

    Update the last couple places where the old config API was used.

diff --git a/lib/App/SD/Model/Ticket.pm b/lib/App/SD/Model/Ticket.pm
index e9bebcc..a453f47 100644
--- a/lib/App/SD/Model/Ticket.pm
+++ b/lib/App/SD/Model/Ticket.pm
@@ -167,7 +167,7 @@ sub props_to_show {
     my $self = shift;
     my $args = shift || {};
     my @props_from_config = split(',',
-        ($self->app_handle->config->get('common_ticket_props') || ''));
+        ($self->app_handle->config->get( key => 'ticket.common-props') || ''));
     my $props_list = @props_from_config ? \@props_from_config :
         $self->app_handle->setting(label => 'common_ticket_props')->get();
 
diff --git a/lib/App/SD/Server/View.pm b/lib/App/SD/Server/View.pm
index 7f25e13..eada473 100644
--- a/lib/App/SD/Server/View.pm
+++ b/lib/App/SD/Server/View.pm
@@ -294,7 +294,9 @@ template '/tickets/hot' => sub {
             if (   $item->has_active_status
                 && ( $item->prop('milestone') || '' ) eq $current_milestone
                 && ( ( $item->prop('owner') || '' ) eq
-                    ( $item->app_handle->config->get('email_address') || '') || !$item->prop('owner') )
+                    ( $item->app_handle->config->get(
+                            key => 'user.email-address'
+                        ) || '') || !$item->prop('owner') )
                 )
             {
                 return 1;

commit 6929bf3b893c05b960ed9adac0b22b3a4ac1e15c
Merge: 48e0622... 669e4c0...
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Jun 22 10:32:20 2009 +0300

    merge master


commit eaf67947af885b073cc2c0ba01c1284ecd547ddc
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Jun 22 23:46:36 2009 +0300

    Pull this SD-specific error message out of Prophet and put it here instead.

diff --git a/lib/App/SD/CLI/Command/Ticket/Create.pm b/lib/App/SD/CLI/Command/Ticket/Create.pm
index fe71d56..5a0972f 100644
--- a/lib/App/SD/CLI/Command/Ticket/Create.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Create.pm
@@ -47,7 +47,8 @@ sub process_template {
         eval { super(); } or chomp ($error = $@ || "Something went wrong!");
 
     return $self->handle_template_errors(
-        error        => $error,
+        error        => $error . "\n\nYou can bypass validation for a "
+                        ."property by appending a ! to it.",
         template_ref => $args{template},
         bad_template => $updated,
         rtype        => $record->type,

commit b579dd8a176e9a787707ec63df888b3c6ae88f38
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jun 23 11:03:26 2009 +0300

    Update sd alias help.

diff --git a/lib/App/SD/CLI/Command/Help/Aliases.pm b/lib/App/SD/CLI/Command/Help/Aliases.pm
index 965326a..5a31235 100644
--- a/lib/App/SD/CLI/Command/Help/Aliases.pm
+++ b/lib/App/SD/CLI/Command/Help/Aliases.pm
@@ -9,32 +9,43 @@ sub run {
 
 print <<EOF
 
-You can create custom command aliases in your local configuration file.
-The format is as follows:
+You can create custom command aliases in the aliases section of
+your local configuration files. The format is as follows:
 
-    alias command to type = command to translate it to
+    [alias]
+        command to type = command to translate it to
 
 As an example, you could create an alias to show all tickets assigned
 to you with the alias 'mine':
 
-    alias mine = ticket list -- owner=you\@domain.com status !~closed|rejected
+    mine = ticket list -- owner=you\@domain.com status !~closed|rejected
 
 SD also provides a command for managing aliases: '${cmd}aliases'. If
-given no arguments, the aliases command will present you with an editor
-window in which aliases can be modified. Aliases will be saved to your
+given no arguments, the aliases command will print the active aliases
+for the current repository (including all non-overridden user-wide
+and global aliases, if any). '${cmd}aliases edit' will present you with an
+editor window in which aliases can be modified. Aliases will be saved to your
 local configuration file when editing is done.
 
-The following arguments are supported:
+Examples (in all examples, 'alias' can be used anywhere 'aliases' appears):
 
-    --show (or -s)
-      Don't present an editor window, just print the current aliases
-      to STDOUT.
+    ${cmd}aliases
+    ${cmd}aliases show
+      Show currently active aliases.
 
-    --add (or -a) 'command to type = command to translate to'
-      Add a new alias from the command line.
+    ${cmd}aliases edit
+      Edit aliases in an editor window.
 
-    --delete (or -d) 'command to type'
-      Delete an existing alias from the command line.
+    ${cmd}aliases set command to type = command to translate to
+    ${cmd}alias command to type = command to translate to
+      Set the given alias (or change it if it already exists).
+
+    ${cmd}aliases delete command to type
+      Delete the given alias.
+
+The --user and --global arguments can be used in conjunction with the
+set (and edit) commands to change what configuration file to use.
+By default, the repository-specific configuration file is used.
 
 For more information on local configuration files, see '${cmd}help config'.
 

commit bdfa44778b5ca8caf2c831900bff07f3f89f682a
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jun 23 11:53:49 2009 +0300

    More help updates.

diff --git a/lib/App/SD/CLI/Command/Help.pm b/lib/App/SD/CLI/Command/Help.pm
index 6c21682..8d82889 100644
--- a/lib/App/SD/CLI/Command/Help.pm
+++ b/lib/App/SD/CLI/Command/Help.pm
@@ -52,7 +52,7 @@ ${cmd}help sync        -  Publishing and importing ticket databases
 ${cmd}help history     -  Viewing repository history
 ${cmd}help environment -  Environment variables which affect sd
 ${cmd}help config      -  Local configuration variables
-${cmd}help summary_format_ticket  -  Details of this config variable
+${cmd}help ticket.summary_format  -  Details of this config variable
 ${cmd}help aliases     -  Command aliases
 ${cmd}help settings    -  Database configuration variables
 
diff --git a/lib/App/SD/CLI/Command/Help/Aliases.pm b/lib/App/SD/CLI/Command/Help/Aliases.pm
index 5a31235..d589482 100644
--- a/lib/App/SD/CLI/Command/Help/Aliases.pm
+++ b/lib/App/SD/CLI/Command/Help/Aliases.pm
@@ -27,7 +27,8 @@ and global aliases, if any). '${cmd}aliases edit' will present you with an
 editor window in which aliases can be modified. Aliases will be saved to your
 local configuration file when editing is done.
 
-Examples (in all examples, 'alias' can be used anywhere 'aliases' appears):
+Examples (in all examples, 'alias' can be used anywhere 'aliases' appears
+and vice-versa):
 
     ${cmd}aliases
     ${cmd}aliases show
diff --git a/lib/App/SD/CLI/Command/Help/Config.pm b/lib/App/SD/CLI/Command/Help/Config.pm
index 1b6e78c..dd71cdd 100644
--- a/lib/App/SD/CLI/Command/Help/Config.pm
+++ b/lib/App/SD/CLI/Command/Help/Config.pm
@@ -8,43 +8,58 @@ sub run {
     my ${cmd}= $self->_get_cmd_name;
 
 print <<EOF
-SD supports both a user-wide configuration file (\$HOME/.sdrc and
-per-database configuration file (/path/to/repo/config). If both configuration
-files are present, the database-specific config file will be used.
+SD supports a layered configuration system with three configuration
+files: a global file (/etc/sdrc), a user-wide configuration file
+(\$HOME/.sdrc) and per-replica configuration file (/path/to/replica/config).
+Configuration variables in more local configuration files override
+those in more global ones.
 
-You can use the '${cmd}config' command to view what configuration file
-SD is loading and what it contains.
+You can use the '${cmd}config' command to view what configuration files
+SD has loaded and all loaded configuration variables, as they apply
+to the current replica.
 
-Currently, the following configuration variables are available:
+The configuration file format is similar to that of the VCS 'Git'. See
+http://www.kernel.org/pub/software/scm/git/docs/git-config.html for
+specifics. The biggest thing you need to know is that the config file
+contains key/value variables, contained in sections. In the help
+documents, we'll refer to variables in the manner:
+"section.subsection.variable-name". In a configuration file, this
+would look like:
 
-    email_address = foo\@bar.com
-      Specifies an email address to use as the default for tickets'
-      reporter field. (Overrides the EMAIL environmental variable if
-      that is also set.)
+    [section "subsection]
+        variable-name = value
 
-    summary_format_ticket = %4s },\$luid | %-11.11s,status | %-60.60s,summary
+Currently, the following configuration variables are available (sorted
+by configuration file section):
+
+    ticket.summary-format = %4s },\$luid | %-11.11s,status | %-60.60s,summary
       Specifies how to format ticket summaries (when listing tickets, e.g.).
-      (See also: help summary_format_ticket.)
+      (See also: help ticket-summary-format.)
+
+    ticket.common-props = id,summary,status,owner,created,original_replica
+      A comma-separated list of ticket properties that are most-often
+      used. These properties will be shown by default in the 'ticket
+      show' command and presented for editing when interactively
+      creating or updating tickets. (Overrides the database-wide
+      setting of the same name.)
 
-    default_sort_ticket_list = status
-      Bug property to determine order of display when listing tickets. (Can
-      be any property; will be compared lexically.)
+    ticket.search.default-sort = status
+      Bug property to determine order of display when searching/listing
+      tickets. (Can be any property; will be compared lexically.)
 
-    default_group_ticket_list = milestone
-      Bug property to group tickets by when displaying lists. (Can be any
-      property.)
+    ticket.search.default-group = milestone
+      Bug property to group tickets by when displaying searches/lists. (Can be
+      any property.)
 
-    disable_ticket_show_history_by_default = 1
+    ticket.show.disable-history = 1
       Don't display ticket history when running '${cmd}ticket show'. Can
       be overridden by passing the '--with-history' arg to the
       command.
 
-    common_ticket_props = id,summary,status,owner,created,original_replica
-      A comma-separated list of ticket properties that are most-often
-      used. These properties will be shown by default in the 'ticket
-      show' command and presented for editing when interactively
-      creating or updating tickets. (Overrides the database-wide
-      setting of the same name.)
+    user.email-address = foo\@bar.com
+      Specifies an email address to use as the default for tickets'
+      reporter field. (Overrides the EMAIL environmental variable if
+      that is also set.)
 
 For information on environmental variables that can affect SD, see
 '${cmd}help environment'.
diff --git a/lib/App/SD/CLI/Command/Help/Environment.pm b/lib/App/SD/CLI/Command/Help/Environment.pm
index 6007ee8..5ba214c 100644
--- a/lib/App/SD/CLI/Command/Help/Environment.pm
+++ b/lib/App/SD/CLI/Command/Help/Environment.pm
@@ -21,6 +21,8 @@ configuration. Example syntax is for bash-like shells.
         
     export SD_CONFIG=/path/to/sd/config/file
       Specify where the configuration file SD is using should reside.
+      If this variable is specified, no other config file will be
+      loaded.
 
     export PROPHET_HISTFILE=~/.sd-history
       Specify where the interactive shell should store its history.
diff --git a/lib/App/SD/CLI/Command/Help/summary_format_ticket.pm b/lib/App/SD/CLI/Command/Help/ticket_summary_format.pm
similarity index 83%
rename from lib/App/SD/CLI/Command/Help/summary_format_ticket.pm
rename to lib/App/SD/CLI/Command/Help/ticket_summary_format.pm
index 2deca7e..8150fe3 100644
--- a/lib/App/SD/CLI/Command/Help/summary_format_ticket.pm
+++ b/lib/App/SD/CLI/Command/Help/ticket_summary_format.pm
@@ -1,20 +1,21 @@
-package App::SD::CLI::Command::Help::summary_format_ticket;
+package App::SD::CLI::Command::Help::ticket_summary_format;
 use Any::Moose;
 extends 'App::SD::CLI::Command::Help';
 
 sub run {
     my $self = shift;
-    $self->print_header('The summary_format_ticket configuration option');
+    $self->print_header('The ticket.summary-format configuration option');
 
 print <<EOF
-The summary_format_ticket configuration directive consists of any number
+The ticket.summary-format configuration directive consists of any number
 of comma-separated pairs, with each pair separated from the next by a vertical
 bar (|). Any amount of whitespace may appear before or after the | and will not
 affect the summary format.
 
 Here is an example:
 
-    summary_format_ticket = %5.5s },\$luid | %8.8s,status | %-52.52s,summary
+    [ticket]
+        summary-format = %5.5s },\$luid | %8.8s,status | %-52.52s,summary
 
 Let's deconstruct this example. It consists of three pairs. The first pair is
 '%5.5s },\$luid'. The first item of the pair should look somewhat familiar to
diff --git a/lib/App/SD/CLI/Command/Ticket/Search.pm b/lib/App/SD/CLI/Command/Ticket/Search.pm
index 4444f00..bd56fe2 100644
--- a/lib/App/SD/CLI/Command/Ticket/Search.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Search.pm
@@ -10,20 +10,20 @@ sub run {
     my $self = shift;
 
     if (  (!$self->has_arg('sort') || !$self->arg('sort'))
-        && $self->app_handle->config->get( key => 'ticket.list.default-sort') )
+        && $self->app_handle->config->get( key => 'ticket.search.default-sort') )
     {
         $self->set_arg(
             'sort' => $self->app_handle->config->get(
-                key => 'ticket.list.default-sort'
+                key => 'ticket.search.default-sort'
             )
         );
     }
 
     if (  (!$self->has_arg('group') || !$self->arg('group'))
-        && $self->app_handle->config->get( key => 'ticket.list.default-group') )
+        && $self->app_handle->config->get( key => 'ticket.search.default-group') )
     {
         $self->set_arg( 'group' =>
-              $self->app_handle->config->get( key => 'ticket.list.default-group') );
+              $self->app_handle->config->get( key => 'ticket.search.default-group') );
     }
 
     # sort output by given prop if user specifies --sort
diff --git a/lib/App/SD/CLI/Dispatcher.pm b/lib/App/SD/CLI/Dispatcher.pm
index 08074e9..7f17855 100644
--- a/lib/App/SD/CLI/Dispatcher.pm
+++ b/lib/App/SD/CLI/Dispatcher.pm
@@ -27,7 +27,8 @@ under help => sub {
     on about   => run_command('Help::About');
     on config  => run_command('Help::Config');
     on copying => run_command('Help::Copying');
-    on summary_format_ticket => run_command('Help::summary_format_ticket');
+    on [ ['summary-format', 'ticket.summary-format', 'ticket_summary_format'] ]
+        => run_command('Help::ticket_summary_format');
 
     on [ ['author', 'authors'] ]         => run_command('Help::Authors');
     on [ ['environment', 'env'] ]        => run_command('Help::Environment');
diff --git a/t/07-sort-group.t b/t/07-sort-group.t
index e8f4ef4..e57401f 100644
--- a/t/07-sort-group.t
+++ b/t/07-sort-group.t
@@ -40,12 +40,12 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--sort', 'owner' ],
 
 my $config_filename = $ENV{'SD_REPO'} . '/config';
 App::SD::Test->write_to_file($config_filename, '
-[ticket "list"]
+[ticket "search"]
     default-sort = owner
 ');
 $ENV{'SD_CONFIG'} = $config_filename;
 
-diag('using ticket.list.default-sort = owner');
+diag('using ticket.search.default-sort = owner');
 run_output_matches( 'sd', [ 'ticket', 'list' ],
     [ qr/(\d+) huzzah! new/,
       qr/(\d+) YATTA new/,
@@ -59,7 +59,7 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--sort' ],
     ]
 );
 
-diag('using ticket.list.default-sort = owner and --sort none');
+diag('using ticket.search.default-sort = owner and --sort none');
 run_output_matches( 'sd', [ 'ticket', 'list', '--sort', 'none' ],
     [ qr/(\d+) YATTA new/,
       qr/(\d+) huzzah! new/,
@@ -83,9 +83,9 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--group', 'owner' ],
     ]
 );
 
-diag('using ticket.list.default-group = owner');
+diag('using ticket.search.default-group = owner');
 App::SD::Test->write_to_file($config_filename, '
-[ticket "list"]
+[ticket "search"]
     default-group = owner
 ');
 

commit f55c04f93606dab43fd9dc9b99c695c2abbf651e
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jun 23 12:50:31 2009 +0300

    Make the Moose stacktrace-eater less likely to eat lines of non-Moose exceptions.

diff --git a/bin/sd b/bin/sd
index 5dea7ef..679c19d 100755
--- a/bin/sd
+++ b/bin/sd
@@ -15,8 +15,9 @@ $ENV{'PROPHET_REPO'} ||= $ENV{'SD_REPO'} || $ENV{'HOME'}.'/.sd';
 unless ($ENV{SD_VERBOSE_ERROR} || $ENV{'TEST_VERBOSE'}) {
     $SIG{__DIE__} = sub {
         my $line = shift;
-        $line =~ s/\n.*//s if ($line =~ /at line/s);
-        $line .= "\n"; $line =~ s/\n+$/\n/gs;
+        $line =~ s/\n.*//s if ($line =~ /at .* line \d+.$/s);
+        $line .= "\n";
+        $line =~ s/\n+$/\n/gs;
         die $line;
     };
 }

commit 355ed5828b3b5da51dd3cf9ee09434f18729a407
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jun 23 16:15:52 2009 +0300

    I've been persuaded that ticket.list > ticket.search

diff --git a/lib/App/SD/CLI/Command/Help/Config.pm b/lib/App/SD/CLI/Command/Help/Config.pm
index dd71cdd..3222b23 100644
--- a/lib/App/SD/CLI/Command/Help/Config.pm
+++ b/lib/App/SD/CLI/Command/Help/Config.pm
@@ -26,7 +26,7 @@ documents, we'll refer to variables in the manner:
 "section.subsection.variable-name". In a configuration file, this
 would look like:
 
-    [section "subsection]
+    [section "subsection"]
         variable-name = value
 
 Currently, the following configuration variables are available (sorted
@@ -43,13 +43,13 @@ by configuration file section):
       creating or updating tickets. (Overrides the database-wide
       setting of the same name.)
 
-    ticket.search.default-sort = status
-      Bug property to determine order of display when searching/listing
+    ticket.list.default-sort = status
+      Bug property to determine order of display when displaying lists of
       tickets. (Can be any property; will be compared lexically.)
 
-    ticket.search.default-group = milestone
-      Bug property to group tickets by when displaying searches/lists. (Can be
-      any property.)
+    ticket.list.default-group = milestone
+      Bug property to group tickets by when displaying lists of tickets. (Can
+      be any property.)
 
     ticket.show.disable-history = 1
       Don't display ticket history when running '${cmd}ticket show'. Can
diff --git a/lib/App/SD/CLI/Command/Ticket/Search.pm b/lib/App/SD/CLI/Command/Ticket/Search.pm
index bd56fe2..4444f00 100644
--- a/lib/App/SD/CLI/Command/Ticket/Search.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Search.pm
@@ -10,20 +10,20 @@ sub run {
     my $self = shift;
 
     if (  (!$self->has_arg('sort') || !$self->arg('sort'))
-        && $self->app_handle->config->get( key => 'ticket.search.default-sort') )
+        && $self->app_handle->config->get( key => 'ticket.list.default-sort') )
     {
         $self->set_arg(
             'sort' => $self->app_handle->config->get(
-                key => 'ticket.search.default-sort'
+                key => 'ticket.list.default-sort'
             )
         );
     }
 
     if (  (!$self->has_arg('group') || !$self->arg('group'))
-        && $self->app_handle->config->get( key => 'ticket.search.default-group') )
+        && $self->app_handle->config->get( key => 'ticket.list.default-group') )
     {
         $self->set_arg( 'group' =>
-              $self->app_handle->config->get( key => 'ticket.search.default-group') );
+              $self->app_handle->config->get( key => 'ticket.list.default-group') );
     }
 
     # sort output by given prop if user specifies --sort

commit 58868c32e5dc978c6014769c195f87599291247c
Author: Christine Spang <spang at bestpractical.com>
Date:   Wed Jun 24 20:59:52 2009 +0300

    Add some help text about using sd config to manipulate config files.

diff --git a/lib/App/SD/CLI/Command/Help/Config.pm b/lib/App/SD/CLI/Command/Help/Config.pm
index 3222b23..a00bdcd 100644
--- a/lib/App/SD/CLI/Command/Help/Config.pm
+++ b/lib/App/SD/CLI/Command/Help/Config.pm
@@ -18,6 +18,20 @@ You can use the '${cmd}config' command to view what configuration files
 SD has loaded and all loaded configuration variables, as they apply
 to the current replica.
 
+'${cmd}config' can also be used to set configuration variables.
+
+Examples:
+
+    ${cmd}config user.email-address user\@example.com
+    ${cmd}config --delete user.email-address
+    ${cmd}config user.email-address
+      Print the current value of this configuration variable.
+    ${cmd}config alias.'this.alias.contains.dots' 'so it must be quoted'
+    ${cmd}config edit
+    ${cmd}config edit --user
+    ${cmd}config edit --global
+      Edit the specified config file in an editor.
+
 The configuration file format is similar to that of the VCS 'Git'. See
 http://www.kernel.org/pub/software/scm/git/docs/git-config.html for
 specifics. The biggest thing you need to know is that the config file

commit 12736f2bb1a27fdd5adeab7f3083e5900287e65c
Author: Christine Spang <spang at bestpractical.com>
Date:   Thu Jun 25 12:27:03 2009 +0300

    Change aliases help to have syntax consistent with the config help.

diff --git a/lib/App/SD/CLI/Command/Help/Aliases.pm b/lib/App/SD/CLI/Command/Help/Aliases.pm
index d589482..9e83a00 100644
--- a/lib/App/SD/CLI/Command/Help/Aliases.pm
+++ b/lib/App/SD/CLI/Command/Help/Aliases.pm
@@ -37,8 +37,7 @@ and vice-versa):
     ${cmd}aliases edit
       Edit aliases in an editor window.
 
-    ${cmd}aliases set command to type = command to translate to
-    ${cmd}alias command to type = command to translate to
+    ${cmd}aliases "command to type" "command to translate to"
       Set the given alias (or change it if it already exists).
 
     ${cmd}aliases delete command to type

commit 73763bcf220d16513fb3999110dec5545b89df6f
Author: Christine Spang <spang at bestpractical.com>
Date:   Thu Jun 25 15:36:47 2009 +0300

    Move SDTestsEditor to Prophet and update tests.
    
    Settings tests belong in Prophet as well, as SD uses this
    command from Prophet without modification.

diff --git a/t/data/sd-settings-first.tmpl b/t/data/sd-settings-first.tmpl
deleted file mode 100644
index 8b0bd30..0000000
--- a/t/data/sd-settings-first.tmpl
+++ /dev/null
@@ -1,31 +0,0 @@
-# uuid: 1AF5CF74-A6D4-417E-A738-CCE64A0A7F71
-milestones: ["alpha","beta","1.0"]
-
-# uuid: 0AEC922F-57B1-44BE-9588-816E5841BB18
-default_component: ["core"]
-
-# uuid: 6CBD84A1-4568-48E7-B90C-F1A5B7BD8ECD
-components: ["core","ui","docs","tests"]
-
-# uuid: 3f0a074f-af13-406f-bf7b-d69bbf360720
-common_ticket_props: ["id","summary","original_replica"]
-
-# uuid: 2F9E6509-4468-438A-A733-246B3061003E
-default_status: ["new"]
-
-# uuid: C879A68F-8CFE-44B5-9EDD-14E53933669E
-active_statuses: ["new","open"]
-
-# uuid: BAB613BD-9E25-4612-8DE3-21E4572859EA
-default_milestone: ["alpha"]
-
-# uuid: c1bced3a-ad2c-42c4-a502-4149205060f1
-prop_descriptions: [{"due":"when this ticket must be finished by","owner":"the email address of the person who is responsible for this ticket","reporter":"the email address of the person who reported this ticket","summary":"a one-line summary of what this ticket is about"}]
-
-# uuid: 24183C4D-EFD0-4B16-A207-ED7598E875E6
-statuses: ["new","open","stalled","closed","rejected"]
-
-# uuid: 3B4B297C-906F-4018-9829-F7CC672274C9
-project_name: ["Your SD Project"]
-
-
diff --git a/t/data/sd-settings-second.tmpl b/t/data/sd-settings-second.tmpl
deleted file mode 100644
index 39f233d..0000000
--- a/t/data/sd-settings-second.tmpl
+++ /dev/null
@@ -1,31 +0,0 @@
-# uuid: 1AF5CF74-A6D4-417E-A738-CCE64A0A7F71
-milestones: ["alpha","beta","1.0"]
-
-# uuid: 0AEC922F-57B1-44BE-9588-816E5841BB18
-default_component: ["core"]
-
-# uuid: 6CBD84A1-4568-48E7-B90C-F1A5B7BD8ECD
-components: ["core","ui","docs","tests"]
-
-# uuid: 3f0a074f-af13-406f-bf7b-d69bbf360720
-common_ticket_props: ["id","summary","original_replica"]
-
-# uuid: 2F9E6509-4468-438A-A733-246B3061003E
-default_status: ["open"]
-
-# uuid: C879A68F-8CFE-44B5-9EDD-14E53933669E
-active_statuses: ["new","open"]
-
-# uuid: BAB613BD-9E25-4612-8DE3-21E4572859EA
-default_milestone: ["alpha"]
-
-# uuid: c1bced3a-ad2c-42c4-a502-4149205060f1
-prop_descriptions: [{"due":"when this ticket must be finished by","owner":"the email address of the person who is responsible for this ticket","reporter":"the email address of the person who reported this ticket","summary":"a one-line summary of what this ticket is about"}]
-
-# uuid: 24183C4D-EFD0-4B16-A207-ED7598E875E6
-statuses: ["new","open","stalled","closed","rejected"]
-
-# uuid: 3B4B297C-906F-4018-9829-F7CC672274C9
-project_name: ["Your SD Project"]
-
-
diff --git a/t/data/sd-settings-third.tmpl b/t/data/sd-settings-third.tmpl
deleted file mode 100644
index 0eeac94..0000000
--- a/t/data/sd-settings-third.tmpl
+++ /dev/null
@@ -1,31 +0,0 @@
-# uuid: 1AF5CF74-A6D4-417E-A738-CCE64A0A7F71
-milestones: ["alpha","beta","1.0"]
-
-# uuid: 0AEC922F-57B1-44BE-9588-816E5841BB18
-default_component: ["ui"]
-
-# uuid: 6CBD84A1-4568-48E7-B90C-F1A5B7BD8ECD
-components: ["core","ui","docs","tests"]
-
-# uuid: 3f0a074f-af13-406f-bf7b-d69bbf360720
-common_ticket_props: ["id","summary","original_replica"]
-
-# uuid: 2F9E6509-4468-438A-A733-246B3061003E
-default_status: ["open"]
-
-# uuid: C879A68F-8CFE-44B5-9EDD-14E53933669E
-active_statuses: ["new","open"]
-
-# uuid: BAB613BD-9E25-4612-8DE3-21E4572859EA
-default_milestone: ["alpha"]
-
-# uuid: c1bced3a-ad2c-42c4-a502-4149205060f1
-prop_descriptions: [{"due":"when this ticket must be finished by","owner":"the email address of the person who is responsible for this ticket","reporter":"the email address of the person who reported this ticket","summary":"a one-line summary of what this ticket is about"}]
-
-# uuid: 24183C4D-EFD0-4B16-A207-ED7598E875E6
-statuses: ["new","open","stalled","closed","rejected"]
-
-# uuid: 3B4B297C-906F-4018-9829-F7CC672274C9
-project_name: ["Your SD Project"]
-
-
diff --git a/t/scripts/SDTestsEditor.pm b/t/scripts/SDTestsEditor.pm
deleted file mode 100755
index 550a92d..0000000
--- a/t/scripts/SDTestsEditor.pm
+++ /dev/null
@@ -1,95 +0,0 @@
-package SDTestsEditor;
-use strict;
-use warnings;
-
-use Prophet::Util;
-use Params::Validate;
-use File::Spec;
-
-=head2 edit( tmpl_files => $tmpl_files, edit_callback => sub {}, verify_callback => sub {} )
-
-Expects @ARGV to contain at least an option and a file to be edited. It
-can also contain a replica uuid, a ticket uuid, and a status file. The last
-item must always be the file to be edited. The others, if they appear, must
-be in that order after the option. The status file must contain the
-string 'status' in its filename.
-
-edit_callback is called on each line of the file being edited. It should make
-any edits to the lines it receives and then print what it wants to be saved to
-the file.
-
-verify_callback is called after editing is done. If you need to write
-whether the template was correct to a status file, for example, this
-should be done here.
-
-=cut
-
-sub edit {
-    my %args = @_;
-    validate( @_, { edit_callback => 1,
-                    verify_callback => 1,
-                    tmpl_files  => 1,
-                   }
-             );
-
-    my $option = shift @ARGV;
-    my $tmpl_file = $args{tmpl_files}->{$option};
-
-    chomp ( my @valid_template =
-        Prophet::Util->slurp("t/data/$tmpl_file") );
-
-    my $status_file = $ARGV[-2] =~ /status/ ? delete $ARGV[-2] : undef;
-    # a bit of a hack to dermine whether the last arg is a filename
-    my $replica_uuid = File::Spec->file_name_is_absolute($ARGV[0]) ? undef : shift @ARGV;
-    my $ticket_uuid = File::Spec->file_name_is_absolute($ARGV[0]) ? undef : shift @ARGV;
-
-    my @template = ();
-    while (<>) {
-        chomp( my $line = $_ );
-        push @template, $line;
-
-        $args{edit_callback}( option => $option, template => \@template,
-            valid_template => \@valid_template,
-            replica_uuid => $replica_uuid,
-            ticket_uuid => $ticket_uuid );
-    }
-
-    $args{verify_callback}( template => \@template,
-        valid_template => \@valid_template, status_file => $status_file );
-}
-
-=head2 check_template_by_line($template, $valid_template, $errors)
-
-$template is a reference to an array containing the template to check,
-split into lines. $valid_template is the same for the template to
-check against. Lines in these arrays should not have trailing newlines.
-$errors is a reference to an array where error messages will be stored.
-
-Lines in $valid_template should consist of either plain strings, or strings
-beginning with 'qr/' (to delimit a regexp object).
-
-Returns true if the templates match and false otherwise.
-
-=cut
-
-sub check_template_by_line {
-    my @template = @{ shift @_ };
-    my @valid_template = @{ shift @_ };
-    my $replica_uuid = shift;
-    my $ticket_uuid = shift;
-    my $errors = shift;
-
-    for my $valid_line (@valid_template) {
-        my $line = shift @template;
-
-        push @$errors, "got nothing, expected [$valid_line]" if !defined($line);
-
-        push @$errors, "[$line] doesn't match [$valid_line]"
-            if ($valid_line =~ /^qr\//) ? $line !~ eval($valid_line)
-            : $line eq $valid_line;
-    }
-
-    return !(@$errors == 0);
-}
-
-1;
diff --git a/t/scripts/settings-editor.pl b/t/scripts/settings-editor.pl
deleted file mode 100755
index c640dd7..0000000
--- a/t/scripts/settings-editor.pl
+++ /dev/null
@@ -1,53 +0,0 @@
-#!perl -i
-use strict;
-use warnings;
-use lib 't/scripts';
-use SDTestsEditor;
-
-# perl script to trick Proc::InvokeEditor with for the settings command
-
-my %tmpl_files = ( '--first' => 'sd-settings-first.tmpl',
-                   '--second' => 'sd-settings-second.tmpl',
-                 );
-
-SDTestsEditor::edit( tmpl_files => { '--first' => 'sd-settings-first.tmpl',
-                   '--second' => 'sd-settings-second.tmpl',
-               },
-    edit_callback => sub {
-        my %args = @_;
-        my $option = $args{option};
-
-        if ($option eq '--first') {
-            s/(?<=^default_status: \[")new(?="\])/open/; # valid json change
-            s/^default_milestone(?=: \["alpha"\])$/invalid_setting/; # changes setting name
-            s/(?<=uuid: B)A(?=B613BD)/F/; # changes a UUID to an invalid one
-            s/^project_name//; # deletes setting
-        } elsif ($option eq '--second') {
-            s/(?<=^default_component: \[")core(?="\])/ui/; # valid json change
-            s/(?<=^default_milestone: \["alpha")]$//; # invalid json
-        }
-        print;
-    },
-    verify_callback => sub {
-        my %args = @_;
-
-        my $ok = 1;
-
-        my %seen;     # lookup table
-        my @vonly;    # answer
-
-        # build lookup table
-        @seen{@{$args{template}}} = ( );
-
-        for my $line (@{$args{valid_template}}) {
-            push(@vonly, $line) unless exists $seen{$line};
-        }
-
-        # if anything is only in the valid template, we don't match
-        $ok = 0 if scalar @vonly;
-
-        open STATUSFILE, '>', $args{status_file};
-        $ok ? print STATUSFILE "ok!" : print STATUSFILE "not ok!";
-        close STATUSFILE;
-    }
-);
diff --git a/t/scripts/ticket-create-editor.pl b/t/scripts/ticket-create-editor.pl
index 2c9f6bf..c896eac 100755
--- a/t/scripts/ticket-create-editor.pl
+++ b/t/scripts/ticket-create-editor.pl
@@ -1,13 +1,13 @@
 #!perl -i
 use strict;
 use warnings;
-use lib 't/scripts';
-use SDTestsEditor;
+use Prophet::Test::Editor;
 
 # perl script to trick Proc::InvokeEditor with for the ticket create command
 
 
-SDTestsEditor::edit( tmpl_files => { '--no-args' => 'sd-ticket-create.tmpl',
+Prophet::Test::Editor::edit(
+    tmpl_files => { '--no-args' => 'sd-ticket-create.tmpl',
                    '--all-props' => 'sd-ticket-create.tmpl',
                    '--verbose' => 'sd-ticket-create-verbose.tmpl',
                    '--verbose-and-all' => 'sd-ticket-create-verbose.tmpl',
@@ -21,7 +21,7 @@ SDTestsEditor::edit( tmpl_files => { '--no-args' => 'sd-ticket-create.tmpl',
         if ( /^=== add new ticket comment below ===$/) {
             my $errors = [];
             my $template_ok =
-                SDTestsEditor::check_template_by_line($args{template},
+                Prophet::Test::Editor::check_template_by_line($args{template},
                 $args{valid_template}, $args{replica_uuid},
                 $args{ticket_uuid}, $errors);
             if ($template_ok) {
diff --git a/t/scripts/ticket-update-editor.pl b/t/scripts/ticket-update-editor.pl
index 2b527db..c1f6872 100755
--- a/t/scripts/ticket-update-editor.pl
+++ b/t/scripts/ticket-update-editor.pl
@@ -1,12 +1,12 @@
 #!perl -i
 use strict;
 use warnings;
-use lib 't/scripts';
-use SDTestsEditor;
+use Prophet::Test::Editor;
 
 # perl script to trick Proc::InvokeEditor with for the ticket update command
 
-SDTestsEditor::edit( tmpl_files => { '--no-args' => 'sd-ticket-update.tmpl',
+Prophet::Test::Editor::edit(
+    tmpl_files => { '--no-args' => 'sd-ticket-update.tmpl',
                    '--all-props' => 'sd-ticket-update-all-props.tmpl',
                    '--verbose' => 'sd-ticket-update-verbose.tmpl',
                    '--verbose-and-all' =>
@@ -38,7 +38,7 @@ SDTestsEditor::edit( tmpl_files => { '--no-args' => 'sd-ticket-update.tmpl',
         if ( /^=== add new ticket comment below ===$/) {
             my $errors = [];
             my $template_ok =
-                SDTestsEditor::check_template_by_line($args{template},
+                Prophet::Test::Editor::check_template_by_line($args{template},
                 $args{valid_template}, $args{replica_uuid},
                 $args{ticket_uuid}, $errors);
             if ($template_ok) {
diff --git a/t/sd-settings.t b/t/sd-settings.t
deleted file mode 100644
index ce344da..0000000
--- a/t/sd-settings.t
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-
-use Prophet::Test tests => 8;
-use App::SD::Test;
-use Prophet::Util;
-use File::Temp qw(tempdir);
-use File::Spec;
-no warnings 'once';
-
-# test the CLI and interactive UIs for showing and updating settings
-
-BEGIN {
-    require File::Temp;
-    $ENV{'PROPHET_REPO'} = $ENV{'SD_REPO'}
-        = File::Temp::tempdir( CLEANUP => 1 ) . '/_svb';
-    diag $ENV{'PROPHET_REPO'};
-}
-
-run_script( 'sd', [ 'init']);
-
-
-my $replica_uuid = replica_uuid;
-
-# test noninteractive set
-run_output_matches( 'sd', [ 'settings', '--set', '--', 'common_ticket_props',
-    '["id","summary","original_replica"]' ],
-    [
-        'Trying to change common_ticket_props from ["id","summary","status","milestone","component","owner","created","due","creator","reporter","original_replica"] to ["id","summary","original_replica"].',
-        ' -> Changed.',
-    ], [], "settings --set went ok",
-);
-
-# check with settings --show
-my @valid_settings_output = Prophet::Util->slurp('t/data/sd-settings-first.tmpl');
-chomp (@valid_settings_output);
-
-run_output_matches(
-    'sd',
-    [ qw/settings --show/ ],
-    [ @valid_settings_output ], [], "changed settings output matches"
-);
-
-# test sd settings (interactive editing)
-
-my $filename = File::Temp->new(
-    TEMPLATE => File::Spec->catfile(File::Spec->tmpdir(), '/statusXXXXX') )->filename;
-diag ("interactive template status will be found in $filename");
-# first set the editor to an editor script
-App::SD::Test->set_editor("settings-editor.pl --first $filename");
-
-# then edit the settings
-run_output_matches( 'sd', [ 'settings' ],
-    [
-        'Setting with uuid "BFB613BD-9E25-4612-8DE3-21E4572859EA" does not exist.',
-        'Changed default_status from ["new"] to ["open"].',
-    ], [], "interactive settings set went ok",);
-
-# check the tempfile to see if the template presented to the editor was correct
-chomp(my $template_ok = Prophet::Util->slurp($filename));
-is($template_ok, 'ok!', "interactive template was correct");
-
-# check the settings with settings --show
- at valid_settings_output = Prophet::Util->slurp('t/data/sd-settings-second.tmpl');
-chomp (@valid_settings_output);
-
-run_output_matches(
-    'sd',
-    [ qw/settings --show/ ],
-    [ @valid_settings_output ], [], "changed settings output matches"
-);
-
-# test setting to invalid json
-my $second_filename = File::Temp->new(
-    TEMPLATE => File::Spec->catfile(File::Spec->tmpdir(), '/statusXXXXX') )->filename;
-diag ("interactive template status will be found in $second_filename");
-App::SD::Test->set_editor("settings-editor.pl --second $second_filename");
-run_output_matches( 'sd', [ 'settings' ],
-    [
-        qr/^An error occured setting default_milestone to \["alpha":/,
-        'Changed default_component from ["core"] to ["ui"].',
-    ], [], "interactive settings set with JSON error went ok",
-);
-
-# check the tempfile to see if the template presented to the editor was correct
-chomp($template_ok = Prophet::Util->slurp($filename));
-is($template_ok, 'ok!', "interactive template was correct");
-
-# check the settings with settings --show
- at valid_settings_output = Prophet::Util->slurp('t/data/sd-settings-third.tmpl');
-chomp (@valid_settings_output);
-
-run_output_matches(
-    'sd',
-    [ qw/settings --show/ ],
-    [ @valid_settings_output ], [], "changed settings output matches"
-);

commit a82a51e31aaef6348a2f6fd74b19f1615cff8274
Author: Christine Spang <spang at bestpractical.com>
Date:   Fri Jun 26 11:55:07 2009 +0300

    Move App::SD::Test->set_editor to Prophet::Test->set_editor_script

diff --git a/lib/App/SD/Test.pm b/lib/App/SD/Test.pm
index 3333970..e457fa9 100644
--- a/lib/App/SD/Test.pm
+++ b/lib/App/SD/Test.pm
@@ -7,7 +7,6 @@ require Prophet::Test;
 use Test::More;
 use File::Spec;
 use File::Temp ();
-use Cwd qw/getcwd/;
 use base qw/Exporter/;
 our @EXPORT = qw(create_ticket_ok update_ticket_ok create_ticket_with_editor_ok create_ticket_comment_ok get_uuid_for_luid get_luid_for_uuid get_ticket_info);
 delete $ENV{'PROPHET_APP_CONFIG'};
@@ -224,23 +223,6 @@ sub get_ticket_info {
     return \%res;
 }
 
-=head2 set_editor SCRIPT
-
-Sets the editor that Proc::InvokeEditor uses (which is used for nicer ticket
-and comment creation / update, etc.).
-
-This should be a non-interactive script found in F<t/scripts>.
-
-=cut
-
-sub set_editor {
-    my ($self, $script) = @_;
-
-    delete $ENV{'VISUAL'};       # Proc::InvokeEditor checks this first
-    $ENV{'EDITOR'} = "$^X " . File::Spec->catfile(getcwd(), 't', 'scripts', $script);
-    diag "export EDITOR=" . $ENV{'EDITOR'} . "\n";
-}
-
 =head2 write_to_file FILENAME DATA
 
 Takes the string given in DATA and writes it to the file whose name is given
diff --git a/t/02-create-with-editor.t b/t/02-create-with-editor.t
index a8eafc2..761a406 100644
--- a/t/02-create-with-editor.t
+++ b/t/02-create-with-editor.t
@@ -50,18 +50,18 @@ sub create_ticket_and_check {
 }
 
 # test template for sd ticket create
-App::SD::Test->set_editor("ticket-create-editor.pl --no-args $replica_uuid");
+Prophet::Test->set_editor_script("ticket-create-editor.pl --no-args $replica_uuid");
 create_ticket_and_check(check_sd_list => 1);
 
 # test template for sd ticket create --all-props
-App::SD::Test->set_editor("ticket-create-editor.pl --all-props $replica_uuid");
+Prophet::Test->set_editor_script("ticket-create-editor.pl --all-props $replica_uuid");
 create_ticket_and_check(extra_args => ['--all-props']);
 
 # test template for sd ticket create --verbose
-App::SD::Test->set_editor("ticket-create-editor.pl --verbose $replica_uuid");
+Prophet::Test->set_editor_script("ticket-create-editor.pl --verbose $replica_uuid");
 create_ticket_and_check(extra_args => ['--verbose']);
 
 # test template for sd ticket create --verbose --all-props
-App::SD::Test->set_editor("ticket-create-editor.pl --verbose-and-all $replica_uuid");
+Prophet::Test->set_editor_script("ticket-create-editor.pl --verbose-and-all $replica_uuid");
 create_ticket_and_check(extra_args => ['--all-props', '--verbose']);
 
diff --git a/t/03-update-ticket-with-editor.t b/t/03-update-ticket-with-editor.t
index a269236..57c76a7 100644
--- a/t/03-update-ticket-with-editor.t
+++ b/t/03-update-ticket-with-editor.t
@@ -41,7 +41,7 @@ run_output_matches( 'sd', [ 'ticket', 'basics', '--batch', '--id', $ticket_id ],
     ]
 );
 
-App::SD::Test->set_editor("ticket-update-editor.pl --no-args $replica_uuid $ticket_uuid");
+Prophet::Test->set_editor_script("ticket-update-editor.pl --no-args $replica_uuid $ticket_uuid");
 
 # update it
 my ($comment_id, $comment_uuid) = App::SD::Test->update_ticket_with_editor_ok($ticket_id, $ticket_uuid);
@@ -80,7 +80,7 @@ sub check_comment_ok {
 check_comment_ok();
 
 # sd ticket edit 20 --all-props
-App::SD::Test->set_editor("ticket-update-editor.pl --all-props $replica_uuid $ticket_uuid");
+Prophet::Test->set_editor_script("ticket-update-editor.pl --all-props $replica_uuid $ticket_uuid");
 
 # update it
 # template should show the hidden component prop
@@ -104,7 +104,7 @@ run_output_matches( 'sd', [ 'ticket', 'basics', '--batch', '--id', $ticket_id  ]
 check_comment_ok();
 
 # sd ticket edit 20 --verbose
-App::SD::Test->set_editor("ticket-update-editor.pl --verbose $replica_uuid $ticket_uuid");
+Prophet::Test->set_editor_script("ticket-update-editor.pl --verbose $replica_uuid $ticket_uuid");
 
 # update it
 ($comment_id, $comment_uuid) = App::SD::Test->update_ticket_with_editor_ok($ticket_id, $ticket_uuid, '--verbose');
@@ -127,7 +127,7 @@ run_output_matches( 'sd', [ 'ticket', 'basics', '--batch', '--id', $ticket_id ],
 check_comment_ok();
 
 # sd ticket edit 20 --verbose --all-props
-App::SD::Test->set_editor("ticket-update-editor.pl --verbose-and-all $replica_uuid $ticket_uuid");
+Prophet::Test->set_editor_script("ticket-update-editor.pl --verbose-and-all $replica_uuid $ticket_uuid");
 
 diag('changing settings for regression test: make sure props aren\'t deleted');
 diag('if they weren\'t presented for editing in the first place');
diff --git a/t/04-update-ticket-comment-with-editor.t b/t/04-update-ticket-comment-with-editor.t
index 16cf518..026cbfa 100644
--- a/t/04-update-ticket-comment-with-editor.t
+++ b/t/04-update-ticket-comment-with-editor.t
@@ -8,7 +8,7 @@ BEGIN {
     require File::Temp;
     $ENV{'PROPHET_REPO'} = $ENV{'SD_REPO'} = File::Temp::tempdir( CLEANUP => 1 ) . '/_svb';
     diag 'export SD_REPO=' . $ENV{'PROPHET_REPO'} . "\n";
-    App::SD::Test->set_editor('ticket-comment-update-editor.pl');
+    Prophet::Test->set_editor_script('ticket-comment-update-editor.pl');
 }
 
 run_script( 'sd', [ 'init']);

commit 0cf13ad324b39026f4273451f9ec93620a55dcca
Author: Christine Spang <spang at bestpractical.com>
Date:   Fri Jun 26 12:21:29 2009 +0300

    Prophet::Util already has write_file; kill App::SD::Test->write_to_file
    
    Also, some tests in t/07-sort-group.t were still using ticket.search
    rather than ticket.list. Fixed that.

diff --git a/lib/App/SD/Test.pm b/lib/App/SD/Test.pm
index e457fa9..aea276e 100644
--- a/lib/App/SD/Test.pm
+++ b/lib/App/SD/Test.pm
@@ -223,19 +223,4 @@ sub get_ticket_info {
     return \%res;
 }
 
-=head2 write_to_file FILENAME DATA
-
-Takes the string given in DATA and writes it to the file whose name is given
-by FILENAME.
-
-=cut
-
-sub write_to_file {
-    my ($self, $filename, $data) = @_;
-
-    open FH, '>', $filename;
-    print FH $data;
-    close FH;
-}
-
 1;
diff --git a/t/06-ticket-show.t b/t/06-ticket-show.t
index 046eae1..7a819a9 100644
--- a/t/06-ticket-show.t
+++ b/t/06-ticket-show.t
@@ -4,6 +4,7 @@ use strict;
 
 use Prophet::Test tests => 6;
 use App::SD::Test;
+use Prophet::Util;
 use File::Temp qw/tempdir/;
 use Term::ANSIColor;
 
@@ -92,7 +93,8 @@ diag("passing --skip history (doesn't show history)");
 check_output_without_history('--skip-history');
 
 my $config_filename = $ENV{'SD_REPO'} . '/config';
-App::SD::Test->write_to_file($config_filename, '
+Prophet::Util->write_file(
+    file => $config_filename, content => '
 [ticket "show"]
     disable-history = true
 ');
diff --git a/t/07-sort-group.t b/t/07-sort-group.t
index e57401f..bdf4123 100644
--- a/t/07-sort-group.t
+++ b/t/07-sort-group.t
@@ -3,6 +3,7 @@
 use strict;
 
 use Prophet::Test tests => 11;
+use Prophet::Util;
 use App::SD::Test;
 use File::Temp qw/tempdir/;
 
@@ -39,13 +40,14 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--sort', 'owner' ],
 );
 
 my $config_filename = $ENV{'SD_REPO'} . '/config';
-App::SD::Test->write_to_file($config_filename, '
-[ticket "search"]
+Prophet::Util->write_file(
+    file => $config_filename, content => '
+[ticket "list"]
     default-sort = owner
 ');
 $ENV{'SD_CONFIG'} = $config_filename;
 
-diag('using ticket.search.default-sort = owner');
+diag('using ticket.list.default-sort = owner');
 run_output_matches( 'sd', [ 'ticket', 'list' ],
     [ qr/(\d+) huzzah! new/,
       qr/(\d+) YATTA new/,
@@ -59,7 +61,7 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--sort' ],
     ]
 );
 
-diag('using ticket.search.default-sort = owner and --sort none');
+diag('using ticket.list.default-sort = owner and --sort none');
 run_output_matches( 'sd', [ 'ticket', 'list', '--sort', 'none' ],
     [ qr/(\d+) YATTA new/,
       qr/(\d+) huzzah! new/,
@@ -84,8 +86,9 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--group', 'owner' ],
 );
 
 diag('using ticket.search.default-group = owner');
-App::SD::Test->write_to_file($config_filename, '
-[ticket "search"]
+Prophet::Util->write_file(
+    file => $config_filename, content => '
+[ticket "list"]
     default-group = owner
 ');
 

commit fd8ecfbb884800ac8b8175c3ddbe789d1e277b76
Author: Christine Spang <spang at bestpractical.com>
Date:   Fri Jun 26 22:40:42 2009 +0300

    Dispatcher logic for grabbing IDs from primary commands for certain commands

diff --git a/lib/App/SD/CLI/Dispatcher.pm b/lib/App/SD/CLI/Dispatcher.pm
index 7f17855..828172b 100644
--- a/lib/App/SD/CLI/Dispatcher.pm
+++ b/lib/App/SD/CLI/Dispatcher.pm
@@ -2,6 +2,7 @@
 package App::SD::CLI::Dispatcher;
 use Prophet::CLI::Dispatcher -base;
 use Any::Moose;
+require Prophet::CLIContext;
 
 Prophet::CLI::Dispatcher->add_command_prefix('App::SD::CLI::Command');
 
@@ -13,13 +14,14 @@ rewrite [ ['about', 'copying'] ] => sub { "help $1" };
 
 on qr'^(?!help)' => sub {
     my $self = shift;
-    my $cmd = $_; 
+    my $cmd = $_;
+
     if ($self->context->has_arg('help')) {
         run("help $cmd", $self, @_);
-    } else { 
+    }
+    else {
         next_rule;
-        }
-
+    }
 };
 
 under help => sub {
@@ -85,22 +87,30 @@ on qr/^(\w+)\s+tickets?(.*)$/ => sub {
 };
 
 under ticket => sub {
+    # all these might possibly have IDs tacked onto the end
+    on
+    qr/^((?:comment\s+)?update|edit|show|display|delete|del|rm|history|claim|take|resolve|close) $Prophet::CLIContext::ID_REGEX$/i => sub {
+        my $self = shift;
+        $self->context->set_id_from_primary_commands;
+        run("ticket $1", $self, @_);
+    };
+
+    on [ [ 'new'    , 'create' ] ]    => run_command('Ticket::Create');
+    on [ [ 'show'   , 'display' ] ]   => run_command('Ticket::Show');
+    on [ [ 'update' , 'edit' ] ]      => run_command('Ticket::Update');
     on [ [ 'search', 'list', 'ls' ] ] => run_command('Ticket::Search');
-    on [ [ 'new',    'create' ] ]  => run_command('Ticket::Create');
-    on [ [ 'show',   'display' ] ] => run_command('Ticket::Show');
-    on [ [ 'update', 'edit' ] ]    => run_command('Ticket::Update');
+    on details  => run_command('Ticket::Details');
     on basics   => run_command('Ticket::Basics');
-    on comments => run_command('Ticket::Comments');
     on comment  => run_command('Ticket::Comment');
-    on details  => run_command('Ticket::Details');
+    on comments => run_command('Ticket::Comments');
 
     under [ [ 'give', 'assign' ] ] => sub {
-        on [qr/^(?:\d+|[0-9a-zA-Z\-\_]{22,24}|[0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12})$/, qr/^\S+$/] => sub {
+        on [qr/^$Prophet::CLIContext::ID_REGEX$/, qr/^\S+$/] => sub {
             my $self = shift;
             my ($id, $owner) = ($1, $2);
 
-            $self->context->set_arg(id    => $id);
-            $self->context->set_arg(type    => 'ticket');
+            $self->context->set_arg(id     => $id);
+            $self->context->set_arg(type   => 'ticket');
             $self->context->set_prop(owner => $owner);
             $self->context->set_type_and_uuid;
             run('ticket update', $self, @_);
@@ -131,12 +141,17 @@ under ticket => sub {
     };
 
     under comment => sub {
-        on [ [ 'new', 'create' ] ] => run_command('Ticket::Comment::Create');
-        on [ [ 'update', 'edit' ] ] => run_command('Ticket::Comment::Update');
+        on create => run_command('Ticket::Comment::Create');
+        on update => run_command('Ticket::Comment::Update');
     };
 
     under attachment => sub {
         on create => run_command('Ticket::Attachment::Create');
+        on [ [ 'create', 'new' ], qr/^$Prophet::CLIContext::ID_REGEX$/ ] => sub {
+            my $self = shift;
+            $self->context->set_id_from_primary_commands;
+            run('ticket attachment create', $self, @_);
+        };
         on search => run_command('Ticket::Attachment::Search');
     };
 

commit 1bee4fd1068661c8364b1d5293ba62b12c67e161
Author: Christine Spang <spang at bestpractical.com>
Date:   Fri Jun 26 22:42:32 2009 +0300

    Change some UI UUIDs to display friendly names instead

diff --git a/lib/App/SD/CLI/Command/Log.pm b/lib/App/SD/CLI/Command/Log.pm
index 3f3aef1..594f6c1 100644
--- a/lib/App/SD/CLI/Command/Log.pm
+++ b/lib/App/SD/CLI/Command/Log.pm
@@ -22,7 +22,7 @@ sub handle_changeset {
                 $c->created,
                 ( $c->creator || '(unknown)' ),
                 $c->original_sequence_no,
-                $c->original_source_uuid
+                $self->config->display_name_for_uuid($c->original_source_uuid)
                 ;
             }
 
diff --git a/lib/App/SD/CLI/Command/Ticket/Show.pm b/lib/App/SD/CLI/Command/Ticket/Show.pm
index a3f6078..74e9b84 100644
--- a/lib/App/SD/CLI/Command/Ticket/Show.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Show.pm
@@ -84,7 +84,7 @@ sub show_history_entry {
     $self->history_entry_header(
          $changeset->creator,
         $changeset->created,
-        $changeset->original_sequence_no,
+        $self->config->display_name_for_uuid($changeset->original_sequence_no),
         $changeset->original_source_uuid);
 
     print $body;

commit dc543e9025bd00ead02987240a4b82f384867bfd
Author: Christine Spang <spang at bestpractical.com>
Date:   Fri Jun 26 22:43:10 2009 +0300

    Some small formatting cleanup

diff --git a/lib/App/SD/CLI/Command/Ticket/Update.pm b/lib/App/SD/CLI/Command/Ticket/Update.pm
index b6eb6cc..479976b 100644
--- a/lib/App/SD/CLI/Command/Ticket/Update.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Update.pm
@@ -13,9 +13,11 @@ sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(),  a => 'all-props'  };
 # allowing the creation of a new comment in the process
 override run => sub {
     my $self = shift;
+
     $self->require_uuid;
     my $record = $self->_load_record;
-   return super() if ($self->context->prop_names && !$self->has_arg('edit'));
+
+    return super() if ($self->context->prop_names && !$self->has_arg('edit'));
     my $template_to_edit = $self->create_record_template($record);
 
     my $done = 0;

commit 395333503b67d565e0f6611a2a61436b6723fe15
Author: Christine Spang <spang at bestpractical.com>
Date:   Fri Jun 26 23:50:46 2009 +0300

    Note what this rule is for.

diff --git a/lib/App/SD/CLI/Dispatcher.pm b/lib/App/SD/CLI/Dispatcher.pm
index 828172b..bddd0ca 100644
--- a/lib/App/SD/CLI/Dispatcher.pm
+++ b/lib/App/SD/CLI/Dispatcher.pm
@@ -71,10 +71,9 @@ on qr'.*' => sub {
     exit 1;
 };
 
-
-
 on browser => run_command('Browser');
 
+# allow doing some things backwards -- e.g. 'list tickets' etc.
 on qr/^(\w+)\s+tickets?(.*)$/ => sub {
     my $self = shift;
     my $primary = $1;

commit 3554472eecd44ea3799b23dda09eb438ec81cca0
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Jun 29 15:42:16 2009 +0300

    Resurrect App::SD::Config for backcompat purposes.

diff --git a/lib/App/SD.pm b/lib/App/SD.pm
index 9e9c834..a4ed331 100644
--- a/lib/App/SD.pm
+++ b/lib/App/SD.pm
@@ -1,6 +1,6 @@
 package App::SD;
 use Any::Moose;
-use Prophet::Config;
+use App::SD::Config;
 
 extends 'Prophet::App';
 
@@ -10,7 +10,7 @@ has '+config' => (
     default => sub {
         my $self = shift;
         $ENV{PROPHET_APP_CONFIG} = $ENV{SD_CONFIG} if defined $ENV{SD_CONFIG};
-        return Prophet::Config->new( app_handle => $self, confname => 'sdrc' );
+        return App::SD::Config->new( app_handle => $self, confname => 'sdrc' );
     }
 );
 
diff --git a/lib/App/SD/Config.pm b/lib/App/SD/Config.pm
new file mode 100644
index 0000000..a05b37b
--- /dev/null
+++ b/lib/App/SD/Config.pm
@@ -0,0 +1,37 @@
+package App::SD::Config;
+use Any::Moose;
+use File::Spec;
+
+extends 'Prophet::Config';
+
+### XXX This class is for BACKCOMPAT ONLY! Eventually, we want to kill it
+### completely.
+
+override _old_app_config_file => sub {
+    my $self = shift;
+
+    # The order of preference for (OLD!) config files is:
+    #   $ENV{SD_CONFIG} > fs_root/config > fs_root/prophetrc (for backcompat)
+    #   $HOME/.sdrc > $ENV{PROPHET_APP_CONFIG} > $HOME/.prophetrc
+
+    # if we set PROPHET_APP_CONFIG here, it will mess up legit uses of the
+    # new config file setup
+    $ENV{'OLD_PROPHET_APP_CONFIG'}
+            =  $self->_file_if_exists($ENV{'SD_CONFIG'})
+            || $self->_file_if_exists(
+                File::Spec->catfile($self->app_handle->handle->fs_root => 'config'))
+            || $self->_file_if_exists(
+                # backcompat
+                File::Spec->catfile($self->app_handle->handle->fs_root => 'prophetrc'))
+            || $self->_file_if_exists(
+                File::Spec->catfile($ENV{'HOME'}.'/.sdrc'))
+            || $ENV{'PROPHET_APP_CONFIG'} # don't overwrite with nothing
+            || ''; # don't write undef
+        $self->SUPER::_old_app_config_file(@_,
+            config_env_var => 'OLD_PROPHET_APP_CONFIG');
+};
+
+__PACKAGE__->meta->make_immutable;
+no Any::Moose;
+
+1;

commit 7300f47adf0fa6c709105a4e6a76e658180c4362
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Jun 29 17:48:28 2009 +0300

    Doc using $1 etc. for args in aliases.

diff --git a/lib/App/SD/CLI/Command/Help/Aliases.pm b/lib/App/SD/CLI/Command/Help/Aliases.pm
index 9e83a00..0cca740 100644
--- a/lib/App/SD/CLI/Command/Help/Aliases.pm
+++ b/lib/App/SD/CLI/Command/Help/Aliases.pm
@@ -20,6 +20,11 @@ to you with the alias 'mine':
 
     mine = ticket list -- owner=you\@domain.com status !~closed|rejected
 
+To create aliases that take additional arguments, use the argument
+number prefixed with a '\$' to refer to them, like this:
+
+    ts = ticket show \$1
+
 SD also provides a command for managing aliases: '${cmd}aliases'. If
 given no arguments, the aliases command will print the active aliases
 for the current repository (including all non-overridden user-wide

commit d3eca042c433951ec45fe89e893535a5902a6851
Author: Christine Spang <spang at bestpractical.com>
Date:   Sun Jul 5 20:25:00 2009 -0400

    sd ticket comment # and sd ticket comments # should work too
    
    (Fixes GitHub tests.)

diff --git a/lib/App/SD/CLI/Dispatcher.pm b/lib/App/SD/CLI/Dispatcher.pm
index bddd0ca..1175242 100644
--- a/lib/App/SD/CLI/Dispatcher.pm
+++ b/lib/App/SD/CLI/Dispatcher.pm
@@ -88,7 +88,7 @@ on qr/^(\w+)\s+tickets?(.*)$/ => sub {
 under ticket => sub {
     # all these might possibly have IDs tacked onto the end
     on
-    qr/^((?:comment\s+)?update|edit|show|display|delete|del|rm|history|claim|take|resolve|close) $Prophet::CLIContext::ID_REGEX$/i => sub {
+    qr/^((?:comment\s+)?comments?|update|edit|show|display|delete|del|rm|history|claim|take|resolve|close) $Prophet::CLIContext::ID_REGEX$/i => sub {
         my $self = shift;
         $self->context->set_id_from_primary_commands;
         run("ticket $1", $self, @_);

commit 789b06e45e18068cee1ebcdc25f45869a6cb7463
Merge: d3eca04... 15aafc4...
Author: Christine Spang <spang at bestpractical.com>
Date:   Sun Jul 5 20:29:28 2009 -0400

    Merge branch 'master' into config-gitlike


commit be94243d53c94600b8c334de940979a3b4957fbd
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Mon Jul 6 16:00:41 2009 -0400

    href fixes for ticket links.

diff --git a/lib/App/SD/Server/Dispatcher.pm b/lib/App/SD/Server/Dispatcher.pm
index ee8cc9c..46dce6d 100644
--- a/lib/App/SD/Server/Dispatcher.pm
+++ b/lib/App/SD/Server/Dispatcher.pm
@@ -27,7 +27,7 @@ on qr'.' => sub {
         #$m->child('mine' => label => 'Mine', url => '/milestone/'.$item.'/mine');
         #$m->child('closed' => label => 'Closed', url => '/milestone/'.$item.'/closed');
     }
-        $milestones->child( none => label => 'None', url => '/no_milestone');
+        $milestones->child( none => label => 'None', url => '/milestone/');
     
     my $components = $tickets->child( components => label => 'Components', url => '/components');
     my $component_list = $self->server->app_handle->setting( label => 'components' )->get();
@@ -39,7 +39,7 @@ on qr'.' => sub {
 
 
     }
-    $components->child('None' => label => 'None', url => '/no_component');
+    $components->child('None' => label => 'None', url => '/component/');
 
     $tickets->child( all => label => 'All' => url => '/tickets/all');
     $self->server->nav->child( create => label => 'New ticket', url => '/ticket/new') unless($self->server->static);

commit 72f08d65d88b800bca837c56f0061890f76d7af3
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Mon Jul 6 17:23:00 2009 -0400

    expose a friendly name for replicas in the web view

diff --git a/lib/App/SD/Server/View.pm b/lib/App/SD/Server/View.pm
index 076f1b7..73bcd54 100644
--- a/lib/App/SD/Server/View.pm
+++ b/lib/App/SD/Server/View.pm
@@ -573,7 +573,7 @@ template ticket_history => sub {
                 };
                 span {
                     { class is 'original_source_uuid' };
-                    $changeset->original_source_uuid;
+                    $self->app_handle->config->display_name_for_uuid($changeset->original_source_uuid);
                 };
                 };
             };

commit 42e88417114f719ddceb56b07cdd3b5029a33de9
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Mon Jul 6 17:23:30 2009 -0400

    fix a redefinition warning in the trac pushencoder

diff --git a/lib/App/SD/Replica/trac/PushEncoder.pm b/lib/App/SD/Replica/trac/PushEncoder.pm
index dadb540..2e2cf13 100644
--- a/lib/App/SD/Replica/trac/PushEncoder.pm
+++ b/lib/App/SD/Replica/trac/PushEncoder.pm
@@ -79,7 +79,6 @@ sub integrate_attachment {
     my $tempdir = File::Temp::tempdir( CLEANUP => 1 );
     my $file = File::Spec->catfile( $tempdir, ( $props{'name'} || 'unnamed' ) );
     open my $fh, '>', $file or die $!;
-    my $fh = $file->openw;
     print $fh $props{content};
     close $fh;
     my %content = ( message     => '(See attachments)', attachments => ["$file"]);

commit f48aca6dea4f753a89528ed122bfb1abda235fc0
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Mon Jul 6 17:23:56 2009 -0400

    improve tickt views of friendly replica names

diff --git a/lib/App/SD/CLI/Command/Ticket/Show.pm b/lib/App/SD/CLI/Command/Ticket/Show.pm
index 74e9b84..a2c824b 100644
--- a/lib/App/SD/CLI/Command/Ticket/Show.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Show.pm
@@ -84,8 +84,10 @@ sub show_history_entry {
     $self->history_entry_header(
          $changeset->creator,
         $changeset->created,
-        $self->config->display_name_for_uuid($changeset->original_sequence_no),
-        $changeset->original_source_uuid);
+        $changeset->original_sequence_no,
+        $self->config->display_name_for_uuid($changeset->original_source_uuid),
+    
+    );
 
     print $body;
 }
@@ -119,7 +121,7 @@ sub show_comment {
         $content =~ s|\n\n|\n|gismx;
     }
 
-    $self->history_entry_header($creator, $created,$creation->original_sequence_no, $creation->original_source_uuid);
+    $self->history_entry_header($creator, $created,$creation->original_sequence_no, $self->config->display_name_for_uuid($creation->original_source_uuid));
     print $content;
     print "\n\n";
 }

commit 82861066066ac51d8c947a5c128b5fbe1982ec9e
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Mon Jul 6 17:43:50 2009 -0400

    Dispatcher cleanups to fix failing tests

diff --git a/lib/App/SD/CLI/Dispatcher.pm b/lib/App/SD/CLI/Dispatcher.pm
index 1175242..c455883 100644
--- a/lib/App/SD/CLI/Dispatcher.pm
+++ b/lib/App/SD/CLI/Dispatcher.pm
@@ -88,7 +88,7 @@ on qr/^(\w+)\s+tickets?(.*)$/ => sub {
 under ticket => sub {
     # all these might possibly have IDs tacked onto the end
     on
-    qr/^((?:comment\s+)?comments?|update|edit|show|display|delete|del|rm|history|claim|take|resolve|close) $Prophet::CLIContext::ID_REGEX$/i => sub {
+    qr/^((?:comment\s+)?(?:comments?|update|edit|show|display|delete|del|rm|history|claim|take|resolve|basics|close)) $Prophet::CLIContext::ID_REGEX$/i => sub {
         my $self = shift;
         $self->context->set_id_from_primary_commands;
         run("ticket $1", $self, @_);
@@ -158,6 +158,14 @@ under ticket => sub {
 };
 
 under attachment => sub {
+    on qr/^(.*)\s+($Prophet::CLIContext::ID_REGEX)$/i => sub {
+        my $self = shift;
+        my $next = $1;
+        my $id = $2;
+        $self->context->set_id($id);
+        run("attachment $next", $self, @_);
+    };
+
     on content => run_command('Attachment::Content');
     on create  => run_command('Attachment::Create');
 };

commit d3ff5b53308276a700b6c74b910611c034df9c5e
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Tue Jul 7 16:01:30 2009 -0400

    Moved the old config upgrade code from prophet to sd

diff --git a/inc/Module/AutoInstall.pm b/inc/Module/AutoInstall.pm
index 739bc85..dfb8ef7 100644
--- a/inc/Module/AutoInstall.pm
+++ b/inc/Module/AutoInstall.pm
@@ -18,7 +18,9 @@ my %FeatureMap = (
 
 # various lexical flags
 my ( @Missing, @Existing,  %DisabledTests, $UnderCPAN,     $HasCPANPLUS );
-my ( $Config,  $CheckOnly, $SkipInstall,   $AcceptDefault, $TestOnly );
+my (
+    $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps
+);
 my ( $PostambleActions, $PostambleUsed );
 
 # See if it's a testing or non-interactive session
@@ -73,6 +75,9 @@ sub _init {
         elsif ( $arg =~ /^--test(?:only)?$/ ) {
             $TestOnly = 1;
         }
+        elsif ( $arg =~ /^--all(?:deps)?$/ ) {
+            $AllDeps = 1;
+        }
     }
 }
 
@@ -115,7 +120,12 @@ sub import {
         )[0]
     );
 
-    $UnderCPAN = _check_lock(1);    # check for $UnderCPAN
+    # We want to know if we're under CPAN early to avoid prompting, but
+    # if we aren't going to try and install anything anyway then skip the
+    # check entirely since we don't want to have to load (and configure)
+    # an old CPAN just for a cosmetic message
+
+    $UnderCPAN = _check_lock(1) unless $SkipInstall;
 
     while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) {
         my ( @required, @tests, @skiptests );
@@ -165,15 +175,24 @@ sub import {
             }
 
             # XXX: check for conflicts and uninstalls(!) them.
-            if (
-                defined( my $cur = _version_check( _load($mod), $arg ||= 0 ) ) )
+            my $cur = _load($mod);
+            if (_version_cmp ($cur, $arg) >= 0)
             {
                 print "loaded. ($cur" . ( $arg ? " >= $arg" : '' ) . ")\n";
                 push @Existing, $mod => $arg;
                 $DisabledTests{$_} = 1 for map { glob($_) } @skiptests;
             }
             else {
-                print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n";
+                if (not defined $cur)   # indeed missing
+                {
+                    print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n";
+                }
+                else
+                {
+                    # no need to check $arg as _version_cmp ($cur, undef) would satisfy >= above
+                    print "too old. ($cur < $arg)\n";
+                }
+
                 push @required, $mod => $arg;
             }
         }
@@ -187,6 +206,7 @@ sub import {
             and (
                 $CheckOnly
                 or ($mandatory and $UnderCPAN)
+                or $AllDeps
                 or _prompt(
                     qq{==> Auto-install the }
                       . ( @required / 2 )
@@ -235,21 +255,38 @@ sub import {
     *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main';
 }
 
+sub _running_under {
+    my $thing = shift;
+    print <<"END_MESSAGE";
+*** Since we're running under ${thing}, I'll just let it take care
+    of the dependency's installation later.
+END_MESSAGE
+    return 1;
+}
+
 # Check to see if we are currently running under CPAN.pm and/or CPANPLUS;
 # if we are, then we simply let it taking care of our dependencies
 sub _check_lock {
     return unless @Missing or @_;
 
+    my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING};
+
     if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) {
-        print <<'END_MESSAGE';
+        return _running_under($cpan_env ? 'CPAN' : 'CPANPLUS');
+    }
 
-*** Since we're running under CPANPLUS, I'll just let it take care
-    of the dependency's installation later.
-END_MESSAGE
-        return 1;
+    require CPAN;
+
+    if ($CPAN::VERSION > '1.89') {
+        if ($cpan_env) {
+            return _running_under('CPAN');
+        }
+        return; # CPAN.pm new enough, don't need to check further
     }
 
-    _load_cpan();
+    # last ditch attempt, this -will- configure CPAN, very sorry
+
+    _load_cpan(1); # force initialize even though it's already loaded
 
     # Find the CPAN lock-file
     my $lock = MM->catfile( $CPAN::Config->{cpan_home}, ".lock" );
@@ -285,7 +322,7 @@ sub install {
     while ( my ( $pkg, $ver ) = splice( @_, 0, 2 ) ) {
 
         # grep out those already installed
-        if ( defined( _version_check( _load($pkg), $ver ) ) ) {
+        if ( _version_cmp( _load($pkg), $ver ) >= 0 ) {
             push @installed, $pkg;
         }
         else {
@@ -324,7 +361,7 @@ sub install {
 
     # see if we have successfully installed them
     while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) {
-        if ( defined( _version_check( _load($pkg), $ver ) ) ) {
+        if ( _version_cmp( _load($pkg), $ver ) >= 0 ) {
             push @installed, $pkg;
         }
         elsif ( $args{do_once} and open( FAILED, '>> .#autoinstall.failed' ) ) {
@@ -379,7 +416,7 @@ sub _install_cpanplus {
         my $success;
         my $obj = $modtree->{$pkg};
 
-        if ( $obj and defined( _version_check( $obj->{version}, $ver ) ) ) {
+        if ( $obj and _version_cmp( $obj->{version}, $ver ) >= 0 ) {
             my $pathname = $pkg;
             $pathname =~ s/::/\\W/;
 
@@ -472,7 +509,7 @@ sub _install_cpan {
         my $obj     = CPAN::Shell->expand( Module => $pkg );
         my $success = 0;
 
-        if ( $obj and defined( _version_check( $obj->cpan_version, $ver ) ) ) {
+        if ( $obj and _version_cmp( $obj->cpan_version, $ver ) >= 0 ) {
             my $pathname = $pkg;
             $pathname =~ s/::/\\W/;
 
@@ -536,7 +573,7 @@ sub _update_to {
     my $ver   = shift;
 
     return
-      if defined( _version_check( _load($class), $ver ) );  # no need to upgrade
+      if _version_cmp( _load($class), $ver ) >= 0;  # no need to upgrade
 
     if (
         _prompt( "==> A newer version of $class ($ver) is required. Install?",
@@ -633,7 +670,7 @@ sub _load {
 
 # Load CPAN.pm and it's configuration
 sub _load_cpan {
-    return if $CPAN::VERSION;
+    return if $CPAN::VERSION and $CPAN::Config and not @_;
     require CPAN;
     if ( $CPAN::HandleConfig::VERSION ) {
         # Newer versions of CPAN have a HandleConfig module
@@ -645,9 +682,11 @@ sub _load_cpan {
 }
 
 # compare two versions, either use Sort::Versions or plain comparison
-sub _version_check {
+# return values same as <=>
+sub _version_cmp {
     my ( $cur, $min ) = @_;
-    return unless defined $cur;
+    return -1 unless defined $cur;  # if 0 keep comparing
+    return 1 unless $min;
 
     $cur =~ s/\s+$//;
 
@@ -658,16 +697,13 @@ sub _version_check {
             ) {
 
             # use version.pm if it is installed.
-            return (
-                ( version->new($cur) >= version->new($min) ) ? $cur : undef );
+            return version->new($cur) <=> version->new($min);
         }
         elsif ( $Sort::Versions::VERSION or defined( _load('Sort::Versions') ) )
         {
 
             # use Sort::Versions as the sorting algorithm for a.b.c versions
-            return ( ( Sort::Versions::versioncmp( $cur, $min ) != -1 )
-                ? $cur
-                : undef );
+            return Sort::Versions::versioncmp( $cur, $min );
         }
 
         warn "Cannot reliably compare non-decimal formatted versions.\n"
@@ -676,7 +712,7 @@ sub _version_check {
 
     # plain comparison
     local $^W = 0;    # shuts off 'not numeric' bugs
-    return ( $cur >= $min ? $cur : undef );
+    return $cur <=> $min;
 }
 
 # nothing; this usage is deprecated.
@@ -766,4 +802,4 @@ END_MAKE
 
 __END__
 
-#line 1004
+#line 1056
diff --git a/inc/Module/Install.pm b/inc/Module/Install.pm
index 5b9ddbf..51eda5d 100644
--- a/inc/Module/Install.pm
+++ b/inc/Module/Install.pm
@@ -28,7 +28,7 @@ BEGIN {
 	# This is not enforced yet, but will be some time in the next few
 	# releases once we can make sure it won't clash with custom
 	# Module::Install extensions.
-	$VERSION = '0.85';
+	$VERSION = '0.91';
 
 	# Storage for the pseudo-singleton
 	$MAIN    = undef;
@@ -353,7 +353,7 @@ sub _read {
 	if ( $] >= 5.006 ) {
 		open( FH, '<', $_[0] ) or die "open($_[0]): $!";
 	} else {
-		open( FH, "< $_[0]"  ) or die "open($_[0]): $!";	
+		open( FH, "< $_[0]"  ) or die "open($_[0]): $!";
 	}
 	my $string = do { local $/; <FH> };
 	close FH or die "close($_[0]): $!";
@@ -384,7 +384,7 @@ sub _write {
 	if ( $] >= 5.006 ) {
 		open( FH, '>', $_[0] ) or die "open($_[0]): $!";
 	} else {
-		open( FH, "> $_[0]"  ) or die "open($_[0]): $!";	
+		open( FH, "> $_[0]"  ) or die "open($_[0]): $!";
 	}
 	foreach ( 1 .. $#_ ) {
 		print FH $_[$_] or die "print($_[0]): $!";
diff --git a/inc/Module/Install/AutoInstall.pm b/inc/Module/Install/AutoInstall.pm
index b7e92a5..58dd026 100644
--- a/inc/Module/Install/AutoInstall.pm
+++ b/inc/Module/Install/AutoInstall.pm
@@ -2,13 +2,13 @@
 package Module::Install::AutoInstall;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 sub AutoInstall { $_[0] }
diff --git a/inc/Module/Install/Base.pm b/inc/Module/Install/Base.pm
index ac416c9..60a74d2 100644
--- a/inc/Module/Install/Base.pm
+++ b/inc/Module/Install/Base.pm
@@ -4,7 +4,7 @@ package Module::Install::Base;
 use strict 'vars';
 use vars qw{$VERSION};
 BEGIN {
-	$VERSION = '0.85';
+	$VERSION = '0.91';
 }
 
 # Suspend handler for "redefined" warnings
@@ -13,42 +13,34 @@ BEGIN {
 	$SIG{__WARN__} = sub { $w };
 }
 
-### This is the ONLY module that shouldn't have strict on
-# use strict;
-
-#line 45
+#line 42
 
 sub new {
-	my ($class, %args) = @_;
-
-	foreach my $method ( qw(call load) ) {
-		next if defined &{"$class\::$method"};
-		*{"$class\::$method"} = sub {
-			shift()->_top->$method(@_);
-		};
+	my $class = shift;
+	unless ( defined &{"${class}::call"} ) {
+		*{"${class}::call"} = sub { shift->_top->call(@_) };
 	}
-
-	bless( \%args, $class );
+	unless ( defined &{"${class}::load"} ) {
+		*{"${class}::load"} = sub { shift->_top->load(@_) };
+	}
+	bless { @_ }, $class;
 }
 
-#line 66
+#line 61
 
 sub AUTOLOAD {
-	my $self = shift;
 	local $@;
-	my $autoload = eval {
-		$self->_top->autoload
-	} or return;
-	goto &$autoload;
+	my $func = eval { shift->_top->autoload } or return;
+	goto &$func;
 }
 
-#line 83
+#line 75
 
 sub _top {
 	$_[0]->{_top};
 }
 
-#line 98
+#line 90
 
 sub admin {
 	$_[0]->_top->{admin}
@@ -56,7 +48,7 @@ sub admin {
 	Module::Install::Base::FakeAdmin->new;
 }
 
-#line 114
+#line 106
 
 sub is_admin {
 	$_[0]->admin->VERSION;
@@ -83,4 +75,4 @@ BEGIN {
 
 1;
 
-#line 162
+#line 154
diff --git a/inc/Module/Install/Can.pm b/inc/Module/Install/Can.pm
index 3e2d523..e65e4f6 100644
--- a/inc/Module/Install/Can.pm
+++ b/inc/Module/Install/Can.pm
@@ -2,16 +2,16 @@
 package Module::Install::Can;
 
 use strict;
-use Module::Install::Base;
-use Config              ();
-use File::Spec          ();
-use ExtUtils::MakeMaker ();
+use Config                ();
+use File::Spec            ();
+use ExtUtils::MakeMaker   ();
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 # check if we can load some module
diff --git a/inc/Module/Install/Fetch.pm b/inc/Module/Install/Fetch.pm
index 0a62208..05f2079 100644
--- a/inc/Module/Install/Fetch.pm
+++ b/inc/Module/Install/Fetch.pm
@@ -2,13 +2,13 @@
 package Module::Install::Fetch;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 sub get_file {
diff --git a/inc/Module/Install/Include.pm b/inc/Module/Install/Include.pm
index 92aad58..7e792e0 100644
--- a/inc/Module/Install/Include.pm
+++ b/inc/Module/Install/Include.pm
@@ -2,13 +2,13 @@
 package Module::Install::Include;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 sub include {
diff --git a/inc/Module/Install/Makefile.pm b/inc/Module/Install/Makefile.pm
index 2b80f0f..98779db 100644
--- a/inc/Module/Install/Makefile.pm
+++ b/inc/Module/Install/Makefile.pm
@@ -2,14 +2,14 @@
 package Module::Install::Makefile;
 
 use strict 'vars';
-use Module::Install::Base;
-use ExtUtils::MakeMaker ();
+use ExtUtils::MakeMaker   ();
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 sub Makefile { $_[0] }
diff --git a/inc/Module/Install/Metadata.pm b/inc/Module/Install/Metadata.pm
index ca16db7..653193d 100644
--- a/inc/Module/Install/Metadata.pm
+++ b/inc/Module/Install/Metadata.pm
@@ -2,18 +2,17 @@
 package Module::Install::Metadata;
 
 use strict 'vars';
-use Module::Install::Base;
+use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
-	@ISA     = qw{Module::Install::Base};
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
 
 my @boolean_keys = qw{
 	sign
-	mymeta
 };
 
 my @scalar_keys = qw{
@@ -440,21 +439,21 @@ sub license_from {
 	/ixms ) {
 		my $license_text = $1;
 		my @phrases      = (
-			'under the same (?:terms|license) as perl itself' => 'perl',        1,
-			'GNU general public license'                      => 'gpl',         1,
-			'GNU public license'                              => 'gpl',         1,
-			'GNU lesser general public license'               => 'lgpl',        1,
-			'GNU lesser public license'                       => 'lgpl',        1,
-			'GNU library general public license'              => 'lgpl',        1,
-			'GNU library public license'                      => 'lgpl',        1,
-			'BSD license'                                     => 'bsd',         1,
-			'Artistic license'                                => 'artistic',    1,
-			'GPL'                                             => 'gpl',         1,
-			'LGPL'                                            => 'lgpl',        1,
-			'BSD'                                             => 'bsd',         1,
-			'Artistic'                                        => 'artistic',    1,
-			'MIT'                                             => 'mit',         1,
-			'proprietary'                                     => 'proprietary', 0,
+			'under the same (?:terms|license) as (?:perl|the perl programming language) itself' => 'perl', 1,
+			'GNU general public license'         => 'gpl',         1,
+			'GNU public license'                 => 'gpl',         1,
+			'GNU lesser general public license'  => 'lgpl',        1,
+			'GNU lesser public license'          => 'lgpl',        1,
+			'GNU library general public license' => 'lgpl',        1,
+			'GNU library public license'         => 'lgpl',        1,
+			'BSD license'                        => 'bsd',         1,
+			'Artistic license'                   => 'artistic',    1,
+			'GPL'                                => 'gpl',         1,
+			'LGPL'                               => 'lgpl',        1,
+			'BSD'                                => 'bsd',         1,
+			'Artistic'                           => 'artistic',    1,
+			'MIT'                                => 'mit',         1,
+			'proprietary'                        => 'proprietary', 0,
 		);
 		while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
 			$pattern =~ s{\s+}{\\s+}g;
@@ -506,17 +505,29 @@ sub requires_from {
 	}
 }
 
+sub test_requires_from {
+	my $self     = shift;
+	my $content  = Module::Install::_readperl($_[0]);
+	my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg;
+	while ( @requires ) {
+		my $module  = shift @requires;
+		my $version = shift @requires;
+		$self->test_requires( $module => $version );
+	}
+}
+
 # Convert triple-part versions (eg, 5.6.1 or 5.8.9) to
 # numbers (eg, 5.006001 or 5.008009).
 # Also, convert double-part versions (eg, 5.8)
 sub _perl_version {
 	my $v = $_[-1];
-	$v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e;	
+	$v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e;
 	$v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e;
 	$v =~ s/(\.\d\d\d)000$/$1/;
 	$v =~ s/_.+$//;
 	if ( ref($v) ) {
-		$v = $v + 0; # Numify
+		# Numify
+		$v = $v + 0;
 	}
 	return $v;
 }
@@ -526,23 +537,58 @@ sub _perl_version {
 
 
 ######################################################################
-# MYMETA.yml Support
+# MYMETA Support
 
 sub WriteMyMeta {
 	die "WriteMyMeta has been deprecated";
 }
 
-sub write_mymeta {
+sub write_mymeta_yaml {
 	my $self = shift;
-	
-	# If there's no existing META.yml there is nothing we can do
-	return unless -f 'META.yml';
 
 	# We need YAML::Tiny to write the MYMETA.yml file
 	unless ( eval { require YAML::Tiny; 1; } ) {
 		return 1;
 	}
 
+	# Generate the data
+	my $meta = $self->_write_mymeta_data or return 1;
+
+	# Save as the MYMETA.yml file
+	print "Writing MYMETA.yml\n";
+	YAML::Tiny::DumpFile('MYMETA.yml', $meta);
+}
+
+sub write_mymeta_json {
+	my $self = shift;
+
+	# We need JSON to write the MYMETA.json file
+	unless ( eval { require JSON; 1; } ) {
+		return 1;
+	}
+
+	# Generate the data
+	my $meta = $self->_write_mymeta_data or return 1;
+
+	# Save as the MYMETA.yml file
+	print "Writing MYMETA.json\n";
+	Module::Install::_write(
+		'MYMETA.json',
+		JSON->new->pretty(1)->canonical->encode($meta),
+	);
+}
+
+sub _write_mymeta_data {
+	my $self = shift;
+
+	# If there's no existing META.yml there is nothing we can do
+	return undef unless -f 'META.yml';
+
+	# We need Parse::CPAN::Meta to load the file
+	unless ( eval { require Parse::CPAN::Meta; 1; } ) {
+		return undef;
+	}
+
 	# Merge the perl version into the dependencies
 	my $val  = $self->Meta->{values};
 	my $perl = delete $val->{perl_version};
@@ -558,7 +604,7 @@ sub write_mymeta {
 	}
 
 	# Load the advisory META.yml file
-	my @yaml = YAML::Tiny::LoadFile('META.yml');
+	my @yaml = Parse::CPAN::Meta::LoadFile('META.yml');
 	my $meta = $yaml[0];
 
 	# Overwrite the non-configure dependency hashs
@@ -572,9 +618,7 @@ sub write_mymeta {
 		$meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } };
 	}
 
-	# Save as the MYMETA.yml file
-	print "Writing MYMETA.yml\n";
-	YAML::Tiny::DumpFile('MYMETA.yml', $meta);	
+	return $meta;
 }
 
 1;
diff --git a/inc/Module/Install/Scripts.pm b/inc/Module/Install/Scripts.pm
index 8aee44b..a1001f5 100644
--- a/inc/Module/Install/Scripts.pm
+++ b/inc/Module/Install/Scripts.pm
@@ -2,13 +2,13 @@
 package Module::Install::Scripts;
 
 use strict 'vars';
-use Module::Install::Base;
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 sub install_script {
diff --git a/inc/Module/Install/Share.pm b/inc/Module/Install/Share.pm
index 447a0c8..f7e877c 100644
--- a/inc/Module/Install/Share.pm
+++ b/inc/Module/Install/Share.pm
@@ -2,13 +2,13 @@
 package Module::Install::Share;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
-use vars qw{$VERSION $ISCORE @ISA};
+use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
-	@ISA     = qw{Module::Install::Base};
 }
 
 sub install_share {
diff --git a/inc/Module/Install/Win32.pm b/inc/Module/Install/Win32.pm
index c00da94..f2f99df 100644
--- a/inc/Module/Install/Win32.pm
+++ b/inc/Module/Install/Win32.pm
@@ -2,12 +2,12 @@
 package Module::Install::Win32;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
-	@ISA     = qw{Module::Install::Base};
+	$VERSION = '0.91';
+	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
 
diff --git a/inc/Module/Install/WriteAll.pm b/inc/Module/Install/WriteAll.pm
index df3900a..12471e5 100644
--- a/inc/Module/Install/WriteAll.pm
+++ b/inc/Module/Install/WriteAll.pm
@@ -2,11 +2,11 @@
 package Module::Install::WriteAll;
 
 use strict;
-use Module::Install::Base;
+use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.85';
+	$VERSION = '0.91';;
 	@ISA     = qw{Module::Install::Base};
 	$ISCORE  = 1;
 }
@@ -41,8 +41,18 @@ sub WriteAll {
 
 	# The Makefile write process adds a couple of dependencies,
 	# so write the META.yml files after the Makefile.
-	$self->Meta->write        if $args{meta};
-	$self->Meta->write_mymeta if $self->mymeta;
+	if ( $args{meta} ) {
+		$self->Meta->write;
+	}
+
+	# Experimental support for MYMETA
+	if ( $ENV{X_MYMETA} ) {
+		if ( $ENV{X_MYMETA} eq 'JSON' ) {
+			$self->Meta->write_mymeta_json;
+		} else {
+			$self->Meta->write_mymeta_yaml;
+		}
+	}
 
 	return 1;
 }
diff --git a/lib/App/SD/Config.pm b/lib/App/SD/Config.pm
index a05b37b..6d3832d 100644
--- a/lib/App/SD/Config.pm
+++ b/lib/App/SD/Config.pm
@@ -4,10 +4,11 @@ use File::Spec;
 
 extends 'Prophet::Config';
 
-### XXX This class is for BACKCOMPAT ONLY! Eventually, we want to kill it
+{
+### XXX This code is for BACKCOMPAT ONLY! Eventually, we want to kill it
 ### completely.
 
-override _old_app_config_file => sub {
+sub _old_app_config_file {
     my $self = shift;
 
     # The order of preference for (OLD!) config files is:
@@ -16,20 +17,166 @@ override _old_app_config_file => sub {
 
     # if we set PROPHET_APP_CONFIG here, it will mess up legit uses of the
     # new config file setup
-    $ENV{'OLD_PROPHET_APP_CONFIG'}
+    my $old_file 
             =  $self->_file_if_exists($ENV{'SD_CONFIG'})
-            || $self->_file_if_exists(
-                File::Spec->catfile($self->app_handle->handle->fs_root => 'config'))
-            || $self->_file_if_exists(
-                # backcompat
-                File::Spec->catfile($self->app_handle->handle->fs_root => 'prophetrc'))
-            || $self->_file_if_exists(
-                File::Spec->catfile($ENV{'HOME'}.'/.sdrc'))
+            || $self->_file_if_exists( File::Spec->catfile($self->app_handle->handle->fs_root => 'config'))
+            || $self->_file_if_exists( File::Spec->catfile($self->app_handle->handle->fs_root => 'prophetrc'))
+            || $self->_file_if_exists( File::Spec->catfile($ENV{'HOME'}.'/.sdrc'))
             || $ENV{'PROPHET_APP_CONFIG'} # don't overwrite with nothing
             || ''; # don't write undef
-        $self->SUPER::_old_app_config_file(@_,
-            config_env_var => 'OLD_PROPHET_APP_CONFIG');
+
+    return $self->_file_if_exists($old_file)
+        || $self->_file_if_exists( File::Spec->catfile( $ENV{'HOME'} => '.prophetrc' ))
+        || $self->_file_if_exists( File::Spec->catfile( $self->app_handle->handle->fs_root => 'config' )) ||
+     $self->_file_if_exists( File::Spec->catfile( $self->app_handle->handle->fs_root => 'prophetrc' )) ||
+      File::Spec->catfile( $self->app_handle->handle->fs_root => 'config' );
+}
+
+
+
+override load => sub  {
+    my $self = shift;
+
+    Prophet::CLI->end_pager();
+
+    # Do backcompat stuff.
+    for my $file ( ($self->_old_app_config_file, $self->dir_file, $self->user_file, $self->global_file) ) {
+        my $content = -f $file ? Prophet::Util->slurp($file) : '[';
+
+        # config file is old
+
+        # Also "converts" empty files but that's fine. If it ever
+        # does happen, we get the positive benefit of writing the
+        # config format to it.
+        if ( $content !~ /\[/ ) {
+
+            $self->convert_ancient_config_file($file);
+        }
+
+    }
+
+    Prophet::CLI->start_pager();
+
+    # Do a regular load.
+    $self->SUPER::load(@_);
+};
+
+### XXX BACKCOMPAT ONLY! We eventually want to kill this hash, modifier and
+### the following methods.
+
+# None of these need to have values mucked with at all, just the keys
+# migrated from old to new.
+our %KEYS_CONVERSION_TABLE = (
+    'email_address' => 'user.email-address',
+    'default_group_ticket_list' => 'ticket.list.default-group',
+    'default_sort_ticket_list' => 'ticket.list.default-sort',
+    'summary_format_ticket' => 'ticket.summary-format',
+    'default_summary_format' => 'record.summary-format',
+    'common_ticket_props' => 'ticket.common-props',
+    'disable_ticket_show_history_by_default' => 'ticket.show.disable-history',
+);
+
+
+
+sub convert_ancient_config_file {
+            my $self = shift;
+            my $file = shift;
+            print "Detected old format config file $file. Converting to ".
+                  "new format... ";
+
+            # read in and parse old config
+            my $config = { _sources => {}, _aliases => {} };
+            $self->_load_old_config_from_file( $file, $config );
+            my $aliases = delete $config->{_aliases};
+            my $sources = delete $config->{_sources};
+
+            # new configuration will include a config format version #
+            my @config_to_set = ( {
+                    key => 'core.config-format-version',
+                    value => $self->FORMAT_VERSION,
+            } );
+
+            # convert its keys to new-style keys by comparing to a conversion
+            # table
+            for my $key ( keys %$config ) {
+                die "Unknown key '$key' in old format config file '$file'."
+                    ." Remove it or ask\non irc.freenode.net #prophet if you"
+                    ." think this is a bug.\n"
+                        unless exists $KEYS_CONVERSION_TABLE{$key};
+                push @config_to_set, {
+                    key   => $KEYS_CONVERSION_TABLE{$key},
+                    value => $config->{$key},
+                };
+            }
+            # convert its aliases
+            for my $alias ( keys %$aliases ) {
+                push @config_to_set, {
+                    key   => "alias.'$alias'",
+                    value => $aliases->{$alias},
+                };
+            }
+            # convert its sources
+            for my $name ( keys %$sources ) {
+                my ($url, $uuid) = split(/ \| /, $sources->{$name}, 2);
+                push @config_to_set, {
+                    key   => "replica.'$name'.url",
+                    value => $url,
+                }, {
+                    key   => "replica.'$name'.uuid",
+                    value => $uuid,
+                };
+            }
+            # move the old config file to a backup
+            my $backup_file = $file;
+            unless ( $self->_deprecated_repo_config_names->{$file} ) {
+                $backup_file = "$file.bak";
+                rename $file, $backup_file;
+            }
+
+            # we want to write the new file to a supported filename if
+            # it's from a deprecated config name (replica/prophetrc)
+            $file = File::Spec->catfile( $self->app_handle->handle->fs_root, 'config' )
+                if $self->_deprecated_repo_config_names->{$file};
+
+            # write the new config file (with group_set)
+            $self->group_set( $file, \@config_to_set, 1);
+
+            # tell the user that we're done
+            print "done.\nOld config can be found at $backup_file; "
+                  ,"new config is $file.\n\n";
+
+}
+
+sub _deprecated_repo_config_names {
+    my $self = shift;
+
+    my %filenames = ( File::Spec->catfile( $self->app_handle->handle->fs_root => 'prophetrc' ) => 1 );
+
+    return wantarray ? %filenames : \%filenames;
 };
+sub _load_old_config_from_file {
+    my $self   = shift;
+    my $file   = shift;
+    my $config = shift || {};
+
+    for my $line (Prophet::Util->slurp($file) ) {
+        $line =~ s/\#.*$//; # strip comments
+        next unless ($line =~ /^(.*?)\s*=\s*(.*)$/);
+        my $key = $1;
+        my $val = $2;
+        if ($key =~ m!alias\s+(.+)!) {
+            $config->{_aliases}->{$1} = $val;
+        } elsif ($key =~ m!source\s+(.+)!) {
+            $config->{_sources}->{$1} = $val;
+        } else {
+            $config->{$key} = $val;
+        }
+    }
+    $config->{_aliases} ||= {}; # default aliases is null.
+    $config->{_sources} ||= {}; # default to no sources.
+}
+
+}
 
 __PACKAGE__->meta->make_immutable;
 no Any::Moose;

commit c5fb6bdee1001711f7114d908eff8363ab4f2639
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Tue Jul 7 16:30:39 2009 -0400

    config option rationalization - first pass

diff --git a/lib/App/SD/CLI/Command/Help/Config.pm b/lib/App/SD/CLI/Command/Help/Config.pm
index a00bdcd..dfeebb8 100644
--- a/lib/App/SD/CLI/Command/Help/Config.pm
+++ b/lib/App/SD/CLI/Command/Help/Config.pm
@@ -57,11 +57,11 @@ by configuration file section):
       creating or updating tickets. (Overrides the database-wide
       setting of the same name.)
 
-    ticket.list.default-sort = status
+    ticket.default-sort = status
       Bug property to determine order of display when displaying lists of
       tickets. (Can be any property; will be compared lexically.)
 
-    ticket.list.default-group = milestone
+    ticket.default-group = milestone
       Bug property to group tickets by when displaying lists of tickets. (Can
       be any property.)
 
diff --git a/lib/App/SD/CLI/Command/Ticket/Search.pm b/lib/App/SD/CLI/Command/Ticket/Search.pm
index 4444f00..c74fd95 100644
--- a/lib/App/SD/CLI/Command/Ticket/Search.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Search.pm
@@ -10,20 +10,20 @@ sub run {
     my $self = shift;
 
     if (  (!$self->has_arg('sort') || !$self->arg('sort'))
-        && $self->app_handle->config->get( key => 'ticket.list.default-sort') )
+        && $self->app_handle->config->get( key => 'ticket.default-sort') )
     {
         $self->set_arg(
             'sort' => $self->app_handle->config->get(
-                key => 'ticket.list.default-sort'
+                key => 'ticket.default-sort'
             )
         );
     }
 
     if (  (!$self->has_arg('group') || !$self->arg('group'))
-        && $self->app_handle->config->get( key => 'ticket.list.default-group') )
+        && $self->app_handle->config->get( key => 'ticket.default-group') )
     {
         $self->set_arg( 'group' =>
-              $self->app_handle->config->get( key => 'ticket.list.default-group') );
+              $self->app_handle->config->get( key => 'ticket.default-group') );
     }
 
     # sort output by given prop if user specifies --sort
diff --git a/lib/App/SD/CLI/Command/Ticket/Show.pm b/lib/App/SD/CLI/Command/Ticket/Show.pm
index a2c824b..4d00967 100644
--- a/lib/App/SD/CLI/Command/Ticket/Show.pm
+++ b/lib/App/SD/CLI/Command/Ticket/Show.pm
@@ -46,11 +46,11 @@ override run => sub {
     }
 
     # allow user to not display history by specifying the --skip-history
-    # arg or setting ticket.show.disable-history config item to a
+    # arg or setting ticket.no-implicit-history-display config item to a
     # true value (can be overridden with --with-history)
     if (!$self->has_arg('skip-history')
         && (  !$self->app_handle->config->get(
-                key => 'ticket.show.disable-history',
+                key => 'ticket.no-implicit-history-display',
                 as => 'bool',
             ) || $self->has_arg('with-history') )
         )
diff --git a/lib/App/SD/Config.pm b/lib/App/SD/Config.pm
index 6d3832d..cee9f17 100644
--- a/lib/App/SD/Config.pm
+++ b/lib/App/SD/Config.pm
@@ -68,12 +68,12 @@ override load => sub  {
 # migrated from old to new.
 our %KEYS_CONVERSION_TABLE = (
     'email_address' => 'user.email-address',
-    'default_group_ticket_list' => 'ticket.list.default-group',
-    'default_sort_ticket_list' => 'ticket.list.default-sort',
+    'default_group_ticket_list' => 'ticket.default-group',
+    'default_sort_ticket_list' => 'ticket.default-sort',
     'summary_format_ticket' => 'ticket.summary-format',
     'default_summary_format' => 'record.summary-format',
     'common_ticket_props' => 'ticket.common-props',
-    'disable_ticket_show_history_by_default' => 'ticket.show.disable-history',
+    'disable_ticket_show_history_by_default' => 'ticket.no-implicit-history-display',
 );
 
 
diff --git a/t/06-ticket-show.t b/t/06-ticket-show.t
index 7a819a9..05056f0 100644
--- a/t/06-ticket-show.t
+++ b/t/06-ticket-show.t
@@ -95,23 +95,23 @@ check_output_without_history('--skip-history');
 my $config_filename = $ENV{'SD_REPO'} . '/config';
 Prophet::Util->write_file(
     file => $config_filename, content => '
-[ticket "show"]
-    disable-history = true
+[ticket]
+    no-implicit-history-display = true
 ');
 $ENV{'SD_CONFIG'} = $config_filename;
 
-diag("config option ticket.show.disable-history set");
+diag("config option no-implicit-history-display set");
 diag("(shouldn't show history)");
 
 check_output_without_history();
 
-diag("config option ticket.show.disable-history set");
+diag("config option no-implicit-history-display set");
 diag("and --skip-history passed (shouldn't show history)");
 
 check_output_without_history('--skip-history');
 
 # config option set and --with-history passed (should show history)
-diag('config option ticket.show.disable-history set');
+diag('config option no-implicit-history-display set');
 diag('and --with-history passed (should show history)');
 
 check_output_with_history('--with-history');
diff --git a/t/07-sort-group.t b/t/07-sort-group.t
index bdf4123..a24469a 100644
--- a/t/07-sort-group.t
+++ b/t/07-sort-group.t
@@ -42,12 +42,12 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--sort', 'owner' ],
 my $config_filename = $ENV{'SD_REPO'} . '/config';
 Prophet::Util->write_file(
     file => $config_filename, content => '
-[ticket "list"]
+[ticket]
     default-sort = owner
 ');
 $ENV{'SD_CONFIG'} = $config_filename;
 
-diag('using ticket.list.default-sort = owner');
+diag('using ticket.default-sort = owner');
 run_output_matches( 'sd', [ 'ticket', 'list' ],
     [ qr/(\d+) huzzah! new/,
       qr/(\d+) YATTA new/,
@@ -61,7 +61,7 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--sort' ],
     ]
 );
 
-diag('using ticket.list.default-sort = owner and --sort none');
+diag('using ticket.default-sort = owner and --sort none');
 run_output_matches( 'sd', [ 'ticket', 'list', '--sort', 'none' ],
     [ qr/(\d+) YATTA new/,
       qr/(\d+) huzzah! new/,
@@ -85,10 +85,10 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--group', 'owner' ],
     ]
 );
 
-diag('using ticket.search.default-group = owner');
+diag('using ticket.default-group = owner');
 Prophet::Util->write_file(
     file => $config_filename, content => '
-[ticket "list"]
+[ticket]
     default-group = owner
 ');
 
@@ -121,7 +121,7 @@ run_output_matches( 'sd', [ 'ticket', 'list', '--group' ],
     ]
 );
 
-diag('using ticket.list.default-group = owner and --group none');
+diag('using ticket.default-group = owner and --group none');
 run_output_matches( 'sd', [ 'ticket', 'list', '--group', 'none' ],
     [ qr/(\d+) YATTA new/,
       qr/(\d+) huzzah! new/,

commit da8b6b77990621edbac8ed91e11841d96b92c82e
Merge: c5fb6bd... d9d5d20...
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Tue Jul 7 16:32:24 2009 -0400

    Merge branch 'master' into config-gitlike
    
    * master:
      default status for gcode
      ask pawwrod if find userinfo in uri


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



More information about the Bps-public-commit mailing list