line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::ActionRole::Methods; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
378650
|
use Moose::Role; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
10
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.002'; |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
around 'dispatch', sub { |
8
|
|
|
|
|
|
|
my $orig = shift; |
9
|
|
|
|
|
|
|
my $self = shift; |
10
|
|
|
|
|
|
|
my $c = shift; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $return = $self->$orig($c, @_); |
13
|
|
|
|
|
|
|
my $rest_method = $self->name . "_" . uc( $c->request->method ); |
14
|
|
|
|
|
|
|
my $sub_return = $self->_dispatch_rest_method( $c, $rest_method ); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
return defined($sub_return) ? $sub_return : $return; |
17
|
|
|
|
|
|
|
}; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
around 'list_extra_info' => sub { |
20
|
|
|
|
|
|
|
my ($orig, $self, @args) = @_; |
21
|
|
|
|
|
|
|
my @allowed_methods = sort $self->get_allowed_methods($self->class,undef,$self->name); |
22
|
|
|
|
|
|
|
return +{ |
23
|
|
|
|
|
|
|
%{ $self->$orig(@args) }, |
24
|
|
|
|
|
|
|
HTTP_METHODS => \@allowed_methods, |
25
|
|
|
|
|
|
|
}; |
26
|
|
|
|
|
|
|
}; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub _dispatch_rest_method { |
29
|
5
|
|
|
5
|
|
11
|
my $self = shift; |
30
|
5
|
|
|
|
|
11
|
my $c = shift; |
31
|
5
|
|
|
|
|
10
|
my $rest_method = shift; |
32
|
|
|
|
|
|
|
|
33
|
5
|
|
|
|
|
153
|
my $req = $c->request; |
34
|
5
|
|
|
|
|
188
|
my $controller = $c->component( $self->class ); |
35
|
5
|
|
|
|
|
224
|
my ($code, $name); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Common case, for foo_GET etc |
38
|
5
|
100
|
|
|
|
34
|
if ( $code = $controller->action_for($rest_method) ) { |
|
|
100
|
|
|
|
|
|
39
|
2
|
|
|
|
|
583
|
return $c->forward( $code, $req->args ); # Forward to foo_GET if it's an action |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
elsif ($code = $controller->can($rest_method)) { |
42
|
1
|
|
|
|
|
227
|
$name = $rest_method; # Stash name and code to run 'foo_GET' like an action below. |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Generic handling for foo_* |
46
|
3
|
100
|
|
|
|
613
|
if (!$code) { |
47
|
|
|
|
|
|
|
my $code_action = { |
48
|
|
|
|
|
|
|
OPTIONS => sub { |
49
|
0
|
|
|
0
|
|
0
|
$name = $rest_method; |
50
|
0
|
|
|
|
|
0
|
$code = sub { $self->_return_options($self->name, @_) }; |
|
0
|
|
|
|
|
0
|
|
51
|
|
|
|
|
|
|
}, |
52
|
|
|
|
|
|
|
HEAD => sub { |
53
|
1
|
|
|
1
|
|
7
|
$rest_method =~ s{_HEAD$}{_GET}i; |
54
|
1
|
|
|
|
|
10
|
$self->_dispatch_rest_method($c, $rest_method); |
55
|
|
|
|
|
|
|
}, |
56
|
|
|
|
|
|
|
default => sub { |
57
|
|
|
|
|
|
|
# Otherwise, not implemented. |
58
|
1
|
|
|
1
|
|
30
|
$name = $self->name . "_not_implemented"; |
59
|
|
|
|
|
|
|
$code = $controller->can($name) # User method |
60
|
|
|
|
|
|
|
# Generic not implemented |
61
|
1
|
|
50
|
|
|
14
|
|| sub { $self->_return_not_implemented($self->name, @_) }; |
62
|
|
|
|
|
|
|
}, |
63
|
2
|
|
|
|
|
34
|
}; |
64
|
2
|
|
|
|
|
72
|
my ( $http_method, $action_name ) = ( $rest_method, $self->name ); |
65
|
2
|
|
|
|
|
46
|
$http_method =~ s{\Q$action_name\E\_}{}; |
66
|
|
|
|
|
|
|
my $respond = ($code_action->{$http_method} |
67
|
2
|
|
66
|
|
|
16
|
|| $code_action->{'default'})->(); |
68
|
2
|
100
|
|
|
|
842
|
return $respond unless $name; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# localise stuff so we can dispatch the action 'as normal, but get |
72
|
|
|
|
|
|
|
# different stats shown, and different code run. |
73
|
|
|
|
|
|
|
# Also get the full path for the action, and make it look like a forward |
74
|
2
|
|
|
|
|
8
|
local $self->{code} = $code; |
75
|
2
|
|
|
|
|
63
|
my @name = split m{/}, $self->reverse; |
76
|
2
|
|
|
|
|
17
|
$name[-1] = $name; |
77
|
2
|
|
|
|
|
9
|
local $self->{reverse} = "-> " . join('/', @name); |
78
|
|
|
|
|
|
|
|
79
|
2
|
|
|
|
|
60
|
$c->execute( $self->class, $self, @{ $req->args } ); |
|
2
|
|
|
|
|
17
|
|
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub get_allowed_methods { |
83
|
0
|
|
|
0
|
1
|
|
my ( $self, $controller, $c, $name ) = @_; |
84
|
0
|
0
|
|
|
|
|
my $class = ref($controller) ? ref($controller) : $controller; |
85
|
|
|
|
|
|
|
my $methods = { |
86
|
0
|
0
|
|
|
|
|
map { /^$name\_(.+)$/ ? ( $1 => 1 ) : () } |
|
0
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
($class->meta->get_all_method_names ) |
88
|
|
|
|
|
|
|
}; |
89
|
0
|
0
|
|
|
|
|
$methods->{'HEAD'} = 1 if $methods->{'GET'}; |
90
|
0
|
|
|
|
|
|
delete $methods->{'not_implemented'}; |
91
|
0
|
|
|
|
|
|
return sort keys %$methods; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub _return_options { |
95
|
0
|
|
|
0
|
|
|
my ( $self, $method_name, $controller, $c) = @_; |
96
|
0
|
|
|
|
|
|
my @allowed = $self->get_allowed_methods($controller, $c, $method_name); |
97
|
0
|
|
|
|
|
|
$c->response->content_type('text/plain'); |
98
|
0
|
|
|
|
|
|
$c->response->status(200); |
99
|
0
|
|
|
|
|
|
$c->response->header( 'Allow' => \@allowed ); |
100
|
0
|
|
|
|
|
|
$c->response->body(q{}); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub _return_not_implemented { |
104
|
0
|
|
|
0
|
|
|
my ( $self, $method_name, $controller, $c ) = @_; |
105
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
|
my @allowed = $self->get_allowed_methods($controller, $c, $method_name); |
107
|
0
|
|
|
|
|
|
$c->response->content_type('text/plain'); |
108
|
0
|
|
|
|
|
|
$c->response->status(405); |
109
|
0
|
|
|
|
|
|
$c->response->header( 'Allow' => \@allowed ); |
110
|
0
|
|
|
|
|
|
$c->response->body( "Method " |
111
|
|
|
|
|
|
|
. $c->request->method |
112
|
|
|
|
|
|
|
. " not implemented for " |
113
|
|
|
|
|
|
|
. $c->uri_for( $method_name ) ); |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
1; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=head1 NAME |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Catalyst::ActionRole::Methods - Dispatch by HTTP Methods |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head1 SYNOPSIS |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
package MyApp::Controller::Example; |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
use Moose; |
127
|
|
|
|
|
|
|
use MooseX::MethodAttributes; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
extends 'Catalyst::Controller'; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub myaction :Chained(/) Does('Methods') CaptureArgs(1) { |
132
|
|
|
|
|
|
|
my ($self, $c, $arg) = @_; |
133
|
|
|
|
|
|
|
# When this action is matched, first execute this action's |
134
|
|
|
|
|
|
|
# body, then an action matching the HTTP method or the not |
135
|
|
|
|
|
|
|
# implemented one if needed. |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub myaction_GET :Action { |
139
|
|
|
|
|
|
|
my ($self, $c, $arg) = @_; |
140
|
|
|
|
|
|
|
# Note that if the 'parent' action has args or capture-args, those are |
141
|
|
|
|
|
|
|
# made available to a matching method action. |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub myaction_POST { |
145
|
|
|
|
|
|
|
my ($self, $c, $arg) = @_; |
146
|
|
|
|
|
|
|
# We match the subroutine name whether its an action or not. If you |
147
|
|
|
|
|
|
|
# make it an action, as in the _GET above, you are allowed to apply |
148
|
|
|
|
|
|
|
# action roles (which is the main advantage to this AFAIK). |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub myaction_not_implemented { |
152
|
|
|
|
|
|
|
my ($self, $c, $arg) = @_; |
153
|
|
|
|
|
|
|
# There's a sane default for this, but you can override as needed. |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub next_action_in_chain_1 :Chained(myaction) Args(0) { ... } |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub next_action_in_chain_2 :Chained(myaction) Args(0) { ... } |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head1 DESCRIPTION |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
This is a L<Moose::Role> version of the classic L<Catalyst::Action::REST> action |
165
|
|
|
|
|
|
|
class. The intention is to offer some of the popular functionality that comes |
166
|
|
|
|
|
|
|
with L<Catalyst::Action::REST> in a more modular, 'build what you need' package. |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
Bulk of this documentation and test cases derive from L<Catalyst::Action::REST> |
169
|
|
|
|
|
|
|
with the current author's gratitude. |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
This Action Role handles doing automatic method dispatching for requests. It |
172
|
|
|
|
|
|
|
takes a normal Catalyst action, and changes the dispatch to append an |
173
|
|
|
|
|
|
|
underscore and method name. First it will try dispatching to an action with |
174
|
|
|
|
|
|
|
the generated name, and failing that it will try to dispatch to a regular |
175
|
|
|
|
|
|
|
method. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub foo :Local :Does('Methods') { |
178
|
|
|
|
|
|
|
... do setup for HTTP method specific handlers ... |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub foo_GET { |
182
|
|
|
|
|
|
|
... do something for GET requests ... |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# alternatively use an Action |
186
|
|
|
|
|
|
|
sub foo_PUT : Action { |
187
|
|
|
|
|
|
|
... do something for PUT requests ... |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
For example, in the example above, calling GET on "/foo" would result in |
191
|
|
|
|
|
|
|
the foo_GET method being dispatched. |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
If a method is requested that is not implemented, this action will |
194
|
|
|
|
|
|
|
return a status 405 (Method Not Found). It will populate the "Allow" header |
195
|
|
|
|
|
|
|
with the list of implemented request methods. You can override this behavior |
196
|
|
|
|
|
|
|
by implementing a custom 405 handler like so: |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub foo_not_implemented { |
199
|
|
|
|
|
|
|
... handle not implemented methods ... |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
If you do not provide an _OPTIONS subroutine, we will automatically respond |
203
|
|
|
|
|
|
|
with a 200 OK. The "Allow" header will be populated with the list of |
204
|
|
|
|
|
|
|
implemented request methods. If you do not provide an _HEAD either, we will |
205
|
|
|
|
|
|
|
auto dispatch to the _GET one in case it exists. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=head1 VERSUS Catalyst::Action::REST |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
L<Catalyst::Action::REST> works fine doesn't it? Why offer a new approach? There's |
210
|
|
|
|
|
|
|
a few reasons: |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
First, when L<Catalyst::Action::REST> was written we did not have |
213
|
|
|
|
|
|
|
L<Moose> and the only way to augment functionality was via inheritance. Now that |
214
|
|
|
|
|
|
|
L<Moose> is common we instead say that it is typically better to use a L<Moose::Role> |
215
|
|
|
|
|
|
|
to augment a class function rather to use a subclass. The role approach is a smaller |
216
|
|
|
|
|
|
|
hammer and it plays nicer when you need to combine several roles to augment a class |
217
|
|
|
|
|
|
|
(as compared to multiple inheritance approaches.). This is why we brought support for |
218
|
|
|
|
|
|
|
action roles into core L<Catalyst::Controller> several years ago. Letting you have |
219
|
|
|
|
|
|
|
this functionality via a role should lead to more flexible systems that play nice |
220
|
|
|
|
|
|
|
with other roles. One nice side effect of this 'play nice with others' is that we |
221
|
|
|
|
|
|
|
were able to hook into the 'list_extra_info' method of the core action class so that |
222
|
|
|
|
|
|
|
you can now see in your developer mode debug output the matched http methods, for |
223
|
|
|
|
|
|
|
example: |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
.-------------------------------------+----------------------------------------. |
226
|
|
|
|
|
|
|
| Path Spec | Private | |
227
|
|
|
|
|
|
|
+-------------------------------------+----------------------------------------+ |
228
|
|
|
|
|
|
|
| /myaction/*/next_action_in_chain | GET, HEAD, POST /myaction (1) | |
229
|
|
|
|
|
|
|
| | => /next_action_in_chain (0) | |
230
|
|
|
|
|
|
|
'-------------------------------------+----------------------------------------' |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
This is not to say its never correct to use an action class, but now you have the |
233
|
|
|
|
|
|
|
choice. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Second, L<Catalyst::Action::REST> has the behavior as noted of altering the core |
236
|
|
|
|
|
|
|
L<Catalyst::Request> class. This might not be desired and has always struck the |
237
|
|
|
|
|
|
|
author as a bit too much side effect / action at a distance. |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
Last, L<Catalyst::Action::REST> is actually a larger distribution with a bunch of |
240
|
|
|
|
|
|
|
other features and dependencies that you might not want. The intention is to offer |
241
|
|
|
|
|
|
|
those bits of functionality as standalone, modern components and allow one to assemble |
242
|
|
|
|
|
|
|
the parts needed, as needed. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
This action role is for the most part a 1-1 port of the action class, with one minor |
245
|
|
|
|
|
|
|
change to reduce the dependency count. Additionally, it does not automatically |
246
|
|
|
|
|
|
|
apply the L<Catalyst::Request::REST> action class to your global L<Catalyst> |
247
|
|
|
|
|
|
|
action class. This feature is left off because its easy to set this yourself if |
248
|
|
|
|
|
|
|
desired via the global L<Catalyst> configuration and we want to follow and promote |
249
|
|
|
|
|
|
|
the idea of 'do one thing well and nothing surprising'. |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
B<NOTE> There is an additional minor change in how we handle return values from actions. In |
252
|
|
|
|
|
|
|
general L<Catalyst> does nothing with an action return value (unless in an auto action). |
253
|
|
|
|
|
|
|
However this might not always be the future case, and you might have used that return value |
254
|
|
|
|
|
|
|
for something in your custom code. In L<Catalyst::Action::REST> the return value was |
255
|
|
|
|
|
|
|
always the return of the dispatched sub action (if any). We tweaked this so that we use |
256
|
|
|
|
|
|
|
the sub action return value, BUT if that value is undefined, we use the parent action |
257
|
|
|
|
|
|
|
return value instead. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
We also dropped saying 'REST' when all we are doing is dispatching on HTTP method. |
260
|
|
|
|
|
|
|
Since the time that the first version of L<Catalysts::Action::REST> was released to |
261
|
|
|
|
|
|
|
CPAN our notion of what 'REST' means has greatly evolved so I think its correct to |
262
|
|
|
|
|
|
|
change the name to be functionality specific and to not confuse people that are new |
263
|
|
|
|
|
|
|
to the REST discipline. |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
This action role is intended to be used in all the places |
266
|
|
|
|
|
|
|
you used to use the action class and have the same results, with the exception |
267
|
|
|
|
|
|
|
of the already mentioned 'not messing with the global request class'. However |
268
|
|
|
|
|
|
|
L<Catalyst::Action::REST> has been around for a long time and is well vetted in |
269
|
|
|
|
|
|
|
production so I would caution care with changing your mission critical systems |
270
|
|
|
|
|
|
|
very quickly. |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=head1 VERSUS NATIVE METHOD ATTRIBUTES |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
L<Catalyst> since version 5.90030 has offered a core approach to dispatch on the |
275
|
|
|
|
|
|
|
http method (via L<Catalyst::ActionRole::HTTPMethods>). Why still use this action role |
276
|
|
|
|
|
|
|
versus the core functionality? ALthough it partly comes down to preference and the |
277
|
|
|
|
|
|
|
author's desire to give current users of L<Catalyst::Action::REST> a path forward, there |
278
|
|
|
|
|
|
|
is some functionality differences beetween the two which may recommend one over the |
279
|
|
|
|
|
|
|
other. For example the core method matching does not offer an automatic default |
280
|
|
|
|
|
|
|
'Not Implemented' response that correctly sets the OPTIONS header. Also the dispatch |
281
|
|
|
|
|
|
|
flow between the two approaches is different and when using chained actions one |
282
|
|
|
|
|
|
|
might be a better choice over the other depending on how your chains are arranged and |
283
|
|
|
|
|
|
|
your desired flow of action. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=head1 METHODS |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
This role contains the following methods. |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=head2 get_allowed_methods |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Returns a list of the allowed methods. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head2 dispatch |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
This method overrides the default dispatch mechanism to the re-dispatching |
296
|
|
|
|
|
|
|
mechanism described above. |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=head1 AUTHOR |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
John Napiorkowski <jnapiork@cpan.org> |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Author list from L<Catalyst::Action::REST> |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
Adam Jacob E<lt>adam@stalecoffee.orgE<gt>, with lots of help from mst and jrockway |
305
|
|
|
|
|
|
|
Marchex, Inc. paid me while I developed this module. (L<http://www.marchex.com>) |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=head1 CONTRIBUTORS |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
The following contributor list was copied from L<Catalyst::Action::REST> |
310
|
|
|
|
|
|
|
from where the bulk of this code was copied. |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
Tomas Doran (t0m) E<lt>bobtfish@bobtfish.netE<gt> |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
John Goulah |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
Christopher Laco |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
Daisuke Maki E<lt>daisuke@endeworks.jpE<gt> |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
Hans Dieter Pearcey |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Brian Phillips E<lt>bphillips@cpan.orgE<gt> |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
Dave Rolsky E<lt>autarch@urth.orgE<gt> |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
Luke Saunders |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
Arthur Axel "fREW" Schmidt E<lt>frioux@gmail.comE<gt> |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
J. Shirley E<lt>jshirley@gmail.comE<gt> |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
Gavin Henry E<lt>ghenry@surevoip.co.ukE<gt> |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
Gerv http://www.gerv.net/ |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
Colin Newell <colin@opusvl.com> |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Wallace Reis E<lt>wreis@cpan.orgE<gt> |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
André Walker (andrewalker) <andre@cpan.org> |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=head1 COPYRIGHT |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
Copyright (c) 2006-2015 the above named AUTHOR and CONTRIBUTORS |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=head1 LICENSE |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
You may distribute this code under the same terms as Perl itself. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=cut |