From d3ecf4fa7162078bf27667fc962d17fd6bdfb2f6 Mon Sep 17 00:00:00 2001 From: Andrew Chilton Date: Sat, 21 Jun 2008 23:05:04 +1200 Subject: [PATCH] Addition of Comments using the new format. --- bin/cil | 166 ++++++++++++++++++++---------------------- issues/c_feb65ae7.cil | 9 +++ issues/i_02ee35bd.cil | 11 ++- issues/i_5c88cb30.cil | 13 ++-- issues/i_6baa8555.cil | 11 ++- issues/i_cbb43db9.cil | 11 ++- lib/CIL.pm | 40 ++++++---- lib/CIL/Base.pm | 110 ++++++++++++++++++++++++++-- lib/CIL/Comment.pm | 43 ++++++++++- lib/CIL/Issue.pm | 99 ++++++------------------- 10 files changed, 304 insertions(+), 209 deletions(-) create mode 100644 issues/c_feb65ae7.cil diff --git a/bin/cil b/bin/cil index d5d67e5..e0926fb 100755 --- a/bin/cil +++ b/bin/cil @@ -49,7 +49,7 @@ Summary : Status : New CreatedBy : $gan <$gae> AssignedTo : $gan <$gae> -Label : +Label : Description... EOF @@ -69,36 +69,40 @@ EOF exit 2; } + my $cil = CIL->new(); + if ( $command eq 'init' ) { my ($path) = @ARGV; $path ||= '.'; - init($path); + init($cil, $path); + } elsif ( $command eq 'list' ) { - list(); + list($cil); } elsif ( $command eq 'summary' ) { - summary(); + summary($cil); } elsif ( $command eq 'show' ) { my ($issue_name) = @ARGV; - show($issue_name); + show($cil, $issue_name); } elsif ( $command eq 'add' ) { - add(); + add($cil); } elsif ( $command eq 'edit' ) { my ($issue_name) = @ARGV; - edit($issue_name); + edit($cil, $issue_name); } elsif ( $command eq 'comment' ) { my ($issue_name) = @ARGV; - comment($issue_name); + comment($cil, $issue_name); + } else { @@ -111,7 +115,7 @@ EOF # commands sub init { - my ($path) = @_; + my ($cil, $path) = @_; # error if $path doesn't exist unless ( -d $path ) { @@ -146,14 +150,16 @@ sub init { } sub list { - check_paths(); + my ($cil) = @_; + + check_paths($cil); # find all the issues - my $issues = CIL->instance->get_issues(); + my $issues = $cil->get_issues(); if ( @$issues ) { separator(); - foreach my $issue ( @$issues ) { - display_issue_short($issue); + foreach my $issue ( sort { $a->Inserted cmp $b->Inserted } @$issues ) { + display_issue_headers($cil, $issue); } separator(); } @@ -163,14 +169,16 @@ sub list { } sub summary { - check_paths(); + my ($cil) = @_; + + check_paths($cil); # find all the issues - my $issues = CIL->instance->get_issues(); + my $issues = $cil->get_issues(); if ( @$issues ) { separator(); foreach my $issue ( @$issues ) { - display_issue_summary($issue); + display_issue_summary($cil, $issue); } separator(); } @@ -180,18 +188,18 @@ sub summary { } sub show { - my ($issue_name) = @_; + my ($cil, $issue_name) = @_; # firstly, read the issue in - my $issue = CIL::Issue->load($issue_name); + my $issue = CIL::Issue->new_from_name($cil, $issue_name); unless ( defined $issue ) { fatal("Couldn't load issue '$issue'"); } - display_issue_full( $issue ); + display_issue_full($cil, $issue); } sub add { - my ($issue_name) = @_; + my ($cil, $issue_name) = @_; # read in the new issue text my $fh = solicit( $new_issue_text ); @@ -201,14 +209,14 @@ sub add { # 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 ); + $issue->save($cil); + display_issue($cil, $issue); } sub edit { - my ($issue_name) = @_; + my ($cil, $issue_name) = @_; - my $issue = CIL::Issue->load($issue_name); + my $issue = CIL::Issue->new_from_name($cil, $issue_name); unless ( defined $issue ) { fatal("Couldn't load issue '$issue'"); } @@ -219,101 +227,72 @@ sub edit { # read in the new issue text my $fh = solicit( join('', @$edit_issue_text) ); - my $issue_edited; - eval { - $issue_edited = CIL::Issue->new_from_fh( $issue->name, $fh ); - }; - if ( $@ ) { - fatal("couldn't parse issue: $@"); - } + my $issue_edited = CIL::Issue->new_from_fh( $issue->name, $fh ); unless ( defined $issue_edited ) { fatal("couldn't create issue (program error)"); } - $issue_edited->save(); - display_issue_full( $issue_edited ); + $issue_edited->save($cil); + display_issue($cil, $issue_edited); } sub comment { - my ($issue_name) = @_; + my ($cil, $issue_name) = @_; - my $issue = CIL::Issue->new_load_issue($issue_name); + my $issue = CIL::Issue->new_from_name($cil, $issue_name); unless ( defined $issue ) { - fatal("Couldn't load issue '$issue'"); + fatal("couldn't load issue '$issue'"); } # read in the new issue text my $fh = solicit( $add_comment_text ); - my $cfg; - eval { - $cfg = Config::IniFiles->new( -file => $fh ); - }; - if ( $@ ) { - fatal("couldn't parse comment: $@"); + my $comment = CIL::Comment->new_from_fh( 'tmp', $fh ); + unless ( defined $comment ) { + fatal("could not create new comment"); } - unless ( defined $cfg ) { - fatal("not a valid inifile"); - } - - my $comment = CIL::Comment->new(); - foreach my $field ( qw(CreatedBy Description) ) { - # modify the data directly, otherwise Updated will kick in - my $value = $cfg->val( 'Comment', $field ); - next unless defined $value; - - $value =~ s/^\s*//; - $value =~ s/\s*$//; - $comment->set_no_update($field, $value); - } - - # tell the comment when it was inserted - $comment->inserted; + # we've got the comment, so let's name it + my $unique_str = $comment->Inserted . $issue->Description; + $comment->set_name( substr(md5_hex($unique_str), 0, 8) ); + $comment->save($cil); # add the comment to the issue, update it's timestamp and save it out $issue->add_comment( $comment ); - $issue->save(); + $issue->save($cil); + display_issue_full($cil, $issue); } ## ---------------------------------------------------------------------------- sub check_paths { - # make sure an issue directory is available - unless ( -d 'issues' ) { - fatal("couldn't find 'issues' directory"); - } -} + my ($cil) = @_; -sub get_all_issues { - my @issues; - my @filenames = ; - foreach my $filename ( sort @filenames ) { - push @issues, CIL::Issue->new_load_issue( basename($filename, '.yaml') ); + # make sure an issue directory is available + unless ( -d $cil->issue_dir ) { + fatal("couldn't find '" . $cil->issue_dir . "' directory"); } - @issues = sort { $a->Inserted cmp $b->Inserted } @issues; - return \@issues; } ## ---------------------------------------------------------------------------- # input/output sub display_issue_summary { - my ($issue) = @_; + my ($cil, $issue) = @_; - my $msg = $issue->name; + my $msg = $issue->name(); $msg .= "\t"; - $msg .= $issue->Status; + $msg .= $issue->Status(); $msg .= "\t"; - $msg .= $issue->Summary; + $msg .= $issue->Summary(); msg($msg); } -sub display_issue_short { - my ($issue) = @_; +sub display_issue_headers { + my ($cil, $issue) = @_; - title( "Issue " . $issue->name ); + title( 'Issue ' . $issue->name() ); field( 'Summary', $issue->Summary() ); field( 'CreatedBy', $issue->CreatedBy() ); field( 'AssignedTo', $issue->AssignedTo() ); @@ -322,28 +301,43 @@ sub display_issue_short { field( 'Labels', join(' ', @{$issue->Label()}) ); } -sub display_issue_full { - my ($issue) = @_; +sub display_issue { + my ($cil, $issue) = @_; separator(); - title('Issue ' . $issue->name); + title( 'Issue ' . $issue->name() ); field( 'Summary', $issue->Summary() ); field( 'Status', $issue->Status() ); field( 'CreatedBy', $issue->CreatedBy() ); field( 'AssignedTo', $issue->AssignedTo() ); - field( 'Inserted', $issue->Inserted() ); - field( 'Updated', $issue->Inserted() ); field( 'Label', $_ ) foreach sort @{$issue->Label()}; field( 'Comment', $_ ) foreach sort @{$issue->Comment()}; field( 'Attachment', $_ ) foreach sort @{$issue->Attachment()}; - separator(); + field( 'Inserted', $issue->Inserted() ); + field( 'Updated', $issue->Inserted() ); text('Description', $issue->Description()); separator(); } +sub display_issue_full { + my ($cil, $issue) = @_; + + display_issue( $cil, $issue ); + + my $comments = $cil->get_comments_for( $issue ); + foreach my $comment ( @$comments ) { + title( 'Comment ' . $comment->name() ); + field( 'CreatedBy', $comment->CreatedBy() ); + field( 'Inserted', $comment->Inserted() ); + field( 'Updated', $comment->Inserted() ); + text('Description', $comment->Description()); + separator(); + } +} + sub msg { print ( defined $_[0] ? $_[0] : '' ); print "\n"; @@ -369,7 +363,6 @@ sub field { sub text { my ($field, $value) = @_; - title($field); msg ""; msg($value); msg ""; @@ -401,6 +394,7 @@ Commands: list show edit + comment See for further information. Report bugs to . diff --git a/issues/c_feb65ae7.cil b/issues/c_feb65ae7.cil new file mode 100644 index 0000000..dcdfaf2 --- /dev/null +++ b/issues/c_feb65ae7.cil @@ -0,0 +1,9 @@ +Issue: +CreatedBy: Andrew Chilton +Inserted: 2008-06-21T10:57:47 +Updated: 2008-06-21T10:57:47 + +Currently I'm adding the ability for each issue name, comment etc to start with +the first 8 hex chars of an MD5 string from the data itself. Therefore, the +main problem with having the epoch and possible duplication goes away. This +issue can then be given a low priority. diff --git a/issues/i_02ee35bd.cil b/issues/i_02ee35bd.cil index 66c1f0a..21546ec 100644 --- a/issues/i_02ee35bd.cil +++ b/issues/i_02ee35bd.cil @@ -1,13 +1,12 @@ -Name: 1209992018 Summary: Labels should be allowed to have a 'required' set Status: New CreatedBy: Andrew Chilton -Inserted: 1209992018 AssignedTo: Andrew Chilton -Updated: 1209992018 -Label: against-v0.1 -Label: priority-medium -Label: type-enhancement +Label: Release-v0.1 +Label: Priority-Medium +Label: Type-Enhancement +Inserted: 2008-05-05T12:53:38 +Updated: 2008-06-21T10:50:05 In the .cil config file, there should a field which determines a 'required' set of labels. diff --git a/issues/i_5c88cb30.cil b/issues/i_5c88cb30.cil index 4b786a8..003dfdc 100644 --- a/issues/i_5c88cb30.cil +++ b/issues/i_5c88cb30.cil @@ -1,13 +1,14 @@ -Name: 1209990558 Summary: Options for issues names needs to be added Status: New CreatedBy: Andrew Chilton -Inserted: 1209990558 AssignedTo: Andrew Chilton -Updated: 1209990799 -Label: against-v0.1 -Label: priority-low -Label: type-enhancement +Label: Milestone-v0.2 +Label: Priority-Low +Label: Release-v0.1 +Label: Type-Enhancement +Comment: feb65ae7 +Inserted: 2008-05-05T12:33:19 +Updated: 2008-06-21T10:57:47 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 diff --git a/issues/i_6baa8555.cil b/issues/i_6baa8555.cil index afcc16b..5352e93 100644 --- a/issues/i_6baa8555.cil +++ b/issues/i_6baa8555.cil @@ -1,13 +1,12 @@ -Name: 1209991618 Summary: Do checking on input fields after adding or editing issue Status: New CreatedBy: Andrew Chilton -Inserted: 1209991618 AssignedTo: Andrew Chilton -Updated: 1209991718 -Label: against-v0.1 -Label: priority-medium -Label: type-enhancement +Label: Priority-Medium +Label: Release-v0.1 +Label: Type-Enhancement +Inserted: 2008-05-05T12:46:58 +Updated: 2008-06-21T10:59:52 For example, if there is a config item in .cil called 'allowed_statuses', all input values in the 'Status' field should be diff --git a/issues/i_cbb43db9.cil b/issues/i_cbb43db9.cil index 05cd80f..0369ce8 100644 --- a/issues/i_cbb43db9.cil +++ b/issues/i_cbb43db9.cil @@ -1,13 +1,12 @@ -Name: 1209990028 Summary: Addition of a 'attach' command Status: New CreatedBy: Andrew Chilton -Inserted: 1209990028 AssignedTo: Andrew Chilton -Updated: 1209991365 -Label: against-v0.1 -Label: priority-medium -Label: type-enhancement +Label: Priority-Medium +Label: Release-v0.1 +Label: Type-Enhancement +Inserted: 2008-05-05T12:20:28 +Updated: 2008-06-21T11:00:02 'cil' currently has no way of adding attachments to issues. diff --git a/lib/CIL.pm b/lib/CIL.pm index b697066..a9d49df 100644 --- a/lib/CIL.pm +++ b/lib/CIL.pm @@ -25,7 +25,7 @@ use strict; use warnings; use File::Glob qw(:glob); -use base qw(Class::Singleton Class::Accessor); +use base qw(Class::Accessor); __PACKAGE__->mk_accessors(qw(issue_dir)); my $defaults = { @@ -34,25 +34,22 @@ my $defaults = { ## ---------------------------------------------------------------------------- -sub _new_instance { - my ($proto) = @_; +sub new { + my ($proto, $cfg) = @_; + + $cfg ||= {}; + my $class = ref $proto || $proto; my $self = bless {}, $class; + + # save the settings for various bits of info foreach my $key ( keys %$defaults ) { - $self->$key( $defaults->{$key} ); + # if we have been passed it in, use it, else use the default + $self->$key( $cfg->{$key} || $defaults->{$key} ); } return $self; } -sub set_config { - my ($self, $config) = @_; - - foreach my $key ( qw(issue_dir) ) { - $self->$key( $config->{$key} ) - if defined $config->{$key}; - } -} - sub list_issues { my ($self) = @_; @@ -77,11 +74,26 @@ sub get_issues { my @issues; foreach my $issue ( @$issue_list ) { - push @issues, CIL::Issue->load( $issue->{name} ); + push @issues, CIL::Issue->new_from_name( $self, $issue->{name} ); } return \@issues; } +sub get_comments_for { + my ($self, $issue) = @_; + + my @comments; + foreach my $comment_name ( @{$issue->Comment} ) { + my $comment = CIL::Comment->new_from_name( $self, $comment_name ); + push @comments, $comment; + } + + # sort them in cronological order + @comments = sort { $a->Inserted cmp $b->Inserted } @comments; + + return \@comments; +} + ## ---------------------------------------------------------------------------- 1; ## ---------------------------------------------------------------------------- diff --git a/lib/CIL/Base.pm b/lib/CIL/Base.pm index db1ba62..ed2c88a 100644 --- a/lib/CIL/Base.pm +++ b/lib/CIL/Base.pm @@ -29,6 +29,84 @@ use DateTime; use base qw(Class::Accessor); __PACKAGE__->mk_accessors(qw(CreatedBy Inserted Updated Description)); +## ---------------------------------------------------------------------------- + +sub new_from_name { + my ($class, $cil, $name) = @_; + + croak 'provide an issue name to load' + unless defined $name; + + my $filename = $class->create_filename($cil, $name); + 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 new_from_data { + my ($class, $name, $data) = @_; + + croak 'please provide an issue name' + unless defined $name; + + # ToDo: check we have all the other correct fields + + # create the issue + my $self = $class->new( $name ); + + my $fields = $class->fields(); + my $array_fields = $class->array_fields(); + + # save each field + foreach my $field ( @$fields ) { + next unless defined $data->{$field}; + + # make it an array if it should be one + if ( exists $array_fields->{$field} and ref $data->{$field} ne 'ARRAY' ) { + $data->{$field} = [ $data->{$field} ]; + } + + # modify the data directly, otherwise Updated will kick in + $self->set_no_update($field, $data->{$field}); + } + $self->set_no_update('Changed', 0); + + return $self; +} + +sub new_from_fh { + my ($class, $name, $fh) = @_; + + croak 'please provide name' + unless defined $name; + + my $data = CIL::Utils->parse_from_fh( $fh, 'Description' ); + return $class->new_from_data( $name, $data ); +} + +sub save { + my ($self, $cil) = @_; + + my $filename = $self->create_filename($cil, $self->name); + + my $fields = $self->fields(); + CIL::Utils->write_cil_file( $filename, $self->{data}, @$fields ); +} + +sub create_filename { + my ($class, $cil, $name) = @_; + + # create the filename from it's parts + my $prefix = $class->prefix(); + my $issue_dir = $cil->issue_dir; + my $filename = "${issue_dir}/${prefix}_${name}.cil"; + + return $filename; +} + # override Class::Accessor's get sub get { my ($self, $field) = @_; @@ -55,18 +133,19 @@ sub set { # since we're actually changing the field, say we updated something $self->{data}{$field} = $value; - $self->set_updated; + $self->set_updated_now; } # so that we can update fields without 'Updated' being changed sub set_no_update { my ($self, $field, $value) = @_; - croak "provide a field name" - unless defined $field; - $self->{data}{$field} = $value; + + my $saved_update_time = $self->Updated; + $self->set( $field, $value ); + $self->Updated( $saved_update_time ); } -sub flag_inserted { +sub set_inserted_now { my ($self) = @_; my $time = DateTime->now; $self->{data}{Inserted} = $time; @@ -74,18 +153,37 @@ sub flag_inserted { $self->{Changed} = 1; } -sub flag_as_updated { +sub set_updated_now { my ($self) = @_; my $time = DateTime->now; $self->{data}{Updated} = $time; $self->{Changed} = 1; } +sub flag_as_updated { + my ($self) = @_; + $self->{Changed} = 1; +} + sub changed { my ($self) = @_; return $self->{Changed}; } +sub set_name { + my ($self, $name) = @_; + + croak 'provide a name' + unless defined $name; + + $self->{name} = $name; +} + +sub name { + my ($self) = @_; + return $self->{name}; +} + ## ---------------------------------------------------------------------------- 1; ## ---------------------------------------------------------------------------- diff --git a/lib/CIL/Comment.pm b/lib/CIL/Comment.pm index 8a283b1..c046569 100644 --- a/lib/CIL/Comment.pm +++ b/lib/CIL/Comment.pm @@ -23,23 +23,58 @@ package CIL::Comment; use strict; use warnings; +use Carp; use Config::IniFiles; use base qw(CIL::Base); +my @FIELDS = ( qw(Issue CreatedBy Inserted Updated Description) ); +my $cfg = { + array => { + Label => 1, + Comment => 1, + Attachment => 1, + }, +}; + ## ---------------------------------------------------------------------------- sub new { - my ($proto) = @_; + my ($proto, $name) = @_; + + croak 'please provide a comment 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} = { + CreatedBy => '', + Inserted => '', + Updated => '', + Description => '', + }; + $self->{Changed} = 0; + + $self->set_inserted_now; + return $self; } +sub prefix { + return 'c'; +} + +sub fields { + return \@FIELDS; +} + +sub array_fields { + return $cfg->{array}; +} + ## ---------------------------------------------------------------------------- 1; ## ---------------------------------------------------------------------------- diff --git a/lib/CIL/Issue.pm b/lib/CIL/Issue.pm index f19e621..4d8bcb3 100644 --- a/lib/CIL/Issue.pm +++ b/lib/CIL/Issue.pm @@ -56,71 +56,34 @@ sub new { $self->set_name( $name ); $self->{data} = { - Summary => '', - Status => '', - CreatedBy => '', - AssignedTo => '', - Label => [], - Comment => [], - Attachment => [], + Summary => '', + Status => '', + CreatedBy => '', + AssignedTo => '', + Inserted => '', + Updated => '', + Label => [], + Comment => [], + Attachment => [], + Description => '', }; $self->{Changed} = 0; - $self->flag_inserted; + $self->set_inserted_now; return $self; } -sub new_from_data { - my ($class, $name, $data) = @_; - - croak 'please provide an issue name' - unless defined $name; - - # ToDo: check we have all the other correct fields - - # create the issue - my $self = $class->new( $name ); - - # save each field - foreach my $field ( @FIELDS ) { - next unless defined $data->{$field}; - - # make it an array if it should be one - if ( exists $cfg->{array}{$field} and ref $data->{$field} ne 'ARRAY' ) { - $data->{$field} = [ $data->{$field} ]; - } - - # modify the data directly, otherwise Updated will kick in - $self->set_no_update($field, $data->{$field}); - } - $self->set_no_update('Changed', 0); - - return $self; +sub prefix { + return 'i'; } -sub new_from_fh { - my ($class, $name, $fh) = @_; - - croak 'please provide an issue name' - unless defined $name; - - my $data = CIL::Utils->parse_from_fh( $fh, 'Description' ); - return $class->new_from_data( $name, $data ); +sub fields { + return \@FIELDS; } -sub set_name { - my ($self, $name) = @_; - - croak 'provide a name' - unless defined $name; - - $self->{name} = $name; -} - -sub name { - my ($self) = @_; - return $self->{name}; +sub array_fields { + return $cfg->{array}; } sub add_label { @@ -130,15 +93,19 @@ sub add_label { unless defined $label; push @{$self->{data}{Label}}, $label; + $self->flag_as_updated(); } sub add_comment { my ($self, $comment) = @_; croak "can only add comments of type CIL::Comment" - unless ref $comment eq 'CIL::Comment'; + unless $comment->isa( 'CIL::Comment' ); + # add the comment name and set this issue's updated time push @{$self->{data}{Comment}}, $comment->name; + $self->Updated( $comment->Updated ); + $self->flag_as_updated(); } sub add_attachment { @@ -150,32 +117,14 @@ sub add_attachment { push @{$self->{data}{Attachment}}, $attachment->name; } -sub load { - my ($class, $name) = @_; - - croak 'provide an issue name to load' - unless defined $name; - - my $filename = CIL->instance->issue_dir . "/i_$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 as_output { my ($self) = @_; return CIL::Utils->format_data_as_output( $self->{data}, @FIELDS ); } -sub save { +sub Comments { my ($self) = @_; - my $name = $self->name; - my $filename = CIL->instance->issue_dir . "/i_$name.cil"; - CIL::Utils->write_cil_file( $filename, $self->{data}, @FIELDS ); + return $self->{Comment}; } ## ---------------------------------------------------------------------------- -- 2.39.5