Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Extending Moose
 for Applications
  Shawn M Moore
  Best Practical Solutions


              1
Twitter!

@sartak Hey wait, what is "meta"?




                2
(or IRC)



   3
Extending Moose for
    Applications




         4
Domain-specific
Metaprogramming




       5
Extending Moose for
    Applications

  Domain-specific
 Metaprogramming



         6
Extending Moose for
    Applications

  Domain-specific
 Metaprogramming



         7
Extending Moose for
    Applications

  Domain-specific
 Metaprogramming



         8
9
Context-oriented Programming with
ContextL

Towards a Secure Programming Language:
An Access Control System for CommonLisp...
Metaprogramming




       11
Metaprogramming

  $obj->can('method_name')




            12
Metaprogramming

  $obj->can('method_name')


   $obj->isa('Class::Name')




             13
Metaprogramming

  $obj->can('method_name')


   $obj->isa('Class::Name')


  $obj->DOES('RoleName')



             14
Metaprogramming

  $obj->can('method_name')


   $obj->isa('Class::Name')


  $obj->DOES('RoleName')


         eval "code...
Metaprogramming
my $code = setup();
$code .= important_stuff();
$code .= teardown();

eval $code;




                   16
Metaprogramming


__PACKAGE__->meta->make_immutable




               17
Metaprogramming


__PACKAGE__->meta->make_immutable




               18
Metaprogramming

my $meta = __PACKAGE__->meta;

$meta->make_immutable;




                   19
Metaprogramming

print $meta;

Moose::Meta::Class=HASH(0x966910)




                    20
Moose::Meta::Class
Attributes                              Methods
name                                    new_object
vers...
Metaprogramming
my $code = setup();
$code .= important_stuff();
$code .= teardown();

eval $code;




                   22
package Point;
use Moose;

has 'x' => (is => 'rw', isa => 'Int');
has 'y' => (is => 'rw', isa => 'Int');

