use strict;
use warnings;
-use CIL::Issue;
-use CIL::Comment;
-use Term::CallEditor;
+
+use Digest::MD5 qw(md5_hex);
use File::Touch;
use File::Glob ':glob';
use File::Basename;
+use Term::CallEditor qw(solicit);
+use CIL::Issue;
+use CIL::Comment;
my $COMMANDS = {
init => 1,
my $gae = $ENV{GIT_AUTHOR_EMAIL} || 'you@example.org';
my $new_issue_text = <<"EOF";
-[Issue]
-Summary =
-Status =New
-CreatedBy =$gan <$gae>
-AssignedTo =$gan <$gae>
-Labels =
-Description = <<END_OF_DESCRIPTION
+Summary :
+Status : New
+CreatedBy : $gan <$gae>
+AssignedTo : $gan <$gae>
+Label :
-END_OF_DESCRIPTION
+Description...
EOF
my $add_comment_text = <<"EOF";
-[Comment]
-CreatedBy = $gan <$gae>
-Description = <<END_OF_DESCRIPTION
+CreatedBy : $gan <$gae>
-END_OF_DESCRIPTION
+Description...
EOF
## ----------------------------------------------------------------------------
# read in the new issue text
my $fh = solicit( $new_issue_text );
- my $issue;
- eval {
- $issue = CIL::Issue->new_parse_issue( $fh );
- };
- if ( $@ ) {
- fatal("couldn't parse issue: $@");
- }
- unless ( defined $issue ) {
- fatal("couldn't parse issue (program error)");
- }
+ my $issue = CIL::Issue->new_from_fh( 'tmp', $fh );
- $issue->inserted;
- $issue->set_no_update( 'Name', $issue->Inserted );
+ # we've got the issue, so let's name it
+ my $unique_str = $issue->Inserted . $issue->Summary . $issue->Description;
+ $issue->set_name( substr(md5_hex($unique_str), 0, 8) );
$issue->save();
display_issue_full( $issue );
}
my $status = $issue->Status;
my $created_by = $issue->CreatedBy;
my $assigned_to = $issue->AssignedTo;
- my $labels = $issue->Labels;
+ my $label = $issue->Label;
my $description = $issue->Description;
# create the ini file, then edit it
Status = $status
CreatedBy = $created_by
AssignedTo = $assigned_to
-Labels = $labels
+Label = $label
Description = <<END_OF_DESCRIPTION
$description
END_OF_DESCRIPTION
my ($issue) = @_;
separator();
- title('Details');
+ title('Issue: ' . $issue->name);
field( 'Summary', $issue->Summary() );
- field( 'Name', $issue->Name() );
field( 'Status', $issue->Status() );
field( 'CreatedBy', $issue->CreatedBy() );
field( 'AssignedTo', $issue->AssignedTo() );
field( 'Inserted', $issue->Inserted() );
field( 'Updated', $issue->Inserted() );
- field( 'Labels', $issue->Labels() );
+ field( 'Label', $issue->Label() );
separator();
text('Description', $issue->Description());
- my $comments = $issue->comments();
- foreach my $comment ( @$comments ) {
- separator();
- title('Comment');
- field( 'CreatedBy', $comment->CreatedBy() );
- field( 'Inserted', $comment->Inserted() );
- field( 'Updated', $comment->Inserted() );
- text('Description', $comment->Description());
- }
separator();
}
+++ /dev/null
----
-AssignedTo: 'Andrew Chilton <andychilton@gmail.com>'
-CreatedBy: 'Andrew Chilton <andychilton@gmail.com>'
-Description: |-
- 'cil' currently has no way of adding attachments to issues.
-
- This should be added so that the actual data cil stores is complete.
-Inserted: 1209990028
-Labels: against-v0.1 type-enhancement priority-medium
-Name: 1209990028
-Status: New
-Summary: Addition of a 'attach' command
-Updated: 1209991365
+++ /dev/null
----
-AssignedTo: 'Andrew Chilton <andychilton@gmail.com>'
-CreatedBy: 'Andrew Chilton <andychilton@gmail.com>'
-Description: |-
- When issues are created, they are given the epoch as it's name.
- Instead of just using the epoch, the user should be able to configure
- the .cil file to use the format of their choice.
-
- e.g. issue_name_format=[ epoch | inc | iso-8601 ]
-
- Possible name formats include:
-
- * an incrementing number
- * but if cil goes distributed (which was originally planned), will
- this work?
-
- * ISO 8601 - YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
- * do we need to have TZD on the end (though obviously this wouldn't
- then be ISO 8601)
-
- * GUID/UUID like 9e28b50a-cba1-4b20-9276-d30ee727b14a
-
- (and maybe others)
-Inserted: 1209990558
-Labels: against-v0.1 type-enhancement priority-low
-Name: 1209990558
-Status: New
-Summary: Options for issues names needs to be added
-Updated: 1209990799
+++ /dev/null
----
-AssignedTo: 'Andrew Chilton <andychilton@gmail.com>'
-CreatedBy: 'Andrew Chilton <andychilton@gmail.com>'
-Description: |-
- For example, if there is a config item in .cil called
- 'allowed_statuses', all input values in the 'Status' field should be
- checked against it.
-
- This could also be done for Labels too.
-Inserted: 1209991618
-Labels: against-v0.1 type-enhancement priority-medium
-Name: 1209991618
-Status: New
-Summary: Do checking on input fields after adding or editing issue
-Updated: 1209991718
+++ /dev/null
----
-AssignedTo: 'Andrew Chilton <andychilton@gmail.com>'
-CreatedBy: 'Andrew Chilton <andychilton@gmail.com>'
-Description: |-
- In the .cil config file, there should a field which determines a
- 'required' set of labels.
-
- e.g.
-
- required_labels=against type priority
-Inserted: 1209992018
-Labels: against-v0.1 type-enhancement priority-medium
-Name: 1209992018
-Status: New
-Summary: Labels should be allowed to have a 'required' set
-Updated: 1209992018
--- /dev/null
+Name: 1209990028
+Summary: Addition of a 'attach' command
+Status: New
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 1209990028
+AssignedTo: Andrew Chilton <andychilton@gmail.com>
+Updated: 1209991365
+Label: against-v0.1
+Label: priority-medium
+Label: type-enhancement
+
+'cil' currently has no way of adding attachments to issues.
+
+This should be added so that the actual data cil stores is complete.
--- /dev/null
+Name: 1209990558
+Summary: Options for issues names needs to be added
+Status: New
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 1209990558
+AssignedTo: Andrew Chilton <andychilton@gmail.com>
+Updated: 1209990799
+Label: against-v0.1
+Label: priority-low
+Label: type-enhancement
+
+When issues are created, they are given the epoch as it's name.
+Instead of just using the epoch, the user should be able to configure
+the .cil file to use the format of their choice.
+
+e.g. issue_name_format=[ epoch | inc | iso-8601 ]
+
+Possible name formats include:
+
+* an incrementing number
+ * but if cil goes distributed (which was originally planned), will
+ this work?
+
+* ISO 8601 - YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
+ * do we need to have TZD on the end (though obviously this wouldn't
+ then be ISO 8601)
+
+* GUID/UUID like 9e28b50a-cba1-4b20-9276-d30ee727b14a
+
+(and maybe others)
--- /dev/null
+Name: 1209991618
+Summary: Do checking on input fields after adding or editing issue
+Status: New
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 1209991618
+AssignedTo: Andrew Chilton <andychilton@gmail.com>
+Updated: 1209991718
+Label: against-v0.1
+Label: priority-medium
+Label: type-enhancement
+
+For example, if there is a config item in .cil called
+'allowed_statuses', all input values in the 'Status' field should be
+checked against it.
+
+This could also be done for Labels too.
--- /dev/null
+Name: 1209992018
+Summary: Labels should be allowed to have a 'required' set
+Status: New
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+Inserted: 1209992018
+AssignedTo: Andrew Chilton <andychilton@gmail.com>
+Updated: 1209992018
+Label: against-v0.1
+Label: priority-medium
+Label: type-enhancement
+
+In the .cil config file, there should a field which determines a
+'required' set of labels.
+
+e.g.
+
+required_labels=against type priority
use base qw(Class::Accessor);
__PACKAGE__->mk_accessors(qw(CreatedBy Inserted Updated Description));
+# override Class::Accessor's get
+sub get {
+ my ($self, $field) = @_;
+ croak "provide a field name"
+ unless defined $field;
+ $self->{data}{$field};
+}
+
# override Class::Accessor's set
sub set {
- my ($self, $key, $value) = @_;
- croak "provide a key name" unless defined $key;
+ my ($self, $field, $value) = @_;
+ croak "provide a field name"
+ unless defined $field;
- my $orig = $self->get($key);
+ my $orig = $self->get($field);
- # get out if both are defined and they're the same
+ # finish if both are defined and they're the same
if ( defined $orig and defined $value ) {
return if $orig eq $value
}
- # get out if neither are defined
- if ( !defined $orig and !defined $value ) {
- return;
- }
+ # finish if neither are defined
+ return unless ( defined $orig or defined $value );
- # since we're actually changing the key, say we updated something
- $self->{data}{$key} = $value;
- $self->updated;
+ # since we're actually changing the field, say we updated something
+ $self->{data}{$field} = $value;
+ $self->set_updated;
}
+# so that we can update fields without 'Updated' being changed
sub set_no_update {
- my ($self, $key, $value) = @_;
- croak "provide a key name" unless defined $key;
- $self->{data}{$key} = $value;
-}
-
-# override Class::Accessor's get
-sub get {
- my ($self, $key) = @_;
- $self->{data}{$key};
+ my ($self, $field, $value) = @_;
+ croak "provide a field name"
+ unless defined $field;
+ $self->{data}{$field} = $value;
}
-sub inserted {
+sub flag_inserted {
my ($self) = @_;
- my $time = time;
+ my $time = DateTime->now;
$self->{data}{Inserted} = $time;
$self->{data}{Updated} = $time;
- $self->{Changed} = '1';
+ $self->{Changed} = 1;
}
-sub updated {
+sub flag_as_updated {
my ($self) = @_;
- my $time = time;
+ my $time = DateTime->now;
$self->{data}{Updated} = $time;
- $self->{Changed} = '1';
+ $self->{Changed} = 1;
}
sub changed {
use strict;
use warnings;
use Carp;
-use Data::Dumper;
use Config::IniFiles;
use YAML qw(LoadFile DumpFile);
+use CIL::Utils;
+
use base qw(CIL::Base);
-__PACKAGE__->mk_accessors(qw(Name Summary AssignedTo Status Labels Comments));
+__PACKAGE__->mk_accessors(qw(Summary Status AssignedTo Label Comment Attachment));
-my @FIELDS = ( qw(Name Summary Description CreatedBy AssignedTo Status Labels Comments) );
+my @FIELDS = ( qw(Summary Status CreatedBy AssignedTo Label Comment Attachment Inserted Updated Description) );
## ----------------------------------------------------------------------------
sub new {
- my ($proto) = @_;
+ my ($proto, $name) = @_;
+
+ croak 'please provide an issue name'
+ unless defined $name;
+
my $class = ref $proto || $proto;
my $self = {};
- $self->{data} = {};
- $self->{Changed} = 0;
bless $self, $class;
- $self->inserted;
+
+ $self->set_name( $name );
+ $self->{data} = {
+ Summary => '',
+ Status => '',
+ CreatedBy => '',
+ AssignedTo => '',
+ Label => [],
+ Comment => [],
+ Attachment => [],
+ };
+ $self->{Changed} = 0;
+
+ $self->flag_inserted;
+
return $self;
}
-sub new_load_issue {
- my ($class, $name) = @_;
+sub new_from_data {
+ my ($class, $name, $data) = @_;
- unless ( defined $name ) {
- croak "provide an issue name to load";
- }
+ croak 'please provide an issue name'
+ unless defined $name;
- my $filename = "issues/$name.yaml";
- unless ( -f $filename ) {
- croak "filename '$filename' does no exist";
- }
+ # ToDo: check we have all the other correct fields
- my $data = LoadFile( $filename );
+ # create the issue
+ my $self = $class->new( $name );
- my $issue = CIL::Issue->new();
+ # save each field
+ foreach my $field ( @FIELDS ) {
+ next unless defined $data->{$field};
- # do the issue
- foreach my $field ( qw(Summary Name Description CreatedBy AssignedTo Status Labels Inserted Updated) ) {
# modify the data directly, otherwise Updated will kick in
- $issue->{data}{$field} = $data->{$field};
- }
-
- # now the comments
- foreach my $c ( @{$data->{comments}} ) {
- my $comment = CIL::Comment->new();
- foreach my $field ( qw(CreatedBy Inserted Updated Description) ) {
- # modify the data directly, otherwise Updated will kick in
- $comment->set_no_update($field, $c->{$field});
- }
- push @{$issue->{comments}}, $comment;
+ $self->set_no_update($field, $data->{$field});
}
- $issue->{data}{Name} = $name;
+ $self->set_no_update('Changed', 0);
- return $issue;
+ return $self;
}
-sub new_parse_issue {
- my ($class, $file) = @_;
+sub new_from_fh {
+ my ($class, $name, $fh) = @_;
- # $file may be a string ($filename) or a file handle ($fh)
- my $cfg = Config::IniFiles->new( -file => $file );
+ croak 'please provide an issue name'
+ unless defined $name;
- unless ( defined $cfg ) {
- croak("not a valid inifile");
- }
+ my $data = CIL::Utils->parse_from_fh( $fh, 'Description' );
+ return $class->new_from_data( $name, $data );
+}
- my $issue = CIL::Issue->new();
- foreach my $field ( qw(Summary Name Description CreatedBy AssignedTo Status Labels Inserted Updated) ) {
- # modify the data directly, otherwise Updated will kick in
- my $value = $cfg->val( 'Issue', $field );
- next unless defined $value;
+sub set_name {
+ my ($self, $name) = @_;
- $value =~ s/^\s*//;
- $value =~ s/\s*$//;
- $issue->set_no_update($field, $value);
- }
- $issue->set_no_update('Comments', []);
- return $issue;
+ croak 'provide a name'
+ unless defined $name;
+
+ $self->{name} = $name;
}
-sub comments {
+sub name {
my ($self) = @_;
- return $self->{comments};
+ return $self->{name};
+}
+
+sub add_label {
+ my ($self, $label) = @_;
+
+ croak 'provide a label when adding one'
+ unless defined $label;
+
+ push @{$self->{data}{Label}}, $label;
}
sub add_comment {
croak "can only add comments of type CIL::Comment"
unless ref $comment eq 'CIL::Comment';
- push @{$self->{comments}}, $comment;
+ push @{$self->{data}{Comment}}, $comment->name;
}
-sub save {
- my ($self) = @_;
- my $name = $self->Name;
- my $filename = "issues/$name.yaml";
- my $data = {};
- %$data = ( %{$self->{data}});
- foreach my $comment ( @{$self->{comments}} ) {
- push @{$data->{comments}}, $comment->{data};
- }
- DumpFile($filename, $data);
+sub add_attachment {
+ my ($self, $attachment) = @_;
+
+ croak "can only add comments of type CIL::Attachment"
+ unless ref $attachment eq 'CIL::Attachment';
+
+ push @{$self->{data}{Attachment}}, $attachment->name;
}
-sub reset {
- my ($self) = @_;
+sub load {
+ my ($class, $name) = @_;
- foreach my $field ( @FIELDS ) {
- delete $self->{$field};
- }
+ croak 'provide an issue name to load'
+ unless defined $name;
+
+ my $filename = "issues/$name.cil";
+
+ croak "filename '$filename' does no exist"
+ unless -f $filename;
+
+ my $data = CIL::Utils->parse_cil_file($filename, 'Description');
+ my $issue = CIL::Issue->new_from_data( $name, $data );
+ return $issue;
+}
+
+sub save {
+ my ($self) = @_;
+ my $name = $self->name;
+ my $filename = "issues/i_$name.cil";
+ CIL::Utils->write_cil_file( $filename, $self->{data}, @FIELDS );
}
## ----------------------------------------------------------------------------
--- /dev/null
+## ----------------------------------------------------------------------------
+# cil is a Command line Issue List
+# Copyright (C) 2008 Andrew Chilton
+#
+# This file is part of 'cil'.
+#
+# cil is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+#
+## ----------------------------------------------------------------------------
+
+package CIL::Utils;
+
+use strict;
+use warnings;
+use File::Slurp;
+
+sub parse_cil_file {
+ my ($class, $filename, $last_field) = @_;
+
+ my @lines = read_file($filename);
+ return unless @lines;
+
+ return $class->parse_from_lines( $last_field, @lines );
+}
+
+sub parse_from_fh {
+ my ($class, $fh, $last_field) = @_;
+
+ my @lines = <$fh>;
+ return unless @lines;
+
+ return $class->parse_from_lines( $last_field, @lines );
+}
+
+sub parse_from_lines {
+ my ($class, $last_field, @lines) = @_;
+ return unless @lines;
+ chomp @lines;
+
+ my $data = {};
+
+ # read all the initial fields
+ while ( my $line = shift @lines ) {
+ my ($key, $value) = split(/\s*:\s*/, $line, 2);
+
+ if ( defined $data->{$key} ) {
+ unless ( ref $data->{$key} eq 'ARRAY' ) {
+ $data->{$key} = [ $data->{$key} ];
+ };
+ push @{$data->{$key}}, $value;
+ }
+ else {
+ $data->{$key} = $value;
+ }
+ }
+
+ # now read everything that's left into the $last_field field
+ $data->{$last_field} = join("\n", @lines);
+
+ return $data;
+}
+
+sub write_cil_file {
+ my ($class, $filename, $data, @fields) = @_;
+
+ my $last_field = pop @fields;
+
+ my @lines;
+
+ foreach my $field ( @fields ) {
+ next if $field eq $last_field;
+
+ if ( ref $data->{$field} eq 'ARRAY' ) {
+ foreach ( sort @{$data->{$field}} ) {
+ push @lines, "$field: $_\n";
+ }
+ }
+ else {
+ push @lines, "$field: $data->{$field}\n";
+ }
+ }
+
+ push @lines, "\n";
+ push @lines, $data->{$last_field}, "\n";
+
+ write_file($filename, \@lines);
+}
+
+## ----------------------------------------------------------------------------
+1;
+## ----------------------------------------------------------------------------
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More qw(no_plan);
+use Data::Dumper;
+use CIL::Utils;
+
+my $parsed_fields = CIL::Utils->parse_cil_file( 't/i_cafebabe.cil', 'Description' );
+
+my $correct_fields = {
+ 'Summary' => 'Addition of a \'attach\' command',
+ 'Status' => 'New',
+ 'CreatedBy' => 'A N Other <a.n.other@example.org>',
+ 'AssignedTo' => 'A Name <aname@example.com>',
+ 'Label' => [
+ 'against-v0.1',
+ 'priority-medium',
+ 'type-enhancement',
+ ],
+ 'Inserted' => '2008-06-15 18:22:01',
+ 'Updated' => '2008-06-15 23:15:27',
+ 'Description' => '\'cil\' currently has no way of adding attachments to issues.
+
+This should be added so that the actual data cil stores is complete.'
+};
+
+is_deeply($parsed_fields, $correct_fields, 'Check parsing of file');
+
+CIL::Utils->write_cil_file( '/tmp/i_deadbeef.cil', $correct_fields, qw(Summary Status CreatedBy AssignedTo Label Inserted Updated Description) );
+CIL::Utils->write_cil_file( '/tmp/i_decaf7ea.cil', $parsed_fields, qw(Summary Status CreatedBy AssignedTo Label Inserted Updated Description) );
+
+# is($parsed_fields, $correct_fields, 'Check parsing of file');
--- /dev/null
+Summary: Addition of a 'attach' command
+Status: New
+CreatedBy: A N Other <a.n.other@example.org>
+AssignedTo: A Name <aname@example.com>
+Label: against-v0.1
+Label: priority-medium
+Label: type-enhancement
+Inserted: 2008-06-15 18:22:01
+Updated: 2008-06-15 23:15:27
+
+'cil' currently has no way of adding attachments to issues.
+
+This should be added so that the actual data cil stores is complete.