The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Plack::Middleware::Profiler::KYTProf;
use strict;
use warnings;
use parent qw(Plack::Middleware);
our $VERSION = '0.071';

use Plack::Util::Accessor qw(
    namespace_regex
    ignore_class_regex
    context_classes_regex
    logger
    threshold
    remove_linefeed
    profiles
    mutes
    enable_profile_if
);
use Module::Load qw(load);

my %PROFILER_SETUPED;

sub prepare_app {
    my $self = shift;

    $self->_setup_enable_profile_if;
}

sub _setup_enable_profile_if {
    my $self = shift;
    $self->enable_profile_if( sub {1} ) unless $self->enable_profile_if;
}

sub _setup_profiler_if_needed {
    my ( $self, $env ) = @_;

    my $pid = $$;
    return if $PROFILER_SETUPED{$pid};
    $PROFILER_SETUPED{$pid} = 1;

    my $is_profiler_enabled = $self->enable_profile_if->($env);
    return unless $is_profiler_enabled;

    $self->_setup_profiler;
}

sub _setup_profiler {
    my $self = shift;

    $self->_load_kytprof;
    $self->_set_kytprof_options;
    $self->_load_profiles;
    $self->_diable_module_profiling;
}

sub _load_kytprof {
    my $self = shift;
    $self->_load_module('Devel::KYTProf');
}

sub _set_kytprof_options {
    my $self = shift;
    Devel::KYTProf->namespace_regex( $self->namespace_regex )
        if $self->namespace_regex;
    Devel::KYTProf->ignore_class_regex( $self->ignore_class_regex )
        if $self->ignore_class_regex;
    Devel::KYTProf->context_classes_regex( $self->context_classes_regex )
        if $self->context_classes_regex;

    # TODO Should we create logger adapter for popular logging framework?
    Devel::KYTProf->logger( $self->logger )       if $self->logger;
    Devel::KYTProf->threshold( $self->threshold ) if $self->threshold;
    Devel::KYTProf->remove_linefeed( $self->remove_linefeed )
        if $self->remove_linefeed;
}

sub _diable_module_profiling {
    my $self = shift;
    foreach my $module ( keys %{ $self->mutes || {} } ) {
        my $method = $self->mutes->{$module};
        Devel::KYTProf->mute( $module, $method );
    }
}

sub _load_profiles {
    my $self = shift;

    my $profiles ||= $self->profiles;
    $profiles ||= [
        'Plack::Middleware::Profiler::KYTProf::Profile::TemplateEngine',
        'Plack::Middleware::Profiler::KYTProf::Profile::KVS'
    ];
    foreach my $profile (@$profiles) {
        $self->_load_module($profile);
        die "profile class must implement load method"
            unless $profile->can('load');
        $profile->load;
    }
}

sub _load_module {
    my ( $self, $module ) = @_;
    eval { load $module; 1; } or die "Can't load ${module}";
}

sub call {
    my ( $self, $env ) = @_;
    $self->_setup_profiler_if_needed($env);

    my $res = $self->app->($env);

    if ( ref($res) && ref($res) eq 'ARRAY' ) {
        return $res;
    }

    Plack::Util::response_cb(
        $res,
        sub {
        }
    );
}

1;

__END__

=encoding utf-8

=head1 NAME

Plack::Middleware::Profiler::KYTProf - Profile psgi app with KYTProf

=head1 SYNOPSIS

    builder {
        enable "Plack::Middleware::Profiler::KYTProf";
        $app;
    };

=head1 DESCRIPTION

Plack::Middleware::Profiler::KYTProf is the PSGI app profiler.
Use enable_profile_if, logger and threshold option in production environment.

Use profiles if you need application specific profiling.
See the sample profile L<Plack::Middleware::Profiler::KYTProf::Profile::TemplateEngine>.

=head1 OPTIONS

NOTE that some options expect a code reference. Maybe, you feel it is complicated. 
However that will enable to control them programmably. It is more useful to your apps.

=over 4

=item enable_profile_if

default

    sub { 1 }

Use code reference if you want to enable profiling programmably 
This option is optional.

=item profiles

You can add profiling target modules if you use this option.

default

    [
        'Plack::Middleware::Profiler::KYTProf::Profile::TemplateEngine',
        'Plack::Middleware::Profiler::KYTProf::Profile::KVS'
    ];

=item namespace_regex

See L<Devel::KYTProf> POD.

default

    undef


=item ignore_class_regex

See L<Devel::KYTProf> POD.

default

    undef


=item context_classes_regex

See L<Devel::KYTProf> POD.

=item logger

See L<Devel::KYTProf> POD.

default

    undef


=item threshold

See L<Devel::KYTProf> POD.

default

    undef

=item remove_linefeed

See L<Devel::KYTProf> POD.

default

    undef

=item mutes

See L<Devel::KYTProf> POD.

default

    undef

=back

=head1 SOURCE AVAILABILITY

This source is in Github:

  http://github.com/dann/p5-plack-middleware-profiler-kytprof

=head1 CONTRIBUTORS

Many thanks to:

=head1 AUTHOR

Dann E<lt>techmemo@gmail.comE<gt>

=head1 SEE ALSO

L<Devel::KYTProf>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut