testing
[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';
fdbdb5e6 8use Scalar::Util 'blessed', 'looks_like_number';
857f87a7 9
b1897d4d 10our $VERSION = '0.03';
f0480c45 11our $AUTHORITY = 'cpan:STEVAN';
857f87a7 12
13use base 'Class::MOP::Class';
14
dc76a410 15# enforce the meta-circularity here
16# and hide the Immutable part
17
18sub meta {
19 my $self = shift;
20 # if it is not blessed, then someone is asking
21 # for the meta of Class::MOP::Class::Immutable
22 return Class::MOP::Class->initialize($self) unless blessed($self);
23 # otherwise, they are asking for the metaclass
24 # which has been made immutable, which is itself
25 return $self;
26}
27
857f87a7 28# methods which can *not* be called
b1897d4d 29for my $meth (qw(
30 add_method
31 alias_method
32 remove_method
33 add_attribute
34 remove_attribute
35 add_package_symbol
36 remove_package_symbol
37)) {
38 no strict 'refs';
39 *{$meth} = sub {
40 confess "Cannot call method '$meth' on an immutable instance";
41 };
42}
857f87a7 43
fdbdb5e6 44sub get_package_symbol {
45 my ($self, $variable) = @_;
46 my ($name, $sigil, $type) = $self->_deconstruct_variable_name($variable);
47 return *{$self->namespace->{$name}}{$type}
48 if exists $self->namespace->{$name};
49 # NOTE:
50 # we have to do this here in order to preserve
51 # perl's autovivification of variables. However
52 # we do cut off direct access to add_package_symbol
53 # as shown above.
54 $self->Class::MOP::Package::add_package_symbol($variable);
55}
56
857f87a7 57# NOTE:
58# superclasses is an accessor, so
59# it just cannot be changed
60sub superclasses {
61 my $class = shift;
62 (!@_) || confess 'Cannot change the "superclasses" on an immmutable instance';
a5e51f0b 63 @{$class->get_package_symbol('@ISA')};
857f87a7 64}
65
66# predicates
67
68sub is_mutable { 0 }
69sub is_immutable { 1 }
70
71sub make_immutable { () }
72
73sub make_metaclass_immutable {
c0cbf4d9 74 my ($class, $metaclass, %options) = @_;
75
4d47b77f 76 # NOTE:
77 # i really need the // (defined-or) operator here
78 $options{inline_accessors} = 1 unless exists $options{inline_accessors};
79 $options{inline_constructor} = 1 unless exists $options{inline_constructor};
80 $options{constructor_name} = 'new' unless exists $options{constructor_name};
81 $options{debug} = 0 unless exists $options{debug};
c0cbf4d9 82
83 my $meta_instance = $metaclass->get_meta_instance;
84 $metaclass->{'___class_precedence_list'} = [ $metaclass->class_precedence_list ];
85 $metaclass->{'___compute_all_applicable_attributes'} = [ $metaclass->compute_all_applicable_attributes ];
86 $metaclass->{'___get_meta_instance'} = $meta_instance;
87 $metaclass->{'___original_class'} = blessed($metaclass);
88
89 if ($options{inline_accessors}) {
90 foreach my $attr_name ($metaclass->get_attribute_list) {
fdbdb5e6 91 my $attr = $metaclass->get_attribute($attr_name);
92 $attr->install_accessors(1); # inline the accessors
c0cbf4d9 93 }
94 }
95
96 if ($options{inline_constructor}) {
97 $metaclass->add_method(
98 $options{constructor_name},
fdbdb5e6 99 $class->_generate_inline_constructor(
100 \%options,
101 $meta_instance,
102 $metaclass->{'___compute_all_applicable_attributes'}
103 )
c0cbf4d9 104 );
105 }
ce2ae40f 106
107 # now cache the method map ...
fdbdb5e6 108 $metaclass->{'___method_map'} = $metaclass->get_method_map;
c0cbf4d9 109
857f87a7 110 bless $metaclass => $class;
111}
112
fdbdb5e6 113sub _generate_inline_constructor {
114 my ($class, $options, $meta_instance, $attrs) = @_;
115 # TODO:
116 # the %options should also include a both
117 # a call 'initializer' and call 'SUPER::'
118 # options, which should cover approx 90%
119 # of the possible use cases (even if it
120 # requires some adaption on the part of
121 # the author, after all, nothing is free)
122 my $source = 'sub {';
123 $source .= "\n" . 'my ($class, %params) = @_;';
124 $source .= "\n" . 'my $instance = ' . $meta_instance->inline_create_instance('$class');
125 $source .= ";\n" . (join ";\n" => map {
126 $class->_generate_slot_initializer($meta_instance, $attrs, $_)
127 } 0 .. (@$attrs - 1));
128 $source .= ";\n" . 'return $instance';
129 $source .= ";\n" . '}';
130 warn $source if $options->{debug};
131 my $code = eval $source;
132 confess "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@" if $@;
133 return $code;
134}
135
136sub _generate_slot_initializer {
137 my ($class, $meta_instance, $attrs, $index) = @_;
138 my $attr = $attrs->[$index];
139 my $default;
140 if ($attr->has_default) {
141 # NOTE:
142 # default values can either be CODE refs
143 # in which case we need to call them. Or
144 # they can be scalars (strings/numbers)
145 # in which case we can just deal with them
146 # in the code we eval.
147 if ($attr->is_default_a_coderef) {
148 $default = '$attrs->[' . $index . ']->default($instance)';
149 }
150 else {
151 $default = $attrs->[$index]->default;
152 # make sure to quote strings ...
153 unless (looks_like_number($default)) {
154 $default = "'$default'";
155 }
156 }
157 }
158 $meta_instance->inline_set_slot_value(
159 '$instance',
160 ("'" . $attr->name . "'"),
161 ('$params{\'' . $attr->init_arg . '\'}' . (defined $default ? (' || ' . $default) : ''))
162 )
163}
164
c0cbf4d9 165# cached methods
166
167sub get_meta_instance { (shift)->{'___get_meta_instance'} }
168sub class_precedence_list { @{(shift)->{'___class_precedence_list'}} }
169sub compute_all_applicable_attributes { @{(shift)->{'___compute_all_applicable_attributes'}} }
373a16ae 170sub get_mutable_metaclass_name { (shift)->{'___original_class'} }
fdbdb5e6 171sub get_method_map { (shift)->{'___method_map'} }
c0cbf4d9 172
857f87a7 1731;
174
175__END__
176
177=pod
178
179=head1 NAME
180
181Class::MOP::Class::Immutable - An immutable version of Class::MOP::Class
182
183=head1 SYNOPSIS
184
be960ba1 185 package Point;
186 use metaclass;
187
188 __PACKAGE__->meta->add_attribute('x' => (accessor => 'x', default => 10));
189 __PACKAGE__->meta->add_attribute('y' => (accessor => 'y'));
190
191 sub new {
192 my $class = shift;
193 $class->meta->new_object(@_);
194 }
195
196 sub clear {
197 my $self = shift;
198 $self->x(0);
199 $self->y(0);
200 }
201
202 __PACKAGE__->meta->make_immutable(); # close the class
203
857f87a7 204=head1 DESCRIPTION
205
c0cbf4d9 206Class::MOP offers many benefits to object oriented development but it
207comes at a cost. Pure Class::MOP classes can be quite a bit slower than
208the typical hand coded Perl classes. This is because just about
209I<everything> is recalculated on the fly, and nothing is cached. The
210reason this is so, is because Perl itself allows you to modify virtually
211everything at runtime. Class::MOP::Class::Immutable offers an alternative
212to this.
213
214By making your class immutable, you are promising that you will not
215modify your inheritence tree or the attributes of any classes in
216that tree. Since runtime modifications like this are fairly atypical
217(and usually recomended against), this is not usally a very hard promise
218to make. For making this promise you are given a wide range of
219optimization options which bring speed close to (and sometimes above)
220those of typical hand coded Perl.
221
857f87a7 222=head1 METHODS
223
224=over 4
225
226=item B<meta>
227
228This will return a B<Class::MOP::Class> instance which is related
229to this class.
230
231=back
232
4d47b77f 233=head2 Introspection and Construction
234
235=over 4
236
237=item B<make_metaclass_immutable>
238
be960ba1 239The arguments to C<Class::MOP::Class::make_immutable> are passed
240to this method, which
241
4d47b77f 242=over 4
243
244=item I<inline_accessors (Bool)>
245
246=item I<inline_constructor (Bool)>
247
248=item I<debug (Bool)>
249
250=item I<constructor_name (Str)>
251
252=back
253
254=item B<is_immutable>
255
256=item B<is_mutable>
257
258=item B<make_immutable>
259
373a16ae 260=item B<get_mutable_metaclass_name>
261
4d47b77f 262=back
263
2243a22b 264=head2 Methods which will die if you touch them.
265
266=over 4
267
268=item B<add_attribute>
269
270=item B<add_method>
271
58d75218 272=item B<add_package_symbol>
2243a22b 273
274=item B<alias_method>
275
2243a22b 276=item B<remove_attribute>
277
278=item B<remove_method>
279
58d75218 280=item B<remove_package_symbol>
2243a22b 281
b6164407 282=back
283
284=head2 Methods which work slightly differently.
285
286=over 4
287
2243a22b 288=item B<superclasses>
289
b6164407 290This method becomes read-only in an immutable class.
291
fdbdb5e6 292=item B<get_package_symbol>
293
294This method must handle package variable autovivification
295correctly, while still disallowing C<add_package_symbol>.
296
2243a22b 297=back
298
299=head2 Cached methods
300
857f87a7 301=over 4
302
2243a22b 303=item B<class_precedence_list>
304
305=item B<compute_all_applicable_attributes>
306
307=item B<get_meta_instance>
308
ce2ae40f 309=item B<get_method_map>
310
2243a22b 311=back
312
1a09d9cc 313=head1 AUTHORS
857f87a7 314
315Stevan Little E<lt>stevan@iinteractive.comE<gt>
316
1a09d9cc 317Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
318
857f87a7 319=head1 COPYRIGHT AND LICENSE
320
321Copyright 2006 by Infinity Interactive, Inc.
322
323L<http://www.iinteractive.com>
324
325This library is free software; you can redistribute it and/or modify
326it under the same terms as Perl itself.
327
328=cut