[Bps-public-commit] r15217 - in Prophet/trunk: . lib/Prophet/CLI lib/Prophet/CLI/Command t

spang at bestpractical.com spang at bestpractical.com
Mon Aug 18 10:15:41 EDT 2008


Author: spang
Date: Mon Aug 18 10:15:29 2008
New Revision: 15217

Modified:
   Prophet/trunk/   (props changed)
   Prophet/trunk/lib/Prophet/CLI.pm
   Prophet/trunk/lib/Prophet/CLI/Command.pm
   Prophet/trunk/lib/Prophet/CLI/Command/Update.pm
   Prophet/trunk/lib/Prophet/CLI/RecordCommand.pm
   Prophet/trunk/t/edit.t

Log:
 r48271 at loki:  spang | 2008-08-14 21:47:49 +0100
 make editing key/value pairs with --edit much nicer and more flexible
 r48274 at loki:  spang | 2008-08-15 00:47:27 +0100
 print the type instead of 'record' in the error message when a record can't be found
 r48275 at loki:  spang | 2008-08-15 15:31:30 +0100
 fixes and cleanups to the update command
 r48276 at loki:  spang | 2008-08-15 15:42:26 +0100
 --edit's behaviour changed
 r48282 at loki:  spang | 2008-08-15 17:25:43 +0100
 add a test that props with no value are not set in edit.t
 r48418 at loki:  spang | 2008-08-18 10:19:14 +0100
 cmp_regex is used with props, not args


Modified: Prophet/trunk/lib/Prophet/CLI.pm
==============================================================================
--- Prophet/trunk/lib/Prophet/CLI.pm	(original)
+++ Prophet/trunk/lib/Prophet/CLI.pm	Mon Aug 18 10:15:29 2008
@@ -220,7 +220,7 @@
 
 =head2 cmp_regex
 
-Returns the regex to use for matching argument key/value separators.
+Returns the regex to use for matching property key/value separators.
 
 =cut
 

Modified: Prophet/trunk/lib/Prophet/CLI/Command.pm
==============================================================================
--- Prophet/trunk/lib/Prophet/CLI/Command.pm	(original)
+++ Prophet/trunk/lib/Prophet/CLI/Command.pm	Mon Aug 18 10:15:29 2008
@@ -1,6 +1,8 @@
 package Prophet::CLI::Command;
 use Moose;
 
+use Params::Validate qw(validate);
+
 has cli => (
     is => 'rw',
     isa => 'Prophet::CLI',
@@ -55,50 +57,100 @@
     return scalar Proc::InvokeEditor->edit($text);
 }
 
-=head2 edit_hash hashref -> hashref
+=head2 edit_hash hash => hashref, ordering => arrayref
 
 Filters the hash through the user's C<$EDITOR> using L<Proc::InvokeEditor>.
 
 No validation is done on the input or output.
 
