making the init_arg even more silly
[gitmo/Class-MOP.git] / examples / C3MethodDispatchOrder.pod
1
2 package # hide from PAUSE 
3     C3MethodDispatchOrder;
4     
5 use strict;
6 use warnings;
7
8 use Carp 'confess';
9 use Algorithm::C3;
10
11 our $VERSION = '0.03';
12
13 use base 'Class::MOP::Class';
14
15 my $_find_method = sub {
16     my ($class, $method) = @_;
17     foreach my $super ($class->class_precedence_list) {
18         return $super->meta->get_method($method)   
19             if $super->meta->has_method($method);
20     }
21 };
22
23 C3MethodDispatchOrder->meta->add_around_method_modifier('initialize' => sub {
24         my $cont = shift;
25     my $meta = $cont->(@_);
26     $meta->add_method('AUTOLOAD' => sub {
27         my $meta = $_[0]->meta;
28         my $method_name;
29         {
30             no strict 'refs';
31             my $label = ${$meta->name . '::AUTOLOAD'};
32             $method_name = (split /\:\:/ => $label)[-1];
33         }
34         my $method = $_find_method->($meta, $method_name);
35         (defined $method) || confess "Method ($method_name) not found";
36         goto &$method;
37     }) unless $meta->has_method('AUTOLOAD');
38     $meta->add_method('can' => sub {
39         $_find_method->($_[0]->meta, $_[1]);
40     }) unless $meta->has_method('can');   
41         return $meta;
42 });
43
44 sub superclasses {
45     my $self = shift;
46     
47     $self->add_package_symbol('@SUPERS' => [])    
48         unless $self->has_package_symbol('@SUPERS');
49             
50     if (@_) {
51         my @supers = @_;
52         @{$self->get_package_symbol('@SUPERS')} = @supers;
53     }
54     @{$self->get_package_symbol('@SUPERS')};        
55 }
56
57 sub class_precedence_list {
58     my $self = shift;
59     return map {
60         $_->name;
61     } Algorithm::C3::merge($self, sub {
62         my $class = shift;
63         map { $_->meta } $class->superclasses;
64     });
65 }
66
67 1;
68
69 __END__
70
71 =pod
72
73 =head1 NAME
74
75 C3MethodDispatchOrder - An example attribute metaclass for changing to C3 method dispatch order
76
77 =head1 SYNOPSIS
78   
79   # a classic diamond inheritence graph 
80   #
81   #    <A>
82   #   /   \
83   # <B>   <C>
84   #   \   /
85   #    <D>
86   
87   package A;
88   use metaclass 'C3MethodDispatchOrder';
89   
90   sub hello { return "Hello from A" }
91   
92   package B;
93   use metaclass 'C3MethodDispatchOrder';
94   B->meta->superclasses('A');
95   
96   package C;
97   use metaclass 'C3MethodDispatchOrder';
98   C->meta->superclasses('A');
99   
100   sub hello { return "Hello from C" }
101   
102   package D;
103   use metaclass 'C3MethodDispatchOrder';
104   D->meta->superclasses('B', 'C');
105   
106   print join ", " => D->meta->class_precedence_list; # prints C3 order D, B, C, A
107   
108   # later in other code ...
109   
110   print D->hello; # print 'Hello from C' instead of the normal 'Hello from A' 
111   
112 =head1 DESCRIPTION
113
114 This is an example of how you could change the method dispatch order of a 
115 class using L<Class::MOP>. Using the L<Algorithm::C3> module, this repleces 
116 the normal depth-first left-to-right perl dispatch order with the C3 method 
117 dispatch order (see the L<Algorithm::C3> or L<Class::C3> docs for more 
118 information about this).
119
120 This example could be used as a template for other method dispatch orders 
121 as well, all that is required is to write a the C<class_precedence_list> method 
122 which will return a linearized list of classes to dispatch along. 
123
124 =head1 AUTHORS
125
126 Stevan Little E<lt>stevan@iinteractive.comE<gt>
127
128 Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
129
130 =head1 COPYRIGHT AND LICENSE
131
132 Copyright 2006 by Infinity Interactive, Inc.
133
134 L<http://www.iinteractive.com>
135
136 This library is free software; you can redistribute it and/or modify
137 it under the same terms as Perl itself.
138
139 =cut