use strict;
use warnings;

package MooseX::Declare;

use aliased 'MooseX::Declare::Syntax::Keyword::Class',      'ClassKeyword';
use aliased 'MooseX::Declare::Syntax::Keyword::Role',       'RoleKeyword';
use aliased 'MooseX::Declare::Syntax::Keyword::Namespace',  'NamespaceKeyword';

use namespace::clean;

our $VERSION = '0.32';

sub import {
    my ($class, %args) = @_;

    my $caller = caller();

    strict->import;
    warnings->import;

    for my $keyword ($class->keywords) {
        $keyword->setup_for($caller, %args, provided_by => $class);
    }
}

sub keywords {
    ClassKeyword->new(identifier => 'class'),
    RoleKeyword->new(identifier => 'role'),
    NamespaceKeyword->new(identifier => 'namespace'),
}

1;

__END__

=head1 NAME

MooseX::Declare - Declarative syntax for Moose

=head1 SYNOPSIS

    use MooseX::Declare;

    class BankAccount {
        has 'balance' => ( isa => 'Num', is => 'rw', default => 0 );

        method deposit (Num $amount) {
            $self->balance( $self->balance + $amount );
        }

        method withdraw (Num $amount) {
            my $current_balance = $self->balance();
            ( $current_balance >= $amount )
                || confess "Account overdrawn";
            $self->balance( $current_balance - $amount );
        }
    }

    class CheckingAccount extends BankAccount {
        has 'overdraft_account' => ( isa => 'BankAccount', is => 'rw' );

        before withdraw (Num $amount) {
            my $overdraft_amount = $amount - $self->balance();
            if ( $self->overdraft_account && $overdraft_amount > 0 ) {
                $self->overdraft_account->withdraw($overdraft_amount);
                $self->deposit($overdraft_amount);
            }
        }
    }

=head1 DESCRIPTION

This module provides syntactic sugar for Moose, the postmodern object system
for Perl 5. When used, it sets up the C<class> and C<role> keywords.

=head1 KEYWORDS

=head2 class

    class Foo { ... }

    my $anon_class = class { ... };

Declares a new class. The class can be either named or anonymous, depending on
whether or not a classname is given. Within the class definition Moose and
L<MooseX::Method::Signatures> are set up automatically in addition to the other
keywords described in this document. At the end of the definition the class
will be made immutable. namespace::autoclean is injected to clean up Moose and
other imports for you.

Because of the way the options are parsed, you cannot have a class named "is",
"with" or "extends".

It's possible to specify options for classes:

=over 4

=item extends

    class Foo extends Bar { ... }

Sets a superclass for the class being declared.

=item with

    class Foo with Role             { ... }
    class Foo with Role1 with Role2 { ... }
    class Foo with (Role1, Role2)   { ... }

Applies a role or roles to the class being declared.

=item is mutable

    class Foo is mutable { ... }

Causes the class not to be made immutable after its definition.

Options can also be provided for anonymous classes using the same syntax:

    my $meta_class = class with Role;

=back

=head2 role

    role Foo { ... }

    my $anon_role = role { ... };

Declares a new role. The role can be either named or anonymous, depending on
whether or not a name is given. Within the role definition Moose::Role and
MooseX::Method::Signatures are set up automatically in addition to the other
keywords described in this document. Again, namespace::autoclean is injected to
clean up Moose::Role and other imports for you.

It's possible to specify options for roles:

=over 4

=item with

    role Foo with Bar { ... }

Applies a role to the role being declared.

=back

=head2 before / after / around / override / augment

    before   foo ($x, $y, $z) { ... }
    after    bar ($x, $y, $z) { ... }
    around   baz ($x, $y, $z) { ... }
    override moo ($x, $y, $z) { ... }
    augment  kuh ($x, $y, $z) { ... }

Add a method modifier. Those work like documented in L<Moose|Moose>, except for
the slightly nicer syntax and the method signatures, which work like documented
in L<MooseX::Method::Signatures|MooseX::Method::Signatures>.

For the C<around> modifier an additional argument called C<$orig> is
automatically set up as the invocant for the method.

=head2 clean

