X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoo.pm;h=2c02cf26e66c768b968070208b9a6e9d1f96a45c;hb=c45702917a553f4c038f0fc77869380b57da4742;hp=2f27387819a066125be2d854879380bc9c8491dc;hpb=dccea57d17d218806e5770d0044c85e30c38e8da;p=gitmo%2FRole-Tiny.git diff --git a/lib/Moo.pm b/lib/Moo.pm index 2f27387..2c02cf2 100644 --- a/lib/Moo.pm +++ b/lib/Moo.pm @@ -3,7 +3,7 @@ package Moo; use strictures 1; use Moo::_Utils; -our $VERSION = '0.009001'; # 0.9.1 +our $VERSION = '0.009004'; # 0.9.4 $VERSION = eval $VERSION; our %MAKERS; @@ -12,14 +12,16 @@ sub import { my $target = caller; my $class = shift; strictures->import; + return if $MAKERS{$target}; # already exported into this package *{_getglob("${target}::extends")} = sub { _load_module($_) for @_; - *{_getglob("${target}::ISA")} = \@_; + # Can't do *{...} = \@_ or 5.10.0's mro.pm stops seeing @ISA + @{*{_getglob("${target}::ISA")}{ARRAY}} = @_; }; *{_getglob("${target}::with")} = sub { require Moo::Role; die "Only one role supported at a time by with" if @_ > 1; - Moo::Role->apply_role_to_package($_[0], $target); + Moo::Role->apply_role_to_package($target, $_[0]); }; $MAKERS{$target} = {}; *{_getglob("${target}::has")} = sub { @@ -46,32 +48,356 @@ sub import { } sub _constructor_maker_for { - my ($class, $target) = @_; + my ($class, $target, $select_super) = @_; return unless $MAKERS{$target}; $MAKERS{$target}{constructor} ||= do { require Method::Generate::Constructor; + require Sub::Defer; + my ($moo_constructor, $con); + + if ($select_super && $MAKERS{$select_super}) { + $moo_constructor = 1; + $con = $MAKERS{$select_super}{constructor}; + } else { + my $t_new = $target->can('new'); + if ($t_new) { + if ($t_new == Moo::Object->can('new')) { + $moo_constructor = 1; + } elsif (my $defer_target = (Sub::Defer::defer_info($t_new)||[])->[0]) { + my ($pkg) = ($defer_target =~ /^(.*)::[^:]+$/); + if ($MAKERS{$pkg}) { + $moo_constructor = 1; + $con = $MAKERS{$pkg}{constructor}; + } + } + } else { + $moo_constructor = 1; # no other constructor, make a Moo one + } + }; + require Moo::_mro unless $moo_constructor; Method::Generate::Constructor ->new( package => $target, accessor_generator => do { require Method::Generate::Accessor; Method::Generate::Accessor->new; - } + }, + ($moo_constructor ? () + : (construction_string => '$class->next::method(@_)')) ) ->install_delayed - ->register_attribute_specs(do { - my @spec; - # using the -last- entry in @ISA means that classes created by - # Role::Tiny as N roles + superclass will still get the attributes - # from the superclass - if (my $super = do { no strict 'refs'; ${"${target}::ISA"}[-1] }) { - if (my $con = $MAKERS{$super}{constructor}) { - @spec = %{$con->all_attribute_specs}; - } - } - @spec; - }); + ->register_attribute_specs(%{$con?$con->all_attribute_specs:{}}) } } 1; + +=head1 NAME + +Moo - Minimalist Object Orientation (with Moose compatiblity) + +=head1 WARNING WARNING WARNING + +This is a 0.9 release because we're fairly sure it works. For us. Until it's +tested in the wild, we make no guarantees it also works for you. + +If this module does something unexpected, please submit a failing test. + +But if it eats your cat, sleeps with your boyfriend, or pushes grandma down +the stairs to save her from the terrible secret of space, it's not our fault. + +=head1 SYNOPSIS + + package Cat::Food; + + use Moo; + use Sub::Quote; + + sub feed_lion { + my $self = shift; + my $amount = shift || 1; + + $self->pounds( $self->pounds - $amount ); + } + + has taste => ( + is => 'ro', + ); + + has brand => ( + is => 'ro', + isa => sub { + die "Only SWEET-TREATZ supported!" unless $_[0] eq 'SWEET-TREATZ' + }, +); + + has pounds => ( + is => 'rw', + isa => quote_sub q{ die "$_[0] is too much cat food!" unless $_[0] < 15 }, + ); + + 1; + +and else where + + my $full = Cat::Food->new( + taste => 'DELICIOUS.', + brand => 'SWEET-TREATZ', + pounds => 10, + ); + + $full->feed_lion; + + say $full->pounds; + +=head1 DESCRIPTION + +This module is an extremely light-weight, high-performance L replacement. +It also avoids depending on any XS modules to allow simple deployments. The +name C is based on the idea that it provides almost -but not quite- two +thirds of L. + +Unlike C this module does not aim at full L compatibility. See +L for more details. + +=head1 WHY MOO EXISTS + +If you want a full object system with a rich Metaprotocol, L is +already wonderful. + +I've tried several times to use L but it's 3x the size of Moo and +takes longer to load than most of my Moo based CGI scripts take to run. + +If you don't want L, you don't want "less metaprotocol" like L, +you want "as little as possible" - which means "no metaprotocol", which is +what Moo provides. + +By Moo 1.0 I intend to have Moo's equivalent of L built in - +if Moose gets loaded, any Moo class or role will act as a Moose equivalent +if treated as such. + +Hence - Moo exists as its name - Minimal Object Orientation - with a pledge +to make it smooth to upgrade to L when you need more than minimal +features. + +=head1 IMPORTED METHODS + +=head2 new + + Foo::Bar->new( attr1 => 3 ); + +or + + Foo::Bar->new({ attr1 => 3 }); + +=head2 BUILDARGS + +This feature from Moose is not yet supported. + +=head2 BUILDALL + +Don't override (or probably even call) this method. Instead, you can define +a C method on your class and the constructor will automatically call the +C method from parent down to child after the object has been +instantiated. Typically this is used for object validation or possibly logging. + +=head2 does + + if ($foo->does('Some::Role1')) { + ... + } + +Returns true if the object composes in the passed role. + +=head1 IMPORTED SUBROUTINES + +=head2 extends + + extends 'Parent::Class'; + +Declares base class. Multiple superclasses can be passed for multiple +inheritance (but please use roles instead). + +Calling extends more than once will REPLACE your superclasses, not add to +them like 'use base' would. + +=head2 with + + with 'Some::Role1'; + with 'Some::Role2'; + +Composes a L into current class. Only one role may be composed in +at a time to allow the code to remain as simple as possible. + +=head2 has + + has attr => ( + is => 'ro', + ); + +Declares an attribute for the class. + +The options for C are as follows: + +=over 2 + +=item * is + +B, must be C or C. Unsurprisingly, C generates an +accessor that will not respond to arguments; to be clear: a getter only. C +will create a perlish getter/setter. + +=item * isa + +Takes a coderef which is meant to validate the attribute. Unlike L Moo +does not include a basic type system, so instead of doing C<< isa => 'Num' >>, +one should do + + isa => quote_sub q{ + die "$_[0] is not a number!" unless looks_like_number $_[0] + }, + +L + +=item * coerce + +This Moose feature is not yet supported + +=begin hide + +Takes a coderef which is meant to coerce the attribute. The basic idea is to +do something like the following: + + coerce => quote_sub q{ + $_[0] + 1 unless $_[0] % 2 + }, + +L + +=end hide + +=item * trigger + +Takes a coderef which will get called any time the attribute is set. Coderef +will be invoked against the object with the new value as an argument. + +Note that Moose also passes the old value, if any; this feature is not yet +supported. + +L + +=item * default + +Takes a coderef which will get called with $self as its only argument +to populate an attribute if no value is supplied to the constructor - or +if the attribute is lazy, when the attribute is first retrieved if no +value has yet been provided. + +Note that if your default is fired during new() there is no guarantee that +other attributes have been populated yet so you should not rely on their +existence. + +L + +=item * predicate + +Takes a method name which will return true if an attribute has a value. + +A common example of this would be to call it C, implying that the +object has a C<$foo> set. + +=item * builder + +Takes a method name which will be called to create the attribute - functions +exactly like default except that instead of calling + + $default->($self); + +Moo will call + + $self->$builder; + +=item * clearer + +Takes a method name which will clear the attribute. + +=item * lazy + +B. Set this if you want values for the attribute to be grabbed +lazily. This is usually a good idea if you have a L which requires +another attribute to be set. + +=item * required + +B. Set this if the attribute must be passed on instantiation. + +=item * weak_ref + +B. Set this if you want the reference that the attribute contains to +be weakened; use this when circular references are possible, which will cause +leaks. + +=item * init_arg + +Takes the name of the key to look for at instantiation time of the object. A +common use of this is to make an underscored attribute have a non-underscored +initialization name. C means that passing the value in on instantiation + +=back + +=head2 before + + before foo => sub { ... }; + +See L<< Class::Method::Modifiers/before method(s) => sub { ... } >> for full +documentation. + +=head2 around + + around foo => sub { ... }; + +See L<< Class::Method::Modifiers/around method(s) => sub { ... } >> for full +documentation. + +=head2 after + + after foo => sub { ... }; + +See L<< Class::Method::Modifiers/after method(s) => sub { ... } >> for full +documentation. + +=head1 SUB QUOTE AWARE + +L allows us to create coderefs that are "inlineable," +giving us a handy, XS-free speed boost. Any option that is L +aware can take advantage of this. + +=head1 INCOMPATIBILITIES WITH MOOSE + +You can only compose one role at a time. If your application is large or +complex enough to warrant complex composition, you wanted L. + +There is no complex type system. C is verified with a coderef, if you +need complex types, just make a library of coderefs, or better yet, functions +that return quoted subs. + +C is not supported in core since the author considers it to be a +bad idea but may be supported by an extension in future. + +There is no meta object. If you need this level of complexity you wanted +L - Moo succeeds at being small because it explicitly does not +provide a metaprotocol. + +No support for C, C, C, or C - override can +be handled by around albeit with a little more typing, and the author considers +augment to be a bad idea. + +L only supports coderefs, because doing otherwise is usually a +mistake anyway. + +C is not supported per se, but of course it will work if you +manually set all the options it implies. + +C is not supported since the author considers it a bad idea. + +C is not supported since it's a very poor replacement for POD.