File Coverage

blib/lib/POE/Declare/Meta.pm
Criterion Covered Total %
statement 58 136 42.6
branch 0 34 0.0
condition n/a
subroutine 20 29 68.9
pod 3 5 60.0
total 81 204 39.7


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