File Coverage

blib/lib/POE/Declare/Meta.pm
Criterion Covered Total %
statement 140 158 88.6
branch 22 38 57.8
condition 0 2 0.0
subroutine 34 35 97.1
pod 4 7 57.1
total 200 240 83.3


line stmt bran cond sub pod time code
1             package POE::Declare::Meta;
2              
3             =pod
4              
5             =head1 NAME
6              
7             POE::Declare::Meta - Metadata object that describes a POE::Declare class
8              
9             =head1 DESCRIPTION
10              
11             B objects are constructed and used internally by
12             L during class construction. B objects
13             are not created directly.
14              
15             Access to the meta object for a L class is via the exported
16             C function.
17              
18             =head1 METHODS
19              
20             =cut
21              
22 6     6   114 use 5.008007;
  6         17  
  6         303  
23 6     6   37 use strict;
  6         9  
  6         180  
24 6     6   27 use warnings;
  6         117  
  6         169  
25 6     6   22 use Carp ();
  6         31  
  6         546  
26 6     6   11029 use File::Temp ();
  6         85574  
  6         461  
27 6     6   42 use Scalar::Util 1.19 ();
  6         140  
  6         96  
28 6     6   26 use Params::Util 1.00 ();
  6         183  
  6         80  
29 6     6   3808 use Class::ISA 0.33 ();
  6         12235  
  6         103  
30 6     6   4257 use Class::Inspector 1.22 ();
  6         17554  
  6         124  
31              
32 6     6   63 use vars qw{$VERSION $DEBUG};
  6         460  
  6         334  
33             BEGIN {
34 6     6   22 $VERSION = '0.59';
35 4         83 $DEBUG = !! $DEBUG;
36             }
37              
38 4     4   23 use constant DEBUG => $DEBUG;
  4         10  
  4         311  
39              
40 4     4   2593 use POE::Declare::Meta::Slot ();
  4         11  
  4         77  
41 4     4   2270 use POE::Declare::Meta::Message ();
  4         10  
  4         86  
42 4     4   2371 use POE::Declare::Meta::Event ();
  4         11  
  4         73  
43 4     4   2237 use POE::Declare::Meta::Timeout ();
  4         11  
  4         69  
44 4     4   24 use POE::Declare::Meta::Attribute ();
  4         6  
  4         58  
45 4     4   2551 use POE::Declare::Meta::Internal ();
  4         11  
  4         67  
46 4     4   22 use POE::Declare::Meta::Param ();
  4         8  
  4         137  
47              
48             use Class::XSAccessor 1.10 {
49 4         36 getters => {
50             name => 'name',
51             alias => 'alias',
52             sequence => 'sequence',
53             compiled => 'compiled',
54             },
55 4     4   24 };
  4         82  
