Telephone +44(0)1524 64544
Email: info(at)shadowcat.co.uk

Sat Dec 22 00:30:00 2012

Slides for the talk moo at ipw-2011

-

   ______
  < Moo! >
   ~~~~~~
          \   ^__^
           \  (oo)\______
              (__)\             )\/\
                  ||~~~~w |
                  ||           ||

-

Moose

-

<3

-

0.01 released
March 15, 2006

-

I started with
about ... 0.18

-

0.18 released
March 10, 2007

-

What is
Moose?

-

What is
Moose
to me?

-

The OO I
always knew
perl5 was
capable of

-

... but wasn't
good enough
to implement

-

Class::Accessor

-

Class::Accessor
Class::MakeMethods
Class::Base

-

Too many!

-

DBIx::Class::AccessorGroup
became
Class::Accessor::Grouped

-

Damian
wrote
Class::Std

-

... but
nobody
used it

-

One class
builder to
rule them all

-

Moose

-

Why did I
stop using
Moose?

-

I didn't.

-

Catalyst
is Moose

-

All Shadowcat's
clients are
using Moose

-

Moose
rocks

-

This is all
your fault.

-

IPW
2009

-

Antiquated
Perl

-

Web::Simple

-

Targets:
usable as CGI

-

Targets:
usable as CGI
fast startup
no XS deps

-

Wrote my own
accessors

-

Wrote my own
constructor

-

Perling like
it's 1999

-

*GRUMBLE*

-

Multiple
projects
like this

-

Web::Simple
Data::Query
HTML::Zoom

-

Moose use
impedes
adoption

-

Mouse?

-

Can be
pure perl

-

Describes
itself as
"Moose minus
the antlers"

-

Well ...

-

It still
has a
metamodel

-

The antlers
are just
smaller

-

Mouse is
Moose--

-

Not what
I wanted

-

So I put
on the
hair shirt

-

Used no object
system at all
in my projects
for a year

-

IT
HURT

-

No method
modifiers

-

  before foo => sub {
    shift->do_before_foo;
  };

-

  sub foo {
    my $self = shift;
    $self->do_before_foo;
    $self->next::method(@_);
  }

-

  after foo => sub {
    shift->do_after_foo;
  };

-

  sub foo {
    my $self = shift;
    my @ret = wantarray
      ? $self->next::method(@_)
      : scalar $self->next::method(@_)
    $self->do_after_foo;
    return wantarray
      ? @ret
      : $ret[0];
  }

-

(and that fails
for void context)

-

Intelligent
accessors

-

  has foo => (
    is => 'ro',
    lazy => 1,
    builder => '_build_foo'
  );

-

  sub foo {
    my $self = shift;
    die "Readonly" in @_;
    unless (exists $self->{foo}) {
      $self->{foo} = $self->_build_foo;
    }
    return $self->{foo}
  }

-

Intelligent
constructors

-

  has foo => (is => 'ro', required => 1);
  has bar => (is => 'ro', default => sub { 0 });

-

  sub new {
    my $class = shift;
    my %args = @_ == 1 ? %{$_[0]} : @_;
    die "foo is required"
      unless exists $args{foo};
    $args{bar} = 0
      unless $args{bar};
    my %new;
    @new{qw(foo bar)} = @args{qw(foo bar)};
    bless (\%new, $class);
  }

-

Subclassing
gets even
worse ...

-

Runtime
role
application

-

  apply_all_roles($obj, @roles)

-

  my $new_class = ref($obj).'::__ANON__'.++$i;
  @{"${new_class}::ISA"} = ref($obj);
  eval "package ${new_class}; use $_" for @roles;
  bless($obj, $new_class);

-

ARGH

-

-

So ...

-

About one
year ago

-

Working on
site in
New York

-

Bored at the
weekend

-

(I am useless
at being
a tourist)

-

Coffee.
Subway.

-

I hacked.
ribasushi
and frew
tested.

-

So, what did
we produce?

-

-

Metaprotocol
only needed
for introspection

-

Accessor
generation

-

Method::Generate::Accessor

-

  sub _generate_simple_get {
    my ($self, $me, $name) = @_;
    my $name_str = perlstring $name;
    "${me}->{${name_str}}";
  }

-

  sub _generate_get {
    my ($self, $name, $spec) = @_;
    my $simple = $self->_generate_simple_get('$_[0]', $name);
    if ($self->is_simple_get($name, $spec)) {
      $simple;
    } else {
      'do { '.$self->_generate_use_default(
        '$_[0]', $name, $spec,
        $self->_generate_simple_has('$_[0]', $name),
      ).'; '.$simple.' }';
    }
  }

-

  has foo => (
    is => 'ro', lazy => 1,
    default => sub { 0 }
  );

-

  sub Class::foo {
    do {
      $_[0]->{"foo"} = $default_for_foo->($_[0])
        unless exists $_[0]->{"foo"}; $_[0]->{"foo"}
    }
  }

-

Hmm ...

-

