File Coverage

blib/lib/Plack/Middleware/FormatOutput.pm
Criterion Covered Total %
statement 66 77 85.7
branch 15 32 46.8
condition 6 11 54.5
subroutine 17 18 94.4
pod 4 4 100.0
total 108 142 76.0


line stmt bran cond sub pod time code
1             package Plack::Middleware::FormatOutput;
2              
3 3     3   101661 use 5.006;
  3         6  
4 3     3   11 use strict;
  3         2  
  3         56  
5 3     3   9 use warnings FATAL => 'all';
  3         4  
  3         87  
6              
7 3     3   383 use parent qw( Plack::Middleware );
  3         242  
  3         12  
8 3     3   11387 use Plack::Util;
  3         3  
  3         49  
9              
10 3     3   1195 use HTTP::Exception '4XX';
  3         10364  
  3         13  
11              
12 3     3   253331 use JSON::XS;
  3         11504  
  3         147  
13 3     3   74152 use YAML::Syck;
  3         4261  
  3         174  
14 3     3   40995 use URI::Escape::XS qw/decodeURIComponent/;
  3         6684  
  3         198  
15 3     3   1441 use Encode;
  3         19163  
  3         2173  
16             our $VERSION = '0.10'; # is set automagically with Milla
17              
18             $YAML::Syck::ImplicitUnicode = 1;
19              
20             ### Try load library
21             sub _try_load {
22 2     2   4 my $mod = shift;
23 2 50   2   171 eval("use $mod; 1") ? return 1 : return 0;
  2         417  
  0            
  0            
24             }
25              
26             ### Set default mime types
27             my $MIME_TYPES = {
28             'application/json' => sub { JSON::XS->new->utf8->allow_nonref->encode($_[0]) },
29             'text/yaml' => sub {
30             local $Data::Dumper::Indent=1; local $Data::Dumper::Quotekeys=0; local $Data::Dumper::Terse=1; local $Data::Dumper::Sortkeys=1;
31             Dump($_[0])
32             },
33             'text/plain' => sub {
34             local $Data::Dumper::Indent=1; local $Data::Dumper::Quotekeys=0; local $Data::Dumper::Terse=1; local $Data::Dumper::Sortkeys=1;
35             Dump($_[0])
36             },
37             'text/html' => sub {
38             my ($data, $self, $env, $header) = @_;
39             if ($self->htmlvis){
40             my $ret = $self->htmlvis->html($data, $env, $header); #struct, env
41             return Encode::encode_utf8($ret) if $ret;
42             }
43             return JSON::XS->new->utf8->allow_nonref->encode($data); # Just show content
44             }
45             };
46              
47             sub prepare_app {
48 2     2 1 169865 my $self = shift;
49              
50             ### Check mime types
51 2         2 foreach my $par (keys %{$self->{mime_type}}){
  2         64  
52 1 50       6 delete $self->{mime_type}{$par} if ref $self->{mime_type}{$par} ne 'CODE';
53             }
54              
55             ### Add default MimeTypes
56 2         4 foreach my $par (keys %{$MIME_TYPES}){
  2         6  
57 8 50       22 $self->{mime_type}{$par} = $MIME_TYPES->{$par} unless exists $self->{mime_type}{$par};
58             }
59              
60             ### Add htmlvis
61 2 50       7 if (_try_load('Rest::HtmlVis')){
62 0 0       0 my $params = $self->{htmlvis} if exists $self->{htmlvis};
63 0         0 $self->{htmlvis} = Rest::HtmlVis->new($params);
64             }
65             }
66              
67             sub mime_type {
68 13     13 1 37 return $_[0]->{mime_type};
69             }
70              
71             sub htmlvis {
72 0     0 1 0 return $_[0]->{htmlvis};
73             }
74              
75             sub call {
76 7     7 1 30044 my($self, $env) = @_;
77              
78             ### Run app
79 7         28 my $res = $self->app->($env);
80              
81             ### Get accept from request header
82 7         509 my $accept = _getAccept($self, $env);
83 7 50       19 return $res unless $accept;
84              
85             ### Return handler that manage response
86             return Plack::Util::response_cb($res, sub {
87 7     7   47 my $res = shift;
88 7 100 66     15 if ( !Plack::Util::status_with_no_entity_body( $res->[0] ) && defined $res->[2] ){
    50          
89              
90             ### File handler streaming body
91 6 50       54 if ( Plack::Util::is_real_fh($res->[2]) ) {
92             return
93 0         0 }
94              
95             ### Set header
96 6 50 33     146 if ($res->[1] && @{$res->[1]}){
  6         17  
97 0         0 Plack::Util::header_set($res->[1], 'Content-Type', $accept);
98             }else{
99 6         12 $res->[1] = ['Content-Type', $accept];
100             }
101              
102             ### Convert data
103 6         11 $res->[2] = [$self->mime_type->{$accept}->($res->[2], $self, $env, $res->[1])];
104             }elsif(! defined $res->[2]){
105 1         13 $res->[2] = []; # backward compatibility
106             }
107 7         146 return $res;
108 7         28 });
109             }
110              
111             sub _getAccept {
112 7     7   7 my ($self, $env) = @_;
113              
114             # Get accept from url
115 7         7 my $accept;
116             # We parse this with reqular because we need this as quick as possible
117 7         29 my $query_string = decodeURIComponent($env->{QUERY_STRING});
118 7 50       20 if ( $query_string=~/format=([\w\/\+]*)/){
119 0 0       0 if (exists $self->mime_type->{$1}){
120 0         0 $accept = $1;
121             }
122             };
123              
124             # Set accept by http header
125 7 50 33     24 if (!$accept && $env->{HTTP_ACCEPT}){
126 7         20 foreach (split(/,/, $env->{HTTP_ACCEPT})){
127 7 50       11 if ($_ eq '*/*'){
128 0 0       0 $accept = exists $self->mime_type->{'text/html'} ? 'text/html' : undef;
129 0         0 last;
130             }
131 7 100       14 next unless exists $self->mime_type->{$_};
132 6         7 $accept = $_;
133 6         16 last;
134             }
135             }
136              
137 7   100     20 return ($accept||'application/json');
138             }
139              
140             1;
141             __END__