Status : New
CreatedBy : $gan <$gae>
AssignedTo : $gan <$gae>
-Label :
+Label :
Description...
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 {
# commands
sub init {
- my ($path) = @_;
+ my ($cil, $path) = @_;
# error if $path doesn't exist
unless ( -d $path ) {
}
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();
}
}
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();
}
}
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 );
# 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'");
}
# 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 = <issues/*.yaml>;
- 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() );
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";
sub text {
my ($field, $value) = @_;
- title($field);
msg "";
msg($value);
msg "";
list
show <issue>
edit <issue>
+ comment <issue>
See <http://kapiti.geek.nz/software/cil.html> for further information.
Report bugs to <andychilton -at- gmail -dot- com>.
--- /dev/null
+Issue:
+CreatedBy: Andrew Chilton <andychilton@gmail.com>
+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.
-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
+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.
-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
+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
-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
+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
-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
+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.
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 = {
## ----------------------------------------------------------------------------
-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) = @_;
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;
## ----------------------------------------------------------------------------
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) = @_;
# 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;
$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;
## ----------------------------------------------------------------------------
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;
## ----------------------------------------------------------------------------
$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 {
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 {
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};
}
## ----------------------------------------------------------------------------