File Coverage

blib/lib/Mojolicious/Plugin/Narada.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Narada;
2              
3 1     1   13614 use strict;
  1         1  
  1         30  
4 1     1   3 use warnings;
  1         1  
  1         22  
5              
6 1     1   489 use version; our $VERSION = qv('0.3.1'); # REMINDER: update Changes
  1         1243  
  1         4  
7              
8             # REMINDER: update dependencies in Build.PL
9 1     1   462 use Mojo::Base 'Mojolicious::Plugin';
  1         6443  
  1         5  
10              
11 1     1   1118 use MojoX::Log::Fast;
  1         56037  
  1         12  
12 1     1   217 use Narada::Config qw( get_config get_config_line );
  0            
  0            
13             use Narada::Lock qw( unlock );
14              
15             my ($Log, $Ident);
16              
17              
18             sub register {
19             my ($self, $app, $conf) = @_;
20              
21             $Log = MojoX::Log::Fast->new($conf->{log});
22             $Ident = $Log->ident();
23              
24             # Replace default logger with Log::Fast.
25             $app->log($Log);
26              
27             # Load Mojo-specific config files.
28             if ($app->can('secrets')) {
29             $app->secrets([split /\n/ms, get_config('cookie.secret')]);
30             } else {
31             $app->secret(get_config_line('cookie.secret'));
32             }
33             $app->config(hypnotoad => {
34             listen => [split /\n/ms, get_config('hypnotoad/listen')],
35             proxy => get_config_line('hypnotoad/proxy'),
36             accepts => get_config_line('hypnotoad/accepts'),
37             workers => get_config_line('hypnotoad/workers'),
38             pid_file => 'var/hypnotoad.pid',
39             });
40              
41             # * Fix url->path and url->base->path.
42             # * Set correct ident while handler runs.
43             # * unlock() if handler died.
44             my $realbase = Mojo::Path->new( get_config_line('basepath') )
45             ->trailing_slash(0)
46             ->leading_slash(1)
47             ->to_string;
48             $app->hook(around_dispatch => sub {
49             my ($next, $c) = @_;
50             my $url = $c->req->url;
51             my $base = $url->base->path;
52             my $path = $url->path;
53             if ($base eq q{} && $path =~ m{\A\Q$realbase\E(.*)\z}mso) {
54             $path->parse($1);
55             }
56             $base->parse($realbase);
57             $path->leading_slash(1);
58             $Log->ident($url->path);
59             my $err = eval { $next->(); 1 } ? undef : $@;
60             unlock();
61             die $err if defined $err; ## no critic(RequireCarping)
62             });
63              
64             $app->helper(proxy => \&_proxy);
65              
66             return;
67             }
68              
69             sub _proxy {
70             my ($this, $cb, @p) = @_;
71             my $__warn__ = $SIG{__WARN__};
72             my $ident = $Log->ident;
73             return $this->isa('Mojolicious')
74             # * Set correct ident while global event handler runs.
75             # * unlock() if global event handler died.
76             ? sub {
77             $Log->ident($Ident);
78             local $SIG{__WARN__} = $__warn__;
79             my $err = eval { $cb->($this, @p, @_); 1 } ? undef : $@;
80             unlock();
81             die $err if defined $err; ## no critic(RequireCarping)
82             }
83             # * Set correct ident while delayed handler runs.
84             # * unlock() if delayed handler died.
85             # * Finalize request with reply->exception() if delayed handler died.
86             : sub {
87             $Log->ident($ident);
88             local $SIG{__WARN__} = $__warn__;
89             my $err = eval { $cb->($this, @p, @_); 1 } ? undef : $@;
90             unlock();
91             $this->reply->exception($err) if defined $err; ## no critic(ProhibitPostfixControls)
92             };
93             }
94              
95              
96             1; # Magic true value required at end of module
97             __END__
98              
99             =encoding utf8
100              
101             =head1 NAME
102              
103             Mojolicious::Plugin::Narada - Narada configuration plugin
104              
105              
106             =head1 SYNOPSIS
107              
108             # Mojolicious
109             $self->plugin('Narada');
110             $self->plugin(Narada => (log => Log::Fast->global));
111              
112             # Mojolicious::Lite
113             plugin 'Narada';
114             plugin Narada => (log => Log::Fast->global);
115              
116             # Global timer
117             package MyApp;
118             sub startup {
119             my $app = shift;
120             Mojo::IOLoop->timer(0 => $app->proxy(sub { say 'Next tick.' }));
121             }
122              
123             # Request-related timer
124             package MyApp::MyController;
125             sub myaction {
126             my $c = shift;
127             $c->render_later;
128             Mojo::IOLoop->timer(2 => $c->proxy(sub {
129             $c->render(text => 'Delayed by 2 seconds!');
130             }));
131             }
132              
133             =head1 DESCRIPTION
134              
135             L<Mojolicious::Plugin::Narada> is a plugin that configure L<Mojolicious>
136             to work in L<Narada> project management environment.
137              
138             Also this plugin add helper C<proxy>, and you B<MUST>
139             use it to wrap all callbacks you setup for handling delayed events like
140             timers or I/O (both global in your app and related to requests in your
141             actions).
142              
143             There is also one feature unrelated to Narada - if callback started by any
144             action throw unhandled exception it will be sent to browser using same
145             C<< $c->reply->exception >> as it already works for actions without
146             delayed response.
147              
148             =over
149              
150             =item Logging
151              
152             L<Mojolicious> default L<Mojo::Log> replaced with L<MojoX::Log::Fast> to
153             support logging to project-local syslog daemon in addition to files.
154             In most cases it works as drop-in replacement and doesn't require any
155             modifications in user code.
156              
157             Also it set C<< $app->log->ident() >> to C<< $c->req->url->path >> to
158             ease log file analyse.
159              
160             =item Configuration
161              
162             You should manually add these lines to C<./you_app> starting script before
163             call to C<< Mojolicious::Commands->start_app() >>:
164              
165             use Narada::Config qw( get_config_line );
166             # mode should be set here because it's used before executing MyApp::startup()
167             local $ENV{MOJO_MODE} = get_config_line('mode');
168              
169             Config file C<config/cookie.secret> automatically loaded and used to
170             initialize C<< $app->secrets() >> (each line of file became separate
171             param).
172              
173             Config file C<config/basepath> automatically loaded and used to fix
174             C<< $c->req->url->base->path >> and C<< $c->req->url->path >> to
175             guarantee their consistency in any environment:
176              
177             =over
178              
179             =item * url->path doesn't contain base->path
180              
181             =item * url->path does have leading slash
182              
183             =item * url->base->path set to content of config/basepath
184              
185             =back
186              
187              
188             These config files automatically loaded from C<config/hypnotoad/*>
189             and used to initialize C<< $app->config(hypnotoad) >>:
190              
191             listen
192             proxy
193             accepts
194             workers
195              
196             Also hypnotoad configured to keep it lock/pid files in C<var/>.
197              
198             =item Locking
199              
200             C<unlock()> will be automatically called after all actions and callbacks,
201             even if they throw unhandled exception.
202              
203             =back
204              
205              
206             =head1 OPTIONS
207              
208             L<Mojolicious::Plugin::Narada> supports the following options.
209              
210             =head2 log
211              
212             plugin Narada => (log => Log::Fast->global);
213              
214             Value for L<MojoX::Log::Fast>->new().
215              
216              
217             =head1 METHODS
218              
219             L<Mojolicious::Plugin::Narada> inherits all methods from
220             L<Mojolicious::Plugin> and implements the following new ones.
221              
222             =head2 register
223              
224             $plugin->register(Mojolicious->new);
225             $plugin->register(Mojolicious->new, {log => Log::Fast->global});
226              
227             Register hooks in L<Mojolicious> application.
228              
229              
230             =head1 SEE ALSO
231              
232             L<Narada>, L<MojoX::Log::Fast>, L<Mojolicious>.
233              
234              
235             =head1 BUGS AND LIMITATIONS
236              
237             No bugs have been reported.
238              
239              
240             =head1 SUPPORT
241              
242             Please report any bugs or feature requests through the web interface at
243             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Mojolicious-Plugin-Narada>.
244             I will be notified, and then you'll automatically be notified of progress
245             on your bug as I make changes.
246              
247             You can also look for information at:
248              
249             =over
250              
251             =item * RT: CPAN's request tracker
252              
253             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Mojolicious-Plugin-Narada>
254              
255             =item * AnnoCPAN: Annotated CPAN documentation
256              
257             L<http://annocpan.org/dist/Mojolicious-Plugin-Narada>
258              
259             =item * CPAN Ratings
260              
261             L<http://cpanratings.perl.org/d/Mojolicious-Plugin-Narada>
262              
263             =item * Search CPAN
264              
265             L<http://search.cpan.org/dist/Mojolicious-Plugin-Narada/>
266              
267             =back
268              
269              
270             =head1 AUTHOR
271              
272             Alex Efros C<< <powerman@cpan.org> >>
273              
274              
275             =head1 LICENSE AND COPYRIGHT
276              
277             Copyright 2013,2014-2015 Alex Efros <powerman@cpan.org>.
278              
279             This program is distributed under the MIT (X11) License:
280             L<http://www.opensource.org/licenses/mit-license.php>
281              
282             Permission is hereby granted, free of charge, to any person
283             obtaining a copy of this software and associated documentation
284             files (the "Software"), to deal in the Software without
285             restriction, including without limitation the rights to use,
286             copy, modify, merge, publish, distribute, sublicense, and/or sell
287             copies of the Software, and to permit persons to whom the
288             Software is furnished to do so, subject to the following
289             conditions:
290              
291             The above copyright notice and this permission notice shall be
292             included in all copies or substantial portions of the Software.
293              
294             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
295             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
296             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
297             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
298             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
299             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
300             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
301             OTHER DEALINGS IN THE SOFTWARE.
302