+If the optional ordering argument is specified, hash keys will be presented
+in that order (with unspecified elements following) for edit.
+
+If the record class for the current type defines a C<props_not_to_edit>
+routine, those props will not be presented for editing.
+
+False values are not returned unless a prop is removed from the output.
+
 =cut
 
 sub edit_hash {
     my $self = shift;
-    my $hash = shift;
+    validate( @_, { hash => 1, ordering => 0 } );
+    my %args = @_;
+    my $hash = $args{'hash'};
+    my @ordering = @{ $args{'ordering'} || [] };
+    my $do_not_edit = $record->can('props_not_to_edit') ? $record->props_not_to_edit : '';
+    my $record = $self->_get_record_class;
+
+    if (@ordering) {
+        # add any keys not in @ordering to the end of it
+        my %keys_in_ordering;
+        map { $keys_in_ordering{$_} = 1 if exists($hash->{$_}) } @ordering;
+        map { push @ordering, $_ if !exists($keys_in_ordering{$_}) } keys %$hash;
+    } else {
+        @ordering = sort keys %$hash;
+    }
+
+    # filter out props we don't want to present for editing
+    @ordering = grep { !/$do_not_edit/ } @ordering;
+
+    my $input = join "\n", map { "$_: $hash->{$_}" } @ordering;
 
-    my $input = join "\n", map { "$_: $hash->{$_}\n" } keys %$hash;
     my $output = $self->edit_text($input);
 
+    die "Aborted.\n" if $input eq $output;
+
+    # parse the output
     my $filtered = {};
-    while ($output =~ m{^(\S+?):\s*(.*)$}mg) {
-        $filtered->{$1} = $2;
+    foreach my $line (split "\n", $output) {
+        if ($line =~ m/^([^:]+):\s*(.*)$/) {
+            my $prop = $1;
+            my $val = $2;
+            # don't return empty values
+            $filtered->{$prop} = $val unless !($val);
+        }
+    }
+
+    # if a key is deleted intentionally, set its value to ''
+    foreach my $prop (keys %$hash) {
+        if (!exists $filtered->{$prop} and $prop =~ !/$do_not_edit/) {
+            $filtered->{$prop} = '';
+        }
     }
 
+    no warnings 'uninitialized';
+    # filter out unchanged keys as they clutter changesets if they're set again
+    map { delete $filtered->{$_} if $hash->{$_} eq $filtered->{$_} } keys %$filtered;
+
     return $filtered;
 }
 
-=head2 edit_props [arg], defaults -> hashref
+=head2 edit_props arg => str, defaults => hashref, ordering => arrayref
 
 Returns a hashref of the command's props mixed in with any default props.
-If the "arg" argument is specified, (default "edit", use C<undef> if you only want default arguments), then L</edit_hash> is invoked on the property list.
+If the "arg" argument is specified, (default "edit", use C<undef> if you only
+want default arguments), then L</edit_hash> is invoked on the property list.
+
+If the C<ordering> argument is specified, properties will be presented in that
+order (with unspecified props following) if filtered through L</edit_hash>.
 
 =cut
 
 sub edit_props {
     my $self = shift;
-    my $arg  = shift || 'edit';
+    my %args = @_;
+    my $arg  = $args{'arg'} || 'edit';
+    my $defaults = $args{'defaults'};
 
     my %props;
-    if (@_ == 1) {
-        %props = (%{ $_[0] }, %{ $self->props });
-    }
-    else {
-        %props = (@_, %{ $self->props });
+    if ($defaults) {
+        %props = (%{ $defaults }, %{ $self->props });
+    } else {
+        %props = %{$self->props};
     }
 
     if ($self->has_arg($arg)) {
-        return $self->edit_hash(\%props);
+        return $self->edit_hash(hash => \%props, ordering => $args{'ordering'});
     }
 
     return \%props;

Modified: Prophet/trunk/lib/Prophet/CLI/Command/Update.pm
==============================================================================
--- Prophet/trunk/lib/Prophet/CLI/Command/Update.pm	(original)
+++ Prophet/trunk/lib/Prophet/CLI/Command/Update.pm	Mon Aug 18 10:15:29 2008
@@ -7,7 +7,20 @@
     my $self   = shift;
     my $record = shift;
 
-    return $self->edit_props('edit');
+    my $props = $record->get_props;
+    # don't feed in existing values if we're not interactively editing
+    my $defaults = $self->has_arg('edit') ? $props : undef;
+
+    my @ordering = ( );
+    # we want props in $record->props_to_show to show up in the editor if --edit
+    # is supplied too
+    if ($record->can('props_to_show') && $self->has_arg('edit')) {
+        @ordering = $record->props_to_show;
+        map { $props->{$_} = '' if !exists($props->{$_}) } @ordering;
+    }
+
+    return $self->edit_props(arg => 'edit', defaults => $defaults,
+        ordering => \@ordering);
 }
 
 sub run {
@@ -16,7 +29,9 @@
     $self->require_uuid;
     my $record = $self->_load_record;
 
-    my $result = $record->set_props( props => $self->edit_record($record) );
+    my $new_props = $self->edit_record($record);
+    my $result = $record->set_props( props => $new_props );
+
     if ($result) {
         print $record->type . " " . $record->luid . " (".$record->uuid.")"." updated.\n";
 
@@ -26,7 +41,6 @@
             . $record->luid . " ("
             . $record->uuid
             . ") not updated.\n";
-
     }
 }
 

Modified: Prophet/trunk/lib/Prophet/CLI/RecordCommand.pm
==============================================================================
--- Prophet/trunk/lib/Prophet/CLI/RecordCommand.pm	(original)
+++ Prophet/trunk/lib/Prophet/CLI/RecordCommand.pm	Mon Aug 18 10:15:29 2008
@@ -69,7 +69,7 @@
     my $self = shift;
     my $record = $self->_get_record_class;
     $record->load( uuid => $self->uuid )
-        || $self->fatal_error("I couldn't find the record " . $self->uuid);
+        || $self->fatal_error("I couldn't find the " . $self->type . ' ' . $self->uuid);
     return $record;
 }
 

Modified: Prophet/trunk/t/edit.t
==============================================================================
--- Prophet/trunk/t/edit.t	(original)
+++ Prophet/trunk/t/edit.t	Mon Aug 18 10:15:29 2008
@@ -22,9 +22,9 @@
 # ------------
 
 $out = run_command('create', '--type=Robot Master', '--edit');
-like($out, $created_re);
+# $out only captures STDOUT, not STDERR
+is($out, '', 'Create aborted on no editor input');
 is($invoked_editor, 1, "Editor invoked once");
-ok($uuid, "got a uuid");
 cleanup();
 
 # ------------
@@ -34,6 +34,7 @@
 name: Shadow Man
 weapon: Shadow Blade
 weakness: Top Spin
+strength: 
 TEXT
 });
 
@@ -46,6 +47,7 @@
 is($shadow_man->prop('name'), 'Shadow Man', 'correct name');
 is($shadow_man->prop('weapon'), 'Shadow Blade', 'correct weapon');
 is($shadow_man->prop('weakness'), 'Top Spin', 'correct weakness');
+is($shadow_man->prop('strength'), undef, 'strength not set');
 cleanup();
 
 # ------------
@@ -97,7 +99,7 @@
 is($crash_man2->uuid, $uuid, "correct uuid");
 is($crash_man2->prop('name'), 'Clash Man', 'corrected name');
 is($crash_man2->prop('weapon'), 'Clash Bomb', 'corrected weapon');
-is($crash_man2->prop('weakness'), 'Air Shooter', 'same weakness');
+is($crash_man2->prop('weakness'), undef, 'weakness deleted');
 cleanup();
 
 # ------------



More information about the Bps-public-commit mailing list