foo
[gitmo/Class-MOP.git] / lib / Class / MOP / Class / Immutable.pm
CommitLineData
857f87a7 1
2package Class::MOP::Class::Immutable;
3
4use strict;
5use warnings;
6
7use Carp 'confess';
c0cbf4d9 8use Scalar::Util 'blessed', 'looks_like_number';
857f87a7 9
10our $VERSION = '0.01';
11
12use base 'Class::MOP::Class';
13
14# methods which can *not* be called
15
857f87a7 16sub add_method { confess 'Cannot call method "add_method" on an immutable instance' }
17sub alias_method { confess 'Cannot call method "alias_method" on an immutable instance' }
18sub remove_method { confess 'Cannot call method "remove_method" on an immutable instance' }
19
20sub add_attribute { confess 'Cannot call method "add_attribute" on an immutable instance' }
21sub remove_attribute { confess 'Cannot call method "remove_attribute" on an immutable instance' }
22
58d75218 23sub add_package_symbol { confess 'Cannot call method "add_package_symbol" on an immutable instance' }
24sub remove_package_symbol { confess 'Cannot call method "remove_package_symbol" on an immutable instance' }
857f87a7 25
26# NOTE:
27# superclasses is an accessor, so
28# it just cannot be changed
29sub superclasses {
30 my $class = shift;
31 (!@_) || confess 'Cannot change the "superclasses" on an immmutable instance';
32 no strict 'refs';
33 @{$class->name . '::ISA'};
34}
35
36# predicates
37
38sub is_mutable { 0 }
39sub is_immutable { 1 }
40
41sub make_immutable { () }
42
43sub make_metaclass_immutable {
c0cbf4d9 44 my ($class, $metaclass, %options) = @_;
45
4d47b77f 46 # NOTE:
47 # i really need the // (defined-or) operator here
48 $options{inline_accessors} = 1 unless exists $options{inline_accessors};
49 $options{inline_constructor} = 1 unless exists $options{inline_constructor};
50 $options{constructor_name} = 'new' unless exists $options{constructor_name};
51 $options{debug} = 0 unless exists $options{debug};
c0cbf4d9 52
53 my $meta_instance = $metaclass->get_meta_instance;
54 $metaclass->{'___class_precedence_list'} = [ $metaclass->class_precedence_list ];
55 $metaclass->{'___compute_all_applicable_attributes'} = [ $metaclass->compute_all_applicable_attributes ];
56 $metaclass->{'___get_meta_instance'} = $meta_instance;
57 $metaclass->{'___original_class'} = blessed($metaclass);
58
59 if ($options{inline_accessors}) {
60 foreach my $attr_name ($metaclass->get_attribute_list) {
61 my $attr = $metaclass->get_attribute($attr_name);
62 $attr->install_accessors(1); # inline the accessors
63 }
64 }
65
66 if ($options{inline_constructor}) {
67 $metaclass->add_method(
68 $options{constructor_name},
69 $class->_generate_inline_constructor(
70 \%options,
71 $meta_instance,
72 $metaclass->{'___compute_all_applicable_attributes'}
73 )
74 );
75 }
76
857f87a7 77 bless $metaclass => $class;
78}
79
c0cbf4d9 80sub _generate_inline_constructor {
81 my ($class, $options, $meta_instance, $attrs) = @_;
82 # TODO:
83 # the %options should also include a both
84 # a call 'initializer' and call 'SUPER::'
85 # options, which should cover approx 90%
86 # of the possible use cases (even if it
87 # requires some adaption on the part of
88 # the author, after all, nothing is free)
89 my $source = 'sub {';
90 $source .= "\n" . 'my ($class, %params) = @_;';
91 $source .= "\n" . 'my $instance = ' . $meta_instance->inline_create_instance('$class');
92 $source .= ";\n" . (join ";\n" => map {
93 $class->_generate_slot_initializer($meta_instance, $attrs, $_)
94 } 0 .. (@$attrs - 1));
95 $source .= ";\n" . 'return $instance';
96 $source .= ";\n" . '}';
495af518 97 warn $source if $options->{debug};
c0cbf4d9 98 my $code = eval $source;
99 confess "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@" if $@;
100 return $code;
857f87a7 101}
102
c0cbf4d9 103sub _generate_slot_initializer {
104 my ($class, $meta_instance, $attrs, $index) = @_;
105 my $attr = $attrs->[$index];
106 my $default;
107 if ($attr->has_default) {
108 if ($attr->is_default_a_coderef) {
109 $default = '$attrs->[' . $index . ']->default($instance)';
110 }
111 else {
112 $default = $attrs->[$index]->default;
113 unless (looks_like_number($default)) {
114 $default = "'$default'";
115 }
116 # TODO:
117 # we should use Data::Dumper to
118 # output any ref's here, obviously
119 # we cannot handle Scalar refs, but
120 # it should work for Array and Hash
121 # refs pretty well.
122 }
123 }
124 $meta_instance->inline_set_slot_value(
125 '$instance',
373a16ae 126 ("'" . $attr->name . "'"),
c0cbf4d9 127 ('$params{\'' . $attr->init_arg . '\'}' . (defined $default ? (' || ' . $default) : ''))
128 )
857f87a7 129}
130
c0cbf4d9 131# cached methods
132
133sub get_meta_instance { (shift)->{'___get_meta_instance'} }
134sub class_precedence_list { @{(shift)->{'___class_precedence_list'}} }
135sub compute_all_applicable_attributes { @{(shift)->{'___compute_all_applicable_attributes'}} }
373a16ae 136sub get_mutable_metaclass_name { (shift)->{'___original_class'} }
c0cbf4d9 137
857f87a7 1381;
139
140__END__
141
142=pod
143
144=head1 NAME
145
146Class::MOP::Class::Immutable - An immutable version of Class::MOP::Class
147
148=head1 SYNOPSIS
149
be960ba1 150 package Point;
151 use metaclass;
152
153 __PACKAGE__->meta->add_attribute('x' => (accessor => 'x', default => 10));
154 __PACKAGE__->meta->add_attribute('y' => (accessor => 'y'));
155
156 sub new {
157 my $class = shift;
158 $class->meta->new_object(@_);
159 }
160
161 sub clear {
162 my $self = shift;
163 $self->x(0);
164 $self->y(0);
165 }
166
167 __PACKAGE__->meta->make_immutable(); # close the class
168
857f87a7 169=head1 DESCRIPTION
170
c0cbf4d9 171Class::MOP offers many benefits to object oriented development but it
172comes at a cost. Pure Class::MOP classes can be quite a bit slower than
173the typical hand coded Perl classes. This is because just about
174I<everything> is recalculated on the fly, and nothing is cached. The
175reason this is so, is because Perl itself allows you to modify virtually
176everything at runtime. Class::MOP::Class::Immutable offers an alternative
177to this.
178
179By making your class immutable, you are promising that you will not
180modify your inheritence tree or the attributes of any classes in
181that tree. Since runtime modifications like this are fairly atypical
182(and usually recomended against), this is not usally a very hard promise
183to make. For making this promise you are given a wide range of
184optimization options which bring speed close to (and sometimes above)
185those of typical hand coded Perl.
186
857f87a7 187=head1 METHODS
188
189=over 4
190
191=item B<meta>
192
193This will return a B<Class::MOP::Class> instance which is related
194to this class.
195
196=back
197
4d47b77f 198=head2 Introspection and Construction
199
200=over 4
201
202=item B<make_metaclass_immutable>
203
be960ba1 204The arguments to C<Class::MOP::Class::make_immutable> are passed
205to this method, which
206
4d47b77f 207=over 4
208
209=item I<inline_accessors (Bool)>
210
211=item I<inline_constructor (Bool)>
212
213=item I<debug (Bool)>
214
215=item I<constructor_name (Str)>
216
217=back
218
219=item B<is_immutable>
220
221=item B<is_mutable>
222
223=item B<make_immutable>
224
373a16ae 225=item B<get_mutable_metaclass_name>
226
4d47b77f 227=back
228
2243a22b 229=head2 Methods which will die if you touch them.
230
231=over 4
232
233=item B<add_attribute>
234
235=item B<add_method>
236
58d75218 237=item B<add_package_symbol>
2243a22b 238
239=item B<alias_method>
240
2243a22b 241=item B<remove_attribute>
242
243=item B<remove_method>
244
58d75218 245=item B<remove_package_symbol>
2243a22b 246
247=item B<superclasses>
248
249=back
250
251=head2 Cached methods
252
857f87a7 253=over 4
254
2243a22b 255=item B<class_precedence_list>
256
257=item B<compute_all_applicable_attributes>
258
259=item B<get_meta_instance>
260
261=back
262
857f87a7 263=head1 AUTHOR
264
265Stevan Little E<lt>stevan@iinteractive.comE<gt>
266
267=head1 COPYRIGHT AND LICENSE
268
269Copyright 2006 by Infinity Interactive, Inc.
270
271L<http://www.iinteractive.com>
272
273This library is free software; you can redistribute it and/or modify
274it under the same terms as Perl itself.
275
276=cut