# Arch Perl library, Copyright (C) 2004 Mikhael Goikhman, Enno Cramer
#
# This program 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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

use 5.006;
use strict;
use warnings;

package ArchWay::Util::RunCmd;

# objects or classes that implement some of these methods:
# cmd_start, cmd_output_line, cmd_end
our @observers;

sub notify (@) {
	my $method = shift;
	foreach my $observer (@observers) {
		$observer->$method(@_) if $observer->can($method);
	}
}

sub unobserve ($) {
	my $observer = shift;
	@observers = grep { $_ ne $observer } @observers;
}   

sub observe ($) {
   my $observer = shift;
	unobserve($observer);
	push @observers, $observer;
}

use Arch::Util;
BEGIN { our $orig_run_cmd = \&Arch::Util::run_cmd; }
no warnings;  # suppress the 'Subroutine redefined' warning

sub Arch::Util::run_cmd (@) {
	my $arg0 = shift || die;
	my @args = (split(' ', $arg0), @_);
	notify('cmd_start', @args);
	my $pipe = Arch::Util::run_pipe_from(@args);
	my @lines;
	while (my $line = <$pipe>) {
		push @lines, $line;
		chomp $line;
		notify('cmd_output_line', $line);
	}
	close($pipe);
	notify('cmd_end');
	return wantarray? do { chomp @lines; @lines }: join('', @lines);
}

1;

__END__

=head1 NAME

ArchWay::Util::RunCmd - add notification capabilities to Arch::Util::run_cmd

=head1 SYNOPSIS

    package Some::CommandTracker;

    sub cmd_start ($@) {
        my $this = shift;
        print "executing: [@_]\n";
    }
    sub cmd_output_line ($$) {
        my $this = shift;
        my $line = shift;
        print "\t($line)\n";
    }
    # ...

    package main;

    use Arch::Util qw(run_tla);  # in any order
    use ArchWay::Util::RunCmd;

    ArchWay::Util::RunCmd::observe("Some::CommandTracker");
    my $archives = run_tla("archives", "-n");

    # this may work too
    ArchWay::Util::RunCmd::observe(Some::CommandTracker->new);

=head1 DESCRIPTION

This class provides a way to register classes/objects that track any
commands executed anywhere in the program using B<run_tla> and B<run_cmd>
of B<Arch::Util>, including their output.

The observer classes/objects need to three or less methods:
B<cmd_start>, B<cmd_output_line>, B<cmd_end>. The observer should not
implement all methods, but it makes sense to implement at least the
first one.

B<cmd_start> method is called with all command arguments as parameters,
i.e. ($observer, "tla", "archives", "-n").

B<cmd_output_line> method is called with command output line as parameter.

B<cmd_end> is called without parameters.

=head1 FUNCTIONS

The following public functions are available:

B<observe>,
B<unobserve>.

=over 4

=item B<ArchWay::Util::RunCmd::observe> I<observer>

Receives an I<observer> class name or object. It should implement some
of the methods described above.

=item B<ArchWay::Util::RunCmd::unobserve> I<observer>

Stops notification of the given I<observer> that should be the same one
passed to B<observe>.

=back

=head1 BUGS

No known bugs.

=head1 AUTHORS

Mikhael Goikhman (migo@homemail.com--Perl-GPL/archway--devel).

=head1 SEE ALSO

For more information, see L<Arch::Util>.

=cut
