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"; |