Successfully reported this slideshow.

Introduction To Moose

15

Share

Loading in …3
×
1 of 129
1 of 129

Introduction To Moose

15

Share

Download to read offline

Introduction to Moose talk given at Italian Perl Workshop 2009.

Video at http://www.shadowcat.co.uk/archive/conference-video/ipw-2009/moose/

Introduction to Moose talk given at Italian Perl Workshop 2009.

Video at http://www.shadowcat.co.uk/archive/conference-video/ipw-2009/moose/

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

Related Audiobooks

Free with a 14 day trial from Scribd

See all

Introduction To Moose

  1. 1. Introduction to Moose Italian Perl Workshop 2009 Mike Whitaker - BBC / EnlightenedPerl.org
  2. 2. About me
  3. 3. About me • BBC iPlayer team
  4. 4. About me • BBC iPlayer team • ex-Yahoo Europe (News team)
  5. 5. About me • BBC iPlayer team • ex-Yahoo Europe (News team) • ex-CricInfo.com
  6. 6. About me • BBC iPlayer team • ex-Yahoo Europe (News team) • ex-CricInfo.com • Board member of EnlightenedPerl.org
  7. 7. Why Moose?
  8. 8. Why Moose? • For that, we need a bit of history:
  9. 9. In the Beginning...
  10. 10. In the Beginning... • ...there was Perl 5
  11. 11. In the Beginning... • ...there was Perl 5 • DIY OO
  12. 12. In the Beginning... • ...there was Perl 5 • DIY OO • perldoc perltoot
  13. 13. In the Beginning... • ...there was Perl 5 • DIY OO • perldoc perltoot package Foo; sub new { my $self = {}; return bless $self; }
  14. 14. In the Beginning... package Foo; • ...there was Perl 5 sub new { my $self = {}; • DIY OO } return bless $self; • perldoc perltoot sub bar { my $self = shift; my $val = shift; package Foo; if (defined $val) { $self->{bar} = $val; sub new { } my $self = {}; else { return bless $self; return $self->{bar}; } } }
  15. 15. Perl 5 Native OO
  16. 16. Perl 5 Native OO • A bit of a hack
  17. 17. Perl 5 Native OO • A bit of a hack • Not really a first-class part of the language
  18. 18. Perl 5 Native OO • A bit of a hack • Not really a first-class part of the language • Have to roll your own
  19. 19. So... People started writing classes to make it easier....
  20. 20. Class::Accessor::Classy Class::Accessor Object::InsideOut fields Hash::FieldHash Badger::Class Rubyish OP Object::Declare Class::Maker UR Class::Gomor Object::Tiny::XS Class::XSAccessor Class::Framework Class::Accessor::Fast Class::Simple Rose::Object MOP::MOP Coat Object::InsideOut Class::STD Class::Object Class::Base Class::ArrayObjects Spiffy Class::Accessor::Grouped Class::Frame Object::Accessor Object::Tiny Object::Simple Class::Generate Class::Builder accessors Fukurama::Class Class::Define Abstract::Meta::Class
  21. 21. Moose
  22. 22. What is Moose?
  23. 23. What is Moose? • (Post)modern Perl OO framework
  24. 24. What is Moose? • (Post)modern Perl OO framework • Deals with the overhead of implementing OO, allows you to get on with coding
  25. 25. What is Moose? • (Post)modern Perl OO framework • Deals with the overhead of implementing OO, allows you to get on with coding • Meta-object implementation - allows you to introspect your classes and objects
  26. 26. What is Moose? • (Post)modern Perl OO framework • Deals with the overhead of implementing OO, allows you to get on with coding • Meta-object implementation - allows you to introspect your classes and objects • Already used in production software
  27. 27. My First Class package Person; use Moose; has name => ( is => 'rw', ); no Moose;
  28. 28. Using the class use Person; my $person = Person->new(); $person->name('Mike'); print $person->name(); > Mike
  29. 29. Adding a method package Person; use Moose; has name => ( is => 'rw' ); sub introduce { print "I'm " . $self->name; }
  30. 30. Using it use Person; my $person = Person->new(); $person->name('Mike'); $person->introduce(); > I'm Mike
  31. 31. Read-only attributes package Person; use Moose; has name => ( is => 'ro' ); sub introduce { print "I'm " . $self->name; }
  32. 32. Read-only attributes (2) use Person; my $person = Person->new(); $person->name('Mike'); # boom!
  33. 33. Read-only attributes (3) use Person; my $person = Person->new( { name => 'Mike' }, ); # no boom today!
  34. 34. Changing defaults has 'name' => ( isa => 'Str', reader => 'get_name', writer => 'set_name', init_arg => 'name', required => 1, );
  35. 35. Types package Person; use Moose; has name => ( is => 'rw', isa => 'Str', );
  36. 36. Types (2) use Person; my $person = Person->new(); $person->name('Mike'); # shiny! $person->name( [] ); # boom! # Not a Str
  37. 37. Types (3) package Person; use Moose; has name => ( is => 'rw', isa => 'Str' ); has dob => ( is => 'rw', isa => 'DateTime', );
  38. 38. Types (4) use Person; my $person = Person->new( { name => 'Mike' } ); my $dob = DateTime->new ( year => 1963, month => 8, day => 5, ); $person->dob($dob); print $person->dob->year; > 1963
  39. 39. Subclassing package Student; use Moose; extends qw/Person/; has overdraft => ( isa => 'Bool', is => 'rw', );
  40. 40. Compound Types package Student; use Moose; extends qw/Person/; has classes => ( isa => 'HashRef[Str]', is => 'rw', );
  41. 41. Required attributes package Student; use Moose; extends qw/Person/; # ... has course => ( isa => 'Str', is => 'ro', required => 1, );
  42. 42. Setting defaults package Student; use Moose; has 'overdraft' => ( isa => 'Bool', default => 1, );
  43. 43. But, what if... package BankAccount; sub balance { # yadda yadda }
  44. 44. And then... package Student; use Moose; extends qw/Person/; has 'account' => ( isa => 'BankAccount', is => 'rw', required => 1, );
  45. 45. Lazy default has 'overdraft' => ( isa => 'Bool', is => 'ro', lazy => 1, default => sub { shift->account->balance < 0; } );
  46. 46. Lazy builder has 'overdraft' => ( isa => 'Bool', is => 'ro', lazy_build => 1, init_arg => undef, ); sub _build_overdraft { return shift->account->balance < 0; }
  47. 47. Method modifiers package Student; # yadda yadda after 'introduce' => sub { my $self = shift; print ', studying ' . $self->course; }
  48. 48. Using it use Student; my $person = Student->new( name => 'Mike', course => 'Computer Science', ); $person->introduce(); > I'm Mike, studying Computer Science
  49. 49. Method modifiers (2) package Student; # yadda yadda around 'introduce' => sub { my ($next, $self, @args) = @_; print "Hi, "; $self->$next(@args); print ', studying ' . $self->course; }
  50. 50. Using around use Student; my $person = Student->new( name => 'Mike', course => 'Computer Science', ); $person->introduce(); > Hi, I'm Mike, studying Computer Science
  51. 51. Roles
  52. 52. Roles • Code fragments that define and provide a small, reusable behaviour
  53. 53. Roles • Code fragments that define and provide a small, reusable behaviour • Not inherited - methods become part of consuming class
  54. 54. Roles • Code fragments that define and provide a small, reusable behaviour • Not inherited - methods become part of consuming class • can be overriden by comsuming class
  55. 55. Roles • Code fragments that define and provide a small, reusable behaviour • Not inherited - methods become part of consuming class • can be overriden by comsuming class • like MI but better!
  56. 56. Example role package Age; use Moose::Role; has dob => ( isa => 'DateTime', is =>'ro' ); sub age { return DateTime->now ->subtract( shift->dob() ) ->years; }
  57. 57. Using a role package Person; use Moose; with qw/Age/; has name => ( isa => 'Str', is => 'ro'); sub introduce { print "I'm " . $self->name . ', age ' . $self->age; }
  58. 58. Using a role (2) use Student; my $person = Student->new( name => 'Mike', dob => ... # yadda yadda course => 'CS', ); $person->introduce(); > Hi, I'm Mike, age 45, studying CS
  59. 59. What we'd really like use Student; my $person = Student->new( name => 'Mike', dob => '05-08-1963', course => 'CS', );
  60. 60. Redefine our attr... package Age; use Moose::Role; has dob => ( isa => 'DateStr', is =>'ro', );
  61. 61. Types to the rescue use Moose::Util::TypeConstraints; subtype 'DateStr' => as 'Str' => where { /^dd-dd-dddd$/ };
  62. 62. Types to the rescue (2) use Moose::Util::TypeConstraints; class_type 'DateTime'; coerce 'DateTime' => from 'Str' => via { my ($d, $m, $y) = split /-/, $_; return DateTime->new( year => $y, month => $m, day => $d ); };
  63. 63. And then... package Age; use Moose::Role; has dob => ( isa => 'DateTime', is =>'ro', coerce => 1, # very important! );
  64. 64. Et voila... use Student; my $person = Student->new( name => 'Mike', dob => '05-08-1963', course => 'CS', ); print $person->age(); > 45
  65. 65. Roles as interfaces package Earnings; use Moose::Role; requires qw/annual_income/; sub monthly_income { return shift->annual_income / 12; }
  66. 66. Roles as interfaces (2) package Person; with qw/Earnings/; package Student; extends qw/Person/; sub annual_income { my $self = shift; return $self->grant_amount; }
  67. 67. Role gotchas
  68. 68. Role gotchas • Roles cannot use extends
  69. 69. Role gotchas • Roles cannot use extends • requires only works with actual methods :(
  70. 70. Role gotchas • Roles cannot use extends • requires only works with actual methods :( • for now, at least
  71. 71. Role gotchas • Roles cannot use extends • requires only works with actual methods :( • for now, at least • watch out for method and attribute conflicts
  72. 72. Method Delegation package Degree; use Moose; use Moose::Utils::TypeConstraints; enum 'Grade' => qw/I IIi IIii III/; has 'grade' => ( isa => 'Grade' ); has 'awarded' => ( isa => 'DateTime', coerce => 1, );
  73. 73. Method Delegation (2) package Graduate; use Moose; extends qw/Student/; has 'degree' => ( isa => 'Degree', is => 'rw', );
  74. 74. Method Delegation (3) my $g = Graduate->new(name => 'Mike'); my $d = Degree->new( awarded => '06-06-1985', grade => 'IIii', ); $g->degree($d); print $g->degree->awarded->year; > 1985
  75. 75. Method Delegation (4) has 'degree' => ( isa => 'Degree', is => 'rw', handles => { graduated => 'awarded' }, );
  76. 76. Method Delegation (5) my $g = Graduate->new(name => 'Mike'); my $d = Degree->new( awarded => '06-06-1985', grade => 'IIii', ); $g->degree($d); print $g->graduated->year; > 1985
  77. 77. More on attributes package Student; use MooseX::Util::TypeConstraints; use Moose; extends qw/Person/; enum 'Result' => qw/pass fail/; has classes => ( isa => 'HashRef[Result]', is => 'rw', predicate => 'has_classes', );
  78. 78. Attribute Helpers my $st = Student->new( name => 'Mike' ); $st->classes( { "Compilers" => 'fail', "Numerical Methods" => 'pass' } ); %{$st->classes}->{"Lisp"} = "pass";
  79. 79. Attribute Helpers (2) use MooseX::AttributeHelpers; has classes => ( metaclass => 'Collection::Hash', isa => 'HashRef[Result]', is => 'rw', provides => { set => 'add_class', keys => 'list_classes', } )
  80. 80. Attribute Helpers (3) my $st = Student->new( name => 'Mike' ); $st->classes( { "Compilers" => 'fail', "Numerical Methods" => 'pass' } ); $st->add_class("Lisp" => 'pass'); print join ", ", $st->list_classes(); > Compilers, Numerical Methods, Lisp
  81. 81. Introspecting Moose
  82. 82. Introspecting Moose • my $metaclass = $self->meta;
  83. 83. Introspecting Moose • my $metaclass = $self->meta;
  84. 84. Introspecting Moose • my $metaclass = $self->meta; • $metaclass->superclasses;
  85. 85. Introspecting Moose • my $metaclass = $self->meta; • $metaclass->superclasses; • $metaclass->linearized_isa;
  86. 86. Introspecting Moose • my $metaclass = $self->meta; • $metaclass->superclasses; • $metaclass->linearized_isa; • $metaclass->has_method("foo");
  87. 87. Introspecting Moose • my $metaclass = $self->meta; • $metaclass->superclasses; • $metaclass->linearized_isa; • $metaclass->has_method("foo"); • $metaclass->get_all_attributes;
  88. 88. Method Signatures
  89. 89. Method Signatures • We have types...
  90. 90. Method Signatures • We have types... • Wouldn't it be nice if types weren't restricted to attrs + accessors?
  91. 91. Method Signatures • We have types... • Wouldn't it be nice if types weren't restricted to attrs + accessors? • Enter: MooseX::Method::Signature
  92. 92. Method Signatures package Student; use Moose; use MooseX::Method::Signatures; method attend (Str $class, DateTime $time) { # ... }
  93. 93. Method Signatures (2) method attend (Str $class, DateTime $time) { if (grep $class, $self->list_classes) { $self->schedule($time, $class); } }
  94. 94. Even more Java-like? use MooseX::Declare; class Student extends Person with Earnings { has 'id' => ( isa => 'StudentID' ); method attend ( Str $class, ... ) { # yadda yadda } }
  95. 95. How is this implemented?
  96. 96. Lasciate ogne speranza, voi ch'intrate
  97. 97. But seriously...
  98. 98. But seriously... • Devel::Declare sticks the interpreter on pause while it plays with your source
  99. 99. But seriously... • Devel::Declare sticks the interpreter on pause while it plays with your source • NOT a source filter
  100. 100. But seriously... • Devel::Declare sticks the interpreter on pause while it plays with your source • NOT a source filter • You don't need to know what's going on...
  101. 101. Why Moose?
  102. 102. Why Moose? • Less code = fewer errors
  103. 103. Why Moose? • Less code = fewer errors • Don't waste time on class implementation
  104. 104. Why Moose? • Less code = fewer errors • Don't waste time on class implementation • Better object model
  105. 105. Why Moose? • Less code = fewer errors • Don't waste time on class implementation • Better object model • "We do this so you don't have to"
  106. 106. Why Moose? • Less code = fewer errors • Don't waste time on class implementation • Better object model • "We do this so you don't have to" • Less need for low level tests
  107. 107. Why Moose? • Less code = fewer errors • Don't waste time on class implementation • Better object model • "We do this so you don't have to" • Less need for low level tests • More descriptive code
  108. 108. Further Reading
  109. 109. Further Reading • http://iinteractive.com/moose/
  110. 110. Further Reading • http://iinteractive.com/moose/ • Moose::Cookbook (CPAN)
  111. 111. Further Reading • http://iinteractive.com/moose/ • Moose::Cookbook (CPAN) • http://www.stonehenge.com/merlyn/ LinuxMag/col94.html
  112. 112. Complex Example package Student; use Moose; # yadda has 'id' => ( isa => 'StudentID', is => 'rw', );
  113. 113. Complex Example (2) use MooseX::ClassAttribute; class_has 'db' => ( isa => 'Str', is => 'rw', default => 'dbi:SQLite:dbname=s.db' ); class_has _schema => ( isa => 'Person::Schema', lazy_build => 1, is => 'ro', );
  114. 114. Complex Example (3) sub _build__schema { my $self = shift; return Person::Schema->connect( $self->db ); }
  115. 115. Complex Example (4) use Config::General; override BUILDARGS => sub { my $args = super; my %conf = Config::General ->new( 'db.conf' )->getall(); $args->{db} = $conf{db} if exists $conf{db}; };
  116. 116. Complex Example (5) has _row => { isa => 'DBIx::Class::Row', lazy_build => 1, } sub _build__row { return shift->_schema ->resultset('Student') ->find($self->id); }
  117. 117. Complex Example (6) has _row => { isa => 'DBIx::Class::Row', lazy_build => 1, handles => [qw/address gender/], }
  118. 118. Complex Example (7) my $student = Student->new( name => 'Mike', id => '3056', ); print $st->address(); # What happens here?
  119. 119. What happens?
  120. 120. What happens? • address method is handled by _row
  121. 121. What happens? • address method is handled by _row • _row is lazy_build, so calls _build__row
  122. 122. What happens? • address method is handled by _row • _row is lazy_build, so calls _build__row • which requires _schema, ALSO lazy_build
  123. 123. What happens? • address method is handled by _row • _row is lazy_build, so calls _build__row • which requires _schema, ALSO lazy_build • which uses id to find the right row in db
  124. 124. What happens? • address method is handled by _row • _row is lazy_build, so calls _build__row • which requires _schema, ALSO lazy_build • which uses id to find the right row in db • Tadaa!
  125. 125. But... # What if the student's ID changes? has 'id' => ( isa => 'StudentID', is => 'rw', trigger => '_build__row', );

Editor's Notes

×