lost of misc changes; fixed the &create method; test cleanup
[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
f0480c45 10our $VERSION = '0.02';
11our $AUTHORITY = 'cpan:STEVAN';
857f87a7 12
13use base 'Class::MOP::Class';
14
15# methods which can *not* be called
16
1396f86b 17sub add_method { confess 'Cannot call method "add_method" on an immutable instance' }
18sub alias_method { confess 'Cannot call method "alias_method" on an immutable instance' }
19sub remove_method { confess 'Cannot call method "remove_method" on an immutable instance' }
20
21sub add_attribute { confess 'Cannot call method "add_attribute" on an immutable instance' }
22sub remove_attribute { confess 'Cannot call method "remove_attribute" on an immutable instance' }
857f87a7 23
58d75218 24sub add_package_symbol { confess 'Cannot call method "add_package_symbol" on an immutable instance' }
25sub remove_package_symbol { confess 'Cannot call method "remove_package_symbol" on an immutable instance' }
857f87a7 26
27# NOTE:
28# superclasses is an accessor, so
29# it just cannot be changed
30sub superclasses {
31 my $class = shift;
32 (!@_) || confess 'Cannot change the "superclasses" on an immmutable instance';
a5e51f0b 33 @{$class->get_package_symbol('@ISA')};
857f87a7 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) {
88dd563c 108 # NOTE:
109 # default values can either be CODE refs
110 # in which case we need to call them. Or
111 # they can be scalars (strings/numbers)
112 # in which case we can just deal with them
113 # in the code we eval.
c0cbf4d9 114 if ($attr->is_default_a_coderef) {
115 $default = '$attrs->[' . $index . ']->default($instance)';
116 }
117 else {
118 $default = $attrs->[$index]->default;
88dd563c 119 # make sure to quote strings ...
c0cbf4d9 120 unless (looks_like_number($default)) {
121 $default = "'$default'";
122 }
c0cbf4d9 123 }
124 }
125 $meta_instance->inline_set_slot_value(
126 '$instance',
373a16ae 127 ("'" . $attr->name . "'"),
c0cbf4d9 128 ('$params{\'' . $attr->init_arg . '\'}' . (defined $default ? (' || ' . $default) : ''))
129 )
857f87a7 130}
131
c0cbf4d9 132# cached methods
133
134sub get_meta_instance { (shift)->{'___get_meta_instance'} }
135sub class_precedence_list { @{(shift)->{'___class_precedence_list'}} }
136sub compute_all_applicable_attributes { @{(shift)->{'___compute_all_applicable_attributes'}} }
373a16ae 137sub get_mutable_metaclass_name { (shift)->{'___original_class'} }
c0cbf4d9 138
857f87a7 1391;
140
141__END__
142
143=pod
144
145=head1 NAME
146
147Class::MOP::Class::Immutable - An immutable version of Class::MOP::Class
148
149=head1 SYNOPSIS
150
be960ba1 151 package Point;
152 use metaclass;
153
154 __PACKAGE__->meta->add_attribute('x' => (accessor => 'x', default => 10));
155 __PACKAGE__->meta->add_attribute('y' => (accessor => 'y'));
156
157 sub new {
158 my $class = shift;
159 $class->meta->new_object(@_);
160 }
161
162 sub clear {
163 my $self = shift;
164 $self->x(0);
165 $self->y(0);
166 }
167
168 __PACKAGE__->meta->make_immutable(); # close the class
169
857f87a7 170=head1 DESCRIPTION
171
c0cbf4d9 172Class::MOP offers many benefits to object oriented development but it
173comes at a cost. Pure Class::MOP classes can be quite a bit slower than
174the typical hand coded Perl classes. This is because just about
175I<everything> is recalculated on the fly, and nothing is cached. The
176reason this is so, is because Perl itself allows you to modify virtually
177everything at runtime. Class::MOP::Class::Immutable offers an alternative
178to this.
179
180By making your class immutable, you are promising that you will not
181modify your inheritence tree or the attributes of any classes in
182that tree. Since runtime modifications like this are fairly atypical
183(and usually recomended against), this is not usally a very hard promise
184to make. For making this promise you are given a wide range of
185optimization options which bring speed close to (and sometimes above)
186those of typical hand coded Perl.
187
857f87a7 188=head1 METHODS
189
190=over 4
191
192=item B<meta>
193
194This will return a B<Class::MOP::Class> instance which is related
195to this class.
196
197=back
198
4d47b77f 199=head2 Introspection and Construction
200
201=over 4
202
203=item B<make_metaclass_immutable>
204
be960ba1 205The arguments to C<Class::MOP::Class::make_immutable> are passed
206to this method, which
207
4d47b77f 208=over 4
209
210=item I<inline_accessors (Bool)>
211
212=item I<inline_constructor (Bool)>
213
214=item I<debug (Bool)>
215
216=item I<constructor_name (Str)>
217
218=back
219
220=item B<is_immutable>
221
222=item B<is_mutable>
223
224=item B<make_immutable>
225
373a16ae 226=item B<get_mutable_metaclass_name>
227
4d47b77f 228=back
229
2243a22b 230=head2 Methods which will die if you touch them.
231
232=over 4
233
234=item B<add_attribute>
235
236=item B<add_method>
237
58d75218 238=item B<add_package_symbol>
2243a22b 239
240=item B<alias_method>
241
2243a22b 242=item B<remove_attribute>
243
244=item B<remove_method>
245
58d75218 246=item B<remove_package_symbol>
2243a22b 247
248=item B<superclasses>
249
250=back
251
252=head2 Cached methods
253
857f87a7 254=over 4
255
2243a22b 256=item B<class_precedence_list>
257
258=item B<compute_all_applicable_attributes>
259
260=item B<get_meta_instance>
261
262=back
263
1a09d9cc 264=head1 AUTHORS
857f87a7 265
266Stevan Little E<lt>stevan@iinteractive.comE<gt>
267
1a09d9cc 268Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
269
857f87a7 270=head1 COPYRIGHT AND LICENSE
271
272Copyright 2006 by Infinity Interactive, Inc.
273
274L<http://www.iinteractive.com>
275
276This library is free software; you can redistribute it and/or modify
277it under the same terms as Perl itself.
278
279=cut