File Coverage

blib/lib/Role/Hooks.pm
Criterion Covered Total %
statement 192 268 72.0
branch 65 122 53.2
condition 54 88 61.3
subroutine 27 34 79.4
pod 4 4 100.0
total 342 516 66.4


line stmt bran cond sub pod time code
1 9     9   2103692 use 5.008001;
  9         75  
2 9     9   47 use strict;
  9         15  
  9         167  
3 9     9   36 use warnings;
  9         13  
  9         309  
4              
5              
6             use Class::Method::Modifiers qw( install_modifier );
7 9     9   3754  
  9         12084  
  9         1048  
8             our $AUTHORITY = 'cpan:TOBYINK';
9             our $VERSION = '0.007';
10              
11             our %CALLBACKS_BEFORE_APPLY;
12             our %CALLBACKS_AFTER_APPLY;
13             our %CALLBACKS_AFTER_INFLATE;
14             our %ARGS;
15              
16             BEGIN { *DEBUG = $ENV{'PERL_ROLE_HOOKS_DEBUG'} ? sub(){1} : sub(){0} };
17 9 50   9   2821  
18             # limited version of Safe::Isa
19             my $_isa = sub { ref( $_[0] ) and $_[0]->isa( $_[1] ) };
20              
21             my ($me, $msg, @args) = @_;
22             require Carp;
23 0     0   0 Carp::croak( @args ? sprintf($msg, @args) : $msg );
24 0         0 }
25 0 0       0  
26             my ($me, $msg, @args) = @_;
27             require Carp;
28             Carp::carp( @args ? sprintf($msg, @args) : $msg );
29 0     0   0 }
30 0         0  
31 0 0       0 my ($me, $msg, @args) = @_;
32             require Carp;
33             Carp::carp( @args ? sprintf($msg, @args) : $msg ) if DEBUG;
34             }
35 0     0   0  
36 0         0 my $target = pop;
37 0         0
38             if ($INC{'Role/Tiny.pm'}
39             and 'Role::Tiny'->can('is_role')
40             and 'Role::Tiny'->is_role($target)) {
41 95     95 1 149 return 'Role::Tiny';
42             }
43 95 100 66     539
      100        
44             # really old versions of Role::Tiny
45             if ($INC{'Role/Tiny.pm'}
46 46         355 and !'Role::Tiny'->can('is_role')
47             and $Role::Tiny::INFO{$target}) {
48             return 'Role::Tiny'; # uncoverable statement
49             }
50 49 0 66     247
      33        
51             if ($INC{'Moose/Meta/Role.pm'}
52             and do { require Moose::Util; 1 }
53 0         0 and Moose::Util::find_meta($target)->$_isa('Moose::Meta::Role')) {
54             return 'Moose::Role';
55             }
56 49 100 66     106
      66        
57 30         110 if ($INC{'Mouse/Meta/Role.pm'}
  30         120  
58             and do { require Mouse::Util; 1 }
59 26         84 and Mouse::Util::find_meta($target)->$_isa('Mouse::Meta::Role')) {
60             return 'Mouse::Role';
61             }
62 23 0 33     12786
      0        
63 0         0 if ($INC{'Role/Basic.pm'}
  0         0  
64             and eval { 'Role::Basic'->_load_role($target) }) {
65 0         0 return 'Role::Basic';
66             }
67            
68 23 50 33     58 no strict 'refs';
69 0         0 no warnings 'once';
70 0         0 my $UM = ${"$target\::USES_MITE"};
71             if ( defined $UM and $UM eq 'Mite::Role' ) {
72             return 'Mite::Role';
73 9     9   63 }
  9         17  
  9         312  
74 9     9   46
  9         13  
  9         2134  
75 23         31 return undef;
  23         76  
76 23 100 100     66 }
77 13         24  
78             my ($me, $target, @callbacks) = @_;
79             return unless @callbacks;
80 10         50 $me->is_role($target) or $me->_croak('%s is not a role', $target);
81             $me->_install_patches($target);
82             push @{ $CALLBACKS_BEFORE_APPLY{$target}||=[] }, @callbacks;
83             return $me;
84 20     20 1 6017 }
85 20 100       51  
86 19 50       47 my ($me, $target, @callbacks) = @_;
87 19         59 return unless @callbacks;
88 19   100     28 $me->is_role($target) or $me->_croak('%s is not a role', $target);
  19         84  
89 19         38 $me->_install_patches($target);
90             push @{ $CALLBACKS_AFTER_APPLY{$target}||=[] }, @callbacks;
91             return $me;
92             }
93 19     19 1 127  
94 19 100       42 no warnings 'uninitialized';
95 18 50       42 my ($me, $target, @callbacks) = @_;
96 18         59 return unless @callbacks;
97 18   100     25 $me->is_role($target) eq 'Role::Tiny'
  18         79  
98 18         38 or $target->isa('Moo::Object')
99             or $me->_croak('%s is not a Moo class or role', $target);
100             $me->_install_patches($target);
101             $me->_install_patches_inflation($target);
102 9     9   56 push @{ $CALLBACKS_AFTER_INFLATE{$target}||=[] }, @callbacks;
  9         14  
  9         19007  
103 6     6 1 98 return $me;
104 6 100       18 }
105 3 50 66     7  
106             {
107             # Internals for monkey-patching role implementations.
108 3         12 #
109 3         7
110 3   50     3 my %patched;
  3         14  
111 3         7 my ($me, $target) = @_;
112            
113             if ($INC{'Role/Tiny.pm'}) {
114             $patched{'Role::Tiny'} ||= $me->_install_patches_roletiny;
115             }
116             if ($INC{'Moo/Role.pm'} or $INC{'Moo.pm'}) {
117             $patched{'Moo::Role'} ||= $me->_install_patches_moorole;
118             }
119             if ($INC{'Moose/Role.pm'} or $INC{'Moose.pm'}) {
120 40     40   59 $patched{'Moose::Role'} ||= $me->_install_patches_mooserole;
121             }
122 40 100       78 if ($INC{'Mouse/Role.pm'} or $INC{'Mouse.pm'}) {
123 27   66     69 $patched{'Mouse::Role'} ||= $me->_install_patches_mouserole;
124             }
125 40 100 100     115 if ($INC{'Role/Basic.pm'}) {
126 21   66     54 $patched{'Role::Basic'} ||= $me->_install_patches_rolebasic;
127             }
128 40 100 100     125
129 19   66     49 my $is_role = $me->is_role($target);
130             if ( defined $is_role and $is_role eq 'Mite::Role' ) {
131 40 50 33     144 $patched{'Mite::Role'}{$target} ||= $me->_install_patches_miterole($target);
132 0   0     0 }
133             }
134 40 50       69
135 0   0     0 my %patched_inflation;
136             my ($me, $target) = @_;
137             if ($INC{'Moo/Role.pm'} or $INC{'Moo.pm'}) {
138 40         70 $patched_inflation{'Moo::Role'} ||= $me->_install_patches_moorole_inflation;
139 40 100 100     146 }
140 6   66     14 }
141            
142             my ($me) = @_;
143             return 1 if $patched{'Role::Tiny'};
144            
145             $me->_debug("Installing patches for Role::Tiny") if DEBUG;
146 3     3   6
147 3 50 33     8 require Role::Tiny;
148 3   66     15
149             install_modifier 'Role::Tiny', around => 'role_application_steps', sub {
150             my $orig = shift;
151             my @steps = $orig->(@_);
152             return (
153 6     6   17 __PACKAGE__ . '::_run_role_tiny_before_callbacks',
154 6 50       18 @steps,
155             __PACKAGE__ . '::_run_role_tiny_after_callbacks',
156 6         6 );
157             };
158 6         612
159             *_run_role_tiny_before_callbacks = sub {
160             my (undef, $to, $role) = @_;
161 8     8   448 $me->_debug("Calling role hooks for $role before application to $to") if DEBUG;
162 8         33 my @callbacks = @{ $CALLBACKS_BEFORE_APPLY{$role} || [] };
163             for my $cb (@callbacks) {
164 8         65 $cb->($role, $to);
165             }
166             return;
167             };
168 6         3939
169             *_run_role_tiny_after_callbacks = sub {
170             my (undef, $to, $role) = @_;
171 8     8   56 $me->_debug("Calling role hooks for $role after application to $to") if DEBUG;
172 8         11 my @callbacks = @{ $CALLBACKS_AFTER_APPLY{$role} || [] };
173 8 100       12 for my $cb (@callbacks) {
  8         43  
174 8         21 $cb->($role, $to);
175 9         34 }
176             if (my $is_role = $me->is_role($to)) {
177 8         50 $me->_debug("Copying role hooks for $role to $to") if DEBUG;
178 6         1780 $me->before_apply($to, @{ $CALLBACKS_BEFORE_APPLY{$role} || [] });
179             $me->after_apply($to, @{ $CALLBACKS_AFTER_APPLY{$role} || [] });
180             if ($is_role eq 'Role::Tiny' or $to->isa('Moo::Object')) {
181 8     8   23142 $me->after_inflate($to, @{ $CALLBACKS_AFTER_INFLATE{$role} || [] });
182 8         15 }
183 8 100       24 }
  8         53  
184 8         23 return;
185 9         52 };
186            
187 8 100       56 return 1;
188 4         8 }
189 4 100       8
  4         30  
190 4 100       15 my ($me) = @_;
  4         20  
191 4 50 33     27 $patched{'Role::Tiny'} ||= $me->_install_patches_roletiny;
192 4 100       9 return 1 if $patched{'Moo::Role'};
  4         18  
193            
194             $me->_debug("Installing patches for Moo::Role") if DEBUG;
195 8         24
196 6         35 require Moo::Role;
197             require List::Util;
198 6         26
199             # Mostly can just rely on Role::Tiny, but need
200             # to move _run_callbacks_before_apply to the
201             # front of the queue!
202 4     4   11 #
203 4   66     19 install_modifier 'Moo::Role', around => 'role_application_steps', sub {
204 4 50       14 my $orig = shift;
205             my @steps = $orig->(@_);
206 4         6 return List::Util::uniqstr(
207             __PACKAGE__ . '::_run_role_tiny_before_callbacks',
208 4         592 @steps,
209 4         3486 );
210             };
211            
212             return 1;
213             }
214            
215             my ($me) = @_;
216 5     5   2382 return 1 if $patched_inflation{'Moo::Role'};
217 5         21
218 5         80 $me->_debug("Installing inflation patches for Moo::Role") if DEBUG;
219            
220             require Moo::HandleMoose;
221            
222 4         31 install_modifier 'Moo::HandleMoose', after => 'inject_real_metaclass_for', sub {
223             my ( $name ) = @_;
224 4         1086 $me->_run_moo_inhale_callbacks( $name );
225             };
226            
227             my %already;
228 1     1   5 *_run_moo_inhale_callbacks = sub {
229 1 50       3 my (undef, $name) = @_;
230             $me->_debug("Calling role hooks for $name after inflation") if DEBUG;
231 1         1 my @callbacks = @{ $CALLBACKS_AFTER_INFLATE{$name} || [] };
232             for my $cb (@callbacks) {
233 1         384 next if $already{"$name|$cb"}++;
234             $cb->($name);
235             }
236 4     4   408709 };
237 4         16
238 1         5933 return 1;
239             }
240 1         207
241             my ($me) = @_;
242 4     4   9 return 1 if $patched{'Moose::Role'};
243 4         5
244 4 50       6 $me->_debug("Installing patches for Moose::Role") if DEBUG;
  4         18  
245 4         6
246 4 100       31 require Moose::Meta::Role;
247 3         19
248             install_modifier 'Moose::Meta::Role', around => 'apply', sub {
249 1         8 my ($orig, $role_meta, $to_meta, %args) = @_;
250             local *ARGS = \%args;
251 1         4 my $role = $role_meta->name;
252             my $to = $to_meta->name;
253             do {
254             $me->_debug("Calling role hooks for $role before application to $to") if DEBUG;
255 4     4   19 my @callbacks = @{ $CALLBACKS_BEFORE_APPLY{$role} || [] };
256 4 50       15 for my $cb (@callbacks) {
257             $cb->($role, $to);
258 4         5 }
259             };
260 4         19 my $application = $role_meta->$orig($to_meta, %args);
261             do {
262             $me->_debug("Calling role hooks for $role after application to $to") if DEBUG;
263 4     4   4991 my @callbacks = @{ $CALLBACKS_AFTER_APPLY{$role} || [] };
264 4         12 for my $cb (@callbacks) {
265 4         29 $cb->($role, $to);
266 4         34 }
267 4         7 };
268 4         5 if ($me->is_role($to)) {
269 4 50       8 $me->_debug("Copying role hooks for $role to $to") if DEBUG;
  4         17  
270 4         18 $me->before_apply($to, @{ $CALLBACKS_BEFORE_APPLY{$role} || [] });
271 6         24 $me->after_apply($to, @{ $CALLBACKS_AFTER_APPLY{$role} || [] });
272             }
273             return $application;
274 4         65 };
275 4         5581
276 4         7 return 1;
277 4 50       11 }
  4         23  
278 4         10
279 6         36 my ($me) = @_;
280             return 1 if $patched{'Mouse::Role'};
281            
282 4 100       35 $me->_debug("Installing patches for Mouse::Role") if DEBUG;
283 2         5
284 2 50       7 require Mouse::Meta::Role;
  2         57  
285 2 50       7
  2         10  
286             install_modifier 'Mouse::Meta::Role', around => 'apply', sub {
287 4         35 my ($orig, $role_meta, $to_meta, %args) = @_;
288 4         32 local *ARGS = \%args;
289             my $role = $role_meta->name;
290 4         1142 my $to = $to_meta->name;
291             do {
292             $me->_debug("Calling role hooks for $role before application to $to") if DEBUG;
293             my @callbacks = @{ $CALLBACKS_BEFORE_APPLY{$role} || [] };
294 0     0   0 for my $cb (@callbacks) {
295 0 0       0 $cb->($role, $to);
296             }
297 0         0 };
298             my $application = $role_meta->$orig($to_meta, %args);
299 0         0 do {
300             $me->_debug("Calling role hooks for $role after application to $to") if DEBUG;
301             my @callbacks = @{ $CALLBACKS_AFTER_APPLY{$role} || [] };
302 0     0   0 for my $cb (@callbacks) {
303 0         0 $cb->($role, $to);
304 0         0 }
305 0         0 };
306 0         0 if ($me->is_role($to)) {
307 0         0 $me->_debug("Copying role hooks for $role to $to") if DEBUG;
308 0 0       0 $me->before_apply($to, @{ $CALLBACKS_BEFORE_APPLY{$role} || [] });
  0         0  
309 0         0 $me->after_apply($to, @{ $CALLBACKS_AFTER_APPLY{$role} || [] });
310 0         0 }
311             return $application;
312             };
313 0         0
314 0         0 return 1;
315 0         0 }
316 0 0       0
  0         0  
317 0         0 my ($me) = @_;
318 0         0 return 1 if $patched{'Role::Basic'};
319            
320             $me->_debug("Installing patches for Role::Basic") if DEBUG;
321 0 0       0
322 0         0 require Role::Basic;
323 0 0       0
  0         0  
324 0 0       0 $me->_carp("Role::Hooks is only tested with Role::Basic 0.07 to 0.13")
  0         0  
325             unless $Role::Basic::VERSION =~ /^0\.(?:0[7-9]|1[0-3])/;
326 0         0
327 0         0 install_modifier 'Role::Basic', around => '_add_role_methods_to_target', sub {
328             my ($orig, $rb, $role, $to, $modifiers) = @_;
329 0         0 local *ARGS = $modifiers;
330             do {
331             $me->_debug("Calling role hooks for $role before application to $to") if DEBUG;
332             my @callbacks = @{ $CALLBACKS_BEFORE_APPLY{$role} || [] };
333 0     0   0 for my $cb (@callbacks) {
334 0 0       0 $cb->($role, $to);
335             }
336 0         0 };
337             my $application = $rb->$orig($role, $to, $modifiers);
338 0         0 do {
339             $me->_debug("Calling role hooks for $role after application to $to") if DEBUG;
340 0 0       0 my @callbacks = @{ $CALLBACKS_AFTER_APPLY{$role} || [] };
341             for my $cb (@callbacks) {
342             $cb->($role, $to);
343             }
344 0     0   0 };
345 0         0 if ($me->is_role($to)) {
346 0         0 $me->_debug("Copying role hooks for $role to $to") if DEBUG;
347 0         0 $me->before_apply($to, @{ $CALLBACKS_BEFORE_APPLY{$role} || [] });
348 0 0       0 $me->after_apply($to, @{ $CALLBACKS_AFTER_APPLY{$role} || [] });
  0         0  
349 0         0 }
350 0         0 return $application;
351             };
352            
353 0         0 return 1;
354 0         0 }
355 0         0
356 0 0       0 my ($me, $target) = @_;
  0         0  
