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 |