1 package MooseX::Declare::Context;
4 use Moose::Util::TypeConstraints;
7 use aliased 'Devel::Declare::Context::Simple', 'DDContext';
9 use namespace::clean -except => 'meta';
11 subtype 'MooseX::Declare::BlockCodePart',
13 where { @$_ > 1 and sub { grep { $_[0] eq $_ } qw( BEGIN END ) } -> ($_->[0]) };
15 subtype 'MooseX::Declare::CodePart',
16 as 'Str|MooseX::Declare::BlockCodePart';
22 builder => '_build_dd_context',
27 has _dd_init_args => (
30 default => sub { {} },
46 has preamble_code_parts => (
49 isa => 'ArrayRef[MooseX::Declare::CodePart]',
51 default => sub { [] },
53 add_preamble_code_parts => 'push',
57 has scope_code_parts => (
60 isa => 'ArrayRef[MooseX::Declare::CodePart]',
62 default => sub { [] },
64 add_scope_code_parts => 'push',
68 has cleanup_code_parts => (
71 isa => 'ArrayRef[MooseX::Declare::CodePart]',
73 default => sub { [] },
75 add_cleanup_code_parts => 'push',
76 add_early_cleanup_code_parts => 'unshift',
83 default => sub { [] },
87 sub inject_code_parts_here {
88 my ($self, @parts) = @_;
90 # get code to inject and rest of line
91 my $inject = $self->_joined_statements(\@parts);
92 my $linestr = $self->get_linestr;
94 # add code to inject to current line and inject it
95 substr($linestr, $self->offset, 0, "$inject");
96 $self->set_linestr($linestr);
104 # return next char in line
105 my $linestr = $self->get_linestr;
106 return substr $linestr, $self->offset, 1;
114 my $len = Devel::Declare::toke_scan_word($self->offset, 1);
117 my $linestr = $self->get_linestr;
118 return substr($linestr, $self->offset, $len);
121 sub inject_code_parts {
122 my ($self, %args) = @_;
124 # default to injecting cleanup code
125 $args{inject_cleanup_code_parts} = 1
126 unless exists $args{inject_cleanup_code_parts};
128 # add preamble and scope statements to injected code
130 $inject .= $self->_joined_statements('preamble');
131 $inject .= ';' . $self->_joined_statements('scope');
133 # if we should also inject the cleanup code
134 if ($args{inject_cleanup_code_parts}) {
135 $inject .= ';' . $self->scope_injector_call($self->_joined_statements('cleanup'));
141 if ($self->peek_next_char eq '{') {
142 $self->inject_if_block("$inject");
145 # there was no block to inject into
147 # require end of statement
148 croak "block or semi-colon expected after " . $self->declarator . " statement"
149 unless $self->peek_next_char eq ';';
151 # if we can't handle non-blocks, we expect one
152 croak "block expected after " . $self->declarator . " statement"
153 unless exists $args{missing_block_handler};
155 # delegate the processing of the missing block
156 $args{missing_block_handler}->($self, $inject, %args);
162 sub _joined_statements {
163 my ($self, $section) = @_;
165 # if the section was not an array reference, get the
166 # section contents of that name
167 $section = $self->${\"${section}_code_parts"}
170 # join statements via semicolon
171 # array references are expected to be in the form [FOO => 1, 2, 3]
172 # which would yield BEGIN { 1; 2; 3 }
173 return join '; ', map {
174 not( ref $_ ) ? $_ : do {
175 my ($block, @parts) = @$_;
176 sprintf '%s { %s }', $block, join '; ', @parts;
182 my ($self, $attrs) = @_;
184 # remember the constructor arguments for the delegated context
185 $self->_dd_init_args($attrs);
188 sub _build_dd_context {
191 # create delegated context with remembered arguments
192 return DDContext->new(%{ $self->_dd_init_args });
199 my $linestr = $self->get_linestr;
200 return if substr($linestr, $self->offset, 1) =~ /[{;]/;
203 # - provide a reserved_words attribute
204 # - allow keywords to consume reserved_words autodiscovery role
205 my $word = $self->peek_next_word;
206 return if !defined $word || $word =~ /^(?:extends|with|is)$/;
208 return scalar $self->strip_name;
217 MooseX::Declare::Context - Per-keyword declaration context
221 This is not a subclass of L<Devel::Declare::Context::Simple>, but it will
222 delegate all default methods and extend it with some attributes and methods
225 A context object will be instanciated for every keyword that is handled by
226 L<MooseX::Declare>. If handlers want to communicate with other handlers (for
227 example handlers that will only be setup inside a namespace block) it must
228 do this via the generated code.
234 A part of code represented by either a C<Str> or a L</BlockCodePart>.
238 An C<ArrayRef> with at least one element that stringifies to either C<BEGIN>
239 or C<END>. The other parts will be stringified and used as the body for the
240 generated block. An example would be this compiletime role composition:
242 ['BEGIN', 'with q{ MyRole }']
248 A required C<Str> containing the file the keyword was encountered in.
250 =head2 preamble_code_parts
252 An C<ArrayRef> of L</CodePart>s that will be used as preamble. A preamble in
253 this context means the beginning of the generated code.
255 =head2 scope_code_parts
257 These parts will come before the actual body and after the
258 L</preamble_code_parts>. It is an C<ArrayRef> of L</CodePart>s.
260 =head2 cleanup_code_parts
262 An C<ArrayRef> of L</CodePart>s that will not be directly inserted
263 into the code, but instead be installed in a handler that will run at
264 the end of the scope so you can do namespace cleanups and such.
268 An C<ArrayRef> that contains the stack of handlers. A keyword that was
269 only setup inside a scoped block will have the blockhandler be put in
274 All methods from L<Devel::Declare::Context::Simple> should be available and
275 will be delegated to an internally stored instance of it.
277 =head2 add_preamble_code_parts(CodePart @parts)
279 =head2 add_scope_code_parts(CodePart @parts)
281 =head2 add_cleanup_code_parts(CodePart @parts)
283 Object->add_preamble_code_parts (CodeRef @parts)
284 Object->add_scope_code_parts (CodeRef @parts)
285 Object->add_cleanup_code_parts (CodeRef @parts)
287 For these three methods please look at the corresponding C<*_code_parts>
288 attribute in the list above. These methods are merely convenience methods
289 that allow adding entries to the code part containers.
291 =head2 inject_code_parts_here
293 True Object->inject_code_parts_here (CodePart @parts)
295 Will inject the passed L</CodePart>s at the current position in the code.
297 =head2 peek_next_char
299 Str Object->peek_next_char ()
301 Will return the next char without stripping it from the stream.
303 =head2 inject_code_parts
305 Object->inject_code_parts (
306 Bool :$inject_cleanup_code_parts,
307 CodeRef :$missing_block_handler
310 This will inject the code parts from the attributes above at the current
311 position. The preamble and scope code parts will be inserted first. Then
312 then call to the cleanup code will be injected, unless the options
313 contain a key named C<inject_cleanup_code_parts> with a false value.
315 The C<inject_if_block> method will be called if the next char is a C<{>
316 indicating a following block.
318 If it is not a block, but a semi-colon is found and the options
319 contained a C<missing_block_handler> key was passed, it will be called
320 as method on the context object with the code to inject and the
321 options as arguments. All options that are not recognized are passed
322 through to the C<missing_block_handler>. You are well advised to prefix
323 option names in your extensions.
325 =head2 strip_name_and_options
327 List Object->strip_name_and_options ()
329 This will remove an identifier plus any options that follow it from the
330 stream. Options are things like C<is Trait>, C<with Role> and
331 C<extends ParentClass>. Currently, only these are supported.
333 The return value is a list with two values:
339 The name that was read.
341 =item HashRef $options
343 The options that followed the name. This is the returned format:
347 extends => ArrayRef[ParentClass],
348 with => ArrayRef[Role],
357 =item * L<MooseX::Declare>
359 =item * L<Devel::Declare>
361 =item * L<Devel::Declare::Context::Simple>
365 =head1 AUTHOR, COPYRIGHT & LICENSE
367 See L<MooseX::Declare>