Why call a sub
if you don't
need to?

-

Sub::Quote

-

  has foo => (
    is => 'ro', lazy => 1,
    default => quote_sub(q{ 0 })
  );

-

  sub Class::foo {
    do {
      $_[0]->{"foo"} = do { @_ = ($_[0]);  0  }
        unless exists $_[0]->{"foo"}; $_[0]->{"foo"}
    }
  }

-

Method::Generate::Constructor

-

  sub Class::new {
    my $class = shift;
    my $args = @_ == 1 ? %{$_[0]} : {@_};
    my $new = bless({}, $class);
    if (exists $args->{"foo"}) {
      $new->{"foo"} = $args->{"foo"};
    }
    return $new;
  }

-

  # generated by the *accessor*
  # system to avoid repeating
  $new->{"foo"} = 

-

BUILDALL

-

  $new->Super1::BUILD($args);
  $new->Super2::BUILD($args);
  return $new;

-

Not quite
that simple

-

Subclass may
need a new
constructor

-

  package Something;
  use base qw(MooThing);
  sub BUILD { ... }

-

  sub MooThing::new {
    my $class = shift;
    if ($class ne "MooThing") {
      Moo->_constructor_maker_for($class,"Class");
      return $class->new(@_);
    }

-

Everything
eval()-ed
on demand

-

Simple
sugar

-

Moose has
lazy_build

-

  has foo => (
    is => 'ro',
    lazy => 1,
    builder => '_build_foo',
    predicate => 'has_foo',
    clearer => 'clear_foo'
  );

-

Bit too
much.

-

  has foo => (
    is => 'ro',
    lazy => 1,
    builder => '_build_foo'
  );

-

  has foo => (
    is => 'lazy'
  );

-

Now available in Moose via
MooseX::AttributeShortcuts

-

Method
Modifiers

-

Class::Method::Modifiers

-

Moo::import
does -

-

  foreach my $type (qw(before after around)) {
    *{_getglob "${target}::${type}"} = sub {
      require Class::Method::Modifiers;
      _install_modifier($target, $type, @_);
    };
  }

-

  # avoid upsetting strict
  sub _getglob { \*{$_[0]} }

-

Moo::Role

-

  foreach my $type (qw(before after around)) {
    *{_getglob "${target}::${type}"} = sub {
      require Class::Method::Modifiers;
      push @{$INFO{$target}{modifiers}||=[]}, [ $type => @_ ];
    };
  }

-

Then during
application

-

  foreach my $modifier (@{$modifiers||[]}) {
    Class::Method::Modifiers::install_modifier(
      $to, @$modifiers
    );
  }

-

Types?

-

No type
system

-

Lots of
arguing
about
Moose's

-

  has foo => (
    ...
    isa => sub {
      <pass check>
        or die "Not a valid thing"
    }
  );

-

Other
differences?

-

Moose:
  use strict;
  use warnings;

-

Moo:
  use strictures 1;

-

strictures?

-

  use strict;
  use warnings FATAL => 'all';

-

when run
from a
test ...

-

  use strict;
  use warnings FATAL => 'all';
  no indirect;
  no multidimensional;
  no bareword::filehandles;

-

Defaults designed
to save newbies
from themselves

-

Runtime role
application

-

Moose creates
a new class,
applies each
role to it

-

Cachable only
per combination
of roles

-

Moo ... is
slightly
more clever

-

Moo ... is
slightly
more evil

-

Creates a
composable
form of
the class

-

  before foo => sub {
    $_[0]->do_before_foo
  };

-

  sub foo {
    my $self = shift;
    $self->do_before_foo;
    $self->next::method(@_);
  }

-

next::method
uses
Class::C3
(or C3 mro
on 5.10+)

-

  @{"${new_class}::ISA"} = qw(
    Composed::Role1
    Composed::Role2
    SuperClass
  );

-

Done.

-

So, total
results?

-

  use Moo;
  use Moo::Role;

-

extends
has
with
before
after
around

-

Quite sufficient
for small projects

-

Web::Simple
now uses Moo

-

Data::Query
now uses Moo

-

DBIx::Class (!)
now uses Moo

-

DBIx::Class (!)
now uses Moo
(for the odd
thing it used
to use Moose for)

-

  perl -Moo -e 'has ...'
  # like perl -Moose

-

13 modules

-

~2000 lines
of code and
documentation

-

Needs only:
  strictures
  Class::Method::Modifiers

-

Needs only:
  strictures
  Class::Method::Modifiers
  Test::More
  Test::Fatal

-

Needs only:
  strictures
  Class::Method::Modifiers
  Test::More
  Test::Fatal
  MRO::Compat

-

It works.

-

It works.
Try it.

-

It works.
Try it.
Complain on
#web-simple

-

'Moo' is
60% of
'Moose'

-

Moo: "almost
but not quite
two thirds
of Moose"

-

Questions?

-

Thank You
IRC:mst
mst@shadowcat.co.uk
@shadowcat_mst