NAME
    MooseX::Extended - Extend Moose with safe defaults and useful features

VERSION
    version 0.03

SYNOPSIS
        package My::Names {
            use MooseX::Extended;
            use MooseX::Extended::Types
              qw(compile Num NonEmptyStr Str PositiveInt ArrayRef);
            use List::Util 'sum';

            # the distinction between `param` and `field` makes it easier to
            # see which are available to `new`
            param _name   => ( isa => NonEmptyStr, init_arg => 'name' );
            param title   => ( isa => Str,         required => 0 );

            # forbidden in the constructor
            field created => ( isa => PositiveInt, default  => sub { time } );

            sub name ($self) {
                my $title = $self->title;
                my $name  = $self->_name;
                return $title ? "$title $name" : $name;
            }

            sub add ( $self, $args ) {
                state $check = compile( ArrayRef [Num, 1] ); # at least one number
                ($args) = $check->($args);
                return sum( $args->@* );
            }

            sub warnit ($self) {
                carp("this is a warning");
            }
        }

DESCRIPTION
    This module is ALPHA code.

    This class attempts to create a safer version of Moose that defaults to
    read-only attributes and is easier to read and write.

    It tries to bring some of the lessons learned from the Corinna project
    <https://github.com/Ovid/Cor>, while acknowledging that you can't always
    get what you want (such as true encapsulation and true methods).

    This:

        package My::Class {
            use MooseX::Extended;

            ... your code here
        }

    Is sort of the equivalent to:

        package My::Class {
            use v5.20.0;
            use Moose;
            use MooseX::StrictConstructor;
            use feature qw( signatures postderef postderef_qq);
            no warnings qw( experimental::signatures experimental::postderef );
            use namespace::autoclean;
            use Carp;
            use mro 'c3';

            ... your code here

            __PACKAGE__->meta->make_immutable;
        }
        1;

    It also exports two functions which are similar to Moose "has": "param"
    and "field".

    A "param" is a required parameter (defaults may be used). A "field" is
    not allowed to be passed to the constructor.

    Note that the "has" function is still available, even if it's not
    needed.

IMMUTABILITY
  Making Your Class Immutable
    You no longer need to end your Moose classes with:

        __PACKAGE__->meta->make_immutable;

    That prevents further changes to the class and provides some
    optimizations to make the code run much faster. However, it's somewhat
    annoying to type. We do this for you, via "B::Hooks::AtRuntime". You no
    longer need to do this yourself.

  Making Your Instance Immutable
    By default, attributes defined via "param" and "field" are read-only.
    However, if they contain a reference, you can fetch the reference,
    mutate it, and now everyone with a copy of that reference has mutated
    state.

    To handle that, we offer a new "clone => $clone_type" pair for
    attributes.

    See the MooseX::Extended::Manual::Cloning documentation.

OBJECT CONSTRUCTION
    Objection construction for MooseX::Extended is like Moose, so no changes
    are needed. However, in addition to "has", we also provide "param" and
    "field" attributes, both of which are "is => 'ro'" by default.

    The "param" is *required*, whether by passing it to the constructor, or
    using "default" or "builder".

    The "field" is *forbidden* in the constructor and lazy by default.

    Here's a short example:

        package Silly::Name {
            use MooseX::Extended;
            use MooseX::Extended::Types qw(compile Num NonEmptyStr Str);

            # these default to 'ro' (but you can override that) and are required
            param _name => ( isa => NonEmptyStr, init_arg => 'name' );
            param title => ( isa => Str,         required => 0 );

            # fields must never be passed to the constructor
            # note that ->title and ->name are guaranteed to be set before
            # this because fields are lazy by default
            field name => (
                isa     => NonEmptyStr,
                default => sub ($self) {
                    my $title = $self->title;
                    my $name  = $self->_name;
                    return $title ? "$title $name" : $name;
                },
            );
        }

    See MooseX::Extended::Manual::Construction for a full explanation.

ATTRIBUTE SHORTCUTS
    When using "field" or "param", we have some attribute shortcuts:

        param name => (
            isa       => NonEmptyStr,
            writer    => 1,   # set_name
            reader    => 1,   # get_name
            predicate => 1,   # has_name
            clearer   => 1,   # clear_name
            builder   => 1,   # _build_name
        );

        sub _build_name ($self) {
            ...
        }

    See MooseX::Extended::Manual::Shortcuts for a full explanation.