Sometimes you don't want the automatic cleaning the C<class> and C<role>
keywords provide using namespace::autoclean. In those cases you can specify the
C<dirty> trait for your class or role:

    use MooseX::Declare;
    class Foo is dirty { ... }

This will prevent cleaning of your namespace, except for the keywords imported
from C<Moose> or C<Moose::Role>. Additionally, a C<clean> keyword is provided,
which allows you to explicitly clean all functions that were defined prior to
calling C<clean>. Here's an example:

    use MooseX::Declare;
    class Foo is dirty {
        sub helper_function { ... }
        clean;
        method foo ($stuff) { ...; return helper_function($stuff); }
    }

With that, the helper function won't be available as a method to a user of your
class, but you're still able to use it inside your class.

=head1 NOTE ON IMPORTS

When creating a class with MooseX::Declare like:

    use MooseX::Declare;
    class Foo { ... }

What actually happens is something like this:

    {
        package Foo;
        use Moose;
        use namespace::autoclean;
        ...
        __PACKAGE__->meta->make_immutable;
    }

So if you declare imports outside the class, the symbols get imported into the
C<main::> namespace, not the class' namespace. The symbols then cannot be called
from within the class:

    use MooseX::Declare;
    use Data::Dump qw/dump/;
    class Foo {
        method dump($value) { return dump($value) } # Data::Dump::dump IS NOT in Foo::
        method pp($value)   { $self->dump($value) } # an alias for our dump method
    }

To solve this, only import MooseX::Declare outside the class definition
(because you have to). Make all other imports inside the class definition.

    use MooseX::Declare;
    class Foo {
        use Data::Dump qw/dump/;
        method dump($value) { return dump($value) } # Data::Dump::dump IS in Foo::
        method pp($value)   { $self->dump($value) } # an alias for our dump method
    }

    Foo->new->dump($some_value);
    Foo->new->pp($some_value);

B<NOTE> that the import C<Data::Dump::dump()> and the method C<Foo::dump()>,
although having the same name, do not conflict with each other, because the
imported C<dump> function will be cleaned during compile time, so only the
method remains there at run time. If you want to do more esoteric things with
imports, have a look at the C<clean> keyword and the C<dirty> trait.

=head1 SEE ALSO

L<Moose>

L<Moose::Role>

L<MooseX::Method::Signatures>

L<namespace::autoclean>

vim syntax: L<http://www.vim.org/scripts/script.php?script_id=2526>

emacs syntax: L<http://github.com/jrockway/cperl-mode>

Geany syntax + notes: L<http://www.cattlegrid.info/blog/2009/09/moosex-declare-geany-syntax.html>

=head1 AUTHOR

Florian Ragwitz E<lt>rafl@debian.orgE<gt>

With contributions from:

=over 4

=item Ash Berlin E<lt>ash@cpan.orgE<gt>

=item Chas. J. Owens IV E<lt>chas.owens@gmail.comE<gt>

=item Chris Prather E<lt>chris@prather.orgE<gt>

=item Dave Rolsky E<lt>autarch@urth.orgE<gt>

=item Devin Austin E<lt>dhoss@cpan.orgE<gt>

=item Hans Dieter Pearcey E<lt>hdp@cpan.orgE<gt>

=item Justin Hunter E<lt>justin.d.hunter@gmail.comE<gt>

=item Matt Kraai E<lt>kraai@ftbfs.orgE<gt>

=item Michele Beltrame E<lt>arthas@cpan.orgE<gt>

=item Nelo Onyiah E<lt>nelo.onyiah@gmail.comE<gt>

=item nperez E<lt>nperez@cpan.orgE<gt>

=item Piers Cawley E<lt>pdcawley@bofh.org.ukE<gt>

=item Rafael Kitover E<lt>rkitover@io.comE<gt>

=item Robert ’phaylon’ Sedlacek E<lt>rs@474.atE<gt>

=item Stevan Little E<lt>stevan.little@iinteractive.comE<gt>

=item Tomas Doran E<lt>bobtfish@bobtfish.netE<gt>

=item Yanick Champoux E<lt>yanick@babyl.dyndns.orgE<gt>

=back

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2008, 2009  Florian Ragwitz

Licensed under the same terms as perl itself.

=cut
