File Coverage

blib/lib/MooseX/Scaffold.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package MooseX::Scaffold;
2              
3 9     9   1002811 use warnings;
  9         103  
  9         326  
4 9     9   48 use strict;
  9         19  
  9         737  
5              
6             =head1 NAME
7              
8             MooseX::Scaffold - Template metaprogramming with Moose
9              
10             =head1 VERSION
11              
12             Version 0.05
13              
14             =cut
15              
16             our $VERSION = '0.05';
17              
18             =head1 SYNOPSIS
19              
20             package MyScaffolder;
21              
22             use MooseX::Scaffold;
23              
24             MooseX::Scaffold->setup_scaffolding_import;
25              
26             sub SCAFFOLD {
27             my $class = shift; my %given = @_;
28              
29             $class->has($given{kind} => is => 'ro', isa => 'Int', required => 1);
30              
31             # Using MooseX::ClassAttribute
32             $class->class_has(kind => is => 'ro', isa => 'Str', default => $given{kind});
33             }
34              
35             package MyAppleClass;
36              
37             use Moose;
38             use MooseX::ClassAttribute;
39             use MyScaffolder kind => 'apple';
40              
41             package MyBananaClass;
42              
43             use Moose;
44             use MooseX::ClassAttribute;
45             use MyScaffolder kind => 'banana';
46              
47             # ... meanwhile, back at the Batcave ...
48              
49             use MyAppleClass;
50             use MyBananaClass;
51              
52             my $apple = MyAppleClass->new(apple => 1);
53             my $banana = MyBananaClass->new(banana => 2);
54              
55             =head1 DESCRIPTION
56              
57             MooseX::Scaffold is a tool for creating or augmenting Moose classes on-the-fly.
58              
59             Scaffolding can be triggered when a C<use> is executed (any import arguments are passed
60             to the scaffold subroutine) or you can explicitly call MooseX::Scaffold->scaffold with the scaffolding
61             subroutine and the package name for the class.
62              
63             Depending on what you're trying to do, MooseX::Scaffold can behave in three different ways (Assume My::Class is the class
64             you're trying to create/augment):
65              
66             load_and_scaffold (scaffold) - Attempt to require My::Class from My/Class.pm or do Moose::Meta::Class->create('My::Class')
67             to make the package on-the-fly. Scaffold the result.
68              
69             load_or_scaffold (load) - Attempt to require My::Class from My/Class.pm and stop if that works. If no My/Class.pm is
70             found in @INC, then make a Moose class on-the-fly and scaffold it.
71             This option can be used to create a default class if one isn't found.
72              
73             scaffold_without_load - Don't attempt to require My::Class, just create it on-the-fly and scaffold it.
74              
75             =head1 METHODS
76              
77             =head2 MooseX::Scaffold->scaffold( ... )
78              
79             Scaffold a class by either loading it or creating it. You can pass through the following:
80              
81             scaffolder
82             scaffolding_package This should be either a subroutine (sub { ... }) or a package name. If a package name
83             is given, then the package should contain a subroutine called SCAFFOLD
84              
85             class
86             class_package The package name of resulting class
87              
88             load_or_scaffold Attempt to load $class_package first and do nothing successful. Otherwise create
89             $class_package and scaffold it
90              
91             scaffold_without_load Scaffold $class_package without attempting to load it first. Does not have
92             any effect if $class_package has been loaded already
93              
94             no_class_attribute Set this to 1 to disable applying the MooseX::ClassAttribute meta-role
95             on class creation. This has no effect if the class is loaded (If you
96             want class_has with a loaded class, make sure to 'use MooseX::ClassAttribute')
97              
98             =head2 MooseX::Scaffold->load_and_scaffold( ... )
99              
100             An alias for ->scaffold
101              
102             =head2 MooseX::Scaffold->load_or_scaffold( ... )
103              
104             An alias for ->scaffold with C<load_or_scaffold> set to 1
105              
106             =head2 MooseX::Scaffold->load( ... )
107              
108             An alias for ->load_or_scaffold
109              
110             =head2 MooseX::Scaffold->scaffold_without_load( ... )
111              
112             An alias for ->scaffold with C<scaffold_without_load> set to 1
113              
114             =head2 MooseX::Scaffold->build_scaffolding_import( ... )
115              
116             Return an anonymous subroutine suitable for use an an import function
117              
118             Anything passable to ->scaffold is fair game. In addition:
119              
120             scaffolder This will default to the package of caller() if unspecified
121              
122             chain_import An (optional) subroutine that will goto'd after scaffolding is complete
123              
124             =head2 MooseX::Scaffold->setup_scaffolding_import( ... )
125              
126             Install an import subroutine. By default, caller() will be used for the exporting package, but
127             another may be specified.
128              
129             Anything passable to ->build_scaffolding_import is fair game. In addition:
130              
131             exporter
132             exporting_package The package that will house the import subroutine (the scaffolding will trigger
133             when the package is used or imported)
134              
135             =cut
136              
137 9     9   15172 use Class::Inspector;
  9         50417  
  9         339  
