Developer-Dashboard
view release on metacpan or search on metacpan
lib/Developer/Dashboard/CLI/Progress.pm view on Meta::CPAN
status => 'pending',
detail_lines => [],
}
} @{$tasks};
my $stream = $args{stream} || \*STDERR;
my $self = bless {
title => $args{title} || 'dashboard progress',
order => \@order,
tasks => \%task_lookup,
stream => $stream,
dynamic => $args{dynamic} ? 1 : 0,
color => $args{color} ? 1 : 0,
rendered => 0,
max_detail_lines => $args{max_detail_lines},
last_rendered_line_count => 0,
}, $class;
$self->render;
return $self;
}
# callback()
# Returns a callback that updates task state from runtime lifecycle events.
# Input: none.
# Output: coderef that accepts one hash reference event.
sub callback {
my ($self) = @_;
return sub {
my ($event) = @_;
$self->update($event);
};
}
# add_tasks($tasks)
# Appends newly discovered tasks to the rendered board without disturbing the
# status and order of already registered tasks.
# Input: array reference of task hashes with id and optional label.
# Output: true value.
sub add_tasks {
my ( $self, $tasks ) = @_;
return 1 if ref($tasks) ne 'ARRAY' || !@{$tasks};
for my $task ( @{$tasks} ) {
next if ref($task) ne 'HASH';
my $id = $task->{id} || next;
next if $self->{tasks}{$id};
push @{ $self->{order} }, $id;
$self->{tasks}{$id} = {
id => $id,
label => $task->{label} || $id,
status => 'pending',
detail_lines => [],
};
}
$self->render;
return 1;
}
# update($event)
# Applies one lifecycle progress event to the tracked task board.
# Input: hash reference with task_id, status, and optional label.
# Output: true value.
sub update {
my ( $self, $event ) = @_;
return 1 if !$event || ref($event) ne 'HASH';
$self->add_tasks( $event->{add_tasks} ) if exists $event->{add_tasks};
my $id = $event->{task_id} || return 1;
my $task = $self->{tasks}{$id} || return 1;
$task->{status} = $event->{status} if defined $event->{status} && $event->{status} ne '';
$task->{label} = $event->{label} if defined $event->{label} && $event->{label} ne '';
if ( exists $event->{detail_lines} ) {
my @detail_lines = ref( $event->{detail_lines} ) eq 'ARRAY' ? @{ $event->{detail_lines} } : ();
my $max = $self->_detail_line_limit;
if ( defined $max && @detail_lines > $max ) {
@detail_lines = @detail_lines[ @detail_lines - $max .. $#detail_lines ];
}
$task->{detail_lines} = \@detail_lines;
}
elsif ( exists $event->{detail_line} ) {
my @detail_lines = @{ $task->{detail_lines} || [] };
push @detail_lines, $event->{detail_line};
my $max = $self->_detail_line_limit;
if ( defined $max && @detail_lines > $max ) {
@detail_lines = @detail_lines[ @detail_lines - $max .. $#detail_lines ];
}
$task->{detail_lines} = \@detail_lines;
}
if ( $task->{status} eq 'done' ) {
$task->{detail_lines} = [];
}
$self->render;
return 1;
}
# finish()
# Finalizes the rendered board by ensuring a trailing newline after dynamic redraws.
# Input: none.
# Output: true value.
sub finish {
my ($self) = @_;
return 1 if !$self->{dynamic} || !$self->{rendered};
my $stream = $self->{stream};
print {$stream} "\n";
return 1;
}
# render()
# Renders the full task board to the configured output stream.
# Input: none.
# Output: true value.
sub render {
my ($self) = @_;
my $stream = $self->{stream};
my $board = $self->render_text;
if ( $self->{dynamic} && $self->{rendered} ) {
for ( 1 .. ( $self->{last_rendered_line_count} || 0 ) ) {
print {$stream} "\e[1A\e[2K";
}
}
print {$stream} $board;
$self->{rendered} = 1;
$self->{last_rendered_line_count} = scalar grep { defined } split /\n/, $board;
return 1;
( run in 2.991 seconds using v1.01-cache-2.11-cpan-9581c071862 )