[Bps-public-commit] r16343 - in sd/trunk: . lib/App/SD/CLI/Command/Ticket lib/App/SD/CLI/Model

jesse at bestpractical.com jesse at bestpractical.com
Fri Oct 17 22:18:13 EDT 2008


Author: jesse
Date: Fri Oct 17 22:18:13 2008
New Revision: 16343

Added:
   sd/trunk/lib/App/SD/CLI/Command/TextEditor.pm
Modified:
   sd/trunk/   (props changed)
   sd/trunk/lib/App/SD/CLI/Command/Ticket/Create.pm
   sd/trunk/lib/App/SD/CLI/Command/Ticket/Update.pm
   sd/trunk/lib/App/SD/CLI/Model/Ticket.pm

Log:
 r46994 at 31b:  jesse | 2008-10-17 22:42:18 +0100
 * refactoring to make the cli commands more usable


Added: sd/trunk/lib/App/SD/CLI/Command/TextEditor.pm
==============================================================================
--- (empty file)
+++ sd/trunk/lib/App/SD/CLI/Command/TextEditor.pm	Fri Oct 17 22:18:13 2008
@@ -0,0 +1,44 @@
+package App::SD::CLI::Command::TextEditor;
+use Moose::Role;
+use Params::Validate qw/validate/;
+
+sub try_to_edit {
+    my $self = shift;
+    my %args = validate(
+        @_,
+        {   template => 1,
+            record   => 1,
+        }
+    );
+
+    my $record = $args{'record'};
+
+    my $template = ${ $args{template} };
+
+    # do the edit
+    my $updated = $self->edit_text($template);
+
+    die "Aborted.\n" if $updated eq $template;    # user didn't change anything
+
+    $self->process_template(
+        template => $args{template},
+        edited   => $updated,
+        record   => $record
+    );
+}
+
+sub handle_template_errors {
+        my $self = shift;
+        my %args = validate ( @_ , { error => 1, template_ref => 1, bad_template => 1});
+
+
+        $self->prompt_Yn("Want to return back to editing?") || die "Aborted.\n";
+
+        ${ $args{'template_ref'} }
+            = "=== Your template contained errors ====\n\n".$args{error}."\n\n"
+            . $args{bad_template};
+        return 0;
+    }
+
+no Moose::Role;
+1;

Modified: sd/trunk/lib/App/SD/CLI/Command/Ticket/Create.pm
==============================================================================
--- sd/trunk/lib/App/SD/CLI/Command/Ticket/Create.pm	(original)
+++ sd/trunk/lib/App/SD/CLI/Command/Ticket/Create.pm	Fri Oct 17 22:18:13 2008
@@ -1,58 +1,65 @@
 package App::SD::CLI::Command::Ticket::Create;
 use Moose;
 
+use Params::Validate qw/validate/;
 extends 'Prophet::CLI::Command::Create';
 with 'App::SD::CLI::Model::Ticket';
 with 'App::SD::CLI::Command';
+with 'App::SD::CLI::Command::TextEditor';
 
 # we want to launch an $EDITOR window to grab props and a comment if no
 # props are specified on the commandline
