Taskwarrior-Hooks/lib/Taskwarrior/Kusarigama/Hook.pm
package Taskwarrior::Kusarigama::Hook;
our $AUTHORITY = 'cpan:YANICK';
# ABSTRACT: Entry-point for Kusarigama's hook scripts
$Taskwarrior::Kusarigama::Hook::VERSION = '0.0.1';
use 5.10.0;
use strict;
use warnings;
use Moo;
use MooseX::MungeHas;
use IPC::Run3;
use Try::Tiny;
use Path::Tiny;
use Hash::Merge qw/merge /;
use List::AllUtils qw/ reduce pairmap pairmap /;
use JSON;
use experimental 'postderef';
with 'Taskwarrior::Kusarigama::Core';
has raw_args => (
is => 'ro',
default => sub { [] },
trigger => sub {
my( $self, $new ) = @_;
pairmap { $self->$a($b) }
map { split ':', $_, 2 } @$new
},
);
has exit_on_failure => (
is => 'ro',
default => 1,
);
has config => sub {
run3 [qw/ task rc.verbose=nothing rc.hooks=off show /], undef, \my $output;
$output =~ s/^.*?---$//sm;
$output =~ s/^Some of your.*//mg;
$output =~ s/^\s+.*//mg;
reduce { merge( $a, $b ) } map {
reduce { +{ $b => $a } } $_->[1], reverse split '\.', $_->[0]
} map { [split ' ', $_, 2] } grep { /\w/ } split "\n", $output;
};
sub run_event {
my( $self, $event ) = @_;
my $method = join '_', 'run', $event;
my @plugins = $self->plugins->@*;
my @tasks = map { from_json($_) } <STDIN>;
try {
$self->$method(\@plugins,@tasks);
}
catch {
say $_;
# TODO die instead of exit
exit 1 if $self->exit_on_failure;
};
}
# TODO document how to abort the pipeline (by dying)
# TODO document 'feedback'
sub run_exit {
my( $self, $plugins, @tasks ) = @_;
$_->on_exit(@tasks) for grep { $_->DOES('Taskwarrior::Kusarigama::Hook::OnExit') } @$plugins;
say for $self->feedback->@*;
}
sub run_launch {
my( $self, $plugins, @tasks ) = @_;
for my $cmd ( grep { $_->DOES('Taskwarrior::Kusarigama::Hook::OnCommand') } @$plugins ) {
next unless $cmd->command_name eq $self->command;
$cmd->on_command(@tasks);
die sprintf "ran custom command '%s'\n", $cmd->command_name;
}
$_->on_launch(@tasks) for grep { $_->DOES('Taskwarrior::Kusarigama::Hook::OnLaunch') } @$plugins;
say for $self->feedback->@*;
}
sub run_add {
my( $self, $plugins, $task ) = @_;
$_->on_add($task) for grep { $_->DOES('Taskwarrior::Kusarigama::Hook::OnAdd') } @$plugins;
say to_json($task);
say for $self->feedback->@*;
}
# TODO document the $old, $new, $diff
sub run_modify {
my( $self, $plugins, $old, $new ) = @_;
for( grep { $_->DOES('Taskwarrior::Kusarigama::Hook::OnModify') } @$plugins ) {
use Hash::Diff;
my $diff = Hash::Diff::diff( $old, $new );
$_->on_modify( $new, $old, $diff );
}
say to_json($new);
say for $self->feedback->@*;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Taskwarrior::Kusarigama::Hook - Entry-point for Kusarigama's hook scripts
=head1 VERSION
version 0.0.1
=head1 SYNOPSIS
# most likely in one of the ~/.task/hooks/on-xxx.pl scripts
use Taskwarrior::Kusarigama::Hook;
Taskwarrior::Kusarigama::Hook->new(
raw_args => @ARGV
)->run_event( 'launch' );
=head1 DESCRIPTION
This is the entry point for kusarigama when running it as a
Taskwarrior hook.
=head1 METHODS
=head2 new
my $kusarigama = Taskwarrior::Kusarigama::Hook->new(
raw_args => [],
);
Constructor. Recognizes the following arguments
=over
=item raw_args
Reference to the list of arguments as passed to the taskwarrior hooks.
=item exit_on_failure
=for TODO We don't really need that, do we? Let's just die right there
If the system should exit with an error code when one of the plugin
throws an exception (and thus abort the executiong of the remaining of the
taskwarrior
pipeline).
Defaults to C<true>.
=back
=head2 config
my $config = $kusarigama->config;
Returns taskwarrior's configuration as C<task show> would.
=head2 run_event
$kusarigama->run_event( 'launch' );
Runs all plugins associated with the provided stage.
If C<exit_on_failure> is true, it will die if a plugin throws an
exception.
=head2 run_exit
$kusarigama->run_exit;
Runs the exit stage part of the plugins.
=head2 run_launch
$kusarigama->run_launch;
Runs the launch stage part of the plugins. Also intercepts and
run custom commands.
=head2 run_add
$kusarigama->run_add;
Runs the add stage part of the plugins.
=head2 run_modify
$kusarigama->run_modify;
Runs the modify stage part of the plugins.
=head1 AUTHOR
Yanick Champoux <yanick@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2016 by Yanick Champoux.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut