File Coverage

blib/lib/Class/AutoGenerate/Declare.pm
Criterion Covered Total %
statement 100 100 100.0
branch 22 26 84.6
condition 5 6 83.3
subroutine 26 26 100.0
pod 13 15 86.6
total 166 173 95.9


line stmt bran cond sub pod time code
1 18     18   111 use strict;
  18         31  
  18         762  
2 18     18   103 use warnings;
  18         31  
  18         2899  
3              
4             package Class::AutoGenerate::Declare;
5              
6             require Exporter;
7              
8             our $VERSION = 0.05;
9              
10             our @ISA = qw( Exporter );
11             our @EXPORT = qw(
12             declare requiring generates
13             extends uses requires defines
14             generate_from conclude_with source_code source_file
15             next_rule last_rule
16             );
17              
18 18     18   119 use Scalar::Util qw/ reftype /;
  18         33  
  18         30777  
19              
20             =head1 NAME
21              
22             Class::AutoGenerate::Declare - Declarations for an auto-generating class loader
23              
24             =head1 SYNOPSIS
25              
26             # Create a customized class loader (auto-generator)
27             package My::ClassLoader;
28             use Class::AutoGenerate -base;
29              
30             # Define a matching rule that generates some code...
31             requiring 'Some::**::Class' => generates { qq{
32             sub print_my_middle_names { print $1,"\n" }
33             } };
34              
35             =head1 DESCRIPTION
36              
37             B I'm trying this idea out. Please let me know what you think by contacting me using the information listed under L. This is an experiment and any and all aspects of the API are up for revision at this point and I'm not even sure I'll maintain it, but I hope it will be found useful to myself and others.
38              
39             You do not use this class directly, but it contains the documentation for how to declare a new auto-generating class loader. To use this class, just tell L that you are building a base class:
40              
41             package My::ClassLoader;
42             use Class::AutoGenerate -base;
43              
44             This will then import the declarations described here into your class loader so that you can define your auto-generation rules.
45              
46             =head1 DECLARATIONS
47              
48             =head2 requiring PATTERN => generates { ... };
49              
50             The C rule tests the pattern against the given package name and runs the C block if there's a match. The pattern can be any of the following:
51              
52             =over
53              
54             =item Package Name
55              
56             If you provide an exact package name (one containing only letters, numbers, underscores, and colons), then only that exact name will be matched.
57              
58             For example:
59              
60             requiring 'TestApp::Model::Flooble' => ...
61              
62             would only match when exactly C was required or used.
63              
64             =item Package Glob
65              
66             If you provide a pattern string containing one or more wildcards, the pattern will match any package matching the wildcard pattern. This is very similar to how file globs work, but we use "::" instead of "/" as our divider. There are three different wildcards available:
67              
68             =over
69              
70             =item 1 Single Asterisk (*). A single asterisk will match zero or more characters of a single package name component.
71              
72             For example:
73              
74             requiring '*::Model::*Collection' => ...
75              
76             will match C and C and C.
77              
78             =item 1 Double Asterisk (**). A double asterisk will match zero or more chaters of a package name, possibly spanning multiple double-colon (::) separators.
79              
80             For example:
81              
82             requiring '**::Model::**Collection' => ...
83              
84             will match C and C and C.
85              
86             =item 1 Question mark (?). A question mark will match exactly one character in a package name.
87              
88             For example:
89              
90             requiring 'TestApp??::Record' => ...
91              
92             will match C and C.
93              
94             =back
95              
96             Each occurrence of a wildcard will be captured for use in the L block. The first wildcard will be C<$1>, the second C<$2>, etc.
97              
98             For example:
99              
100             requiring 'TestApp??::**::*' => ...
101              
102             would match C and would have the following values available in C:
103              
104             $1 = '3';
105             $2 = '8';
106             $3 = 'A::Package::Name';
107             $4 = 'Blah';
108              
109             =item Regular Expression
110              
111             You may use a regular expression to match anything more complicated than this. (In fact, the previous matching mechanism are converted to regular expressions, but are convenient for handling the common cases.)
112              
113             For example:
114              
115             requiring qr/^(.*)::(\w+)::(\w+)(\d{2})$/ => ...
116              
117             Any captures performed in the regular expression will be available as C<$1>, C<$2>, etc. in the L block.
118              
119             =item Array of Matches
120              
121             Finally, you may also place a series of matches into an array. The given generates block will be used if any of the matches match a given module name.
122              
123             requiring [ 'App', 'App::**', qr/^SomeOther::(Thing|Whatsit)$/ ] => ...
124              
125             =back
126              
127             =cut
128              
129             sub _compile_glob_pattern($) {
130 53     53   90 my $glob = shift;
131              
132             # If it's a regexp, we don't want to compile it as if it's a glob!
133 53 100 66     193 return $glob if ref $glob and ref $glob eq 'Regexp';
134              
135             # The following code was adapted from Jifty::Dispatcher of trunk r2520
136            
137             # Escape and normalize
138 50         114 $glob = quotemeta($glob);
139 50         218 $glob =~ s{(?:\\:)(?:\\:)}{::}g;
140              
141             # Check to see if they have any glob wildcards
142 50         180 my $has_capture = ( $glob =~ / \\ [\*\?] /x );
143 50 100       124 if ($has_capture) {
144              
145             # Double-asterisk will match anything
146 31         76 $glob =~ s{ \\ \* \\ \* }{([\\w:]*)}gx;
147              
148             # Single-asterisk will match any number of characters, but not colons
149 31         78 $glob =~ s{ \\ \* }{(\\w*)}gx;
150              
151             # Single-question mark will match a single character, but not colons
152 31         61 $glob =~ s{ \\ \? }{(\\w)}gx
153              
154             }
155              
156             # If they haven't asked ot capture anything in particular, capture all
157             else {
158 19         49 $glob = "($glob)";
159             }
160              
161             # Make a regexp
162 50         8088 return qr{^$glob$};
163             }
164              
165             # This variable used to communicate when declare { requiring ... }
166             our $declare_to = undef;
167              
168             sub _register_rules($$$) {
169 47     47   80 my $class = shift;
170 47         69 my $pattern = shift;
171 47         69 my $code = shift;
172              
173             # If an array, push the generates code for each pattern
174 47 100 100     285 if (ref $pattern and reftype $pattern eq 'ARRAY') {
175 2         17 &_register_rules($class, $_, $code) foreach @$pattern;
176             }
177              
178             # Otherwise, compile globs and push in the pattern => code rule thingies
179             else {
180 45         157 $pattern = _compile_glob_pattern $pattern;
181 45 100       508 push @{ $declare_to || $class->_declarations }, [ $pattern => $code ];
  45         463  
182             }
183             }
184              
185             sub requiring($$) {
186 35     35 1 74 my $pattern = shift;
187 35         59 my $code = shift;
188              
189             # Register a new rule (or rules) for the caller
190 35         160 my $package = caller;
191 35         177 _register_rules $package, $pattern, $code;
192             }
193              
194             =head2 generates { ... }
195              
196             This handles the second half of the requiring/generates statement. The code block may contain any code you need, but you'll probably want it to contain statements for generating code to go into the required class.
197              
198             requiring 'My::*' => generates {
199             my $name = $1;
200              
201             extends "My::Base::$name";
202              
203             uses 'Scalar::Util', 'looks_like_number';
204              
205             defines '$scalar' => 14;
206             defines '@array' => [ 1, 2, 3 ];
207             defines '%hash' => { x => 1, y => 2 };
208              
209             defines 'package_name' => sub { $package };
210             defines 'short_name' => sub { $name };
211             };
212              
213             If we included the rule above, intantiated the class loader, and then ran:
214              
215             use My::Flipper;
216              
217             A class would be generated named C that uses C as its only base class, imports the C function from L, defines a scalar package variable C<$scalar> set to 14, an array package variable, C<@array>, set to C<(1, 2, 3)>, a hash package variable named C<%hash> set to C<(x => 1, y => 2)>, and two subroutines named C and C.
218              
219             =cut
220              
221 35     35 1 3706 sub generates(&) { shift }
222              
223             =head2 declare { ... };
224              
225             A declare block may be used to wrap your class loader code, but is not required. The block will be passed a single argument, C<$self>, which is the initialized class loader object. It is helpful if you need a reference to your C<$self>.
226              
227             For example,
228              
229             package My::Classloader;
230             use Class::Autogenerate -base;
231              
232             declare {
233             my $self = shift;
234             my $base = $self->{base};
235              
236             requiring "$base::**' => generates {};
237             };
238              
239             1;
240              
241             # later...
242             use My::Classloader;
243             BEGIN { My::Classloader->new( base => 'Foo' ) };
244              
245             You may have multiple C blocks in your class loader.
246              
247             It is important to note that the C block modifies the semantics of how the class loader is built. Normally, the C rules are all generated and associated with the class loader package immediately. A C block causes all rules inside the block to be held until the class loader is constructed. During construction, the requiring rules in C blocks are built and associated with the constructed class loader instance directly.
248              
249             =cut
250              
251             sub declare(&) {
252 1     1 1 13 my $code = shift;
253              
254             # Wrap that code in a little more code that sets things up
255             my $declaration = sub {
256 2     2   3 my $self = shift;
257              
258             # $declare_to signals to requiring to register rules differently
259 2         4 local $declare_to = [];
260 2         14 $code->($self);
261 2         8 return @$declare_to;
262 1         6 };
263              
264             # Register the declaration
265 1         4 my $package = caller;
266 1         2 push @{ $package->_declarations }, $declaration;
  1         17  
267             }
268              
269             =head2 extends CLASSES
270              
271             This subroutine is used with L to mark the generated class as extending the named class or classes. This pushes the named classes into the C<@ISA> array for the class when it is generated.
272              
273             B You need to ask Perl to include this class on your own. This is not exactly equivalent to in this regard. If a class might not be included already, you may wish to do something like the following:
274              
275             require My::Parent::Class;
276             extends 'My::Parent::Class';
277              
278             =cut
279              
280             sub extends(@) {
281 18     18   190 no strict 'refs';
  18         37  
  18         42953  
282 25     25 1 1197 push @{ $Class::AutoGenerate::package . '::ISA' }, @_;
  25         455  
283             }
284              
285             =head2 uses CLASS, ARGS
286              
287             This subroutine states that the generated class uses another package. The first argument is the class to use and the remaining arguments are passed to the import method of the used class (the first argument may also be a version number, see L).
288              
289             =cut
290              
291             sub uses($;@) {
292 2     2 1 14 my $class = shift;
293 2         6 my $args = join ', ', map { "'".quotemeta($_)."'" } @_;
  1         7  
294 2 100       8 $args = " ($args)" if $args;
295              
296 2         176 eval "package $Class::AutoGenerate::package; use $class$args;";
  1         7  
  1         487  
  1         516  
  1         6  
297 2 50       2606 die $@ if $@;
298             }
299              
300             =head2 requires EXPR
301              
302             This is similar to L, but uses L instead of C.
303              
304             =cut
305              
306             sub requires($) {
307 3     3 1 15 my $expr = shift;
308              
309             # Make a nice string unless it's barewordable... this might not always do
310             # the right thing...
311 3 100       25 $expr = '"' . quotemeta($expr) . '"' unless $expr =~ /^[\w:]+$/;
312              
313 3         204 eval "package $Class::AutoGenerate::package; require $expr;";
314 3 50       5783 die $@ if $@;
315             }
316              
317             =head2 defines NAME => VALUE
318              
319             This is the general purpose definition declaration. If the given name starts with a dollar sign ($), then a scalar value is created. If the given name starts with an at sign (@), then an array value is added to the class. If the given starts with a percent sign (%), then a hash value will be generated. Finally, if it starts with a letter, underscore, or ampersand (&), a subroutine is added to the package.
320              
321             The given value must be appropriate for the type of definition being generated.
322              
323             =cut
324              
325             sub defines($$) {
326 30     30 1 567 my $name = shift;
327 30         55 my $value = shift;
328              
329             # It's a scalar
330 30 100       184 if ($name =~ s/^\$//) {
    100          
    100          
331 18     18   173 no strict 'refs';
  18         47  
  18         2256  
332 1         2 ${ $Class::AutoGenerate::package . '::' . $name } = $value;
  1         14  
333             }
334              
335             # It's an array
336             elsif ($name =~ s/^\@//) {
337 18     18   106 no strict 'refs';
  18         32  
  18         1566  
338 1         2 @{ $Class::AutoGenerate::package . '::' . $name } = @$value;
  1         6  
339             }
340              
341             # It's a hash
342             elsif ($name =~ s/^\%//) {
343 18     18   101 no strict 'refs';
  18         36  
  18         1507  
344 1         4 %{ $Class::AutoGenerate::package . '::' . $name } = %$value;
  1         15  
345             }
346              
347             # It's a method
348             else {
349 27         111 $name =~ s/^\&//;
350              
351 18     18   755 no strict 'refs';
  18         35  
  18         9253  
352 27         35 *{ $Class::AutoGenerate::package . '::' . $name } = $value;
  27         719  
353             }
354             }
355              
356             =head2 generate_from SOURCE
357              
358             If you need to inject code directly into the package generated, this is the general purpose way to do it. Just pass a string (or use one of the helpers L or L below) and that code will be evaluated within the new package.
359              
360             requiring 'Some::Class' => generates {
361             extends 'Class::Access::Fast';
362              
363             generate_from source_code qq{
364              
365             __PACKAGE__->mk_accessors( qw/ name title description / );
366              
367             };
368             };
369              
370             B If user input has any effect on the code generated, you should make certain that all input is carefully validated to prevent code injection.
371              
372             =cut
373              
374             sub generate_from($) {
375 3     3 1 10 my $source_code = shift;
376              
377 3     3 0 220 eval "package $Class::AutoGenerate::package; $source_code";
  3     3 0 642  
  3         547  
378 3 50       17 die $@ if $@;
379             }
380              
381             =head2 conclude_with SOURCE
382              
383             This is a special helper used in place of L for code that could cause a loop during code generation. This can occur because Perl does not realize that the generated module has been loaded until I the L block has been completely executed. Therefore, the use of C and C might cause a loop under certain conditions.
384              
385             Rather than try to explain who to contrive such a situation, here's a contrived example where C is helpful:
386              
387             package My::Util;
388             use UNIVERSAL::require; # helper that makes "Any::Class"->require; work
389              
390             sub require_helpers {
391             my $class = shift;
392             my $module = shift;
393              
394             for my $name ( qw( Bob Larry ) ) {
395             my $helper = "My::Thing::${module}::Helper::$name";
396             $helper->require;
397             }
398             }
399              
400             package My::ClassLoader;
401             use Class::AutoGenerate -base;
402              
403             use UNIVERSAL::require;
404              
405             requiring 'My::Thing::*' => generates {
406             my $module = $1;
407              
408             defines 'do_something' => sub { ... };
409              
410             conclude_with source_code "My::Util->require_helpers('$module');";
411             };
412              
413             requiring 'My::Thing::*::Helper::*' => generates {
414             my $module = $1;
415             my $name = $2;
416              
417             # We only make helpers for something that exists!
418             my $thing = "My::Thing::$module";
419             $thing->require or next_rule;
420              
421             defines 'help_with_something' => sub { ... };
422             };
423              
424             If we had used C rather than C in the code above, a loop would have been generated upon calling C. This would have resulted in a call to C in the sample, which would have resulted in a called to C, which would have resulted in another call to C to see if such a module exists. Unfortunately, since Perl hasn't yet recorded that "My::Thing::Flup" has already been loaded, this will fail.
425              
426             By using C, the code given is not executed until Perl has already noted that the class is loaded, so the loop stops and this code should execute successfully.
427              
428             B If user input has any effect on the code generated, you should make certain that all input is carefully validated to prevent code injection.
429              
430             =cut
431              
432             sub conclude_with($) {
433 2     2 1 4 my $code = shift;
434              
435 2         3 push @{ $Class::AutoGenerate::conclude_with }, $code;
  2         8  
436             }
437              
438             =head2 source_code SOURCE
439              
440             This method is purely for use with making your code a little easier to read. It doesn't do anything but return the argument passed to it.
441              
442             B If user input has any effect on the code generated, you should make certain that all input is carefully validated to prevent code injection.
443              
444             =cut
445              
446 3     3 1 27 sub source_code($) { shift }
447              
448             =head2 source_file FILENAME
449              
450             Given a file name, this evalutes the Perl in that file within the context of the package.
451              
452             requiring 'Another::Class' => generates {
453             generate_from source_file 'code_base.pl';
454             };
455              
456             B If user input has any effect on this file included, you should make certain that all input is carefully validated to prevent code injection.
457              
458             =cut
459              
460             sub source_file($) {
461 1     1 1 5 my $filename = shift;
462              
463             # Open the file...
464 1 50       49 open my $fh, '<', $filename or die "failed to open $filename: $!";
465              
466             # Slurp it down...
467 1         5 local $/;
468 1         42 return <$fh>;
469             }
470              
471             =head2 next_rule
472              
473             By calling the C statement, you will prevent the current L statement from finishing. Instead, it will quit and the next L rule will be tried.
474              
475             =cut
476              
477 1     1 1 21 sub next_rule() { die "NEXT_RULE\n" }
478              
479             =head2 last_rule
480              
481             The C statement causes the class loader to stop completely and return that it found no matching Perl modules.
482              
483             =cut
484              
485 1     1 1 14 sub last_rule() { die "LAST_RULE\n" }
486              
487             =head1 SEE ALSO
488              
489             L
490              
491             =head1 AUTHOR
492              
493             Andrew Sterling Hanenkamp C<< >>
494              
495             =head1 COPYRIGHT AND LICENSE
496              
497             Copyright 2007 Boomer Consulting, Inc.
498              
499             This program is free software and may be modified and distributed under the same terms as Perl itself.
500              
501             =cut
502              
503             1;