357 0         0 return 1 if $ENV{MITE_COMPILE};
358 0         0 install_modifier $target, around => '__FINALIZE_APPLICATION__', sub {
359             my ($orig, $role, $to, $modifiers) = @_;
360             local *ARGS = $modifiers || {};
361 0 0       0 my $indirect = ( $modifiers || {} )->{-indirect};
362 0         0 if ( not $indirect ) {
363 0 0       0 $me->_debug("Calling role hooks for $role before application to $to") if DEBUG;
  0         0  
364 0 0       0 my @callbacks = @{ $CALLBACKS_BEFORE_APPLY{$role} || [] };
  0         0  
365             for my $cb (@callbacks) {
366 0         0 $cb->($role, $to);
367 0         0 }
368             }
369 0         0 $role->$orig($to, $modifiers);
370             if ( not $indirect ) {
371             $me->_debug("Calling role hooks for $role after application to $to") if DEBUG;
372             my @callbacks = @{ $CALLBACKS_AFTER_APPLY{$role} || [] };
373 2     2   3 for my $cb (@callbacks) {
374 2 50       5 $cb->($role, $to);
375             }
376 3     3   1333 }
377 3   100     13 my $to_type = $me->is_role($to);
378 3   100     7 if (defined $to_type and $to_type eq 'Mite::Role') {
379 3 100       7 $me->_debug("Copying role hooks for $role to $to") if DEBUG;
380 2         3 $me->before_apply($to, @{ $CALLBACKS_BEFORE_APPLY{$role} || [] });
381 2 50       2 $me->after_apply($to, @{ $CALLBACKS_AFTER_APPLY{$role} || [] });
  2         6  
382 2         3 }
383 3         11 };
384             1;
385             }
386 3         18 }
387 3 100       43  
388 2         3 1;
389 2 50       2  
  2         5  
390 2         3  
391 3         10 =pod
392              
393             =encoding utf-8
394 3         12  
395 3 100 66     11 =head1 NAME
396 1         2  
397 1 50       1 Role::Hooks - role callbacks
  1         3  
398 1 50       1  
  1         5  
