Typo fix
[gitmo/Mouse.git] / lib / Mouse.pm
index bfe4be7..18c60d1 100644 (file)
-#!perl
 package Mouse;
 use strict;
 use warnings;
+use 5.006;
+use base 'Exporter';
 
-our $VERSION = '0.01';
+our $VERSION = '0.15';
+
+BEGIN {
+    if ($ENV{MOUSE_DEBUG}) {
+        *DEBUG = sub (){ 1 };
+    } else {
+        *DEBUG = sub (){ 0 };
+    }
+}
 
-use Sub::Exporter;
 use Carp 'confess';
 use Scalar::Util 'blessed';
+use Mouse::Util;
 
 use Mouse::Meta::Attribute;
 use Mouse::Meta::Class;
 use Mouse::Object;
-use Mouse::TypeRegistry;
-
-do {
-    my $CALLER;
-
-    my %exports = (
-        meta => sub {
-            my $meta = Mouse::Meta::Class->initialize($CALLER);
-            return sub { $meta };
-        },
-
-        extends => sub {
-            my $caller = $CALLER;
-            return sub {
-                $caller->meta->superclasses(@_);
-            };
-        },
-
-        has => sub {
-            return sub {
-                my $package = caller;
-                my $names = shift;
-                $names = [$names] if !ref($names);
-
-                for my $name (@$names) {
-                    Mouse::Meta::Attribute->create($package, $name, @_);
-                }
-            };
-        },
-
-        confess => sub {
-            return \&confess;
-        },
-
-        blessed => sub {
-            return \&blessed;
-        },
-    );
-
-    my $exporter = Sub::Exporter::build_exporter({
-        exports => \%exports,
-        groups  => { default => [':all'] },
-    });
-
-    sub import {
-        $CALLER = caller;
-
-        strict->import;
-        warnings->import;
-
-        my $meta = Mouse::Meta::Class->initialize($CALLER);
-        $meta->superclasses('Mouse::Object')
-            unless $meta->superclasses;
-
-        goto $exporter;
+use Mouse::Util::TypeConstraints;
+
+our @EXPORT = qw(extends has before after around blessed confess with);
+
+sub extends { Mouse::Meta::Class->initialize(caller)->superclasses(@_) }
+
+sub has {
+    my $meta = Mouse::Meta::Class->initialize(caller);
+
+    my $names = shift;
+    $names = [$names] if !ref($names);
+
+    for my $name (@$names) {
+        if ($name =~ s/^\+//) {
+            Mouse::Meta::Attribute->clone_parent($meta, $name, @_);
+        }
+        else {
+            Mouse::Meta::Attribute->create($meta, $name, @_);
+        }
+    }
+}
+
+sub before {
+    my $meta = Mouse::Meta::Class->initialize(caller);
+
+    my $code = pop;
+
+    for (@_) {
+        $meta->add_before_method_modifier($_ => $code);
+    }
+}
+
+sub after {
+    my $meta = Mouse::Meta::Class->initialize(caller);
+
+    my $code = pop;
+
+    for (@_) {
+        $meta->add_after_method_modifier($_ => $code);
+    }
+}
+
+sub around {
+    my $meta = Mouse::Meta::Class->initialize(caller);
+
+    my $code = pop;
+
+    for (@_) {
+        $meta->add_around_method_modifier($_ => $code);
+    }
+}
+
+sub with {
+    Mouse::Util::apply_all_roles((caller)[0], @_);
+}
+
+sub import {
+    my $class = shift;
+
+    strict->import;
+    warnings->import;
+
+    my $caller = caller;
+
+    # we should never export to main
+    if ($caller eq 'main') {
+        warn qq{$class does not export its sugar to the 'main' package.\n};
+        return;
     }
 
-    sub unimport {
-        my $caller = caller;
+    my $meta = Mouse::Meta::Class->initialize($caller);
+    $meta->superclasses('Mouse::Object')
+        unless $meta->superclasses;
 
+    no strict 'refs';
+    no warnings 'redefine';
+    *{$caller.'::meta'} = sub { $meta };
+
+    if (@_) {
+        __PACKAGE__->export_to_level( 1, $class, @_);
+    } else {
+        # shortcut for the common case of no type character
         no strict 'refs';
-        for my $keyword (keys %exports) {
-            next if $keyword eq 'meta'; # we don't delete this one
-            delete ${ $caller . '::' }{$keyword};
+        for my $keyword (@EXPORT) {
+            *{ $caller . '::' . $keyword } = *{__PACKAGE__ . '::' . $keyword};
         }
     }
-};
+}
+
+sub unimport {
+    my $caller = caller;
+
+    no strict 'refs';
+    for my $keyword (@EXPORT) {
+        delete ${ $caller . '::' }{$keyword};
+    }
+}
 
 sub load_class {
     my $class = shift;
-    return if ref($class);
-    return unless defined($class) && length($class);
 
+    if (ref($class) || !defined($class) || !length($class)) {
+        my $display = defined($class) ? $class : 'undef';
+        confess "Invalid class name ($display)";
+    }
+
+    return 1 if $class eq 'Mouse::Object';
     return 1 if is_class_loaded($class);
 
     (my $file = "$class.pm") =~ s{::}{/}g;
@@ -100,12 +143,28 @@ sub is_class_loaded {
 
     return 0 if ref($class) || !defined($class) || !length($class);
 
-    no strict 'refs';
-    return 1 if defined ${"${class}::VERSION"} || defined @{"${class}::ISA"};
-    foreach my $symbol (keys %{"${class}::"}) {
-            next if substr($symbol, -2, 2) eq '::';
-            return 1 if defined &{"${class}::${symbol}"};
+    # walk the symbol table tree to avoid autovififying
+    # \*{${main::}{"Foo::"}} == \*main::Foo::
+
+    my $pack = \*::;
+    foreach my $part (split('::', $class)) {
+        return 0 unless exists ${$$pack}{"${part}::"};
+        $pack = \*{${$$pack}{"${part}::"}};
     }
+
+    # check for $VERSION or @ISA
+    return 1 if exists ${$$pack}{VERSION}
+             && defined *{${$$pack}{VERSION}}{SCALAR};
+    return 1 if exists ${$$pack}{ISA}
+             && defined *{${$$pack}{ISA}}{ARRAY};
+
+    # check for any method
+    foreach ( keys %{$$pack} ) {
+        next if substr($_, -2, 2) eq '::';
+        return 1 if defined *{${$$pack}{$_}}{CODE};
+    }
+
+    # fail
     return 0;
 }
 
@@ -115,11 +174,7 @@ __END__
 
 =head1 NAME
 
-Mouse - Moose minus antlers
-
-=head1 VERSION
-
-Version 0.01 released ???
+Mouse - Moose minus the antlers
 
 =head1 SYNOPSIS
 
@@ -142,16 +197,44 @@ Version 0.01 released ???
 
     has 'z' => (is => 'rw', isa => 'Int');
 
-    #after 'clear' => sub {
-    #    my $self = shift;
-    #    $self->z(0);
-    #};
+    after 'clear' => sub {
+        my $self = shift;
+        $self->z(0);
+    };
 
 =head1 DESCRIPTION
 
-Moose.
+L<Moose> is wonderful.
+
+Unfortunately, it's a little slow. Though significant progress has been made
+over the years, the compile time penalty is a non-starter for some
+applications.
 
-=head1 INTERFACE
+Mouse aims to alleviate this by providing a subset of Moose's
+functionality, faster. In particular, L<Moose/has> is missing only a few
+expert-level features.
+
+We're also going as light on dependencies as possible. Most functions we use
+from L<Scalar::Util> are copied into this dist. L<Scalar::Util> is required if
+you'd like weak references; there's simply no way to do it from pure Perl.
+L<Class::Method::Modifiers> is required if you want support for L</before>,
+L</after>, and L</around>.
+
+=head2 MOOSE COMPAT
+
+Compatibility with Moose has been the utmost concern. Fewer than 1% of the
+tests fail when run against Moose instead of Mouse. Mouse code coverage is also
+over 96%. Even the error messages are taken from Moose. The Mouse code just
+runs the test suite 4x faster.
+
+The idea is that, if you need the extra power, you should be able to run
+C<s/Mouse/Moose/g> on your codebase and have nothing break. To that end,
+nothingmuch has written L<Squirrel> (part of this distribution) which will act
+as Mouse unless Moose is loaded, in which case it will act as Moose.
+
+Mouse also has the blessings of Moose's author, stevan.
+
+=head1 KEYWORDS
 
 =head2 meta -> Mouse::Meta::Class
 
@@ -161,10 +244,119 @@ Returns this class' metaclass instance.
 
 Sets this class' superclasses.
 
+=head2 before (method|methods) => Code
+
+Installs a "before" method modifier. See L<Moose/before> or
+L<Class::Method::Modifiers/before>.
+
+Use of this feature requires L<Class::Method::Modifiers>!
+
+=head2 after (method|methods) => Code
+
+Installs an "after" method modifier. See L<Moose/after> or
+L<Class::Method::Modifiers/after>.
+
+Use of this feature requires L<Class::Method::Modifiers>!
+
+=head2 around (method|methods) => Code
+
+Installs an "around" method modifier. See L<Moose/around> or
+L<Class::Method::Modifiers/around>.
+
+Use of this feature requires L<Class::Method::Modifiers>!
+
 =head2 has (name|names) => parameters
 
 Adds an attribute (or if passed an arrayref of names, multiple attributes) to
-this class.
+this class. Options:
+
+=over 4
+
+=item is => ro|rw
+
+If specified, inlines a read-only/read-write accessor with the same name as
+the attribute.
+
+=item isa => TypeConstraint
+
+Provides basic type checking in the constructor and accessor. Basic types such
+as C<Int>, C<ArrayRef>, C<Defined> are supported. Any unknown type is taken to
+be a class check (e.g. isa => 'DateTime' would accept only L<DateTime>
+objects).
+
+=item required => 0|1
+
+Whether this attribute is required to have a value. If the attribute is lazy or
+has a builder, then providing a value for the attribute in the constructor is
+optional.
+
+=item init_arg => Str | Undef
+
+Allows you to use a different key name in the constructor.  If undef, the
+attribue can't be passed to the constructor.
+
+=item default => Value | CodeRef
+
+Sets the default value of the attribute. If the default is a coderef, it will
+be invoked to get the default value. Due to quirks of Perl, any bare reference
+is forbidden, you must wrap the reference in a coderef. Otherwise, all
+instances will share the same reference.
+
+=item lazy => 0|1
+
+If specified, the default is calculated on demand instead of in the
+constructor.
+
+=item predicate => Str
+
+Lets you specify a method name for installing a predicate method, which checks
+that the attribute has a value. It will not invoke a lazy default or builder
+method.
+
+=item clearer => Str
+
+Lets you specify a method name for installing a clearer method, which clears
+the attribute's value from the instance. On the next read, lazy or builder will
+be invoked.
+
+=item handles => HashRef|ArrayRef
+
+Lets you specify methods to delegate to the attribute. ArrayRef forwards the
+given method names to method calls on the attribute. HashRef maps local method
+names to remote method names called on the attribute. Other forms of
+L</handles>, such as regular expression and coderef, are not yet supported.
+
+=item weak_ref => 0|1
+
+Lets you automatically weaken any reference stored in the attribute.
+
+Use of this feature requires L<Scalar::Util>!
+
+=item trigger => CodeRef
+
+Any time the attribute's value is set (either through the accessor or the constructor), the trigger is called on it. The trigger receives as arguments the instance, the new value, and the attribute instance.
+
+Mouse 0.05 supported more complex triggers, but this behavior is now removed.
+
+=item builder => Str
+
+Defines a method name to be called to provide the default value of the
+attribute. C<< builder => 'build_foo' >> is mostly equivalent to
+C<< default => sub { $_[0]->build_foo } >>.
+
+=item auto_deref => 0|1
+
+Allows you to automatically dereference ArrayRef and HashRef attributes in list
+context. In scalar context, the reference is returned (NOT the list length or
+bucket status). You must specify an appropriate type constraint to use
+auto_deref.
+
+=item lazy_build => 0|1
+
+Automatically define lazy => 1 as well as builder => "_build_$attr", clearer =>
+"clear_$attr', predicate => 'has_$attr' unless they are already defined.
+
+=back
 
 =head2 confess error -> BOOM
 
@@ -183,8 +375,8 @@ You may use L</extends> to replace the superclass list.
 
 =head2 unimport
 
-Please unimport Mouse so that if someone calls one of the keywords (such as
-L</extends>) it will break loudly instead breaking subtly.
+Please unimport Mouse (C<no Mouse>) so that if someone calls one of the
+keywords (such as L</extends>) it will break loudly instead breaking subtly.
 
 =head1 FUNCTIONS
 
@@ -194,10 +386,24 @@ This will load a given C<Class::Name> (or die if it's not loadable).
 This function can be used in place of tricks like
 C<eval "use $module"> or using C<require>.
 
-=head1 AUTHOR
+=head2 is_class_loaded Class::Name -> Bool
+
+Returns whether this class is actually loaded or not. It uses a heuristic which
+involves checking for the existence of C<$VERSION>, C<@ISA>, and any
+locally-defined method.
+
+=head1 AUTHORS
 
 Shawn M Moore, C<< <sartak at gmail.com> >>
 
+Yuval Kogman, C<< <nothingmuch at woobling.org> >>
+
+tokuhirom
+
+Yappo
+
+with plenty of code borrowed from L<Class::MOP> and L<Moose>
+
 =head1 BUGS
 
 No known bugs.