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