399             =head1 SYNOPSIS
400 2         10  
401 2         525 package Local::Role {
402             use Moo::Role;
403             use Role::Hooks;
404            
405             Role::Hooks->after_apply(__PACKAGE__, sub {
406             my ($role, $target) = @_;
407             print "$role has been applied to $target.\n";
408             });
409             }
410            
411             package Local::Class {
412             use Moo;
413             with "Local::Role"; # prints above message
414             }
415              
416             =head1 DESCRIPTION
417              
418             This module allows a role to run a callback when it is applied to a class or
419             to another role.
420              
421             =head2 Compatibility
422              
423             It should work with L<Role::Tiny>, L<Moo::Role>, L<Moose::Role>,
424             L<Mouse::Role>, L<Role::Basic>, and L<Mite>. Not all class builders work well
425             with all role builders (for example, a Moose class consuming a Mouse role).
426             But when they do work together, Role::Hooks should be able to run the
427             callbacks. (The only combination I've tested is Moo with Moose though.)
428              
429             Some other role implementations (such as L<Moos::Role>, L<exact::role>,
430             and L<OX::Role>) are just wrappers around one of the supported role builders,
431             so should mostly work.
432              
433             With Role::Basic, the C<after_apply> hook is called a little earlier than
434             would be ideal; after the role has been fully loaded and its methods have
435             been copied into the target package, but before handling C<requires>, and
436             before patching the C<DOES> method in the target package. If you are using
437             Role::Basic, consider switching to Role::Tiny.
438              
439             With Mite, the C<before_apply> hook is called fairly late; after the role
440             is fully loaded and attributes and methods have been copied into the target
441             package, after C<DOES> has been patched, but before method modifiers from the
442             role have been applied to the target package.
443              
444             Apart from Role::Tiny/Moo::Role, a hashref of additional arguments (things
445             like "-excludes" and "-alias") can be passed when consuming a role. Although
446             I discourage people from using these in general, if you need access to
447             these arguments in the callback, you can check C<< %Role::Hooks::ARGS >>.
448              
449             Roles generated via L<Package::Variant> should work; see
450             F<t/20packagevariant.t> for a demonstration.
451              
452             =head2 Methods
453              
454             =over
455              
456             =item C<< before_apply >>
457              
458             Role::Hooks->before_apply($rolename, $callback);
459              
460             Sets up a callback for a role that will be called before the role is applied
461             to a target package. The callback will be passed two parameters: the role
462             being applied and the target package.
463              
464             The role being applied may not be the same role as the role the callback was
465             defined in!
466              
467             package Local::Role1 {
468             use Moo::Role;
469             use Role::Hooks;
470             Role::Hooks->before_apply(__PACKAGE__, sub {
471             my ($role, $target) = @_;
472             print "$role has been applied to $target.\n";
473             });
474             }
475            
476             package Local::Role2 {
477             use Moo::Role;
478             with "Local::Role1";
479             }
480            
481             package Local::Class1 {
482             use Moo::Role;
483             with "Local::Role2";
484             }
485              
486             This will print:
487              
488             Local::Role1 has been applied to Local::Role2.
489             Local::Role2 has been applied to Local::Class1.
490              
491             If you only care about direct applications of roles (i.e. the first one):
492              
493             Role::Hooks->before_apply(__PACKAGE__, sub {
494             my ($role, $target) = @_;
495             return if $role ne __PACKAGE__;
496             print "$role has been applied to $target.\n";
497             });
498              
499             If you only care about roles being applied to classes (i.e. the second one):
500              
501             Role::Hooks->before_apply(__PACKAGE__, sub {
502             my ($role, $target) = @_;
503             return if Role::Hooks->is_role($target);
504             print "$role has been applied to $target.\n";
505             });
506              
507             =item C<< after_apply >>
508              
509             Role::Hooks->after_apply($rolename, $callback);
510              
511             The same as C<< before_apply >>, but called later in the role application
512             process.
513              
514             Note that when the callback is called, even though it's after the role has
515             been applied to the target, it doesn't mean the target has finished being
516             built. For example, there might be C<has> statements after the C<with>
517             statement, and those will not have been evaluated yet.
518              
519             If you want to throw an error when someone applies your role to an
520             inappropriate target, it is probably better to do that in C<before_apply> if
521             you can.
522              
523             =item C<< after_inflate >>
524              
525             Role::Hooks->after_inflate($pkg_name, $callback);
526              
527             Even though this is part of Role::Hooks, it works on classes too.
528             But it only works on classes and roles built using Moo. This runs
529             your callback if your Moo class or role gets "inflated" to a Moose
530             class or role.
531              
532             If you set up a callback for a role, then the callback will also
533             get called if any packages that role was applied to get inflated.
534              
535             =item C<< is_role >>
536              
537             Will return true if the given package seems to be a role, false otherwise.
538              
539             (In fact, returns a string representing which role builder the role seems
540             to be using -- "Role::Tiny", "Moose::Role", "Mouse::Role", "Role::Basic",
541             or "Mite::Role"; roles built using Moo::Role are detected as "Role::Tiny".)
542              
543             =back
544              
545             =head1 ENVIRONMENT
546              
547             The environment variable C<PERL_ROLE_HOOKS_DEBUG> may be set to true to
548             enable debugging messages.
549              
550             =head1 BUGS
551              
552             Please report any bugs to
553             L<http://rt.cpan.org/Dist/Display.html?Queue=Role-Hooks>.
554              
555             =head1 SEE ALSO
556              
557             L<Role::Tiny>, L<Moose::Role>.
558              
559             =head1 AUTHOR
560              
561             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
562              
563             =head1 COPYRIGHT AND LICENCE
564              
565             This software is copyright (c) 2020-2022 by Toby Inkster.
566              
567             This is free software; you can redistribute it and/or modify it under
568             the same terms as the Perl 5 programming language system itself.
569              
570             =head1 DISCLAIMER OF WARRANTIES
571              
572             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
573             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
574             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.