138 9     9   8937 use Carp::Clan;
  9         20742  
  9         64  
139 9     9   26072 use Moose();
  0            
  0            
140             no Moose;
141             use Moose::Exporter;
142             use MooseX::ClassAttribute();
143              
144             use MooseX::Scaffold::Class;
145              
146             =head2 MooseX::Scaffold->load_package( $package )
147              
148             =head2 MooseX::Scaffold->load_class( $class )
149              
150             A convenience method that will attempt to require $package or $class if not already loaded
151              
152             Essentially does ...
153              
154             eval "require $package;" or die $@
155              
156             ... but uses Class::Inspector to check for $package existence first (%INC is not trustworthy)
157              
158             =cut
159              
160             sub load_package {
161             my $self = shift;
162             my $package = shift;
163             return 1 if Class::Inspector->loaded($package);
164             eval "require $package;" or die $@;
165             return 1; # FIXME
166             }
167              
168             sub load_class {
169             return shift->load_package(@_);
170             }
171              
172             sub setup_scaffolding_import {
173             my $self = shift;
174             my %given = @_;
175              
176             my $exporting_package = $given{exporting_package};
177             $exporting_package ||= $given{exporter} ? delete $given{exporter} : scalar caller;
178              
179             my $scaffolder = $given{scaffolder} ||= scalar caller;
180              
181             my ( $import, $unimport ) = $self->build_scaffolding_import( %given );
182              
183             eval "package $exporting_package;";
184             croak "Couldn't open exporting package $exporting_package since: $@" if $@;
185              
186             no strict 'refs';
187             *{ $exporting_package . '::import' } = $import;
188             }
189              
190             sub build_scaffolding_import {
191             my $self = shift;
192             my %given = @_;
193              
194             my $scaffolder = $given{scaffolder} ||= scalar caller;
195             my $chain_import = $given{chain_import};
196              
197             return sub {
198             my $CALLER = Moose::Exporter::_get_caller(@_);
199             my $exporting_package = shift;
200              
201             return if $CALLER eq 'main';
202              
203             # TODO Check to see if $CALLER is a Moose::Object?
204             $self->scaffold(
205             class_package => $CALLER,
206             exporting_package => $exporting_package,
207             %given, \@_
208             );
209              
210             goto &$chain_import if $chain_import;
211             };
212             }
213              
214             sub load {
215             my $self = shift;
216             return $self->scaffold(@_, load_or_scaffold => 1);
217             }
218              
219             sub load_or_scaffold {
220             my $self = shift;
221             return $self->load(@_);
222             }
223              
224             sub load_and_scaffold {
225             my $self = shift;
226             return $self->scaffold(@_);
227             }
228              
229             sub scaffold_without_load {
230             my $self = shift;
231             return $self->scaffold(@_, scaffold_without_load => 1);
232             }
233              
234             sub scaffold {
235             my $self = shift;
236             my $arguments = [];
237             $arguments = pop @_ if ref $_[-1] eq 'ARRAY';
238             my %given = @_;
239              
240             my $class_package = $given{class_package} || $given{class};
241             my $scaffolder = $given{scaffolding_package} || $given{scaffolder};
242             my $load_or_scaffold = $given{load_or_scaffold};
243             my $scaffold_without_load = $given{scaffold_without_load};
244             my $no_class_attribute = $given{no_class_attribute};
245              
246             if (Class::Inspector->loaded($class_package)) {
247             return if ! $scaffold_without_load && $load_or_scaffold;
248             }
249             else {
250             if (! $scaffold_without_load && Class::Inspector->installed($class_package)) {
251             eval "require $class_package;";
252             die $@ if $@;
253             return if $load_or_scaffold;
254             }
255             else {
256             my $meta = Moose::Meta::Class->create($class_package);
257             unless ($no_class_attribute) {
258             MooseX::ClassAttribute->init_meta( for_class => $class_package );
259             }
260             }
261             }
262              
263             my $scaffolding_package;
264             if (ref $scaffolder eq 'CODE') {
265             }
266             else {
267             $scaffolding_package = $scaffolder;
268             $self->_load_scaffolding_package( $scaffolding_package );
269             $scaffolder = $scaffolding_package->can('SCAFFOLD');
270             croak "Unable to find method SCAFFOLD in package $scaffolding_package" unless $scaffolder;
271             }
272              
273             $self->_scaffold( $class_package, $scaffolder, @$arguments, scaffolding_package => $scaffolding_package );
274              
275             }
276              
277             sub _load_scaffolding_package {
278             my $self = shift;
279             my $scaffolding_package = shift;
280             return if Class::Inspector->loaded($scaffolding_package);
281             eval "require $scaffolding_package;" or croak "Unable to load scaffolding class $scaffolding_package since: $@";
282             }
283              
284             sub _scaffold {
285             my $self = shift;
286             my $class_package = shift;
287             my $scaffolder = shift;
288              
289             my $class = MooseX::Scaffold::Class->new($class_package);
290             $scaffolder->($class, @_, class_package => $class_package);
291             }
292              
293             sub parent_package {
294             my $self = shift;
295             my $package = shift;
296             return $self->repackage($package, undef, shift);
297             }
298              
299             sub child_package {
300             my $self = shift;
301             my $package = shift;
302             return $self->repackage($package, shift);
303             }
304              
305             sub repackage {
306             my $self = shift;
307             my $package = shift;
308             my $replacement = shift;
309             my $count = shift;
310              
311             $count = 0 unless defined $count && length $count;
312              
313             return $package unless $count >= 1;
314            
315             my @package = split m/::/, $package;
316             pop @package while $count--;
317             push @package, $replacement if defined $replacement && length $replacement;
318             return join '::', @package;
319             }
320              
321             =head1 AUTHOR
322              
323             Robert Krimen, C<< <rkrimen at cpan.org> >>
324              
325             =head1 SOURCE
326              
327             You can contribute or fork this project via GitHub:
328              
329             L<http://github.com/robertkrimen/moosex-scaffold/tree/master>
330              
331             git clone git://github.com/robertkrimen/moosex-scaffold.git MooseX-Scaffold
332              
333             =head1 BUGS
334              
335             Please report any bugs or feature requests to C<bug-moosex-classscaffold at rt.cpan.org>, or through
336             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=MooseX-Scaffold>. I will be notified, and then you'll
337             automatically be notified of progress on your bug as I make changes.
338              
339              
340              
341              
342             =head1 SUPPORT
343              
344             You can find documentation for this module with the perldoc command.
345              
346             perldoc MooseX::Scaffold
347              
348              
349             You can also look for information at:
350              
351             =over 4
352              
353             =item * RT: CPAN's request tracker
354              
355             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=MooseX-Scaffold>
356              
357             =item * AnnoCPAN: Annotated CPAN documentation
358              
359             L<http://annocpan.org/dist/MooseX-Scaffold>
360              
361             =item * CPAN Ratings
362              
363             L<http://cpanratings.perl.org/d/MooseX-Scaffold>
364              
365             =item * Search CPAN
366              
367             L<http://search.cpan.org/dist/MooseX-Scaffold>
368              
369             =back
370              
371              
372             =head1 ACKNOWLEDGEMENTS
373              
374              
375             =head1 COPYRIGHT & LICENSE
376              
377             Copyright 2008 Robert Krimen, all rights reserved.
378              
379             This program is free software; you can redistribute it and/or modify it
380             under the same terms as Perl itself.
381              
382              
383             =cut
384              
385             1; # End of MooseX::Scaffold