| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
=head1 NAME |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
WWW::Mechanize::Plugin::Cookbook - how to write plugins for WWW::Mechanize::Pluggable |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
This document describes what a C plugin is, |
|
8
|
|
|
|
|
|
|
how they work in connection with the base module, and gives examples |
|
9
|
|
|
|
|
|
|
of how one would design a new plugin. |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=over 4 |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
This cookbook addresses the current state of the C interface; |
|
14
|
|
|
|
|
|
|
future versions are expected to greatly streamline the process of creating |
|
15
|
|
|
|
|
|
|
plugins and hooks. |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=back |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 PLUGIN BASICS |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
A plugin is basically as specially-named package that is automatically loaded |
|
22
|
|
|
|
|
|
|
by a parent class. This document outlines the interface between |
|
23
|
|
|
|
|
|
|
C and its plugin classes. |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head2 Flow of Control |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
When C is loaded, it searches C<@INC> for |
|
28
|
|
|
|
|
|
|
modules whose names begin with C and calls |
|
29
|
|
|
|
|
|
|
C for the package, using the arguments supplied on |
|
30
|
|
|
|
|
|
|
C's own C |
|
31
|
|
|
|
|
|
|
parameterize the plugins if you wish. |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
When a C object is instantiated, its |
|
34
|
|
|
|
|
|
|
C method calls each of the plugins' C method. |
|
35
|
|
|
|
|
|
|
Typically, C exports methods back into |
|
36
|
|
|
|
|
|
|
the caller's namespace, and also calls C and C |
|
37
|
|
|
|
|
|
|
to wrap any of C's methods it desires. |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
When a C method is called, C's |
|
40
|
|
|
|
|
|
|
C takes control. It calls any pre-hooks that have been installed |
|
41
|
|
|
|
|
|
|
for the method; if any of them return a true value, the actual method |
|
42
|
|
|
|
|
|
|
call is skipped. C then calls the method |
|
43
|
|
|
|
|
|
|
(if it should) using the same context in which the method was originally |
|
44
|
|
|
|
|
|
|
callled, saving the return value. The post-hooks are then called, and the |
|
45
|
|
|
|
|
|
|
return value from the method is returned to the original caller. |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head2 What you can do |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
Essentially, you now have complete control over what any method in the base |
|
50
|
|
|
|
|
|
|
class does. You can |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=over 4 |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=item * alter the parameter list |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=item * process the call yourself |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=item * conditionally get involved, or not |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=item * post-process the results after the call |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=back |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 API TO WWW::MECHANIZE::PLUGGABLE |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head2 import |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Called as C. |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
This routine is optional; it is called when your plugin is loaded |
|
71
|
|
|
|
|
|
|
by C. You can use this to parameterize |
|
72
|
|
|
|
|
|
|
your plugin via arguments on the C |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
It's recommended that you supply arguments as key-value pairs; |
|
75
|
|
|
|
|
|
|
this will make it possible for C |
|
76
|
|
|
|
|
|
|
to remove the "used-up" parameters from the c |
|
77
|
|
|
|
|
|
|
returning the keys you want to have removed. |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Here's a sample C method: |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub import { |
|
82
|
|
|
|
|
|
|
my($class, %args) = @_; |
|
83
|
|
|
|
|
|
|
if defined(my $value = $args{'mine'}) { |
|
84
|
|
|
|
|
|
|
if (_is_appropriate($value)) { |
|
85
|
|
|
|
|
|
|
# do whatever ,,, |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
return ("mine"); |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
This looks for the C parameter on the C |
|
92
|
|
|
|
|
|
|
It processes it as appropriate and returns the |
|
93
|
|
|
|
|
|
|
key so that C will delete it. |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=head2 init |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
Called as C. |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
The C method allows your plugin a chance to export |
|
100
|
|
|
|
|
|
|
subroutines and store information appropriate for its |
|
101
|
|
|
|
|
|
|
proper functioning in the parent C |
|
102
|
|
|
|
|
|
|
object. It also can be used to set up pre-hooks and |
|
103
|
|
|
|
|
|
|
post-hooks for methods. |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=over 4 |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
Note that at present it isn't possible to add hooks for |
|
108
|
|
|
|
|
|
|
methods installed by other plugins; a future release of |
|
109
|
|
|
|
|
|
|
this software may be able to do this. |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=back |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Because other plugins will be doing the same thing, it's |
|
114
|
|
|
|
|
|
|
important to choose unique method names and field names. |
|
115
|
|
|
|
|
|
|
It's proabably a good idea to prefix field names with the |
|
116
|
|
|
|
|
|
|
name of your plugin, like C<_MyPlugin_data>. |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
It's possible that we may change the interface in a future |
|
119
|
|
|
|
|
|
|
release of C to support |
|
120
|
|
|
|
|
|
|
"inside-out" objects (see http://www.windley.com/archives/2005/08/best_practices.shtml |
|
121
|
|
|
|
|
|
|
for an example). |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Sample init function: |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub init { |
|
126
|
|
|
|
|
|
|
my($parent_object, %args) = @_; |
|
127
|
|
|
|
|
|
|
$parent_object->{_myplugin_foo} = "my data"; |
|
128
|
|
|
|
|
|
|
*{caller() . '::myplugin_method'} = \&my_implementation; |
|
129
|
|
|
|
|
|
|
$parent_object->pre_hook('get', sub { &my_prehook(@_) } ); |
|
130
|
|
|
|
|
|
|
$parent_object->post_hook('get', sub { &my_prehook(@_) } ); |
|
131
|
|
|
|
|
|
|
my @removed; |
|
132
|
|
|
|
|
|
|
if ($args{'my_arg'}) { |
|
133
|
|
|
|
|
|
|
# process my_arg |
|
134
|
|
|
|
|
|
|
push @removes, 'my_arg'; |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
@removed; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
The anonymous subroutine wrapping the hook setup currently is |
|
140
|
|
|
|
|
|
|
necessary to prevent the hook from being called during its |
|
141
|
|
|
|
|
|
|
installation; this needs to be fixed. The anonymous subroutine |
|
142
|
|
|
|
|
|
|
works for the moment, and will work in future releases, so |
|
143
|
|
|
|
|
|
|
go head and use it for now. |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
Also note that we have the same kind of interface that we do |
|
146
|
|
|
|
|
|
|
in C; you can parameterize a particular plugin by |
|
147
|
|
|
|
|
|
|
putting the parameters (key=>value-style) on the C |
|
148
|
|
|
|
|
|
|
and then processing them in C, and deleting them |
|
149
|
|
|
|
|
|
|
after processing by returning the list of names. |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=head2 pre_hook |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
Called as C<$parent_object->pre_hook('method_name", $subref)>. |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Installs the referenced subroutine as a pre-hook for the |
|
156
|
|
|
|
|
|
|
named method. Currently, only C methods can |
|
157
|
|
|
|
|
|
|
be hooked; future releases may allow methods supplied by plugins |
|
158
|
|
|
|
|
|
|
to be hooked as well. |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head2 post_hook |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Called as C<$parent_object->pre_hook('method_name", $subref)>. |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Installs the referenced subroutine as a post-hook for the |
|
165
|
|
|
|
|
|
|
named method. Currently, only C methods can |
|
166
|
|
|
|
|
|
|
be hooked; future releases may allow methods supplied by plugins |
|
167
|
|
|
|
|
|
|
to be hooked as well. |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=head1 YOUR CODE |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
Since pre-hooks and post-hooks are all about getting your |
|
172
|
|
|
|
|
|
|
code involved in things, this section details how all that |
|
173
|
|
|
|
|
|
|
works. |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head2 Prehooks and posthooks |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Called as C. |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
This is the subroutine that you passed a reference to in |
|
180
|
|
|
|
|
|
|
the call to either C or C. It can do |
|
181
|
|
|
|
|
|
|
anything you like; it has access to both the |
|
182
|
|
|
|
|
|
|
\C object and to the |
|
183
|
|
|
|
|
|
|
internal C object, as well as to the |
|
184
|
|
|
|
|
|
|
parameters with which the method was called. |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
If your code is a pre-hook, it can cause |
|
187
|
|
|
|
|
|
|
C to skip the method |
|
188
|
|
|
|
|
|
|
call altogether by returning a true value. |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
Sample pre-hook: |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub my_prehook { |
|
193
|
|
|
|
|
|
|
my($pluggable, $mech, @args) = @_; |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# We'll assume that this is a hook for 'get'. |
|
196
|
|
|
|
|
|
|
if ($args[0] =~ /$selected_url/) { |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# alter the URL to what we want |
|
199
|
|
|
|
|
|
|
$args[0] =~ s/$what_we_dont_want/$what_we_do/; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
# force another try with the altered URL. |
|
202
|
|
|
|
|
|
|
$pluggable->get(@args); |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# don't actually do the get with the old URL. |
|
205
|
|
|
|
|
|
|
return 'skip'; |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
We used this approach because the interface currently doesn't |
|
209
|
|
|
|
|
|
|
allow us to alter the parameter list; this is something we |
|
210
|
|
|
|
|
|
|
probably should do in the next release. |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=head1 RECIPIES |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=head2 Adding a new acessor |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
To avoid doing a lot extra monkey coding, C is highly recommended. |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
package WWW::Mechanize::Plugin::MyPlugin; |
|
219
|
|
|
|
|
|
|
use base qw(Class::Accessor::Fast); |
|
220
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors(qw(foo bar baz)); |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
You can now use the newly-created accessors any way you like; often |
|
223
|
|
|
|
|
|
|
you'll use them to store data for other methods that are exported to |
|
224
|
|
|
|
|
|
|
C. |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head2 Adding a new method |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
This is done (for the moment) by using a construct like this: |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
*{caller() . '::new_method'} = \&localsub; |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
This would call any subroutine or method call to new_method |
|
233
|
|
|
|
|
|
|
via the Mech::Pluggable object to be dispatched to localsub |
|
234
|
|
|
|
|
|
|
in this package. |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head2 Replacing a method |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
In init, install a pre_hook for the method which does |
|
239
|
|
|
|
|
|
|
something like this: |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub init { |
|
242
|
|
|
|
|
|
|
pre_hook('desired_method', sub { \&substitute(@_) }); |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub substitute { |
|
245
|
|
|
|
|
|
|
my($pluggable, $mech, @_) = @_; |
|
246
|
|
|
|
|
|
|
# Do whatever you want; |
|
247
|
|
|
|
|
|
|
return "skip"; |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Note the anonymous sub construct in the call to pre_hook. |
|
251
|
|
|
|
|
|
|
This is necessary because the construct C<\&substitute> |
|
252
|
|
|
|
|
|
|
tries to call substitute() immediately, which we do |
|
253
|
|
|
|
|
|
|
I want. |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
We return "skip" as a mnemonic that a true value causes |
|
256
|
|
|
|
|
|
|
the real call to be skipped. |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=head2 Retrying a method call |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
This is done with a prehook to count how many times |
|
261
|
|
|
|
|
|
|
we've tried to retry an action, and a posthook to |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=over 4 |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=item * take whatever action is needed to set up for the retry |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=item * call back() on the Mech object |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=item * repeat the last action again on the Mech object |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=item * up the count of tries |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=back |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
The prehook is needed to keep the retry from going into |
|
276
|
|
|
|
|
|
|
an infinite loop. |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=head1 CREDITS |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
The Perl Advent Calendar (http://www.perladvent.org/2004/6th/) for bringing Module::Pluggable to my attention. |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
Damian Conway, for showing us how to do things that Just Work. |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Andy Lester, for WWW::Mechanize. |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=cut |
|
287
|
|
|
|
|
|
|
|
|
288
|
11
|
|
|
11
|
|
22508
|
use strict; # or get dinged by CPANTS |
|
|
11
|
|
|
|
|
121
|
|
|
|
11
|
|
|
|
|
506
|
|
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
"It's only documentation but I like it"; |