/
Builder.pm
163 lines (117 loc) · 3.55 KB
/
Builder.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package Plack::Builder;
use strict;
use parent qw( Exporter );
our @EXPORT = qw( builder add enable enable_if mount );
use Carp ();
use Plack::App::URLMap;
use Plack::Middleware::Conditional; # TODO delayed load?
sub new {
my $class = shift;
bless { middlewares => [ ] }, $class;
}
sub add_middleware {
my($self, $mw, @args) = @_;
if (ref $mw ne 'CODE') {
my $mw_class = Plack::Util::load_class($mw, 'Plack::Middleware');
$mw = sub { $mw_class->wrap($_[0], @args) };
}
push @{$self->{middlewares}}, $mw;
}
sub add_middleware_if {
my($self, $cond, $mw, @args) = @_;
if (ref $mw ne 'CODE') {
my $mw_class = Plack::Util::load_class($mw, 'Plack::Middleware');
$mw = sub { $mw_class->wrap($_[0], @args) };
}
push @{$self->{middlewares}}, sub {
Plack::Middleware::Conditional->wrap($_[0], condition => $cond, builder => $mw);
};
}
# do you want remove_middleware() etc.?
sub to_app {
my($self, $app) = @_;
for my $mw (reverse @{$self->{middlewares}}) {
$app = $mw->($app);
}
$app;
}
# DSL goes here
our $_add = our $_add_if = our $_mount = sub {
Carp::croak("enable/mount should be called inside builder {} block");
};
sub add { Carp::carp("add is deprecated. Use 'enable'"); $_add->(@_) }
sub enable { $_add->(@_) }
sub enable_if(&$@) { $_add_if->(@_) }
sub mount { $_mount->(@_) }
sub builder(&) {
my $block = shift;
my $self = __PACKAGE__->new;
my $urlmap = Plack::App::URLMap->new;
local $_mount = sub {
$urlmap->map(@_);
$urlmap;
};
local $_add = sub {
$self->add_middleware(@_);
};
local $_add_if = sub {
$self->add_middleware_if(@_);
};
my $app = $block->();
$self->to_app($app);
}
1;
__END__
=head1 NAME
Plack::Builder - OO and DSL to enable Plack Middlewares
=head1 SYNOPSIS
# in .psgi
use Plack::Builder;
my $app = sub { ... };
builder {
enable "Plack::Middleware::Foo";
enable "Plack::Middleware::Bar", opt => "val";
enable "Plack::Middleware::Baz";
$app;
};
# use URLMap
builder {
mount "/foo" => builder {
enable "Plack::Middleware::Foo";
$app;
};
mount "/bar" => $app2;
mount "http://example.com/" => builder { $app3 };
};
=head1 DESCRIPTION
Plack::Builder gives you a quick domain specific language (DSL) to
wrap your application with Plack::Middleware subclasses. The
middleware you're trying to use should use L<Plack::Middleware> as a
base class to use this DSL, inspired by Rack::Builder.
Whenever you call C<add> on any middleware, the middleware app is
pushed to the stack inside the builder, and then reversed when it
actually creates a wrapped application handler, so:
builder {
enable "Plack::Middleware::Foo";
enable "Plack::Middleware::Bar", opt => "val";
$app;
};
is syntactically equal to:
$app = Plack::Middleware::Bar->wrap($app, opt => "val");
$app = Plack::Middleware::Foo->wrap($app);
In other words, you're supposed to C<add> middleware from outer to inner.
=head1 URLMap support
Plack::Builder has a native support for L<Plack::App::URLMap> with C<mount> method.
use Plack::Builder;
my $app = builder {
mount "/foo" => $app1;
mount "/bar" => builder {
enable "Plack::Middleware::Foo";
$app2;
};
};
See L<Plack::App::URLMap>'s C<map> method to see what they mean. With
builder you can't use C<map> as a DSL, for the obvious reason :)
=head1 SEE ALSO
L<Plack::Middleware> L<Plack::App::URLMap>
=cut