441 lines
13 KiB
Perl
441 lines
13 KiB
Perl
package MooseX::Declare::Syntax::NamespaceHandling;
|
|
# ABSTRACT: Handle namespaced blocks
|
|
|
|
our $VERSION = '0.43';
|
|
|
|
use Moose::Role;
|
|
use Moose::Util qw( does_role );
|
|
use MooseX::Declare::Util qw( outer_stack_peek );
|
|
use Carp;
|
|
|
|
use aliased 'MooseX::Declare::Context::Namespaced';
|
|
use aliased 'MooseX::Declare::Context::WithOptions';
|
|
use aliased 'MooseX::Declare::Context::Parameterized';
|
|
use aliased 'MooseX::Declare::StackItem';
|
|
|
|
use namespace::autoclean;
|
|
|
|
#pod =head1 DESCRIPTION
|
|
#pod
|
|
#pod Allows the implementation of namespaced blocks like the
|
|
#pod L<role|MooseX::Declare::Syntax::Keyword::Role> and
|
|
#pod L<class|MooseX::Declare::Syntax::Keyword::Class> keyword handlers.
|
|
#pod
|
|
#pod Namespaces are automatically nested. Meaning that, for example, a C<class Bar>
|
|
#pod declaration inside another C<class Foo> block gives the inner one actually the
|
|
#pod name C<Foo::Bar>.
|
|
#pod
|
|
#pod =head1 CONSUMES
|
|
#pod
|
|
#pod =for :list
|
|
#pod * L<MooseX::Declare::Syntax::KeywordHandling>
|
|
#pod * L<MooseX::Declare::Syntax::InnerSyntaxHandling>
|
|
#pod
|
|
#pod =cut
|
|
|
|
with qw(
|
|
MooseX::Declare::Syntax::KeywordHandling
|
|
MooseX::Declare::Syntax::InnerSyntaxHandling
|
|
);
|
|
|
|
#pod =head1 REQUIRED METHODS
|
|
#pod
|
|
#pod =head2 handle_missing_block
|
|
#pod
|
|
#pod Object->handle_missing_block (Object $context, Str $body, %args)
|
|
#pod
|
|
#pod This must be implemented to decide what to do in case the statement is
|
|
#pod terminated rather than followed by a block. It will receive the context
|
|
#pod object, the produced code that needs to be injected, and all the arguments
|
|
#pod that were passed to the call to L<MooseX::Declare::Context/inject_code_parts>.
|
|
#pod
|
|
#pod The return value will be ignored.
|
|
#pod
|
|
#pod =cut
|
|
|
|
requires qw(
|
|
handle_missing_block
|
|
);
|
|
|
|
#pod =head1 EXTENDABLE STUB METHODS
|
|
#pod
|
|
#pod =head2 add_namespace_customizations
|
|
#pod
|
|
#pod =head2 add_optional_customizations
|
|
#pod
|
|
#pod Object->add_namespace_customizations (Object $context, Str $package, HashRef $options)
|
|
#pod Object->add_optional_customizations (Object $context, Str $package, HashRef $options)
|
|
#pod
|
|
#pod These will be called (in this order) by the L</parse> method. They allow specific hooks
|
|
#pod to attach before/after/around the customizations for the namespace and the provided
|
|
#pod options that are not attached to the namespace directly.
|
|
#pod
|
|
#pod While this distinction might seem superficial, we advise library developers facilitating
|
|
#pod this role to follow the precedent. This ensures that when another component needs to
|
|
#pod tie between the namespace and any additional customizations everything will run in the
|
|
#pod correct order. An example of this separation would be
|
|
#pod
|
|
#pod class Foo is mutable ...
|
|
#pod
|
|
#pod being an option of the namespace generation, while
|
|
#pod
|
|
#pod class Foo with Bar ...
|
|
#pod
|
|
#pod is an additional optional customization.
|
|
#pod
|
|
#pod =head2 handle_post_parsing
|
|
#pod
|
|
#pod Object->handle_post_parsing (Object $context, Str $package, Str | Object $name)
|
|
#pod
|
|
#pod Allows for additional modifications to the namespace after everything else has been
|
|
#pod done. It will receive the context, the fully qualified package name, and either a
|
|
#pod string with the name that was specified (might not be fully qualified, since
|
|
#pod namespaces can be nested) or the anonymous metaclass instance if no name was
|
|
#pod specified.
|
|
#pod
|
|
#pod The return value of this method will be the value returned to the user of the
|
|
#pod keyword. If you always return the C<$package> argument like this:
|
|
#pod
|
|
#pod sub handle_post_parsing {
|
|
#pod my ($self, $context, $package, $name) = @_;
|
|
#pod return $package;
|
|
#pod }
|
|
#pod
|
|
#pod and set this up in a C<foo> keyword handler, you can use it like this:
|
|
#pod
|
|
#pod foo Cthulhu {
|
|
#pod
|
|
#pod my $fhtagn = foo Fhtagn { }
|
|
#pod my $anon = foo { };
|
|
#pod
|
|
#pod say $fhtagn; # Cthulhu::Fhtagn
|
|
#pod say $anon; # some autogenerated package name
|
|
#pod }
|
|
#pod
|
|
#pod =head2 make_anon_metaclass
|
|
#pod
|
|
#pod Class::MOP::Class Object->make_anon_metaclass ()
|
|
#pod
|
|
#pod This method should be overridden if you want to provide anonymous namespaces.
|
|
#pod
|
|
#pod It does not receive any arguments for customization of the metaclass, because
|
|
#pod the configuration and customization will be done by L<MooseX::Declare> in the
|
|
#pod package of the generated class in the same way as in those that have specified
|
|
#pod names. This way ensures that anonymous and named namespaces are always handled
|
|
#pod equally.
|
|
#pod
|
|
#pod If you do not extend this method (it will return nothing by default), an error
|
|
#pod will be thrown when a user attempts to declare an anonymous namespace.
|
|
#pod
|
|
#pod =cut
|
|
|
|
sub add_namespace_customizations { }
|
|
sub add_optional_customizations { }
|
|
sub handle_post_parsing { }
|
|
sub make_anon_metaclass { }
|
|
|
|
around context_traits => sub { super, WithOptions, Namespaced };
|
|
|
|
sub parse_specification {
|
|
my ($self, $ctx) = @_;
|
|
|
|
$self->parse_namespace_specification($ctx);
|
|
$self->parse_option_specification($ctx);
|
|
|
|
return;
|
|
}
|
|
|
|
sub parse_namespace_specification {
|
|
my ($self, $ctx) = @_;
|
|
return scalar $ctx->strip_namespace;
|
|
}
|
|
|
|
sub parse_option_specification {
|
|
my ($self, $ctx) = @_;
|
|
return scalar $ctx->strip_options;
|
|
}
|
|
|
|
sub generate_inline_stack {
|
|
my ($self, $ctx) = @_;
|
|
|
|
return join ', ',
|
|
map { $_->serialize }
|
|
@{ $ctx->stack },
|
|
$self->generate_current_stack_item($ctx);
|
|
}
|
|
|
|
sub generate_current_stack_item {
|
|
my ($self, $ctx) = @_;
|
|
|
|
return StackItem->new(
|
|
identifier => $self->identifier,
|
|
is_dirty => $ctx->options->{is}{dirty},
|
|
is_parameterized => does_role($ctx, Parameterized) && $ctx->has_parameter_signature,
|
|
handler => ref($self),
|
|
namespace => $ctx->namespace,
|
|
);
|
|
}
|
|
|
|
#pod =method parse
|
|
#pod
|
|
#pod Any Object->parse (Object $context)
|
|
#pod
|
|
#pod This is the main handling routine for namespaces. It will remove the namespace
|
|
#pod name and its options. If the handler was invoked without a name, options or
|
|
#pod a following block, it is assumed that this is an instance of an autoquoted
|
|
#pod bareword like C<< class => "Foo" >>.
|
|
#pod
|
|
#pod The return value of the C<parse> method is also the value that is returned
|
|
#pod to the user of the keyword.
|
|
#pod
|
|
#pod =cut
|
|
|
|
sub parse {
|
|
my ($self, $ctx) = @_;
|
|
|
|
# keyword comes first
|
|
$ctx->skip_declarator;
|
|
|
|
# read the name and unwrap the options
|
|
$self->parse_specification($ctx);
|
|
|
|
my $name = $ctx->namespace;
|
|
|
|
my ($package, $anon);
|
|
|
|
# we have a name in the declaration, which will be used as package name
|
|
if (defined $name) {
|
|
$package = $name;
|
|
|
|
# there is an outer namespace stack item, meaning we namespace below
|
|
# it, if the name starts with ::
|
|
if (my $outer = outer_stack_peek $ctx->caller_file) {
|
|
$package = $outer . $package
|
|
if $name =~ /^::/;
|
|
}
|
|
}
|
|
|
|
# no name, no options, no block. Probably { class => 'foo' }
|
|
elsif (not(keys %{ $ctx->options }) and $ctx->peek_next_char ne '{') {
|
|
return;
|
|
}
|
|
|
|
# we have options and/or a block, but not name
|
|
else {
|
|
$anon = $self->make_anon_metaclass
|
|
or croak sprintf 'Unable to create an anonymized %s namespace', $self->identifier;
|
|
$package = $anon->name;
|
|
}
|
|
|
|
# namespace and mx:d initialisations
|
|
$ctx->add_preamble_code_parts(
|
|
"package ${package}",
|
|
sprintf(
|
|
"use %s %s => '%s', file => __FILE__, stack => [ %s ]",
|
|
$ctx->provided_by,
|
|
outer_package => $package,
|
|
$self->generate_inline_stack($ctx),
|
|
),
|
|
);
|
|
|
|
# allow consumer to provide specialisations
|
|
$self->add_namespace_customizations($ctx, $package);
|
|
|
|
# make options a separate step
|
|
$self->add_optional_customizations($ctx, $package);
|
|
|
|
# finish off preamble with a namespace cleanup
|
|
$ctx->add_preamble_code_parts(
|
|
$ctx->options->{is}->{dirty}
|
|
? 'use namespace::clean -except => [qw( meta )]'
|
|
: 'use namespace::autoclean'
|
|
);
|
|
|
|
# clean up our stack afterwards, if there was a name
|
|
$ctx->add_cleanup_code_parts(
|
|
['BEGIN',
|
|
'MooseX::Declare::Util::outer_stack_pop __FILE__',
|
|
],
|
|
);
|
|
|
|
# actual code injection
|
|
$ctx->inject_code_parts(
|
|
missing_block_handler => sub { $self->handle_missing_block(@_) },
|
|
);
|
|
|
|
# a last chance to change things
|
|
$self->handle_post_parsing($ctx, $package, defined($name) ? $name : $anon);
|
|
}
|
|
|
|
#pod =head1 SEE ALSO
|
|
#pod
|
|
#pod =for :list
|
|
#pod * L<MooseX::Declare>
|
|
#pod * L<MooseX::Declare::Syntax::MooseSetup>
|
|
#pod
|
|
#pod =cut
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=pod
|
|
|
|
=encoding UTF-8
|
|
|
|
=head1 NAME
|
|
|
|
MooseX::Declare::Syntax::NamespaceHandling - Handle namespaced blocks
|
|
|
|
=head1 VERSION
|
|
|
|
version 0.43
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Allows the implementation of namespaced blocks like the
|
|
L<role|MooseX::Declare::Syntax::Keyword::Role> and
|
|
L<class|MooseX::Declare::Syntax::Keyword::Class> keyword handlers.
|
|
|
|
Namespaces are automatically nested. Meaning that, for example, a C<class Bar>
|
|
declaration inside another C<class Foo> block gives the inner one actually the
|
|
name C<Foo::Bar>.
|
|
|
|
=head1 METHODS
|
|
|
|
=head2 parse
|
|
|
|
Any Object->parse (Object $context)
|
|
|
|
This is the main handling routine for namespaces. It will remove the namespace
|
|
name and its options. If the handler was invoked without a name, options or
|
|
a following block, it is assumed that this is an instance of an autoquoted
|
|
bareword like C<< class => "Foo" >>.
|
|
|
|
The return value of the C<parse> method is also the value that is returned
|
|
to the user of the keyword.
|
|
|
|
=head1 CONSUMES
|
|
|
|
=over 4
|
|
|
|
=item *
|
|
|
|
L<MooseX::Declare::Syntax::KeywordHandling>
|
|
|
|
=item *
|
|
|
|
L<MooseX::Declare::Syntax::InnerSyntaxHandling>
|
|
|
|
=back
|
|
|
|
=head1 REQUIRED METHODS
|
|
|
|
=head2 handle_missing_block
|
|
|
|
Object->handle_missing_block (Object $context, Str $body, %args)
|
|
|
|
This must be implemented to decide what to do in case the statement is
|
|
terminated rather than followed by a block. It will receive the context
|
|
object, the produced code that needs to be injected, and all the arguments
|
|
that were passed to the call to L<MooseX::Declare::Context/inject_code_parts>.
|
|
|
|
The return value will be ignored.
|
|
|
|
=head1 EXTENDABLE STUB METHODS
|
|
|
|
=head2 add_namespace_customizations
|
|
|
|
=head2 add_optional_customizations
|
|
|
|
Object->add_namespace_customizations (Object $context, Str $package, HashRef $options)
|
|
Object->add_optional_customizations (Object $context, Str $package, HashRef $options)
|
|
|
|
These will be called (in this order) by the L</parse> method. They allow specific hooks
|
|
to attach before/after/around the customizations for the namespace and the provided
|
|
options that are not attached to the namespace directly.
|
|
|
|
While this distinction might seem superficial, we advise library developers facilitating
|
|
this role to follow the precedent. This ensures that when another component needs to
|
|
tie between the namespace and any additional customizations everything will run in the
|
|
correct order. An example of this separation would be
|
|
|
|
class Foo is mutable ...
|
|
|
|
being an option of the namespace generation, while
|
|
|
|
class Foo with Bar ...
|
|
|
|
is an additional optional customization.
|
|
|
|
=head2 handle_post_parsing
|
|
|
|
Object->handle_post_parsing (Object $context, Str $package, Str | Object $name)
|
|
|
|
Allows for additional modifications to the namespace after everything else has been
|
|
done. It will receive the context, the fully qualified package name, and either a
|
|
string with the name that was specified (might not be fully qualified, since
|
|
namespaces can be nested) or the anonymous metaclass instance if no name was
|
|
specified.
|
|
|
|
The return value of this method will be the value returned to the user of the
|
|
keyword. If you always return the C<$package> argument like this:
|
|
|
|
sub handle_post_parsing {
|
|
my ($self, $context, $package, $name) = @_;
|
|
return $package;
|
|
}
|
|
|
|
and set this up in a C<foo> keyword handler, you can use it like this:
|
|
|
|
foo Cthulhu {
|
|
|
|
my $fhtagn = foo Fhtagn { }
|
|
my $anon = foo { };
|
|
|
|
say $fhtagn; # Cthulhu::Fhtagn
|
|
say $anon; # some autogenerated package name
|
|
}
|
|
|
|
=head2 make_anon_metaclass
|
|
|
|
Class::MOP::Class Object->make_anon_metaclass ()
|
|
|
|
This method should be overridden if you want to provide anonymous namespaces.
|
|
|
|
It does not receive any arguments for customization of the metaclass, because
|
|
the configuration and customization will be done by L<MooseX::Declare> in the
|
|
package of the generated class in the same way as in those that have specified
|
|
names. This way ensures that anonymous and named namespaces are always handled
|
|
equally.
|
|
|
|
If you do not extend this method (it will return nothing by default), an error
|
|
will be thrown when a user attempts to declare an anonymous namespace.
|
|
|
|
=head1 SEE ALSO
|
|
|
|
=over 4
|
|
|
|
=item *
|
|
|
|
L<MooseX::Declare>
|
|
|
|
=item *
|
|
|
|
L<MooseX::Declare::Syntax::MooseSetup>
|
|
|
|
=back
|
|
|
|
=head1 AUTHOR
|
|
|
|
Florian Ragwitz <rafl@debian.org>
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
|
|
|
This software is copyright (c) 2008 by Florian Ragwitz.
|
|
|
|
This is free software; you can redistribute it and/or modify it under
|
|
the same terms as the Perl 5 programming language system itself.
|
|
|
|
=cut
|