sub clear { ... ...
Point->meta
my $point = Point->meta;




                    24
Point->meta
my $point = Point->meta;

$point->name # ?




                    25
Point->meta
my $point = Point->meta;

$point->name # Point




                       26
Point->meta
my $point = Point->meta;

$point->name # Point

$point->get_attribute_list # ?




                       27
Point->meta
my $point = Point->meta;

$point->name # Point

$point->get_attribute_list # y, x




                       28
Point->meta
my $point = Point->meta;

$point->name # Point

$point->get_attribute_list # y, x

$point->has_method('clear')...
Point->meta
my $point = Point->meta;

$point->name # Point

$point->get_attribute_list # y, x

$point->has_method('clear')...
package Point3D;
use Moose;

extends 'Point';

has 'z' => (is => 'rw', isa => 'Int');

after clear => sub { ... };




   ...
Point3D->meta
my $point3d = Point3D->meta;




                   32
Point3D->meta
my $point3d = Point3D->meta;

$point3d->name # ?




                     33
Point3D->meta
my $point3d = Point3D->meta;

$point3d->name # Point3D




                   34
Point3D->meta
my $point3d = Point3D->meta;

$point3d->name # Point3D

$point3d->get_attribute_list # ?




               ...
Point3D->meta
my $point3d = Point3D->meta;

$point3d->name # Point3D

$point3d->get_attribute_list # z




               ...
Point3D->meta
my $point3d = Point3D->meta;

$point3d->name # Point3D

$point3d->get_attribute_list # z

$point3d->has_meth...
Point3D->meta
my $point3d = Point3D->meta;

$point3d->name # Point3D

$point3d->get_attribute_list # z

$point3d->has_meth...
package Point3D;
use Moose;

extends 'Point';

has 'z' => (is => 'rw', isa => 'Int');


after clear => sub { ... };



   ...
Finding Things
      Local                   Global
has_attribute             find_attribute_by_name

has_method           ...
REST Interface
my $class = url2class($url);
my $meta = $class->meta;

for ($meta->get_all_attributes) {
   my $name = $_->...
use Moose;
    42
my $point = Moose::Meta::Class->create(
   'Point',
);




                         43
my $point = Moose::Meta::Class->create(
   'Point',
);

$point->superclasses('Moose::Object');




                       ...
my $point = Moose::Meta::Class->create(
   'Point',
);

$point->superclasses('Moose::Object');

$point->add_attribute(
   ...
…

$point->add_attribute(
   'y',
   is => 'ro',
   isa => 'Int',
);




                         46
…

$point->add_attribute(
   'y',
   is => 'ro',
   isa => 'Int',
);

$point->add_method(clear => sub {
    ...
});




  ...
Ecosystem

    Classes
   Attributes
    Methods
     Roles
Type Constraint
 Type Coercion

                  48
Ecosystem

    Classes            Moose::Meta::Class

   Attributes
    Methods
     Roles
Type Constraint
 Type Coercion
...
Ecosystem

    Classes                Moose::Meta::Class

   Attributes            Moose::Meta::Attribute

    Methods    ...
Ecosystem

my $x =
  Point->meta->get_attribute('x')

$x->name         #x
$x->get_read_method # x
$x->type_constraint # In...
Ecosystem

my $clear =
 Point->meta->get_method('clear')

$clear->name      # clear
$clear->package_name # Point




     ...
Extending


1. Extend a metaclass
2. Use it
Extending


Class that counts its instances
package HasCounter;
use Moose::Role;

has count => (
   is   => 'rw',
   isa   => 'Int',
   default => 0,
);

sub incremen...
package CountInstances;
use Moose::Role;

with 'HasCounter';

after new_object => sub {
   my $self = shift;
   $self->inc...
package Point;
use Moose -traits => [
   'CountInstances',
];

has x => (
   is => 'ro',
   isa => 'Int',
);

...
Point->meta->count # 0

Point->new
Point->meta->count # 1

Point->new for 1 .. 5
Point->meta->count # 6
Base                 Meta
Point->new

             Point->meta->new_object

                         after new_object

   ...
package Line;
use Moose;

has start => (
   is => 'ro',
   isa => 'Point',
);

has end => (
   is => 'ro',
   isa => 'Poin...
Base                Meta
Line->new

            Line->meta->new_object
package HasCounter;
use Moose::Role;
use MooseX::AttributeHelpers;

has count => (
   traits => ['Counter'],
   provides =...
package FieldType;
use Moose::Role;
use Moose::Util::TypeConstraints;

has render_as => (
   is => 'ro',
   isa => (enum [...
package User;
use Moose;

has name => (
   traits => ['FieldType'],
   is     => 'rw',
   isa    => 'Str',
   render_as =>...
...

has password => (
   traits => ['FieldType'],
   is     => 'rw',
   isa    => 'Str',
   render_as => 'password',
);
...

has biography => (
   traits => ['FieldType'],
   is     => 'rw',
   isa    => 'Str',
   render_as => 'textarea',
);
traits => ['FieldType'],
render_as => 'text',

traits => ['FieldType'],
render_as => 'password',

traits => ['FieldType'],...
traits => ['FieldType'],
render_as => 'text',

traits => ['FieldType'],
render_as => 'password',

traits => ['FieldType'],...
Moose::Exporter
Moose::Util::MetaRole
Moose::Exporter
Moose::Util::MetaRole
Moose::Exporter
Moose::Util::MetaRole
package MyWeb::OO;

use Moose ();
use Moose::Exporter;
use Moose::Util::MetaRole;

use FieldType;

Moose::Exporter->setup_...
my $class = shift;
my %options = @_;

Moose->init_meta(%options);
   Moose::Util::MetaRole::apply_metaclass_roles(
   for_...
package User;
use MyWeb::OO;

has name => (
   is     => 'rw',
   isa    => 'Str',
   field_type => 'text',
);

...
package User;
use MyWeb::OO;
use MyWeb::OO::Persistent;
use MyWeb::OO::RESTful;
use MyWeb::OO::IncludeInAdminUI;
use MyWeb...
Immutability
sub _initialize_body {
  my $self = shift;
  my $source = 'sub {';
  $source .= "n" . 'my $class = shift;';

  $source .= ...
KiokuDB
Fey::ORM
KiokuDB
   Fey::ORM

ContextL (CLOS)
ROLES!
Thank you!
Extending Moose
Upcoming SlideShare
Loading in …5
×

Extending Moose

3,627 views

Published on

Using Moose provides many immediate and obvious benefits, starting with the
obviation of typing "use strict" and "use warnings" in your classes.

The real power of Moose, however, rests in its extensibility. By subclassing
Moose's metaclasses, you can augment and change Moose's behavior to suit your
application's needs. This extensibility is powered by the meta-object protocol
of Moose's foundation, Class::MOP.

The motivating example for extending Moose will be the creation of a small web
framework to empower a Twitter-alike. The focus will be creating meta-level
roles so that metaclasses may select exactly which changes in behavior they
wish to exhibit. Modules that will be used include Moose::Exporter (to define
sugary keywords) and Moose::Util::MetaRole (to extend classes composably).

Experience with using Moose to create regular classes is expected. Having some
familiarity with roles will let you get more out of the talk. No experience
with metaprogramming is required.

Published in: Technology, Education
  • Be the first to comment

Extending Moose

  1. 1. Extending Moose for Applications Shawn M Moore Best Practical Solutions 1
  2. 2. Twitter! @sartak Hey wait, what is "meta"? 2
  3. 3. (or IRC) 3
  4. 4. Extending Moose for Applications 4
  5. 5. Domain-specific Metaprogramming 5
  6. 6. Extending Moose for Applications Domain-specific Metaprogramming 6
  7. 7. Extending Moose for Applications Domain-specific Metaprogramming 7
  8. 8. Extending Moose for Applications Domain-specific Metaprogramming 8
  9. 9. 9
  10. 10. Context-oriented Programming with ContextL Towards a Secure Programming Language: An Access Control System for CommonLisp Rhapsody: How CLOS simplifies System-in- a-Package Design 10
  11. 11. Metaprogramming 11
  12. 12. Metaprogramming $obj->can('method_name') 12
  13. 13. Metaprogramming $obj->can('method_name') $obj->isa('Class::Name') 13
  14. 14. Metaprogramming $obj->can('method_name') $obj->isa('Class::Name') $obj->DOES('RoleName') 14
  15. 15. Metaprogramming $obj->can('method_name') $obj->isa('Class::Name') $obj->DOES('RoleName') eval "code" 15
  16. 16. Metaprogramming my $code = setup(); $code .= important_stuff(); $code .= teardown(); eval $code; 16
  17. 17. Metaprogramming __PACKAGE__->meta->make_immutable 17
  18. 18. Metaprogramming __PACKAGE__->meta->make_immutable 18
  19. 19. Metaprogramming my $meta = __PACKAGE__->meta; $meta->make_immutable; 19
  20. 20. Metaprogramming print $meta; Moose::Meta::Class=HASH(0x966910) 20
  21. 21. Moose::Meta::Class Attributes Methods name new_object version clone_object attributes rebless_instance methods subclasses superclasses linearized_isa roles add_attribute attribute_metaclass has_method method_metaclass get_all_method_names constructor_name is_immutable constructor_class calculate_all_roles 21
  22. 22. Metaprogramming my $code = setup(); $code .= important_stuff(); $code .= teardown(); eval $code; 22
  23. 23. package Point; use Moose; has 'x' => (is => 'rw', isa => 'Int'); has 'y' => (is => 'rw', isa => 'Int'); sub clear { ... } 23
  24. 24. Point->meta my $point = Point->meta; 24
  25. 25. Point->meta my $point = Point->meta; $point->name # ? 25
  26. 26. Point->meta my $point = Point->meta; $point->name # Point 26
  27. 27. Point->meta my $point = Point->meta; $point->name # Point $point->get_attribute_list # ? 27
  28. 28. Point->meta my $point = Point->meta; $point->name # Point $point->get_attribute_list # y, x 28
  29. 29. Point->meta my $point = Point->meta; $point->name # Point $point->get_attribute_list # y, x $point->has_method('clear') # ? 29
  30. 30. Point->meta my $point = Point->meta; $point->name # Point $point->get_attribute_list # y, x $point->has_method('clear') # 1 30
  31. 31. package Point3D; use Moose; extends 'Point'; has 'z' => (is => 'rw', isa => 'Int'); after clear => sub { ... }; 31
  32. 32. Point3D->meta my $point3d = Point3D->meta; 32
  33. 33. Point3D->meta my $point3d = Point3D->meta; $point3d->name # ? 33
  34. 34. Point3D->meta my $point3d = Point3D->meta; $point3d->name # Point3D 34
  35. 35. Point3D->meta my $point3d = Point3D->meta; $point3d->name # Point3D $point3d->get_attribute_list # ? 35
  36. 36. Point3D->meta my $point3d = Point3D->meta; $point3d->name # Point3D $point3d->get_attribute_list # z 36
  37. 37. Point3D->meta my $point3d = Point3D->meta; $point3d->name # Point3D $point3d->get_attribute_list # z $point3d->has_method('clear') # ? 37
  38. 38. Point3D->meta my $point3d = Point3D->meta; $point3d->name # Point3D $point3d->get_attribute_list # z $point3d->has_method('clear') # 1 38
  39. 39. package Point3D; use Moose; extends 'Point'; has 'z' => (is => 'rw', isa => 'Int'); after clear => sub { ... }; 39
  40. 40. Finding Things Local Global has_attribute find_attribute_by_name has_method find_method_by_name get_attribute_list get_all_attributes get_method_list get_all_methods superclasses class_precedence_list 40
  41. 41. REST Interface my $class = url2class($url); my $meta = $class->meta; for ($meta->get_all_attributes) { my $name = $_->name; my $tc = $_->type_constraint; my $default = $_->default; if ($_->is_required) { ... } } 41
  42. 42. use Moose; 42
  43. 43. my $point = Moose::Meta::Class->create( 'Point', ); 43
  44. 44. my $point = Moose::Meta::Class->create( 'Point', ); $point->superclasses('Moose::Object'); 44
  45. 45. my $point = Moose::Meta::Class->create( 'Point', ); $point->superclasses('Moose::Object'); $point->add_attribute( 'x', is => 'ro', isa => 'Int', ); 45
  46. 46. … $point->add_attribute( 'y', is => 'ro', isa => 'Int', ); 46
  47. 47. … $point->add_attribute( 'y', is => 'ro', isa => 'Int', ); $point->add_method(clear => sub { ... }); 47
  48. 48. Ecosystem Classes Attributes Methods Roles Type Constraint Type Coercion 48
  49. 49. Ecosystem Classes Moose::Meta::Class Attributes Methods Roles Type Constraint Type Coercion 49
  50. 50. Ecosystem Classes Moose::Meta::Class Attributes Moose::Meta::Attribute Methods Moose::Meta::Method Roles Moose::Meta::Role Type Constraint Moose::Meta::TypeConstraint Type Coercion Moose::Meta::TypeCoercion 50
  51. 51. Ecosystem my $x = Point->meta->get_attribute('x') $x->name #x $x->get_read_method # x $x->type_constraint # Int 51
  52. 52. Ecosystem my $clear = Point->meta->get_method('clear') $clear->name # clear $clear->package_name # Point 52
  53. 53. Extending 1. Extend a metaclass 2. Use it
  54. 54. Extending Class that counts its instances
  55. 55. package HasCounter; use Moose::Role; has count => ( is => 'rw', isa => 'Int', default => 0, ); sub increment { my $self = shift; $self->count( $self->count + 1 ); }
  56. 56. package CountInstances; use Moose::Role; with 'HasCounter'; after new_object => sub { my $self = shift; $self->increment; };
  57. 57. package Point; use Moose -traits => [ 'CountInstances', ]; has x => ( is => 'ro', isa => 'Int', ); ...
  58. 58. Point->meta->count # 0 Point->new Point->meta->count # 1 Point->new for 1 .. 5 Point->meta->count # 6
  59. 59. Base Meta Point->new Point->meta->new_object after new_object Point->meta->increment
  60. 60. package Line; use Moose; has start => ( is => 'ro', isa => 'Point', ); has end => ( is => 'ro', isa => 'Point', );
  61. 61. Base Meta Line->new Line->meta->new_object
  62. 62. package HasCounter; use Moose::Role; use MooseX::AttributeHelpers; has count => ( traits => ['Counter'], provides => { inc => 'increment', }, );
  63. 63. package FieldType; use Moose::Role; use Moose::Util::TypeConstraints; has render_as => ( is => 'ro', isa => (enum [ 'text', 'textarea', 'password', ..., ]), );
  64. 64. package User; use Moose; has name => ( traits => ['FieldType'], is => 'rw', isa => 'Str', render_as => 'text', );
  65. 65. ... has password => ( traits => ['FieldType'], is => 'rw', isa => 'Str', render_as => 'password', );
  66. 66. ... has biography => ( traits => ['FieldType'], is => 'rw', isa => 'Str', render_as => 'textarea', );
  67. 67. traits => ['FieldType'], render_as => 'text', traits => ['FieldType'], render_as => 'password', traits => ['FieldType'], render_as => 'textarea',
  68. 68. traits => ['FieldType'], render_as => 'text', traits => ['FieldType'], render_as => 'password', traits => ['FieldType'], render_as => 'textarea',
  69. 69. Moose::Exporter Moose::Util::MetaRole
  70. 70. Moose::Exporter Moose::Util::MetaRole
  71. 71. Moose::Exporter Moose::Util::MetaRole
  72. 72. package MyWeb::OO; use Moose (); use Moose::Exporter; use Moose::Util::MetaRole; use FieldType; Moose::Exporter->setup_import_methods( also => 'Moose', ); sub init_meta { ... }
  73. 73. my $class = shift; my %options = @_; Moose->init_meta(%options); Moose::Util::MetaRole::apply_metaclass_roles( for_class => $options{for_class}, attribute_metaclass_roles => ['FieldType'], ); return $options{for_class}->meta;
  74. 74. package User; use MyWeb::OO; has name => ( is => 'rw', isa => 'Str', field_type => 'text', ); ...
  75. 75. package User; use MyWeb::OO; use MyWeb::OO::Persistent; use MyWeb::OO::RESTful; use MyWeb::OO::IncludeInAdminUI; use MyWeb::OO::SpamTarget; database_name 'person'; # legacy has email => ( is => 'rw', isa => 'Str', field_type => 'text', spam_this => 1, admin_editable => 0, primary_key => 1, );
  76. 76. Immutability
  77. 77. sub _initialize_body { my $self = shift; my $source = 'sub {'; $source .= "n" . 'my $class = shift;'; $source .= "n" . 'return $class->Moose::Object::new(@_)'; $source .= "n if $class ne '" . $self->associated_metaclass->name . "';n"; $source .= $self->_generate_params('$params', '$class'); $source .= $self->_generate_instance('$instance', '$class'); $source .= $self->_generate_slot_initializers; $source .= $self->_generate_triggers(); $source .= ";n" . $self->_generate_BUILDALL(); $source .= ";nreturn $instance"; $source .= ";n" . '}'; ...
  78. 78. KiokuDB Fey::ORM
  79. 79. KiokuDB Fey::ORM ContextL (CLOS)
  80. 80. ROLES!
  81. 81. Thank you!

×