INVALID ATTRIBUTE NAMES
    The following Moose code will print "WhoAmI". However, the second
    attribute name is clearly invalid.

        package Some::Class {
            use Moose;

            has name   => ( is => 'ro' );
            has '-bad' => ( is => 'ro' );
        }

        my $object = Some::Class->new( name => 'WhoAmI' );
        say $object->name;

    "MooseX::Extended" will throw a
    "Moose::Exception::InvalidAttributeDefinition" exception if it
    encounters an illegal method name for an attribute.

    This also applies to various attributes which allow method names, such
    as "clone", "builder", "clearer", "writer", "reader", and "predicate".

DEBUGGER SUPPORT
    When running MooseX::Extended under the debugger, there are some
    behavioral differences you should be aware of.

    *   Your classes won't be immutable

        Ordinarily, we call "__PACKAGE__->meta->make_immutable" for you.
        This relies on B::Hooks::AtRuntime's "after_runtime" function.
        However, that runs too late under the debugger and dies. Thus, we
        disable this feature under the debugger. Your classes may run a bit
        slower, but hey, it's the debugger!

    *   "namespace::autoclean" will frustrate you

        It's very frustrating when running under the debugger and doing
        this:

                13==>       my $total = sum(3,4,5);
                DB<4>
                Undefined subroutine &main::sum called at (eval 423) ...

        We had removed "namespace::autoclean" when running under the
        debugger, but backed that out:
        <https://github.com/Ovid/moosex-extreme/issues/11>.

MANUAL
    *   MooseX::Extended::Manual::Overview

    *   MooseX::Extended::Manual::Construction

    *   MooseX::Extended::Manual::Shortcuts

    *   MooseX::Extended::Manual::Cloning

RELATED MODULES
    *   MooseX::Extended::Types is included in the distribution.

        This provides core types for you.

    *   MooseX::Extended::Role is included in the distribution.

        "MooseX::Extended", but for roles.

TODO
    Some of this may just be wishful thinking. Some of this would be
    interesting if others would like to collaborate.

  Tests
    Tests! Many more tests! Volunteers welcome :)

  Configurable Types
    We provide "MooseX::Extended::Types" for convenience, along with the
    "declare" function. We should write up (and test) examples of extending
    it.

  Configurability
    Not everyone wants everything. In particular, using "MooseX::Extended"
    with DBIx::Class will be fatal because the latter allows unknown
    arguments to constructors. Or someone might want their "own" extreme
    Moose, requiring "v5.36.0" or not using the C3 mro. What's the best way
    to allow this?

    We probably want to do with with "Moose::Exporter", providing our own
    "import" method and calling the one generated by "Moose::Exporter", but
    I need to read the docs on that more carefully (and maybe the code).

  "BEGIN::Lift"
    This idea maybe belongs in "MooseX::Extended::OverKill", but ...

    Quite often you see things like this:

        BEGIN { extends 'Some::Parent' }

    Or this:

        sub serial_number; # required by a role, must be compile-time
        has serial_number => ( ... );

    In fact, there are a variety of Moose functions which would work better
    if they ran at compile-time instead of runtime, making them look a touch
    more like native functions. My various attempts at solving this have
    failed, but I confess I didn't try too hard.

NOTES
    There are a few things you might be interested to know about this module
    when evaluating it.

    Most of this is written with bog-standard Moose, so there's nothing
    terribly weird inside, but you may wish to note that we use
    B::Hooks::AtRuntime and true. They seem sane, but *caveat emptor*.

    This module was originally released on github as "MooseX::Extreme", but
    enough people pointed out that it was not extreme at all. That's why the
    repository is <https://github.com/Ovid/moosex-extreme/>.

SEE ALSO
    *   MooseX::Modern <https://metacpan.org/pod/MooseX::Modern>

    *   Corinna <https://github.com/Ovid/Cor>

AUTHOR
    Curtis "Ovid" Poe <curtis.poe@gmail.com>

COPYRIGHT AND LICENSE
    This software is Copyright (c) 2022 by Curtis "Ovid" Poe.

    This is free software, licensed under:

      The Artistic License 2.0 (GPL Compatible)

