File Coverage

blib/lib/EntityModel/Web/PSGI.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package EntityModel::Web::PSGI;
2             # ABSTRACT: PSGI support for EntityModel::Web framework
3             use EntityModel::Class {
4 2         28 web => { type => 'EntityModel::Web' },
5             template => { type => 'EntityModel::Template' },
6 2     2   21993 };
  2         151254  
7 2     2   8041 use EntityModel::Web::Context;
  0            
  0            
8             use EntityModel::Web::Request;
9             use EntityModel::Web::Response;
10              
11             our $VERSION = '0.002';
12              
13             =head1 NAME
14              
15             EntityModel::Web::PSGI - serve L definitions through PSGI
16              
17             =head1 VERSION
18              
19             version 0.002
20              
21             =head1 SYNOPSIS
22              
23             # execute via plackup for example: plackup ./app.psgi
24             use EntityModel;
25             use EntityModel::Web::PSGI;
26             # Load a model which includes a web definition
27             my $model = EntityModel->new->add_plugin(Web => {
28             })->load_from(JSON => {
29             file => $ENV{ENTITYMODEL_JSON_MODEL}
30             });
31             # Create the PSGI wrapper
32             my $app = EntityModel::Web::PSGI->new;
33             # Set up web and template information
34             my ($web) = grep $_->isa('EntityModel::Web'), $model->plugin->list;
35             my $tmpl = EntityModel::Template->new(
36             include_path => $ENV{ENTITYMODEL_TEMPLATE_PATH}
37             );
38             $tmpl->process_template(\qq{[% PROCESS Main.tt2 %]});
39             $app->template($tmpl);
40             $app->web($web);
41             # Return our PSGI coderef
42             sub { $app->run_psgi(@_) };
43              
44             =head1 DESCRIPTION
45              
46             Preliminary support for running L definitions through
47             a PSGI interface.
48              
49             Expects the L L attribute to be set before any
50             requests are served, with at least one site definition if you want this
51             to do anything useful.
52              
53             Currently also proxies a L attribute, although expect this to
54             be deprecated in a future version (it really shouldn't be here).
55              
56             =head1 METHODS
57              
58             =head2 web
59              
60             Accessor for the L definition used for this PSGI instance.
61             Returns $self if used as a mutator:
62              
63             my $web;
64             $psgi->web($web)->psgi_request(...);
65              
66             =head2 template
67              
68             Accessor for the L definition used for this PSGI instance.
69             Returns $self if used as a mutator:
70              
71             my $template;
72             warn $psgi->template($template)->web;
73              
74             =cut
75              
76             =head2 run_psgi
77              
78             Process a PSGI request. Will be called by the L framework.
79              
80             =cut
81              
82             sub run_psgi {
83             my $self = shift;
84             my $env = shift;
85              
86             # Populate initial request values from $env
87             my $req = EntityModel::Web::Request->new(
88             method => lc($env->{REQUEST_METHOD} || ''),
89             path => $env->{REQUEST_URI},
90             version => $env->{SERVER_PROTOCOL},
91             host => $env->{SERVER_NAME},
92             port => $env->{SERVER_PORT},
93             # Convert HTTP_SOME_HEADER to some_header
94             header => [ map {; /^HTTP_(.*)/ ? +{ name => lc($1), value => $env->{$1} } : () } keys %$env ],
95             );
96              
97             # Create our context using this request information
98             my $ctx = EntityModel::Web::Context->new(
99             request => $req,
100             template => $self->template,
101             );
102             $ctx->find_page_and_data($self->web);
103              
104             # Early return if we had no page match
105             return $self->psgi_result(
106             $env,
107             404,
108             [],
109             'Not found'
110             ) unless $ctx->page;
111              
112             # Prepare for page rendering
113             $ctx->resolve_data;
114              
115             # Get a response object
116             my $resp = EntityModel::Web::Response->new(
117             context => $ctx,
118             page => $ctx->page,
119             request => $req,
120             );
121             # then ignore it and generate the body and a hardcoded 200 return
122             # FIXME use proper status code here and support streaming/async!
123             my $body = $ctx->process;
124             return $self->psgi_result(
125             $env,
126             200,
127             [ 'Content-Type' => 'text/html' ],
128             $body
129             );
130             }
131              
132             =head2 psgi_result
133              
134             Returns an appropriate PSGI response, either
135             an arrayref or a coderef depending on server
136             support for async/streaming.
137              
138             =cut
139              
140             sub psgi_result {
141             my $self = shift;
142             my ($env, $rslt, $hdr, $body) = @_;
143             logInfo("Streaming: %s", $env->{'psgi.streaming'} ? 'yes' : 'no');
144             return [ $rslt, $hdr, [ $body ] ] unless $env->{'psgi.streaming'};
145              
146             return sub {
147             my $responder = shift;
148             my $writer = $responder->([
149             $rslt,
150             $hdr,
151             ]);
152             $writer->write($body);
153             $writer->close;
154             };
155             }
156              
157             1;
158              
159             __END__