2 package Class::MOP::Class::Immutable;
8 use Scalar::Util 'blessed', 'looks_like_number';
10 our $VERSION = '0.02';
11 our $AUTHORITY = 'cpan:STEVAN';
13 use base 'Class::MOP::Class';
15 # methods which can *not* be called
17 sub add_method { confess 'Cannot call method "add_method" on an immutable instance' }
18 sub alias_method { confess 'Cannot call method "alias_method" on an immutable instance' }
19 sub remove_method { confess 'Cannot call method "remove_method" on an immutable instance' }
21 sub add_attribute { confess 'Cannot call method "add_attribute" on an immutable instance' }
22 sub remove_attribute { confess 'Cannot call method "remove_attribute" on an immutable instance' }
24 sub add_package_symbol { confess 'Cannot call method "add_package_symbol" on an immutable instance' }
25 sub remove_package_symbol { confess 'Cannot call method "remove_package_symbol" on an immutable instance' }
28 # superclasses is an accessor, so
29 # it just cannot be changed
32 (!@_) || confess 'Cannot change the "superclasses" on an immmutable instance';
33 @{$class->get_package_symbol('@ISA')};
39 sub is_immutable { 1 }
41 sub make_immutable { () }
43 sub make_metaclass_immutable {
44 my ($class, $metaclass, %options) = @_;
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};
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);
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
66 if ($options{inline_constructor}) {
67 $metaclass->add_method(
68 $options{constructor_name},
69 $class->_generate_inline_constructor(
72 $metaclass->{'___compute_all_applicable_attributes'}
77 # now cache the method map ...
78 $metaclass->{'___method_map'} = $metaclass->get_method_map;
80 bless $metaclass => $class;
83 sub _generate_inline_constructor {
84 my ($class, $options, $meta_instance, $attrs) = @_;
86 # the %options should also include a both
87 # a call 'initializer' and call 'SUPER::'
88 # options, which should cover approx 90%
89 # of the possible use cases (even if it
90 # requires some adaption on the part of
91 # the author, after all, nothing is free)
93 $source .= "\n" . 'my ($class, %params) = @_;';
94 $source .= "\n" . 'my $instance = ' . $meta_instance->inline_create_instance('$class');
95 $source .= ";\n" . (join ";\n" => map {
96 $class->_generate_slot_initializer($meta_instance, $attrs, $_)
97 } 0 .. (@$attrs - 1));
98 $source .= ";\n" . 'return $instance';
99 $source .= ";\n" . '}';
100 warn $source if $options->{debug};
101 my $code = eval $source;
102 confess "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@" if $@;
106 sub _generate_slot_initializer {
107 my ($class, $meta_instance, $attrs, $index) = @_;
108 my $attr = $attrs->[$index];
110 if ($attr->has_default) {
112 # default values can either be CODE refs
113 # in which case we need to call them. Or
114 # they can be scalars (strings/numbers)
115 # in which case we can just deal with them
116 # in the code we eval.
117 if ($attr->is_default_a_coderef) {
118 $default = '$attrs->[' . $index . ']->default($instance)';
121 $default = $attrs->[$index]->default;
122 # make sure to quote strings ...
123 unless (looks_like_number($default)) {
124 $default = "'$default'";
128 $meta_instance->inline_set_slot_value(
130 ("'" . $attr->name . "'"),
131 ('$params{\'' . $attr->init_arg . '\'}' . (defined $default ? (' || ' . $default) : ''))
137 sub get_meta_instance { (shift)->{'___get_meta_instance'} }
138 sub class_precedence_list { @{(shift)->{'___class_precedence_list'}} }
139 sub compute_all_applicable_attributes { @{(shift)->{'___compute_all_applicable_attributes'}} }
140 sub get_mutable_metaclass_name { (shift)->{'___original_class'} }
141 sub get_method_map { (shift)->{'___method_map'} }
151 Class::MOP::Class::Immutable - An immutable version of Class::MOP::Class
158 __PACKAGE__->meta->add_attribute('x' => (accessor => 'x', default => 10));
159 __PACKAGE__->meta->add_attribute('y' => (accessor => 'y'));
163 $class->meta->new_object(@_);
172 __PACKAGE__->meta->make_immutable(); # close the class
176 Class::MOP offers many benefits to object oriented development but it
177 comes at a cost. Pure Class::MOP classes can be quite a bit slower than
178 the typical hand coded Perl classes. This is because just about
179 I<everything> is recalculated on the fly, and nothing is cached. The
180 reason this is so, is because Perl itself allows you to modify virtually
181 everything at runtime. Class::MOP::Class::Immutable offers an alternative
184 By making your class immutable, you are promising that you will not
185 modify your inheritence tree or the attributes of any classes in
186 that tree. Since runtime modifications like this are fairly atypical
187 (and usually recomended against), this is not usally a very hard promise
188 to make. For making this promise you are given a wide range of
189 optimization options which bring speed close to (and sometimes above)
190 those of typical hand coded Perl.
198 This will return a B<Class::MOP::Class> instance which is related
203 =head2 Introspection and Construction
207 =item B<make_metaclass_immutable>
209 The arguments to C<Class::MOP::Class::make_immutable> are passed
210 to this method, which
214 =item I<inline_accessors (Bool)>
216 =item I<inline_constructor (Bool)>
218 =item I<debug (Bool)>
220 =item I<constructor_name (Str)>
224 =item B<is_immutable>
228 =item B<make_immutable>
230 =item B<get_mutable_metaclass_name>
234 =head2 Methods which will die if you touch them.
238 =item B<add_attribute>
242 =item B<add_package_symbol>
244 =item B<alias_method>
246 =item B<remove_attribute>
248 =item B<remove_method>
250 =item B<remove_package_symbol>
252 =item B<superclasses>
256 =head2 Cached methods
260 =item B<class_precedence_list>
262 =item B<compute_all_applicable_attributes>
264 =item B<get_meta_instance>
266 =item B<get_method_map>
272 Stevan Little E<lt>stevan@iinteractive.comE<gt>
274 Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
276 =head1 COPYRIGHT AND LICENSE
278 Copyright 2006 by Infinity Interactive, Inc.
280 L<http://www.iinteractive.com>
282 This library is free software; you can redistribute it and/or modify
283 it under the same terms as Perl itself.