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