| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Kossy; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 10 |  |  | 10 |  | 607570 | use strict; | 
|  | 10 |  |  |  |  | 94 |  | 
|  | 10 |  |  |  |  | 290 |  | 
| 4 | 10 |  |  | 10 |  | 50 | use warnings; | 
|  | 10 |  |  |  |  | 18 |  | 
|  | 10 |  |  |  |  | 240 |  | 
| 5 | 10 |  |  | 10 |  | 285 | use 5.008004; | 
|  | 10 |  |  |  |  | 31 |  | 
| 6 | 10 |  |  | 10 |  | 5188 | use utf8; | 
|  | 10 |  |  |  |  | 130 |  | 
|  | 10 |  |  |  |  | 49 |  | 
| 7 | 10 |  |  | 10 |  | 304 | use Carp qw//; | 
|  | 10 |  |  |  |  | 19 |  | 
|  | 10 |  |  |  |  | 152 |  | 
| 8 | 10 |  |  | 10 |  | 46 | use Cwd qw//; | 
|  | 10 |  |  |  |  | 16 |  | 
|  | 10 |  |  |  |  | 138 |  | 
| 9 | 10 |  |  | 10 |  | 42 | use File::Basename qw//; | 
|  | 10 |  |  |  |  | 25 |  | 
|  | 10 |  |  |  |  | 154 |  | 
| 10 | 10 |  |  | 10 |  | 5300 | use Text::Xslate; | 
|  | 10 |  |  |  |  | 125920 |  | 
|  | 10 |  |  |  |  | 584 |  | 
| 11 | 10 |  |  | 10 |  | 5787 | use HTML::FillInForm::Lite qw//; | 
|  | 10 |  |  |  |  | 36331 |  | 
|  | 10 |  |  |  |  | 289 |  | 
| 12 | 10 |  |  | 10 |  | 6560 | use JSON qw//; | 
|  | 10 |  |  |  |  | 109240 |  | 
|  | 10 |  |  |  |  | 296 |  | 
| 13 | 10 |  |  | 10 |  | 74 | use Scalar::Util qw//; | 
|  | 10 |  |  |  |  | 19 |  | 
|  | 10 |  |  |  |  | 166 |  | 
| 14 | 10 |  |  | 10 |  | 4758 | use Try::Tiny; | 
|  | 10 |  |  |  |  | 18891 |  | 
|  | 10 |  |  |  |  | 556 |  | 
| 15 | 10 |  |  | 10 |  | 4829 | use Encode; | 
|  | 10 |  |  |  |  | 142689 |  | 
|  | 10 |  |  |  |  | 843 |  | 
| 16 | 10 |  |  | 10 |  | 4221 | use Router::Boom; | 
|  | 10 |  |  |  |  | 39139 |  | 
|  | 10 |  |  |  |  | 450 |  | 
| 17 |  |  |  |  |  |  | use Class::Accessor::Lite ( | 
| 18 | 10 |  |  |  |  | 49 | new => 0, | 
| 19 |  |  |  |  |  |  | rw => [qw/root_dir/] | 
| 20 | 10 |  |  | 10 |  | 80 | ); | 
|  | 10 |  |  |  |  | 20 |  | 
| 21 | 10 |  |  | 10 |  | 719 | use base qw/Exporter/; | 
|  | 10 |  |  |  |  | 38 |  | 
|  | 10 |  |  |  |  | 1006 |  | 
| 22 | 10 |  |  | 10 |  | 4670 | use Kossy::Exception; | 
|  | 10 |  |  |  |  | 30 |  | 
|  | 10 |  |  |  |  | 386 |  | 
| 23 | 10 |  |  | 10 |  | 4406 | use Kossy::Connection; | 
|  | 10 |  |  |  |  | 28 |  | 
|  | 10 |  |  |  |  | 319 |  | 
| 24 | 10 |  |  | 10 |  | 4000 | use Kossy::Request; | 
|  | 10 |  |  |  |  | 34 |  | 
|  | 10 |  |  |  |  | 343 |  | 
| 25 | 10 |  |  | 10 |  | 68 | use Kossy::Response; | 
|  | 10 |  |  |  |  | 20 |  | 
|  | 10 |  |  |  |  | 188 |  | 
| 26 | 10 |  |  | 10 |  | 58 | use HTTP::Headers::Fast; | 
|  | 10 |  |  |  |  | 22 |  | 
|  | 10 |  |  |  |  | 19540 |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | our $VERSION = '0.60'; | 
| 29 |  |  |  |  |  |  | our @EXPORT = qw/new root_dir psgi build_app _router _connect get post router filter _wrap_filter/; | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | our $XSLATE_CACHE = 1; | 
| 32 |  |  |  |  |  |  | our $XSLATE_CACHE_DIR; | 
| 33 |  |  |  |  |  |  | our $SECURITY_HEADER = 1; | 
| 34 |  |  |  |  |  |  | our $JSON_SERIALIZER; | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | # cache underscore translation | 
| 37 |  |  |  |  |  |  | HTTP::Headers::Fast::_standardize_field_name('X-Frame-Options'); | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | sub new { | 
| 40 | 6 |  |  | 6 | 1 | 1421 | my $class = shift; | 
| 41 | 6 |  |  |  |  | 11 | my %args; | 
| 42 | 6 | 100 |  |  |  | 20 | if ( @_ < 2 ) { | 
| 43 | 2 |  |  |  |  | 3 | my $root_dir = shift; | 
| 44 | 2 |  |  |  |  | 7 | my @caller = caller; | 
| 45 | 2 |  | 66 |  |  | 61 | $root_dir ||= File::Basename::dirname( Cwd::realpath($caller[1]) ); | 
| 46 | 2 |  |  |  |  | 6 | $args{root_dir} = $root_dir; | 
| 47 |  |  |  |  |  |  | } | 
| 48 |  |  |  |  |  |  | else { | 
| 49 | 4 |  |  |  |  | 14 | %args = @_; | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  |  | 
| 52 | 6 |  |  |  |  | 24 | bless \%args, $class; | 
| 53 |  |  |  |  |  |  | } | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | sub psgi { | 
| 56 | 3 |  |  | 3 | 1 | 1947 | my $self = shift; | 
| 57 | 3 | 50 |  |  |  | 15 | if ( ! ref $self ) { | 
| 58 | 3 |  |  |  |  | 5 | my %args; | 
| 59 | 3 | 50 |  |  |  | 12 | if ( @_ < 2 ) { | 
| 60 | 3 |  |  |  |  | 7 | my $root_dir = shift; | 
| 61 | 3 |  |  |  |  | 11 | my @caller = caller; | 
| 62 | 3 |  | 66 |  |  | 128 | $root_dir ||= File::Basename::dirname( Cwd::realpath($caller[1]) ); | 
| 63 | 3 |  |  |  |  | 12 | $args{root_dir} = $root_dir; | 
| 64 |  |  |  |  |  |  | } | 
| 65 |  |  |  |  |  |  | else { | 
| 66 | 0 |  |  |  |  | 0 | %args = @_; | 
| 67 |  |  |  |  |  |  | } | 
| 68 | 3 |  |  |  |  | 16 | $self = $self->new(%args); | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  |  | 
| 71 | 3 |  |  |  |  | 14 | $self->build_app; | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | sub build_app { | 
| 75 | 3 |  |  | 3 | 0 | 7 | my $self = shift; | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | #router | 
| 78 | 3 |  |  |  |  | 27 | my $router = Router::Boom->new; | 
| 79 | 3 |  |  |  |  | 99 | $router->add($_ => $self->_router->{$_} ) for keys %{$self->_router}; | 
|  | 3 |  |  |  |  | 11 |  | 
| 80 | 3 |  |  |  |  | 107 | my $security_header_local = $SECURITY_HEADER; | 
| 81 | 3 |  |  |  |  | 6 | my %match_cache; | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 3 |  |  |  |  | 21 | my $tx = __PACKAGE__->_build_text_xslate( | 
| 84 |  |  |  |  |  |  | root_dir  => $self->root_dir, | 
| 85 |  |  |  |  |  |  | cache     => $XSLATE_CACHE, | 
| 86 |  |  |  |  |  |  | cache_dir => $XSLATE_CACHE_DIR, | 
| 87 |  |  |  |  |  |  | ); | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 3 |  |  |  |  | 24 | my $json_serializer = __PACKAGE__->_build_json_serializer(); | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | sub { | 
| 92 | 19 |  |  | 19 |  | 103788 | my $env = shift; | 
| 93 | 19 |  |  |  |  | 47 | $Kossy::Response::SECURITY_HEADER = $security_header_local; | 
| 94 |  |  |  |  |  |  | try { | 
| 95 | 19 | 100 |  |  |  | 837 | my $header = bless { | 
| 96 |  |  |  |  |  |  | 'content-type' => 'text/html; charset=UTF-8', | 
| 97 |  |  |  |  |  |  | $security_header_local ? ('x-frame-options' => 'DENY') : (), | 
| 98 |  |  |  |  |  |  | }, 'HTTP::Headers::Fast'; | 
| 99 | 19 |  |  |  |  | 156 | my $c = Kossy::Connection->new({ | 
| 100 |  |  |  |  |  |  | tx => $tx, | 
| 101 |  |  |  |  |  |  | req => Kossy::Request->new($env), | 
| 102 |  |  |  |  |  |  | res => Kossy::Response->new(200, $header), | 
| 103 |  |  |  |  |  |  | stash => {}, | 
| 104 |  |  |  |  |  |  | json_serializer => $json_serializer, | 
| 105 |  |  |  |  |  |  | }); | 
| 106 | 19 |  |  |  |  | 319 | my $method = uc($env->{REQUEST_METHOD}); | 
| 107 | 19 |  |  |  |  | 68 | my $cache_key = $method . '-' . $env->{PATH_INFO}; | 
| 108 |  |  |  |  |  |  | my ($match,$args) = try { | 
| 109 | 19 | 100 |  |  |  | 785 | if ( exists $match_cache{$cache_key} ) { | 
| 110 | 2 |  |  |  |  | 6 | return @{$match_cache{$cache_key}}; | 
|  | 2 |  |  |  |  | 8 |  | 
| 111 |  |  |  |  |  |  | } | 
| 112 | 17 |  |  |  |  | 303 | my $path_info = Encode::decode_utf8( $env->{PATH_INFO},  Encode::FB_CROAK | Encode::LEAVE_SRC ); | 
| 113 | 17 |  |  |  |  | 230 | my @match = $router->match($path_info); | 
| 114 | 17 | 100 |  |  |  | 6105 | if ( !@match ) { | 
| 115 | 2 |  |  |  |  | 11 | $c->halt(404); | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 15 | 100 |  |  |  | 65 | if ( !exists $match[0]->{$method}) { | 
| 119 | 2 |  |  |  |  | 11 | $c->halt(405); | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | $match_cache{$cache_key} = [$match[0]->{$method},$match[1]] | 
| 122 | 13 | 100 |  |  |  | 21 | if ! scalar keys %{$match[1]}; | 
|  | 13 |  |  |  |  | 75 |  | 
| 123 | 13 |  |  |  |  | 55 | return ($match[0]->{$method},$match[1]); | 
| 124 |  |  |  |  |  |  | } catch { | 
| 125 | 4 | 50 | 33 |  |  | 85 | if ( ref $_ && ref $_ eq 'Kossy::Exception' ) { | 
| 126 | 4 |  |  |  |  | 21 | die $_; #rethrow | 
| 127 |  |  |  |  |  |  | } | 
| 128 | 0 |  |  |  |  | 0 | $c->halt(400,'unexpected character in request'); | 
| 129 | 19 |  |  |  |  | 168 | }; | 
| 130 |  |  |  |  |  |  |  | 
| 131 | 15 |  |  |  |  | 413 | my $code = $match->{__action__}; | 
| 132 | 15 |  | 50 |  |  | 55 | my $filters = $match->{__filter__} || []; | 
| 133 | 15 | 50 | 33 |  |  | 99 | if ( $] == 5.020000 || $] == 5.020100 ) { | 
| 134 |  |  |  |  |  |  | # workaround for 5.20.0 or 5.20.1 https://github.com/kazeburo/Kossy/pull/10 | 
| 135 | 0 |  |  |  |  | 0 | my %args = map { $_ => Encode::decode_utf8($args->{$_}) } keys %$args; | 
|  | 0 |  |  |  |  | 0 |  | 
| 136 | 0 |  |  |  |  | 0 | $c->args(\%args); | 
| 137 |  |  |  |  |  |  | } else { | 
| 138 | 15 |  |  |  |  | 93 | $c->args({%$args}); | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  | my $app = sub { | 
| 141 | 15 |  |  |  |  | 39 | my ($self, $c) = @_; | 
| 142 | 15 |  |  |  |  | 29 | my $response; | 
| 143 | 15 |  |  |  |  | 67 | my $res = $code->($self, $c); | 
| 144 | 15 | 50 |  |  |  | 600 | Carp::croak "Undefined Response" if ! defined $res; | 
| 145 | 15 |  | 100 |  |  | 70 | my $res_t = ref($res) || ''; | 
| 146 | 15 | 100 |  |  |  | 90 | if ( $res_t eq 'Kossy::Response' ) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 147 | 2 |  |  |  |  | 7 | $response = $res; | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  | elsif ( $res_t eq 'Plack::Response' ) { | 
| 150 | 0 |  |  |  |  | 0 | $response = bless $res, 'Kossy::Response'; | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  | elsif ( $res_t eq 'ARRAY' ) { | 
| 153 | 0 |  |  |  |  | 0 | $response = Kossy::Response->new(@$res); | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  | elsif ( !$res_t ) { | 
| 156 | 13 |  |  |  |  | 35 | $c->res->body($res); | 
| 157 | 13 |  |  |  |  | 105 | $response = $c->res; | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  | else { | 
| 160 | 0 |  |  |  |  | 0 | Carp::croak sprintf "Unknown Response: %s", $res_t; | 
| 161 |  |  |  |  |  |  | } | 
| 162 | 15 |  |  |  |  | 101 | $response; | 
| 163 | 15 |  |  |  |  | 176 | }; | 
| 164 |  |  |  |  |  |  |  | 
| 165 | 15 |  |  |  |  | 53 | for my $filter ( reverse @$filters ) { | 
| 166 | 0 |  |  |  |  | 0 | $app = $self->_wrap_filter($filter,$app); | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  | # do all | 
| 169 | 15 |  |  |  |  | 28 | local $Kossy::Response::DIRECT; | 
| 170 | 15 |  |  |  |  | 34 | $app->($self, $c)->finalize; | 
| 171 |  |  |  |  |  |  | } catch { | 
| 172 | 4 | 50 | 33 |  |  | 115 | if ( ref $_ && ref $_ eq 'Kossy::Exception' ) { | 
| 173 | 4 |  |  |  |  | 18 | return $_->response; | 
| 174 |  |  |  |  |  |  | } | 
| 175 | 0 |  |  |  |  | 0 | die $_; | 
| 176 | 19 |  |  |  |  | 199 | }; | 
| 177 | 3 |  |  |  |  | 28 | }; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | sub _build_text_xslate { | 
| 181 | 5 |  |  | 5 |  | 7634 | my ($class, %args) = @_; | 
| 182 | 5 |  |  |  |  | 23 | my ($root_dir, $cache, $cache_dir) = @args{qw/root_dir cache cache_dir/}; | 
| 183 |  |  |  |  |  |  |  | 
| 184 | 5 |  |  |  |  | 65 | my $fif = HTML::FillInForm::Lite->new(); | 
| 185 |  |  |  |  |  |  | my $tx = Text::Xslate->new( | 
| 186 |  |  |  |  |  |  | path => [ $root_dir . '/views' ], | 
| 187 |  |  |  |  |  |  | input_layer => ':utf8', | 
| 188 |  |  |  |  |  |  | module => ['Text::Xslate::Bridge::TT2Like','Number::Format' => [':subs']], | 
| 189 |  |  |  |  |  |  | function => { | 
| 190 |  |  |  |  |  |  | fillinform => sub { | 
| 191 | 0 |  |  | 0 |  | 0 | my $q = shift; | 
| 192 |  |  |  |  |  |  | return sub { | 
| 193 | 0 |  |  |  |  | 0 | my ($html) = @_; | 
| 194 | 0 |  |  |  |  | 0 | return Text::Xslate::mark_raw( $fif->fill( \$html, $q ) ); | 
| 195 |  |  |  |  |  |  | } | 
| 196 | 0 |  |  |  |  | 0 | } | 
| 197 |  |  |  |  |  |  | }, | 
| 198 | 5 | 100 |  |  |  | 277 | cache => $cache, | 
| 199 |  |  |  |  |  |  | defined $cache_dir ? ( cache_dir => $cache_dir ) : (), | 
| 200 |  |  |  |  |  |  | ); | 
| 201 | 5 |  |  |  |  | 127347 | return $tx | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | sub _build_json_serializer { | 
| 205 | 7 |  |  | 7 |  | 11200 | my $class = shift; | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 7 | 100 |  |  |  | 27 | if (defined $JSON_SERIALIZER) { | 
| 208 | 4 | 100 | 100 |  |  | 57 | if (Scalar::Util::blessed($JSON_SERIALIZER) && $JSON_SERIALIZER->can('encode')) { | 
| 209 | 2 |  |  |  |  | 8 | return $JSON_SERIALIZER | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  | else { | 
| 212 | 2 |  |  |  |  | 312 | Carp::croak '$Kossy::JSON_SERIALIZER must have `encode` method'; | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | # default case | 
| 217 | 3 |  |  |  |  | 101 | return JSON->new()->allow_blessed(1)->convert_blessed(1)->ascii(1); | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | my $_ROUTER={}; | 
| 222 |  |  |  |  |  |  | sub _router { | 
| 223 | 75 |  |  | 75 |  | 900 | my $klass = shift; | 
| 224 | 75 | 100 |  |  |  | 121 | my $class = ref $klass ? ref $klass : $klass; | 
| 225 | 75 | 100 |  |  |  | 152 | if ( !$_ROUTER->{$class} ) { | 
| 226 | 4 |  |  |  |  | 10 | $_ROUTER->{$class} = {}; | 
| 227 |  |  |  |  |  |  | } | 
| 228 | 75 |  |  |  |  | 229 | $_ROUTER->{$class}; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | sub _connect { | 
| 232 | 28 |  |  | 28 |  | 43 | my $class = shift; | 
| 233 | 28 |  |  |  |  | 49 | my ( $methods, $pattern, $filter, $code ) = @_; | 
| 234 | 28 | 50 |  |  |  | 59 | $methods = ref($methods) ? $methods : [$methods]; | 
| 235 | 28 | 50 |  |  |  | 50 | if (!$code) { | 
| 236 | 28 |  |  |  |  | 36 | $code = $filter; | 
| 237 | 28 |  |  |  |  | 38 | $filter = []; | 
| 238 |  |  |  |  |  |  | } | 
| 239 | 28 |  |  |  |  | 45 | for my $method ( @$methods ) { | 
| 240 | 53 |  |  |  |  | 145 | $class->_router->{$pattern}->{$method} = { | 
| 241 |  |  |  |  |  |  | __action__ => $code, | 
| 242 |  |  |  |  |  |  | __filter__ => $filter | 
| 243 |  |  |  |  |  |  | }; | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | sub get { | 
| 248 | 22 |  |  | 22 | 1 | 2279 | my $class = caller; | 
| 249 | 22 |  |  |  |  | 64 | $class->_connect( ['GET','HEAD'], @_  ); | 
| 250 |  |  |  |  |  |  | } | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | sub post { | 
| 253 | 3 |  |  | 3 | 1 | 17 | my $class = caller; | 
| 254 | 3 |  |  |  |  | 25 | $class->_connect( ['POST'], @_  ); | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | sub router { | 
| 258 | 3 |  |  | 3 | 1 | 26 | my $class = caller; | 
| 259 | 3 |  |  |  |  | 12 | $class->_connect( @_  ); | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | my $_FILTER={}; | 
| 263 |  |  |  |  |  |  | sub filter { | 
| 264 | 0 |  |  | 0 | 1 |  | my $class = caller; | 
| 265 | 0 | 0 |  |  |  |  | if ( !$_FILTER->{$class} ) { | 
| 266 | 0 |  |  |  |  |  | $_FILTER->{$class} = {}; | 
| 267 |  |  |  |  |  |  | } | 
| 268 | 0 | 0 |  |  |  |  | if ( @_ ) { | 
| 269 | 0 |  |  |  |  |  | $_FILTER->{$class}->{$_[0]} = $_[1]; | 
| 270 |  |  |  |  |  |  | } | 
| 271 | 0 |  |  |  |  |  | $_FILTER->{$class}; | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | sub _wrap_filter { | 
| 275 | 0 |  |  | 0 |  |  | my $klass = shift; | 
| 276 | 0 | 0 |  |  |  |  | my $class = ref $klass ? ref $klass : $klass; | 
| 277 | 0 | 0 |  |  |  |  | if ( !$_FILTER->{$class} ) { | 
| 278 | 0 |  |  |  |  |  | $_FILTER->{$class} = {}; | 
| 279 |  |  |  |  |  |  | } | 
| 280 | 0 |  |  |  |  |  | my ($filter,$app) = @_; | 
| 281 | 0 |  |  |  |  |  | my $filter_subref = $_FILTER->{$class}->{$filter}; | 
| 282 | 0 | 0 |  |  |  |  | Carp::croak sprintf("Filter:%s is not exists", $filter) unless $filter_subref; | 
| 283 | 0 |  |  |  |  |  | return $filter_subref->($app); | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | 1; | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | __END__ |