next-method
[gitmo/Moose.git] / lib / Moose / Meta / SafeMixin.pm
CommitLineData
505c6fac 1
2package Moose::Meta::SafeMixin;
3
4use strict;
5use warnings;
6
7use Scalar::Util 'blessed';
8use Carp 'confess';
9
10our $VERSION = '0.01';
11
12use base 'Class::MOP::Class';
13
14sub mixin {
15 # fetch the metaclass for the
16 # caller and the mixin arg
17 my $metaclass = shift;
18 my $mixin = $metaclass->initialize(shift);
19
20 # according to Scala, the
21 # the superclass of our class
22 # must be a subclass of the
23 # superclass of the mixin (see above)
24 my ($super_meta) = $metaclass->superclasses();
25 my ($super_mixin) = $mixin->superclasses();
26 ($super_meta->isa($super_mixin))
fc5609d2 27 || confess "The superclass ($super_meta) must extend a subclass of the " .
28 "superclass of the mixin ($super_mixin)"
505c6fac 29 if defined $super_mixin && defined $super_meta;
30
fc5609d2 31 # check for conflicts here ...
32
33 $metaclass->has_attribute($_)
34 && confess "Attribute conflict ($_)"
35 foreach $mixin->get_attribute_list;
36
37 foreach my $method_name ($mixin->get_method_list) {
38 # skip meta, cause everyone has that :)
39 next if $method_name =~ /meta/;
40 $metaclass->has_method($method_name) && confess "Method conflict ($method_name)";
41 }
42
505c6fac 43 # collect all the attributes
44 # and clone them so they can
fc5609d2 45 # associate with the new class
505c6fac 46 # add all the attributes in ....
fc5609d2 47 foreach my $attr ($mixin->get_attribute_list) {
48 $metaclass->add_attribute(
49 $mixin->get_attribute($attr)->clone()
50 );
51 }
505c6fac 52
53 # add all the methods in ....
fc5609d2 54 foreach my $method_name ($mixin->get_method_list) {
55 # no need to mess with meta
56 next if $method_name eq 'meta';
57 my $method = $mixin->get_method($method_name);
58 # and ignore accessors, the
59 # attributes take care of that
60 next if blessed($method) && $method->isa('Class::MOP::Attribute::Accessor');
61 $metaclass->alias_method($method_name => $method);
505c6fac 62 }
63}
64
651;
66
67__END__
68
69=pod
70
71=head1 NAME
72
73Moose::Meta::SafeMixin - A meta-object for safe mixin-style composition
74
75=head1 SYNOPSIS
76
77=head1 DESCRIPTION
78
79This is a meta-object which provides B<safe> mixin-style composition
80of classes. The key word here is "safe" because we enforce a number
81of rules about mixing in which prevent some of the instability
82inherent in other mixin systems. However, it should be noted that we
83still allow you enough rope with which to shoot yourself in the foot
84if you so desire.
85
86=over 4
87
88=item *
89
90In order to mix classes together, they must inherit from a common
91superclass. This assures at least some level of similarity between
92the classes being mixed together, which should result in a more
93stable end product.
94
95The only exception to this rule is if the class being mixed in has
96no superclasses at all. In this case we assume the mixin is valid.
97
98=item *
99
100Since we enforce a common ancestral relationship, we need to be
101mindful of method and attribute conflicts. The common ancestor
102increases the potential of method conflicts because it is common
103for subclasses to override their parents methods. However, it is
104less common for attributes to be overriden. The way these are
105resolved is to use a Trait/Role-style conflict mechanism.
106
107If two classes are mixed together, any method or attribute conflicts
108will result in a failure of the mixin and a fatal exception. It is
109not possible to resolve a method or attribute conflict dynamically.
110This is because to do so would open the possibility of breaking
111classes in very subtle and dangerous ways, particularly in the area
112of method interdependencies. The amount of implementation knowledge
113which would need to be known by the mixee would (IMO) increase the
114complexity of the feature exponentially for each class mixed in.
115
116However fear not, there is a solution (see below) ...
117
118=item *
119
120Safe mixin's offer the possibility of CLOS style I<before>, I<after>
121and I<around> methods with which method conflicts can be resolved.
122
123A method, which would normally conflict, but which is labeled with
124either a I<before>, I<after> or I<around> attribute, will instead be
125combined with the original method in the way implied by the attribute.
126
127The result of this is a generalized event-handling system for classes.
128Which can be used to create things more specialized, such as plugins
129and decorators.
130
131=back
132
133=head2 What kinda crack are you on ?!?!?!?
134
135This approach may seem crazy, but I am fairly confident that it will
136work, and that it will not tie your hands unnessecarily. All these
137features have been used with certain degrees of success in the object
138systems of other languages, but none (IMO) provided a complete
139solution.
140
141In CLOS, I<before>, I<after> and I<around> methods provide a high
142degree of flexibility for adding behavior to methods, but do not address
143any concerns regarding classes since in CLOS, classes and methods are
144separate components of the system.
145
146In Scala, mixins are restricted by their ancestral relationships, which
147results in a need to have seperate "traits" to get around this restriction.
148In addition, Scala does not seem to have any means of method conflict
149resolution for mixins (at least not that I can find).
150
151In Perl 6, the role system forces manual disambiguation which (as
152mentioned above) can cause issues with method interdependecies when
153composing roles together. This problem will grow exponentially in one
154direction with each role composed and in the other direction with the
155number of roles that role itself is composed of. The result is that the
156complexity of the system becomes unmanagable for all but very simple or
157very shallow roles. Now, this is not to say that roles are unusable, in
158fact, this feature (IMO) promotes good useage of roles by keeping them
159both small and simple. But, the same behaviors cannot be applied to
160class mixins without hitting these barriers all too quickly.
161
162The same too can be said of the original Traits system, with its
163features for aliasing and exclusion of methods.
164
165So after close study of these systems, and in some cases actually
166implementing said systems, I have come to the see that each on it's
167own is not robust enough and that combining the best parts of each
168gives us (what I hope is) a better, safer and saner system.
169
170=head1 METHODS
171
172=over 4
173
174=item B<mixin ($mixin)>
175
176=back
177
178=head1 AUTHOR
179
180Stevan Little E<lt>stevan@iinteractive.comE<gt>
181
182=head1 COPYRIGHT AND LICENSE
183
184Copyright 2006 by Infinity Interactive, Inc.
185
186L<http://www.iinteractive.com>
187
188This library is free software; you can redistribute it and/or modify
189it under the same terms as Perl itself.
190
191=cut