File Coverage

blib/lib/Clustericious/Plugin/AutodataHandler.pm
Criterion Covered Total %
statement 72 81 88.8
branch 12 20 60.0
condition 17 28 60.7
subroutine 14 15 93.3
pod 1 1 100.0
total 116 145 80.0


line stmt bran cond sub pod time code
1             package Clustericious::Plugin::AutodataHandler;
2              
3 25     25   20207 use strict;
  25         65  
  25         669  
4 25     25   124 use warnings;
  25         51  
  25         641  
5 25     25   135 use base 'Mojolicious::Plugin';
  25         89  
  25         2923  
6 25     25   265 use Mojo::ByteStream 'b';
  25         53  
  25         1222  
7 25     25   220 use Clustericious::Log;
  25         119  
  25         175  
8 25     25   21446 use PerlX::Maybe qw( maybe );
  25         38162  
  25         1431  
9 25     25   1308 use Path::Class qw( dir );
  25         25232  
  25         21860  
10              
11             # ABSTRACT: Handle data types automatically
12             our $VERSION = '1.27'; # VERSION
13              
14              
15             sub _default_coders
16             {
17             my %coders =
18 372         727 map { $_ => 1 }
19 372         1059 map { $_ =~ s/\.pm$//; $_ }
  372         718  
20 372         1252 map { $_->basename }
21 372         24010 grep { ! $_->is_dir }
22 186         51094 map { $_->children( no_hidden => 1 ) }
23 784         16782 grep { -d $_ }
24 62     62   181 map { dir $_, 'Clustericious', 'Coder' } @INC;
  784         30318  
25 62         1463 [ keys %coders ];
26             }
27              
28             sub register
29             {
30 62     62 1 7101 my ($self, $app, $conf) = @_;
31              
32             my @coders = $app->isa('Clustericious::App')
33             ? $app->config->coders( default => __PACKAGE__->_default_coders )
34 62 100 33     565 : @{ $conf->{coders} // __PACKAGE__->_default_coders };
  2         16  
35            
36             my %types = (
37             'application/x-www-form-urlencoded' => {
38 2     2   59 decode => sub { my ($data, $c) = @_; $c->req->params->to_hash }
  2         7  
39             }
40 62         454 );
41 62         131 my %formats;
42            
43 62         148 foreach my $coder (@coders)
44             {
45 124         2693 require join('/', qw( Clustericious Coder ), "$coder.pm");
46 124         759 my $coder = join('::', qw( Clustericious Coder ), $coder)->coder;
47             $types{$coder->{type}} = {
48             maybe encode => $coder->{encode},
49             maybe decode => $coder->{decode},
50             },
51             $formats{$coder->{format}} = $coder->{type}
52 124 50       954 if $coder->{format};
53             }
54              
55 62   50     329 my $default_decode = $conf->{default_decode} // 'application/x-www-form-urlencoded';
56 62   50     241 my $default_encode = $conf->{default_encode} // 'application/json'; # TODO: not used
57              
58             my $find_type = sub {
59 35     35   260 my ($c) = @_;
60              
61 35         120 my $headers = $c->tx->req->content->headers;
62              
63 35   100     610 foreach my $type (map { /^([^;]*)/ } # get only stuff before ;
  45   100     1020  
64             split(',', $headers->header('Accept') || ''),
65             $headers->content_type || '')
66             {
67 37 100 100     196 return $type if $types{$type} and $types{$type}->{encode};
68             }
69              
70 23   100     94 my $format = $c->stash->{format} // 'json';
71 23 100       320 $format = 'json' unless $formats{$format};
72              
73 23         70 $formats{$format};
74 62         258 };
75              
76              
77             $app->renderer->add_handler('autodata' => sub {
78 35     35   4141 my ($r, $c, $output, $data) = @_;
79              
80 35         109 my $type = $find_type->($c);
81 35 50       133 LOGDIE "no encoder for $type" unless $types{$type}->{encode};
82 35         110 $$output = $types{$type}->{encode}->($c->stash("autodata"));
83 35         150 $c->tx->res->headers->content_type($type);
84 62         285 });
85              
86             my $parse_autodata = sub {
87 6     6   269 my ($c) = @_;
88              
89 6   33     24 my $content_type = $c->req->headers->content_type || $default_decode;
90 6 100       184 if ($content_type =~ /^([^;]+);/) {
91             # strip charset
92 1         5 $content_type = $1;
93             }
94 6   33     28 my $entry = $types{$content_type} || $types{$default_decode};
95              
96             # TODO: avoid passing $c in, only used by
97             # application/x-www-form-urlencoded above
98 6         19 $c->stash->{autodata} = $entry->{decode}->($c->req->body, $c);
99 62         1540 };
100              
101 62         216 $app->plugins->on( parse_autodata => $parse_autodata );
102              
103             $app->plugins->on( add_autodata_type => sub {
104 0     0   0 my($plugins, $args) = @_;
105              
106 0 0       0 LOGDIE "No extension provided" unless defined $args->{extension};
107 0         0 my $ext = $args->{extension};
108 0   0     0 my $mime = $args->{mime_type} // 'application/x-' . $ext;
109              
110 0         0 $formats{$ext} = $mime;
111            
112 0 0       0 if(defined $args->{encode}) {
113 0         0 $types{$mime}->{encode} = $args->{encode};
114             }
115            
116 0 0       0 if(defined $args->{decode}) {
117 0         0 $types{$mime}->{decode} = $args->{decode};
118             }
119 62         754 });
120              
121 62         766 $app->helper( parse_autodata => $parse_autodata );
122            
123             $app->hook(before_render => sub {
124 175     175   23538 my($c, $args) = @_;
125 175 100 100     491 $c->stash->{handler} = "autodata" if exists($c->stash->{autodata}) || exists($args->{autodata});
126 62         1239 });
127             }
128              
129             1;
130              
131             __END__
132              
133             =pod
134              
135             =encoding UTF-8
136              
137             =head1 NAME
138              
139             Clustericious::Plugin::AutodataHandler - Handle data types automatically
140              
141             =head1 VERSION
142              
143             version 1.27
144              
145             =head1 SYNOPSIS
146              
147             package YourApp::Routes;
148            
149             use Clustericious::RouteBuilder;
150            
151             get '/some/route' => sub {
152             my $c = shift;
153             $c->stash->{autodata} = { x => 1, y => 'hello, z => [1,2,3] };
154             };
155              
156             =head1 DESCRIPTION
157              
158             Adds a renderer that automatically serializes that "autodata" in the
159             stash into a format based on HTTP Accept and Content-Type headers.
160             Also adds a helper called C<parse_autodata> that handles incoming data by
161             Content-Type.
162              
163             Supports application/json, text/x-yaml and
164             application/x-www-form-urlencoded (in-bound only).
165              
166             When C<parse_autodata> is called from within a route like this:
167              
168             $self->parse_autodata;
169              
170             POST data is parsed according to the type in the 'Content-Type'
171             header with the data left in stash->{autodata}. It is also
172             returned by the above call.
173              
174             If a route leaves data in stash->{autodata}, it is rendered by this
175             handler, which chooses the type with the first acceptable type listed
176             in the Accept header, the Content-Type header, or the default. (By
177             default, the default is application/json, but you can override that
178             too).
179              
180             =head1 AUTHOR
181              
182             Original author: Brian Duggan
183              
184             Current maintainer: Graham Ollis E<lt>plicease@cpan.orgE<gt>
185              
186             Contributors:
187              
188             Curt Tilmes
189              
190             Yanick Champoux
191              
192             =head1 COPYRIGHT AND LICENSE
193              
194             This software is copyright (c) 2013 by NASA GSFC.
195              
196             This is free software; you can redistribute it and/or modify it under
197             the same terms as the Perl 5 programming language system itself.
198              
199             =cut