56              
57              
58              
59              
60              
61             #####################################################################
62             # Constructor
63              
64             sub new {
65 8     8 0 17 my $class = shift;
66              
67             # The name of the class
68 8         17 my $name = shift;
69 8 50       252 unless ( Params::Util::_CLASS($name) ) {
70 0         0 Carp::croak("Invalid class name '$name'");
71             }
72 8 50       172 unless ( Class::Inspector->loaded($name) ) {
73 0         0 Carp::croak("Class $name is not loaded");
74             }
75 8 50       382 unless ( $name->isa('POE::Declare::Object') ) {
76 0         0 Carp::croak("Class $name is not a POE::Declare::Object subclass");
77             }
78              
79             # Create the object
80 8         61 my $self = bless {
81             name => $name,
82             alias => $name,
83             sequence => 0,
84             attr => { },
85             }, $class;
86              
87 8         38 $self;
88             }
89              
90              
91              
92              
93              
94             #####################################################################
95             # Accessors
96              
97             =pod
98              
99             =head2 name
100              
101             The C accessor returns the name of the class for this meta instance.
102              
103             =cut
104              
105             # sub name {
106             # $_[0]->{name};
107             # }
108              
109             =pod
110              
111             =head2 alias
112              
113             The C accessor returns the alias root string that will be used for
114             objects that are created of this type.
115              
116             Normally this will be identical to the class C but may be changed
117             at constructor time.
118              
119             =cut
120              
121             # sub alias {
122             # $_[0]->{alias};
123             # }
124              
125             =pod
126              
127             =head2 sequence
128              
129             Because each object has its own L, each session also needs
130             its own session alias, and the session alias is derived from a combination
131             of the C method an an incrementing C value.
132              
133             The C accessor returns the most recently requested value from the
134             sequence. As with sequence in SQL, not all values pulled from the sequence
135             will necesarily be used in an object, and objects will not necesarily have
136             incrementing sequence values.
137              
138             =cut
139              
140             # sub sequence {
141             # $_[0]->{sequence};
142             # }
143              
144              
145              
146              
147              
148             #####################################################################
149             # Methods
150              
151             =pod
152              
153             =head2 next_alias
154              
155             The C method generates and returns a new session alias,
156             by taking the C base string and appending an incremented
157             C value.
158              
159             The typical alias string returned will look something like
160             C<'My::Class.123'>.
161              
162             =cut
163              
164             sub next_alias {
165 4     4 1 37 $_[0]->{alias} . '.' . ++$_[0]->{sequence};
166             }
167              
168             =pod
169              
170             =head2 super_path
171              
172             The C method is provided as a convenience, and returns a list
173             of the inheritance path for the class.
174              
175             It is equivalent to C.
176              
177             =cut
178              
179             sub super_path {
180 74     74 1 302 Class::ISA::self_and_super_path( $_[0]->name );
181             }
182              
183             =pod
184              
185             =head2 attr
186              
187             my $attribute = My::Class->meta->attr('foo');
188              
189             The C method is used to get a single named attribute meta object
190             within the class meta object.
191              
192             Returns a L object or C if no such
193             named attribute exists.
194              
195             =cut
196              
197             sub attr {
198 60     60 1 3200 my $self = shift;
199 60         79 my $name = shift;
200 60         169 foreach my $c ( $self->super_path ) {
201 86 50       1993 my $meta = $POE::Declare::META{$c} or next;
202 86 100       282 my $attr = $meta->{attr}->{$name} or next;
203 48         246 return $attr;
204             }
205 12         34 return undef;
206             }
207              
208             # Fetch all named attributes (from this or parents)
209             sub attrs {
210 6     6 0 11 my $self = shift;
211 6         12 my %hash = ();
212 6         19 foreach my $c ( $self->super_path ) {
213 12 50       223 my $meta = $POE::Declare::META{$c} or next;
214 12         22 my $attr = $meta->{attr};
215 12         33 foreach ( keys %$attr ) {
216 54         115 $hash{$_} = $attr->{$_};
217             }
218             }
219 6         33 return values %hash;
220             }
221              
222              
223              
224              
225              
226             #####################################################################
227             # Compilation
228              
229             sub as_perl {
230 8     8 0 18 my $self = shift;
231 8         40 my $name = $self->name;
232 8         20 my $attr = $self->{attr};
233              
234             # Go over all our methods, and add any required events
235 8         49 my $methods = Class::Inspector->methods($name, 'expanded');
236 8         6473 foreach my $method ( @$methods ) {
237 392         475 my $mname = $method->[2];
238 392         484 my $mcode = $method->[3];
239 392         629 my $maddr = Scalar::Util::refaddr($mcode);
240 392 100       978 my $mevent = $POE::Declare::EVENT{$maddr} or next;
241 24         62 my $mattr = $self->attr($mname);
242 24 100       66 if ( $mattr ) {
243             # Make sure the existing attribute is an event
244 12 50       70 next if $mattr->isa('POE::Declare::Meta::Event');
245 0         0 Carp::croak("Event '$mname' in $name clashes with non-event in parent class");
246 0         0 next;
247             }
248              
249             # Add an attribute for the event
250 12         21 my $class = $mevent->[0];
251 12         36 my @param = @$mevent[1..$#$mevent];
252 12         140 $self->{attr}->{$mname} = $class->new(
253             name => $mname,
254             @param,
255             );
256             }
257              
258             # Get all the package fragments
259 28         164 my $code = join "\n", (
260             "package $name;",
261             "",
262             "BEGIN {",
263             " no strict 'refs';",
264             " delete \${\"\${name}::\"}{'meta'};",
265             " use strict;",
266             "}",
267             "",
268             "sub meta () { \$POE::Declare::META{'$name'} }",
269             map {
270 8         64 $attr->{$_}->as_perl
271             } sort keys %$attr
272             );
273              
274             # Load the code
275 8         20 if ( DEBUG ) {
276             # Compile the combined code via a temp file
277             my ($fh, $filename) = File::Temp::tempfile();
278             $fh->print("$code\n\n1;\n");
279             close $fh;
280             require $filename;
281             unlink $filename;
282              
283             # Print the debugging output
284             my @trace = map {
285             s/\s*[{;]$//;
286             s/^s/ s/;
287             s/^p/\np/;
288             "$_\n"
289             } grep {
290             /^(?:package|sub)\b/
291             } split /\n/, $code;
292             print STDERR @trace, "\n$name code saved as $filename\n\n";
293             } else {
294 4   0 4 1 26 eval("$code\n\n1;\n");
  4     4   21  
  4     4   228  
  4     4   20  
  4     5   6  
  4         94  
  4         8  
  4         225  
  4         24  
  4         6  
  4         39  
  8         819  
  5         1471  
295 8 50       36 die $@ if $@;
296 8 50       31 Carp::croak("Failed to compile code for $name") if $@;
297             }
298              
299             return (
300 8         193 $self->{compiled} = 1
301             );
302             }
303              
304             # sub compiled {
305             # $_[0]->{compiled};
306             # }
307              
308              
309              
310              
311              
312             #####################################################################
313             # Run-Time Support Methods
314              
315             # Resolve the inline states for a class
316             sub _package_states {
317 4     4   7 my $self = shift;
318 9 100       1636 unless ( exists $self->{_package_states} ) {
319             # Cache for speed reasons
320 8         34 $self->{_package_states} = [
321             sort map {
322 18         76 $_->name
323             } grep {
324 2         10 $_->isa('POE::Declare::Meta::Event')
325             } $self->attrs
326             ];
327             }
328 4 50       15 if ( wantarray ) {
329 4         7 return @{$self->{_package_states}};
  4         43  
330             } else {
331 0         0 return $self->{_package_states};
332             }
333             }
334              
335             # Resolve the parameter list
336             sub _params {
337 6     11   81 my $self = shift;
338 6 100       23 unless ( exists $self->{_params} ) {
339             # Cache for speed reasons
340 6         42 $self->{_params} = [
341             sort map {
342 18         94 $_->name
343             } grep {
344 2         11 $_->isa('POE::Declare::Meta::Param')
345             } $self->attrs
346             ];
347             }
348 6 50       20 if ( wantarray ) {
349 6         8 return @{$self->{_params}};
  6         31  
350             } else {
351 0         0 return $self->{_params};
352             }
353             }
354              
355             # Resolve the message list
356             sub _messages {
357 2     2   4 my $self = shift;
358 2 50       10 unless ( exists $self->{_messages} ) {
359             # Cache for speed reasons
360 0         0 $self->{_messages} = [
361             sort map {
362 18         117 $_->name
363             } grep {
364 2         9 $_->isa('POE::Declare::Meta::Message')
365             } $self->attrs
366             ];
367             }
368 2 50       9 if ( wantarray ) {
369 2         2 return @{$self->{_messages}};
  2         11  
370             } else {
371 0           return $self->{_messages};
372             }
373             }
374              
375             # Resolve the timeout list
376             sub _timeouts {
377 0     0     my $self = shift;
378 0 0         unless ( exists $self->{_timeouts} ) {
379             # Cache for speed reasons
380 0           $self->{_timeouts} = [
381             sort map {
382 0           $_->name
383             } grep {
384 0           $_->isa('POE::Declare::Meta::Timeout')
385             } $self->attrs
386             ];
387             }
388 0 0         if ( wantarray ) {
389 0           return @{$self->{_timeouts}};
  0            
390             } else {
391 0           return $self->{_timeouts};
392             }
393             }
394              
395             1;
396              
397             =pod
398              
399             =head1 SUPPORT
400              
401             Bugs should be always be reported via the CPAN bug tracker at
402              
403             L
404              
405             For other issues, or commercial enhancement or support, contact the author.
406              
407             =head1 AUTHORS
408              
409             Adam Kennedy Eadamk@cpan.orgE
410              
411             =head1 SEE ALSO
412              
413             L
414              
415             =head1 COPYRIGHT
416              
417             Copyright 2006 - 2012 Adam Kennedy.
418              
419             This program is free software; you can redistribute
420             it and/or modify it under the same terms as Perl itself.
421              
422             The full text of the license can be found in the
423             LICENSE file included with this module.
424              
425             =cut