File Coverage

blib/lib/MooseX/Declare/Context.pm
Criterion Covered Total %
statement 64 64 100.0
branch 16 20 80.0
condition 3 3 100.0
subroutine 13 13 100.0
pod 3 6 50.0
total 99 106 93.4


line stmt bran cond sub pod time code
1             package MooseX::Declare::Context;
2             # ABSTRACT: Per-keyword declaration context
3              
4             our $VERSION = '0.43';
5              
6 24     24   12524 use Moose 0.90;
  24         462  
  24         141  
7 24     24   106880 use Moose::Util::TypeConstraints qw(subtype as where);
  24         34  
  24         154  
8 24     24   9752 use Carp qw/croak/;
  24         36  
  24         1583  
9              
10 24     24   99 use aliased 'Devel::Declare::Context::Simple', 'DDContext';
  24         437  
  24         168  
11              
12 24     24   3298 use namespace::autoclean;
  24         37  
  24         182  
13              
14             #pod =head1 DESCRIPTION
15             #pod
16             #pod This is not a subclass of L<Devel::Declare::Context::Simple>, but it will
17             #pod delegate all default methods and extend it with some attributes and methods
18             #pod of its own.
19             #pod
20             #pod A context object will be instantiated for every keyword that is handled by
21             #pod L<MooseX::Declare>. If handlers want to communicate with other handlers (for
22             #pod example handlers that will only be setup inside a namespace block) it must
23             #pod do this via the generated code.
24             #pod
25             #pod In addition to all the methods documented here, all methods from
26             #pod L<Devel::Declare::Context::Simple> are available and will be delegated to an
27             #pod internally stored instance of it.
28             #pod
29             #pod =type BlockCodePart
30             #pod
31             #pod An C<ArrayRef> with at least one element that stringifies to either C<BEGIN>
32             #pod or C<END>. The other parts will be stringified and used as the body for the
33             #pod generated block. An example would be this compiletime role composition:
34             #pod
35             #pod ['BEGIN', 'with q{ MyRole }']
36             #pod
37             #pod =cut
38              
39             subtype 'MooseX::Declare::BlockCodePart',
40             as 'ArrayRef',
41             where { @$_ > 1 and sub { grep { $_[0] eq $_ } qw( BEGIN END ) } -> ($_->[0]) };
42              
43             #pod =type CodePart
44             #pod
45             #pod A part of code represented by either a C<Str> or a L</BlockCodePart>.
46             #pod
47             #pod =cut
48              
49             subtype 'MooseX::Declare::CodePart',
50             as 'Str|MooseX::Declare::BlockCodePart';
51              
52             has _dd_context => (
53             is => 'ro',
54             isa => DDContext,
55             required => 1,
56             builder => '_build_dd_context',
57             lazy => 1,
58             handles => qr/.*/,
59             );
60              
61             has _dd_init_args => (
62             is => 'rw',
63             isa => 'HashRef',
64             default => sub { {} },
65             required => 1,
66             );
67              
68              
69             has provided_by => (
70             is => 'ro',
71             isa => 'ClassName',
72             required => 1,
73             );
74              
75              
76             #pod =attr caller_file
77             #pod
78             #pod A required C<Str> containing the file the keyword was encountered in.
79             #pod
80             #pod =cut
81              
82             has caller_file => (
83             is => 'rw',
84             isa => 'Str',
85             required => 1,
86             );
87              
88             #pod =attr preamble_code_parts
89             #pod
90             #pod An C<ArrayRef> of L</CodePart>s that will be used as preamble. A preamble in
91             #pod this context means the beginning of the generated code.
92             #pod
93             #pod =method add_preamble_code_parts(CodePart @parts)
94             #pod
95             #pod Object->add_preamble_code_parts (CodeRef @parts)
96             #pod
97             #pod See L</add_cleanup_code_parts>.
98             #pod
99             #pod =cut
100              
101             has preamble_code_parts => (
102             traits => ['Array'],
103             is => 'ro',
104             isa => 'ArrayRef[MooseX::Declare::CodePart]',
105             required => 1,
106             default => sub { [] },
107             handles => {
108             add_preamble_code_parts => 'push',
109             },
110             );
111              
112             #pod =attr scope_code_parts
113             #pod
114             #pod These parts will come before the actual body and after the
115             #pod L</preamble_code_parts>. It is an C<ArrayRef> of L</CodePart>s.
116             #pod
117             #pod =method add_scope_code_parts(CodePart @parts)
118             #pod
119             #pod Object->add_scope_code_parts (CodeRef @parts)
120             #pod
121             #pod See L</add_cleanup_code_parts>.
122             #pod
123             #pod =cut
124              
125             has scope_code_parts => (
126             traits => ['Array'],
127             is => 'ro',
128             isa => 'ArrayRef[MooseX::Declare::CodePart]',
129             required => 1,
130             default => sub { [] },
131             handles => {
132             add_scope_code_parts => 'push',
133             },
134             );
135              
136             #pod =attr cleanup_code_parts
137             #pod
138             #pod An C<ArrayRef> of L</CodePart>s that will not be directly inserted
139             #pod into the code, but instead be installed in a handler that will run at
140             #pod the end of the scope so you can do namespace cleanups and such.
141             #pod
142             #pod =method add_cleanup_code_parts(CodePart @parts)
143             #pod
144             #pod Object->add_cleanup_code_parts (CodeRef @parts)
145             #pod
146             #pod For these three methods please look at the corresponding C<*_code_parts>
147             #pod attribute in the list above. These methods are merely convenience methods
148             #pod that allow adding entries to the code part containers.
149             #pod
150             #pod =cut
151              
152             has cleanup_code_parts => (
153             traits => ['Array'],
154             is => 'ro',
155             isa => 'ArrayRef[MooseX::Declare::CodePart]',
156             required => 1,
157             default => sub { [] },
158             handles => {
159             add_cleanup_code_parts => 'push',
160             add_early_cleanup_code_parts => 'unshift',
161             },
162             );
163              
164             #pod =attr stack
165             #pod
166             #pod An C<ArrayRef> that contains the stack of handlers. A keyword that was
167             #pod only setup inside a scoped block will have the blockhandler be put in
168             #pod the stack.
169             #pod
170             #pod =cut
171              
172             has stack => (
173             is => 'rw',
174             isa => 'ArrayRef',
175             default => sub { [] },
176             required => 1,
177             );
178              
179             #pod =method inject_code_parts_here
180             #pod
181             #pod True Object->inject_code_parts_here (CodePart @parts)
182             #pod
183             #pod Will inject the passed L</CodePart>s at the current position in the code.
184             #pod
185             #pod =cut
186              
187             sub inject_code_parts_here {
188 4     4 1 11 my ($self, @parts) = @_;
189              
190             # get code to inject and rest of line
191 4         13 my $inject = $self->_joined_statements(\@parts);
192 4         20 my $linestr = $self->get_linestr;
193              
194             # add code to inject to current line and inject it
195 4         52 substr($linestr, $self->offset, 0, "$inject");
196 4         53 $self->set_linestr($linestr);
197              
198 4         58 return 1;
199             }
200              
201             #pod =method peek_next_char
202             #pod
203             #pod Str Object->peek_next_char ()
204             #pod
205             #pod Will return the next char without stripping it from the stream.
206             #pod
207             #pod =cut
208              
209             sub peek_next_char {
210 66     66 1 110 my ($self) = @_;
211              
212             # return next char in line
213 66         233 my $linestr = $self->get_linestr;
214 66         839 return substr $linestr, $self->offset, 1;
215             }
216              
217             sub peek_next_word {
218 64     64 0 95 my ($self) = @_;
219              
220 64         193 $self->skipspace;
221              
222 64         723 my $len = Devel::Declare::toke_scan_word($self->offset, 1);
223 64 100       556 return unless $len;
224              
225 63         201 my $linestr = $self->get_linestr;
226 63         597 return substr($linestr, $self->offset, $len);
227             }
228              
229             #pod =method inject_code_parts
230             #pod
231             #pod Object->inject_code_parts (
232             #pod Bool :$inject_cleanup_code_parts,
233             #pod CodeRef :$missing_block_handler
234             #pod )
235             #pod
236             #pod This will inject the code parts from the attributes above at the current
237             #pod position. The preamble and scope code parts will be inserted first. Then
238             #pod then call to the cleanup code will be injected, unless the options
239             #pod contain a key named C<inject_cleanup_code_parts> with a false value.
240             #pod
241             #pod The C<inject_if_block> method will be called if the next char is a C<{>
242             #pod indicating a following block.
243             #pod
244             #pod If it is not a block, but a semi-colon is found and the options
245             #pod contained a C<missing_block_handler> key was passed, it will be called
246             #pod as method on the context object with the code to inject and the
247             #pod options as arguments. All options that are not recognized are passed
248             #pod through to the C<missing_block_handler>. You are well advised to prefix
249             #pod option names in your extensions.
250             #pod
251             #pod =cut
252              
253             sub inject_code_parts {
254 61     61 1 176 my ($self, %args) = @_;
255              
256             # default to injecting cleanup code
257             $args{inject_cleanup_code_parts} = 1
258 61 50       256 unless exists $args{inject_cleanup_code_parts};
259              
260             # add preamble and scope statements to injected code
261 61         69 my $inject;
262 61         295 $inject .= $self->_joined_statements('preamble');
263 61         195 $inject .= ';' . $self->_joined_statements('scope');
264              
265             # if we should also inject the cleanup code
266 61 50       193 if ($args{inject_cleanup_code_parts}) {
267 61         172 $inject .= ';' . $self->scope_injector_call($self->_joined_statements('cleanup'));
268             }
269              
270 61         1127 $inject .= ';';
271              
272             # we have a block
273 61 100       246 if ($self->peek_next_char eq '{') {
274 60         1041 $self->inject_if_block("$inject");
275             }
276              
277             # there was no block to inject into
278             else {
279             # require end of statement
280 1 50       11 croak "block or semi-colon expected after " . $self->declarator . " statement"
281             unless $self->peek_next_char eq ';';
282              
283             # if we can't handle non-blocks, we expect one
284             croak "block expected after " . $self->declarator . " statement"
285 1 50       11 unless exists $args{missing_block_handler};
286              
287             # delegate the processing of the missing block
288 1         5 $args{missing_block_handler}->($self, $inject, %args);
289             }
290              
291 61         3974 return 1;
292             }
293              
294             sub _joined_statements {
295 187     187   223 my ($self, $section) = @_;
296              
297             # if the section was not an array reference, get the
298             # section contents of that name
299 187 100       372 $section = $self->${\"${section}_code_parts"}
  183         5582  
300             unless ref $section;
301              
302             # join statements via semicolon
303             # array references are expected to be in the form [FOO => 1, 2, 3]
304             # which would yield BEGIN { 1; 2; 3 }
305             return join '; ', map {
306 374 100       2486 not( ref $_ ) ? $_ : do {
307 61         145 my ($block, @parts) = @$_;
308 61         682 sprintf '%s { %s }', $block, join '; ', @parts;
309             };
310 187         225 } @{ $section };
  187         329  
311             }
312              
313             sub BUILD {
314 129     129 0 114048 my ($self, $attrs) = @_;
315              
316             # remember the constructor arguments for the delegated context
317 129         4330 $self->_dd_init_args($attrs);
318             }
319              
320             sub _build_dd_context {
321 129     129   224 my ($self) = @_;
322              
323             # create delegated context with remembered arguments
324 129         207 return DDContext->new(%{ $self->_dd_init_args });
  129         3528  
325             }
326              
327             sub strip_word {
328 65     65 0 100 my ($self) = @_;
329              
330 65         349 $self->skipspace;
331 65         1254 my $linestr = $self->get_linestr;
332 65 100       961 return if substr($linestr, $self->offset, 1) =~ /[{;]/;
333              
334             # TODO:
335             # - provide a reserved_words attribute
336             # - allow keywords to consume reserved_words autodiscovery role
337 64         962 my $word = $self->peek_next_word;
338 64 100 100     919 return if !defined $word || $word =~ /^(?:extends|with|is)$/;
339              
340 61         392 return scalar $self->strip_name;
341             }
342              
343             #pod =head1 SEE ALSO
344             #pod
345             #pod =for :list
346             #pod * L<MooseX::Declare>
347             #pod * L<Devel::Declare>
348             #pod * L<Devel::Declare::Context::Simple>
349             #pod
350             #pod =cut
351              
352             1;
353              
354             __END__
355              
356             =pod
357              
358             =encoding UTF-8
359              
360             =head1 NAME
361              
362             MooseX::Declare::Context - Per-keyword declaration context
363              
364             =head1 VERSION
365              
366             version 0.43
367              
368             =head1 DESCRIPTION
369              
370             This is not a subclass of L<Devel::Declare::Context::Simple>, but it will
371             delegate all default methods and extend it with some attributes and methods
372             of its own.
373              
374             A context object will be instantiated for every keyword that is handled by
375             L<MooseX::Declare>. If handlers want to communicate with other handlers (for
376             example handlers that will only be setup inside a namespace block) it must
377             do this via the generated code.
378              
379             In addition to all the methods documented here, all methods from
380             L<Devel::Declare::Context::Simple> are available and will be delegated to an
381             internally stored instance of it.
382              
383             =head1 ATTRIBUTES
384              
385             =head2 caller_file
386              
387             A required C<Str> containing the file the keyword was encountered in.
388              
389             =head2 preamble_code_parts
390              
391             An C<ArrayRef> of L</CodePart>s that will be used as preamble. A preamble in
392             this context means the beginning of the generated code.
393              
394             =head2 scope_code_parts
395              
396             These parts will come before the actual body and after the
397             L</preamble_code_parts>. It is an C<ArrayRef> of L</CodePart>s.
398              
399             =head2 cleanup_code_parts
400              
401             An C<ArrayRef> of L</CodePart>s that will not be directly inserted
402             into the code, but instead be installed in a handler that will run at
403             the end of the scope so you can do namespace cleanups and such.
404              
405             =head2 stack
406              
407             An C<ArrayRef> that contains the stack of handlers. A keyword that was
408             only setup inside a scoped block will have the blockhandler be put in
409             the stack.
410              
411             =head1 METHODS
412              
413             =head2 add_preamble_code_parts(CodePart @parts)
414              
415             Object->add_preamble_code_parts (CodeRef @parts)
416              
417             See L</add_cleanup_code_parts>.
418              
419             =head2 add_scope_code_parts(CodePart @parts)
420              
421             Object->add_scope_code_parts (CodeRef @parts)
422              
423             See L</add_cleanup_code_parts>.
424              
425             =head2 add_cleanup_code_parts(CodePart @parts)
426              
427             Object->add_cleanup_code_parts (CodeRef @parts)
428              
429             For these three methods please look at the corresponding C<*_code_parts>
430             attribute in the list above. These methods are merely convenience methods
431             that allow adding entries to the code part containers.
432              
433             =head2 inject_code_parts_here
434              
435             True Object->inject_code_parts_here (CodePart @parts)
436              
437             Will inject the passed L</CodePart>s at the current position in the code.
438              
439             =head2 peek_next_char
440              
441             Str Object->peek_next_char ()
442              
443             Will return the next char without stripping it from the stream.
444              
445             =head2 inject_code_parts
446              
447             Object->inject_code_parts (
448             Bool :$inject_cleanup_code_parts,
449             CodeRef :$missing_block_handler
450             )
451              
452             This will inject the code parts from the attributes above at the current
453             position. The preamble and scope code parts will be inserted first. Then
454             then call to the cleanup code will be injected, unless the options
455             contain a key named C<inject_cleanup_code_parts> with a false value.
456              
457             The C<inject_if_block> method will be called if the next char is a C<{>
458             indicating a following block.
459              
460             If it is not a block, but a semi-colon is found and the options
461             contained a C<missing_block_handler> key was passed, it will be called
462             as method on the context object with the code to inject and the
463             options as arguments. All options that are not recognized are passed
464             through to the C<missing_block_handler>. You are well advised to prefix
465             option names in your extensions.
466              
467             =head1 TYPES
468              
469             =head2 BlockCodePart
470              
471             An C<ArrayRef> with at least one element that stringifies to either C<BEGIN>
472             or C<END>. The other parts will be stringified and used as the body for the
473             generated block. An example would be this compiletime role composition:
474              
475             ['BEGIN', 'with q{ MyRole }']
476              
477             =head2 CodePart
478              
479             A part of code represented by either a C<Str> or a L</BlockCodePart>.
480              
481             =head1 SEE ALSO
482              
483             =over 4
484              
485             =item *
486              
487             L<MooseX::Declare>
488              
489             =item *
490              
491             L<Devel::Declare>
492              
493             =item *
494              
495             L<Devel::Declare::Context::Simple>
496              
497             =back
498              
499             =head1 AUTHOR
500              
501             Florian Ragwitz <rafl@debian.org>
502              
503             =head1 COPYRIGHT AND LICENSE
504              
505             This software is copyright (c) 2008 by Florian Ragwitz.
506              
507             This is free software; you can redistribute it and/or modify it under
508             the same terms as the Perl 5 programming language system itself.
509              
510             =cut