+
 override run => sub {
     my $self = shift;
     my @prop_set = $self->prop_set;
     my $record = $self->_get_record_object;
 
-    # only invoke editor if no props specified on the commandline or edit arg
-    # specified
-    if (!@prop_set || $self->has_arg('edit')) {
-        my $ticket_string_to_edit = $self->create_record_string();
-
-        TRY_AGAIN:
-        my $ticket = $self->edit_text($ticket_string_to_edit);
-
-        die "Aborted.\n"
-            if $ticket eq $ticket_string_to_edit; # user didn't change anything
-
-        (my $props_ref, my $comment) = $self->parse_record_string($ticket);
-
-        for my $prop (keys %$props_ref) {
-            $self->set_prop($prop => $props_ref->{$prop});
-        }
-
-        my $error;
-        {
-            local $@;
-            eval { super(); } or $error = $@ || "Something went wrong!";
-        }
-        if ( $error ) {
-            print STDERR "Couldn't create a record, error:\n\n", $error, "\n";
-            die "Aborted.\n" unless $self->prompt_Yn( "Want to return back to editing?" );
-
-            ($ticket_string_to_edit, $error) = ($ticket, '');
-            goto TRY_AGAIN;
-        }
+    # only invoke editor if no props specified on the commandline or edit arg # specified
+   return super() if (@{$self->prop_set} && !$self->has_arg('edit'));
+
 
-        # retrieve the created record from the superclass
-        $record = $self->record();
+    my $template_to_edit = $self->create_record_template();
 
-        $self->add_comment( content => $comment, uuid => $record->uuid )
-            if $comment;
+    my $done = 0;
 
-    } else {
-        super();
+    while (!$done) {
+      $done =  $self->try_to_edit( template => \$template_to_edit, record => $record);
     }
+
 };
 
+sub process_template {
+    my $self = shift;
+    my %args = validate( @_, { template => 1, edited => 1, record => 1 } );
+
+    my $record      = $args{record};
+    my $do_not_edit = $record->props_not_to_edit;
+    my $updated     = $args{edited};
+    ( my $props_ref, my $comment ) = $self->parse_record_template($updated);
+
+    for my $prop ( keys %$props_ref ) {
+        $self->context->set_prop( $prop => $props_ref->{$prop} );
+    }
+
+    my $error;
+        local $@;
+        eval { super(); } or $error = $@ || "Something went wrong!";
+
+    return $self->handle_template_errors(
+        error        => $error,
+        template_ref => $args{template},
+        bad_template => $updated
+    ) if ($error);
+
+    $self->add_comment( content => $comment, uuid => $self->record->uuid )
+        if $comment;
+
+    return 1;
+}
+
+
+
 __PACKAGE__->meta->make_immutable;
 no Moose;
 

Modified: sd/trunk/lib/App/SD/CLI/Command/Ticket/Update.pm
==============================================================================
--- sd/trunk/lib/App/SD/CLI/Command/Ticket/Update.pm	(original)
+++ sd/trunk/lib/App/SD/CLI/Command/Ticket/Update.pm	Fri Oct 17 22:18:13 2008
@@ -1,47 +1,52 @@
 package App::SD::CLI::Command::Ticket::Update;
 use Moose;
+use Params::Validate qw/validate/;
 
 extends 'Prophet::CLI::Command::Update';
 with 'App::SD::CLI::Model::Ticket';
 with 'App::SD::CLI::Command';
+with 'App::SD::CLI::Command::TextEditor';
+
 
 # use an editor to edit if no props are specified on the commandline,
 # allowing the creation of a new comment in the process
 override run => sub {
     my $self = shift;
     $self->require_uuid;
-
     my $record = $self->_load_record;
-    my $props = $record->get_props;
+   return super() if (@{$self->prop_set} && !$self->has_arg('edit'));
+    my $template_to_edit = $self->create_record_template($record);
 
-    if (@{$self->prop_set} && !$self->has_arg('edit')) {
-        return super();
-    }
+    my $done = 0;
 
-    my $ticket_string_to_edit = $self->create_record_string($record);
-    my $do_not_edit = $record->props_not_to_edit;
+    while (!$done) {
+      $done =  $self->try_to_edit( template => \$template_to_edit, record => $record);
+    }
 
-    TRY_AGAIN:
-    my $updated = $self->edit_text($ticket_string_to_edit);
+};
 
-    die "Aborted.\n"
-        if $updated eq $ticket_string_to_edit; # user didn't change anything
+sub process_template {
+    my $self = shift;
+    my %args = validate( @_, { template => 1, edited => 1, record => 1 } );
 
-    my ($props_ref, $comment) = $self->parse_record_string($updated);
+    my $record      = $args{record};
+    my $do_not_edit = $record->props_not_to_edit;
+    my $updated     = $args{edited};
+    my ( $props_ref, $comment ) = $self->parse_record_template($updated);
 
     no warnings 'uninitialized';
 
     # if a formerly existing prop was removed from the output, delete it
     # (deleting is currently the equivalent of setting to '', and
     # we want to do this all in one changeset)
-    for my $prop (keys %{$record->get_props}) {
-        unless ($prop =~ $do_not_edit) {
+    for my $prop ( keys %{ $record->get_props } ) {
+        unless ( $prop =~ $do_not_edit ) {
             $props_ref->{$prop} = '' if !exists $props_ref->{$prop};
         }
     }
 
     # don't add props that didn't change to the changeset
-    for my $prop (keys %$props_ref) {
+    for my $prop ( keys %$props_ref ) {
         delete $props_ref->{$prop}
             if $props_ref->{$prop} eq $record->prop($prop);
     }
@@ -49,27 +54,24 @@
     # set the new props
     if ( keys %$props_ref ) {
         my $error;
-        {
-            local $@;
-            eval { $record->set_props( props => $props_ref ) }
-                or $error = $@ || "Something went wrong!";
-        }
-        if ( $error ) {
-            print STDERR "Couldn't update the record, error:\n\n", $error, "\n";
-            die "Aborted.\n" unless $self->prompt_Yn( "Want to return back to editing?" );
+        local $@;
+        eval { $record->set_props( props => $props_ref ) }
+            or $error = $@ || "Something went wrong!";
+
+        return $self->handle_template_errors(
+            error        => $error,
+            template_ref => $args{template},
+            bad_template => $updated
+        ) if ($error);
 
-            ($ticket_string_to_edit, $error) = ($updated, '');
-            goto TRY_AGAIN;
-        }
         print 'Updated ticket ' . $record->luid . ' (' . $record->uuid . ")\n";
-    }
-    else {
+    } else {
         print "No changes in properties.\n";
     }
 
-    $self->add_comment( content => $comment, uuid => $record->uuid )
-        if $comment;
-};
+    $self->add_comment( content => $comment, uuid => $record->uuid ) if $comment;
+    return 1;
+}
 
 __PACKAGE__->meta->make_immutable;
 no Moose;

Modified: sd/trunk/lib/App/SD/CLI/Model/Ticket.pm
==============================================================================
--- sd/trunk/lib/App/SD/CLI/Model/Ticket.pm	(original)
+++ sd/trunk/lib/App/SD/CLI/Model/Ticket.pm	Fri Oct 17 22:18:13 2008
@@ -4,6 +4,19 @@
 use constant record_class => 'App::SD::Model::Ticket';
 
 
+=head2 separator_pattern
+
+A pattern that will match on lines that count as section separators
+in tickets represented as strings. Separator string text is remembered
+as C<$1>.
+
+=cut
+
+use constant separator_pattern => qr/^=== (.*) ===$/;
+use constant comment_pattern => qr/^\s*#/;
+
+
+
 =head2 add_comment content => str, uuid => str
 
 A convenience method that takes a content string and a ticket uuid and creates
@@ -29,7 +42,7 @@
     $command->run();
 }
 
-=head2 metadata_separator_text
+=head2 metadata_separator
 
 Returns a string of text that goes in the comment denoting the beginning of
 uneditable ticket metadata in a string representing a ticket.
@@ -41,68 +54,30 @@
 
 =cut
 
-sub metadata_separator_text {
-    'required ticket metadata (changes here will not be saved)'
-}
-
-=head2 editable_props_separator_text
-
-Returns a string that denotes the text that goes in the comment denoting the
-beginning of prop: value pairs that are updatable in a string representing a
-ticket.
+use constant metadata_separator => 'required ticket metadata (changes here will not be saved)';
+use constant editable_props_separator => 'edit ticket details below';
+use constant comment_separator => 'add new ticket comment below';
 
-=cut
-
-sub editable_props_separator_text { 'edit ticket details below' }
-
-=head2 comment_separator_text
-
-Returns a string that goes in the comment that separates the prop: value lines
-from the ticket comment in a string representing a ticket. The ticket comment
-will be free text to the end of the new ticket. May contain arbitrary newlines.
-
-=cut
-
-sub comment_separator_text { 'add new ticket comment below' }
-
-=head2 separator_pattern
-
-A pattern that will match on lines that count as section separators
-in tickets represented as strings. Separator string text is remembered
-as C<$1>.
-
-=cut
-
-sub separator_pattern { qr/^=== (.*) ===$/ }
-
-=head2 create_separator $text
+=head2 _build_separator $text
 
 Takes a string and returns it in separator form.
 
 =cut
 
-sub create_separator {
+sub _build_separator {
     my $self = shift;
     my $text = shift;
 
     return "=== $text ===";
 }
 
-=head2 comment_pattern
-
-Returns a pattern that will match on lines that count as comments in
-tickets represented as strings.
-
-=cut
 
-sub comment_pattern { qr/^\s*#/ }
-
-=head2 create_record_string RECORD
+=head2 create_record_template RECORD
 
 Creates a string representing a new record, prefilling default props
 and props specified on the command line. Intended to be presented to
-the user for editing using L<Prophet::CLI::Command->edit_text>
-and then parsed using L</create_record_string>.
+the user for editing using L<Prophet::CLI::Command->edit>
+and then parsed using L</create_record_template>.
 
 If RECORD is given, then we are updating that record rather than
 creating a new one, and the ticket string will be created from its
@@ -110,16 +85,22 @@
 
 =cut
 
-sub create_record_string {
+sub create_record_template {
     my $self = shift;
     my $record = shift;
-    my $update = 0;
+    my $update ;
+
+    if ($record) { $update = 1 } 
+    else {
+        
+            $record = $self->_get_record_object;
+            $update = 0;
+    }
 
-    defined($record) ? $update = 1 : $record = $self->_get_record_object;
 
     my $props_not_to_edit = $record->props_not_to_edit;
     my (@metadata_order, @editable_order);
-    my (%metadata_props, %editable_props);
+    my (%immutable_props, %editable_props);
 
     # separate out user-editable props so we can both show all
     # the props that will be added to the new ticket and prevent
@@ -130,7 +111,7 @@
             if ($prop eq 'id' && $update) {
                 # id isn't a *real* prop, so we have to mess with it some more
                 push @metadata_order, $prop;
-                $metadata_props{$prop} = $record->luid . ' (' . $record->uuid . ")";
+                $immutable_props{$prop} = $record->luid . ' (' . $record->uuid . ")";
             }
             elsif (!(($prop eq 'id' or $prop eq 'created') && !$update)) {
                 push @metadata_order, $prop;
@@ -139,7 +120,7 @@
                 # we don't want to display id/created for ticket creates
                 # because they can't by their nature be specified until the
                 # ticket is actually created
-                $metadata_props{$prop} = $update ? $record->prop($prop) : undef;
+                $immutable_props{$prop} = $update ? $record->prop($prop) : undef;
             }
         } else {
             push @editable_order, $prop;
@@ -148,8 +129,8 @@
     }
 
     # fill in prop defaults if we're creating a new ticket
-    unless ($update) {
-        $record->default_props(\%metadata_props);
+    if (! $update) {
+        $record->default_props(\%immutable_props);
         $record->default_props(\%editable_props);
     }
 
@@ -159,31 +140,55 @@
         $self->delete_arg('edit');
     }
 
-    # make undef values empty strings to avoid interpolation warnings
-    # (we can't do this earlier because $record->default_props only
-    # overrides undefined props)
-    map { $metadata_props{$_} = '' if !defined($metadata_props{$_}) }
-        @metadata_order;
-    map { $editable_props{$_} = '' if !defined($editable_props{$_}) }
-        @editable_order;
-
-    my $metadata_separator = $self->create_separator(metadata_separator_text());
-    my $editable_separator = $self->create_separator(editable_props_separator_text());
-    my $comment_separator = $self->create_separator(comment_separator_text());
-
-    my $metadata_props_string = join "\n",
-                        map { "$_: $metadata_props{$_}" } @metadata_order;
-    my $editable_props_string = join "\n",
-                        map { "$_: $editable_props{$_}" } @editable_order;
+    my $immutable_props_string = $self->_build_kv_pairs(
+        order => \@metadata_order,
+        data  => \%immutable_props
+    );
+    my $editable_props_string = $self->_build_kv_pairs(
+        order => \@editable_order,
+        data  => \%editable_props
+    );
 
     # glue all the parts together
-    my $ticket_string = $metadata_separator . "\n\n" . $metadata_props_string
-                    . "\n\n" . $editable_separator . "\n\n" .
-                    $editable_props_string . "\n\n" . $comment_separator
-                    . "\n";
+    return join( "\n\n",
+
+    $self->_build_template_section(
+        header => metadata_separator,
+        data   => $immutable_props_string
+    ),
+
+    $self->_build_template_section(
+        header => editable_props_separator,
+        data => $editable_props_string
+    ),
+    $self->_build_template_section(
+        header =>  comment_separator,
+        data   => ''
+        )
+
+);
 }
 
-=head2 parse_record_string $str
+sub _build_template_section {
+    my $self = shift;
+    my %args = validate (@_, { header => 1, data => 0 });
+    return $self->_build_separator($args{'header'}) ."\n\n". ( $args{data} || '');
+}
+
+sub _build_kv_pairs {
+    my $self = shift;
+    my %args = validate (@_, { order => 1, data => 1 });
+
+    my $string = '';
+    for my $prop ( @{$args{order}}) {
+        $string .= "$prop: ".($args{data}->{$prop} ||'') ."\n";
+    }
+    return $string;
+}
+
+
+
+=head2 parse_record_template $str
 
 Takes a string containing a ticket record consisting of prop: value pairs
 followed by a separator, followed by an optional comment.
@@ -193,7 +198,7 @@
 
 =cut
 
-sub parse_record_string {
+sub parse_record_template {
     my $self = shift;
     my $ticket = shift;
 
@@ -203,21 +208,25 @@
     my $comment = '';
 
     for my $line (@lines) {
-        if ($line =~ separator_pattern()) {
+        if ($line =~ separator_pattern) {
             $last_seen_sep = $1;
-        } elsif ($line =~ comment_pattern() or
-            # skip comments and unchangeable props
-            $last_seen_sep eq metadata_separator_text()) {
+        } elsif ($line =~ comment_pattern) {
+            # skip comments 
+            next;
+        } elsif ( $last_seen_sep eq metadata_separator) {
+            # skip unchangeable props
             next;
-        } elsif ($last_seen_sep eq editable_props_separator_text()) {
+        } elsif ($last_seen_sep eq editable_props_separator) {
             # match prop: value pairs. whitespace in between is ignored.
             if ($line =~ m/^([^:]+):\s*(.*)$/) {
                 my $prop = $1;
                 my $val = $2;
                 $new_props{$prop} = $val unless !($val);
             }
-        } elsif ($last_seen_sep eq comment_separator_text()) {
+        } elsif ($last_seen_sep eq comment_separator) {
             $comment .= $line . "\n";
+        } else {
+            # Throw away the section 
         }
     }
 



More information about the Bps-public-commit mailing list