File Coverage

blib/lib/Mojolicious/Plugin/FastHelpers.pm
Criterion Covered Total %
statement 49 52 94.2
branch 13 16 81.2
condition 4 7 57.1
subroutine 139 142 97.8
pod 1 1 100.0
total 206 218 94.5


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::FastHelpers;
2 2     2   68718 use Mojo::Base 'Mojolicious::Plugin';
  2         4  
  2         11  
3              
4 2     2   263 use Mojo::Util 'monkey_patch';
  2         6  
  2         113  
5              
6 2   50 2   11 use constant DEBUG => $ENV{MOJO_FASTHELPERS_DEBUG} || 0;
  2         6  
  2         943  
7              
8             our $VERSION = '0.02';
9              
10             sub register {
11 2     2 1 62 my ($self, $app, $config) = @_;
12              
13 2         8 $self->_add_helper_classes;
14 2         11 $self->_monkey_patch_add_helper($app);
15             }
16              
17             sub _monkey_patch_add_helper {
18 2     2   5 my ($self, $app) = @_;
19 2         7 my $renderer = $app->renderer;
20              
21             # Add any helper that has been added already
22 2         14 _add_helper_method($_) for sort map { (split /\./, $_)[0] } keys %{$renderer->helpers};
  139         283  
  2         5  
23              
24 2         35 state $patched = {};
25 2 50       42 return if $patched->{ref($renderer)}++;
26              
27             # Add new helper methods when calling $app->helper(...)
28 2         19 my $orig = $renderer->can('add_helper');
29             monkey_patch $renderer => add_helper => sub {
30 0     0   0 my ($renderer, $name) = (shift, shift);
        0      
31 0         0 _add_helper_method($name);
32 0         0 $orig->($renderer, $name, @_);
33 2         11 };
34             }
35              
36             sub _add_helper_classes {
37 2     2   4 my $self = shift;
38              
39 2         5 for my $class (qw(Mojolicious Mojolicious::Controller)) {
40 4         11 my $helper_class = "${class}::_FastHelpers";
41 4 50       31 next if UNIVERSAL::isa($class, $helper_class);
42 4 50       215 eval "package $helper_class;1" or die $@;
43              
44             monkey_patch $class => can => sub {
45 8     8   82146 my ($self, $name, @rest) = @_;
        8      
        8      
46 8 100       121 return undef unless my $can = $self->SUPER::can($name, @rest);
47 5 100 100     74 return undef if $can eq ($helper_class->can($name) // ''); # Hiding helper methods from can()
48 3         14 return $can;
49 4         31 };
50              
51 2     2   14 no strict 'refs';
  2         4  
  2         630  
52 4         73 unshift @{"${class}::ISA"}, $helper_class;
  4         98  
53             }
54             }
55              
56             sub _add_helper_method {
57 139     139   1786 my $name = shift;
58 139 100       573 return if Mojolicious::_FastHelpers->can($name); # No need to add it again
59              
60             monkey_patch 'Mojolicious::_FastHelpers' => $name => sub {
61 2     2   3162 my $app = shift;
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        0      
62 2 100       7 Carp::croak qq/Can't locate object method "$name" via package "@{[ref $app]}"/
  1         359  
63             unless my $helper = $app->renderer->get_helper($name);
64 1         32 return $app->build_controller->$helper(@_);
65 125         614 };
66              
67             monkey_patch 'Mojolicious::Controller::_FastHelpers' => $name => sub {
68 2     2   1378 my $c = shift;
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
        2      
69 2   33     14 my $p = $c->{_FastHelpers} ||= $c->app->renderer->get_helper('')->($c);
70 2 100       5058 Carp::croak qq/Can't locate object method "$name" via package "@{[ref $c]}"/ unless $p->can($name);
  1         88  
71 1         5 return $p->$name(@_);
72 125         1821 };
73             }
74              
75             1;
76              
77             =encoding utf8
78              
79             =head1 NAME
80              
81             Mojolicious::Plugin::FastHelpers - Faster helpers for your Mojolicious application
82              
83             =head1 SYNOPSIS
84              
85             =head2 Lite app
86              
87             use Mojolicious::Lite;
88             plugin "FastHelpers";
89             app->start;
90              
91             =head1 DESCRIPTION
92              
93             L is a L plugin which can speed
94             up your helpers, by avoiding C.
95              
96             It does this by injecting some new classes into the inheritance tree of
97             L and L.
98              
99             =head2 Warning
100              
101             This module must be considered EXPERIMENTAL. There might even be some security
102             isseus, so use it with care.
103              
104             It is not currently used in production anywhere I know of, and I'm not sure if
105             I can endorce such usage.
106              
107             This is strictly a (unproven) proof of concept.
108              
109             =head2 Benchmarks
110              
111             There is a benchmark test bundled with this distribution, if you want to run it
112             yourself, but here is a quick overview:
113              
114             $ TEST_BENCHMARK=200000 prove -vl t/benchmark.t
115             ok 1 - App::Normal 2.08688 wallclock secs ( 2.08 usr + 0.00 sys = 2.08 CPU) @ 96153.85/s (n=200000)
116             ok 2 - Ctrl::Normal 0.654221 wallclock secs ( 0.65 usr + 0.00 sys = 0.65 CPU) @ 307692.31/s (n=200000)
117             ok 3 - App::FastHelpers 1.62765 wallclock secs ( 1.62 usr + -0.01 sys = 1.61 CPU) @ 124223.60/s (n=200000)
118             ok 4 - Ctrl::FastHelpers 0.131942 wallclock secs ( 0.13 usr + 0.00 sys = 0.13 CPU) @ 1538461.54/s (n=200000)
119             ok 5 - App::FastHelpers (1.61s) is not slower than App::Normal (2.08s)
120             ok 6 - Ctrl::FastHelpers (0.13s) is not slower than Ctrl::Normal (0.65s)
121              
122             Rate App::Normal App::FastHelpers Ctrl::Normal Ctrl::FastHelpers
123             App::Normal 96154/s -- -23% -69% -94%
124             App::FastHelpers 124224/s 29% -- -60% -92%
125             Ctrl::Normal 307692/s 220% 148% -- -80%
126             Ctrl::FastHelpers 1538462/s 1500% 1138% 400% --
127              
128             =head1 METHODS
129              
130             =head2 register
131              
132             Will create new classes for your application and
133             L, and monkey patch in all the helpers.
134              
135             =head1 AUTHOR
136              
137             Jan Henning Thorsen
138              
139             =head1 COPYRIGHT AND LICENSE
140              
141             Copyright (C) 2018, Jan Henning Thorsen
142              
143             This program is free software, you can redistribute it and/or modify it under
144             the terms of the Artistic License version 2.0.
145              
146             =head1 SEE ALSO
147              
148             L
149              
150             =cut