File Coverage

blib/lib/Mojolicious/Plugin/FastHelpers.pm
Criterion Covered Total %
statement 46 46 100.0
branch 12 14 85.7
condition 3 5 60.0
subroutine 73 74 98.6
pod 1 1 100.0
total 135 140 96.4


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::FastHelpers;
2 5     5   24024 use Mojo::Base 'Mojolicious::Plugin';
  5         12  
  5         24  
3              
4 4     4   946 use Mojo::Util qw(md5_sum monkey_patch);
  4         8  
  4         132  
5 3     3   788 use Mojolicious::Plugin::FastHelpers::Controller;
  3         8  
  3         30  
6              
7 3   50 3   110 use constant DEBUG => $ENV{MOJO_FASTHELPERS_DEBUG} || 0;
  3         7  
  3         1661  
8              
9             our $VERSION = '0.01';
10              
11             sub register {
12 5     5 1 15920 my ($self, $app, $config) = @_;
13 5         14 bless $app, _generate_class_with_helpers($app, $app);
14              
15 5 50       24 $app->controller_class(_generate_class_with_helpers($app->controller_class, $app))
16             unless $app->controller_class->isa('Mojolicious::Plugin::FastHelpers::Controller');
17             }
18              
19             sub _generate_class_with_helpers {
20 11     11   96 my ($target, $app) = @_;
21 11         16 my @helpers = sort keys %{$app->renderer->helpers};
  11         31  
22 11   66     486 my $superclass = ref $target || $target;
23 11         137 my $new_class = join '::', $superclass, '__FAST__', md5_sum(join '::', @helpers);
24              
25             # Already generated
26 11 100       82 return $new_class if $new_class->can('new');
27              
28 9         15 warn qq/[FastHelpers] $new_class->isa("$superclass")\n/ if DEBUG;
29 3 50   3   20 eval qq(package $new_class;use Mojo::Base "$superclass";1) or die $@;
  3     3   6  
  3     5   11  
  3     3   24  
  3         7  
  3         12  
  9         818  
30 9         29 my %hidden;
31 9         19 for my $name (keys %{$app->renderer->helpers}) {
  9         24  
32 625         11793 my ($method) = split /\./, $name;
33 625 100       3543 if ($new_class->can($method)) {
    100          
34 108         168 warn qq/[FastHelpers] $new_class->can("$method")\n/ if DEBUG;
35             }
36             elsif ($new_class->isa('Mojolicious::Controller')) {
37 276         483 $hidden{$name} = 1;
38 276         500 monkey_patch $new_class, $method => $app->renderer->get_helper($method);
39             }
40             else {
41 241         424 $hidden{$name} = 1;
42             monkey_patch $new_class, $method => sub {
43 2     2   1294 my $app = shift;
        2      
        4      
        6      
        6      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        4      
        0      
44 2         6 my $helper = $app->renderer->get_helper($method);
45 2         29 $app->build_controller->$helper(@_);
46 241         986 };
47             }
48             }
49              
50             # Speed up $c->app() by avoiding Mojolicious::Plugin::FastHelpers::Controller->app()
51 9 100       151 $new_class->attr('app') if $new_class->isa('Mojolicious::Controller');
52              
53             # Hide helpers
54 9 100   7   242 monkey_patch $new_class, can => sub { return $hidden{$_[1]} ? undef : UNIVERSAL::can($_[0], $_[1]) };
  5     7   857  
        7      
55              
56 9         132 return $new_class;
57             }
58              
59             1;
60              
61             =encoding utf8
62              
63             =head1 NAME
64              
65             Mojolicious::Plugin::FastHelpers - Faster helpers for your Mojolicious application
66              
67             =head1 SYNOPSIS
68              
69             =head2 Lite app
70              
71             use Mojolicious::Lite;
72              
73             # Add your helpers
74             helper "what.ever" => sub { return 42 };
75              
76             # Need to be called after all helpers have been added
77             plugin "FastHelpers";
78             app->start;
79              
80             =head2 Full app
81              
82             package MyApp;
83             use Mojo::Base "Mojolicious";
84              
85             sub startup {
86             my $app = shift;
87              
88             # Add your helpers
89             $app->helper(whatever => sub { rand });
90              
91             # Need to be called after all helpers have been added
92             $app->plugin("FastHelpers");
93             }
94              
95             package MyApp::Controller::Test;
96              
97             # Need to inherit from Mojolicious::Plugin::FastHelpers::Controller
98             # instead of Mojolicious::Controller
99             use Mojo::Base "Mojolicious::Plugin::FastHelpers::Controller";
100              
101             # Add actions as you would normally do
102             sub my_action {
103             my $c = shift;
104             ...
105             }
106              
107             =head1 DESCRIPTION
108              
109             L is a L plugin which can speed
110             up your helpers, by avoiding C.
111              
112             This module is currently EXPERIMENTAL. There might even be some security
113             isseus, so use it with care.
114              
115             =head2 Benchmarks
116              
117             There is a benchmark test bundled with this distribution, if you want to run it
118             yourself, but here is a quick overview:
119              
120             $ TEST_BENCHMARK=200000 prove -vl t/benchmark.t
121             ok 1 - fast_app 1.81834 wallclock secs ( 1.81 usr + 0.00 sys = 1.81 CPU) @ 110497.24/s (n=200000)
122             ok 2 - fast_controller 0.0192509 wallclock secs ( 0.02 usr + 0.00 sys = 0.02 CPU) @ 10000000.00/s (n=200000)
123             ok 3 - normal_app 2.02593 wallclock secs ( 2.02 usr + 0.00 sys = 2.02 CPU) @ 99009.90/s (n=200000)
124             ok 4 - normal_controller 0.619834 wallclock secs ( 0.62 usr + 0.00 sys = 0.62 CPU) @ 322580.65/s (n=200000)
125             ok 5 - fast_app (1.81s) is not slower than normal_app (2.02s)
126             ok 6 - fast_controller (0.02s) is not slower than normal_controller (0.62s)
127              
128             Rate normal_app fast_app normal_controller fast_controller
129             normal_app 99010/s -- -10% -69% -99%
130             fast_app 110497/s 12% -- -66% -99%
131             normal_controller 322581/s 226% 192% -- -97%
132             fast_controller 10000000/s 10000% 8950% 3000% --
133              
134             =head1 METHODS
135              
136             =head2 register
137              
138             Will create new classes for your application and
139             L, and monkey patch in all the helpers.
140              
141             =head1 AUTHOR
142              
143             Jan Henning Thorsen
144              
145             =head1 COPYRIGHT AND LICENSE
146              
147             Copyright (C) 2018, Jan Henning Thorsen
148              
149             This program is free software, you can redistribute it and/or modify it under
150             the terms of the Artistic License version 2.0.
151              
152             =head1 SEE ALSO
153              
154             L
155              
156             =cut