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