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
16sub reinitialize { confess 'Cannot call method "reinitialize" on an immutable instance' }
17
18sub add_method { confess 'Cannot call method "add_method" on an immutable instance' }
19sub alias_method { confess 'Cannot call method "alias_method" on an immutable instance' }
20sub remove_method { confess 'Cannot call method "remove_method" on an immutable instance' }
21
22sub add_attribute { confess 'Cannot call method "add_attribute" on an immutable instance' }
23sub remove_attribute { confess 'Cannot call method "remove_attribute" on an immutable instance' }
24
58d75218 25sub add_package_symbol { confess 'Cannot call method "add_package_symbol" on an immutable instance' }
26sub remove_package_symbol { confess 'Cannot call method "remove_package_symbol" on an immutable instance' }
857f87a7 27
28# NOTE:
29# superclasses is an accessor, so
30# it just cannot be changed
31sub superclasses {
32 my $class = shift;
33 (!@_) || confess 'Cannot change the "superclasses" on an immmutable instance';
34 no strict 'refs';
35 @{$class->name . '::ISA'};
36}
37
38# predicates
39
40sub is_mutable { 0 }
41sub is_immutable { 1 }
42
43sub make_immutable { () }
44
45sub make_metaclass_immutable {
c0cbf4d9 46 my ($class, $metaclass, %options) = @_;
47
4d47b77f 48 # NOTE:
49 # i really need the // (defined-or) operator here
50 $options{inline_accessors} = 1 unless exists $options{inline_accessors};
51 $options{inline_constructor} = 1 unless exists $options{inline_constructor};
52 $options{constructor_name} = 'new' unless exists $options{constructor_name};
53 $options{debug} = 0 unless exists $options{debug};
c0cbf4d9 54
55 my $meta_instance = $metaclass->get_meta_instance;
56 $metaclass->{'___class_precedence_list'} = [ $metaclass->class_precedence_list ];
57 $metaclass->{'___compute_all_applicable_attributes'} = [ $metaclass->compute_all_applicable_attributes ];
58 $metaclass->{'___get_meta_instance'} = $meta_instance;
59 $metaclass->{'___original_class'} = blessed($metaclass);
60
61 if ($options{inline_accessors}) {
62 foreach my $attr_name ($metaclass->get_attribute_list) {
63 my $attr = $metaclass->get_attribute($attr_name);
64 $attr->install_accessors(1); # inline the accessors
65 }
66 }
67
68 if ($options{inline_constructor}) {
69 $metaclass->add_method(
70 $options{constructor_name},
71 $class->_generate_inline_constructor(
72 \%options,
73 $meta_instance,
74 $metaclass->{'___compute_all_applicable_attributes'}
75 )
76 );
77 }
78
857f87a7 79 bless $metaclass => $class;
80}
81
c0cbf4d9 82sub _generate_inline_constructor {
83 my ($class, $options, $meta_instance, $attrs) = @_;
84 # TODO:
85 # the %options should also include a both
86 # a call 'initializer' and call 'SUPER::'
87 # options, which should cover approx 90%
88 # of the possible use cases (even if it
89 # requires some adaption on the part of
90 # the author, after all, nothing is free)
91 my $source = 'sub {';
92 $source .= "\n" . 'my ($class, %params) = @_;';
93 $source .= "\n" . 'my $instance = ' . $meta_instance->inline_create_instance('$class');
94 $source .= ";\n" . (join ";\n" => map {
95 $class->_generate_slot_initializer($meta_instance, $attrs, $_)
96 } 0 .. (@$attrs - 1));
97 $source .= ";\n" . 'return $instance';
98 $source .= ";\n" . '}';
495af518 99 warn $source if $options->{debug};
c0cbf4d9 100 my $code = eval $source;
101 confess "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@" if $@;
102 return $code;
857f87a7 103}
104
c0cbf4d9 105sub _generate_slot_initializer {
106 my ($class, $meta_instance, $attrs, $index) = @_;
107 my $attr = $attrs->[$index];
108 my $default;
109 if ($attr->has_default) {
110 if ($attr->is_default_a_coderef) {
111 $default = '$attrs->[' . $index . ']->default($instance)';
112 }
113 else {
114 $default = $attrs->[$index]->default;
115 unless (looks_like_number($default)) {
116 $default = "'$default'";
117 }
118 # TODO:
119 # we should use Data::Dumper to
120 # output any ref's here, obviously
121 # we cannot handle Scalar refs, but
122 # it should work for Array and Hash
123 # refs pretty well.
124 }
125 }
126 $meta_instance->inline_set_slot_value(
127 '$instance',
128 $attr->name,
129 ('$params{\'' . $attr->init_arg . '\'}' . (defined $default ? (' || ' . $default) : ''))
130 )
857f87a7 131}
132
c0cbf4d9 133# cached methods
134
135sub get_meta_instance { (shift)->{'___get_meta_instance'} }
136sub class_precedence_list { @{(shift)->{'___class_precedence_list'}} }
137sub compute_all_applicable_attributes { @{(shift)->{'___compute_all_applicable_attributes'}} }
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
226=back
227
2243a22b 228=head2 Methods which will die if you touch them.
229
230=over 4
231
232=item B<add_attribute>
233
234=item B<add_method>
235
58d75218 236=item B<add_package_symbol>
2243a22b 237
238=item B<alias_method>
239
240=item B<reinitialize>
241
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
857f87a7 264=head1 AUTHOR
265
266Stevan Little E<lt>stevan@iinteractive.comE<gt>
267
268=head1 COPYRIGHT AND LICENSE
269
270Copyright 2006 by Infinity Interactive, Inc.
271
272L<http://www.iinteractive.com>
273
274This library is free software; you can redistribute it and/or modify
275it under the same terms as Perl itself.
276
277=cut