2 package Class::MOP::Attribute;
8 use Scalar::Util 'blessed', 'reftype', 'weaken';
10 our $VERSION = '0.09';
13 require Class::MOP::Class;
14 Class::MOP::Class->initialize(blessed($_[0]) || $_[0]);
17 # NOTE: (meta-circularity)
18 # This method will be replaced in the
19 # boostrap section of Class::MOP, by
20 # a new version which uses the
21 # &Class::MOP::Class::construct_instance
22 # method to build an attribute meta-object
23 # which itself is described with attribute
25 # - Ain't meta-circularity grand? :)
31 (defined $name && $name)
32 || confess "You must provide a name for the attribute";
33 $options{init_arg} = $name
34 if not exists $options{init_arg};
38 accessor => $options{accessor},
39 reader => $options{reader},
40 writer => $options{writer},
41 predicate => $options{predicate},
42 init_arg => $options{init_arg},
43 default => $options{default},
44 # keep a weakened link to the
45 # class we are associated with
46 associated_class => undef,
51 # this is a primative (and kludgy) clone operation
52 # for now, it will be replaced in the Class::MOP
53 # bootstrap with a proper one, however we know
54 # that this one will work fine for now.
59 || confess "Can only clone an instance";
60 return bless { %{$self}, %options } => blessed($self);
63 sub initialize_instance_slot {
64 my ($self, $meta_instance, $instance, $params) = @_;
65 my $init_arg = $self->{init_arg};
66 # try to fetch the init arg from the %params ...
68 $val = $params->{$init_arg} if exists $params->{$init_arg};
69 # if nothing was in the %params, we can use the
70 # attribute's default value (if it has one)
71 if (!defined $val && defined $self->{default}) {
72 $val = $self->default($instance);
74 $meta_instance->set_slot_value($instance, $self->name, $val);
78 # the next bunch of methods will get bootstrapped
79 # away in the Class::MOP bootstrapping section
81 sub name { $_[0]->{name} }
83 sub associated_class { $_[0]->{associated_class} }
85 sub has_accessor { defined($_[0]->{accessor}) ? 1 : 0 }
86 sub has_reader { defined($_[0]->{reader}) ? 1 : 0 }
87 sub has_writer { defined($_[0]->{writer}) ? 1 : 0 }
88 sub has_predicate { defined($_[0]->{predicate}) ? 1 : 0 }
89 sub has_init_arg { defined($_[0]->{init_arg}) ? 1 : 0 }
90 sub has_default { defined($_[0]->{default}) ? 1 : 0 }
92 sub accessor { $_[0]->{accessor} }
93 sub reader { $_[0]->{reader} }
94 sub writer { $_[0]->{writer} }
95 sub predicate { $_[0]->{predicate} }
96 sub init_arg { $_[0]->{init_arg} }
98 # end bootstrapped away method section.
99 # (all methods below here are kept intact)
101 sub is_default_a_coderef {
102 (reftype($_[0]->{default}) && reftype($_[0]->{default}) eq 'CODE')
106 my ($self, $instance) = @_;
107 if ($instance && $self->is_default_a_coderef) {
108 # if the default is a CODE ref, then
109 # we pass in the instance and default
110 # can return a value based on that
111 # instance. Somewhat crude, but works.
112 return $self->{default}->($instance);
119 sub slots { (shift)->name }
123 sub attach_to_class {
124 my ($self, $class) = @_;
125 (blessed($class) && $class->isa('Class::MOP::Class'))
126 || confess "You must pass a Class::MOP::Class instance (or a subclass)";
127 weaken($self->{associated_class} = $class);
130 sub detach_from_class {
132 $self->{associated_class} = undef;
138 my ( $self, $instance, $value ) = @_;
140 Class::MOP::Class->initialize(Scalar::Util::blessed($instance))
142 ->set_slot_value( $instance, $self->name, $value );
146 my ( $self, $instance ) = @_;
148 Class::MOP::Class->initialize(Scalar::Util::blessed($instance))
150 ->get_slot_value( $instance, $self->name );
153 ## Method generation helpers
155 sub generate_accessor_method {
158 $attr->set_value( $_[0], $_[1] ) if scalar(@_) == 2;
159 $attr->get_value( $_[0] );
163 sub generate_accessor_method_inline {
165 my $attr_name = $self->name;
166 my $meta_instance = $self->associated_class->instance_metaclass;
168 my $code = eval 'sub {'
169 . $meta_instance->inline_set_slot_value('$_[0]', "'$attr_name'", '$_[1]') . ' if scalar(@_) == 2; '
170 . $meta_instance->inline_get_slot_value('$_[0]', "'$attr_name'")
172 confess "Could not generate inline accessor because : $@" if $@;
177 sub generate_reader_method {
180 confess "Cannot assign a value to a read-only accessor" if @_ > 1;
181 $attr->get_value( $_[0] );
185 sub generate_reader_method_inline {
187 my $attr_name = $self->name;
188 my $meta_instance = $self->associated_class->instance_metaclass;
190 my $code = eval 'sub {'
191 . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
192 . $meta_instance->inline_get_slot_value('$_[0]', "'$attr_name'")
194 confess "Could not generate inline accessor because : $@" if $@;
199 sub generate_writer_method {
202 $attr->set_value( $_[0], $_[1] );
206 sub generate_writer_method_inline {
208 my $attr_name = $self->name;
209 my $meta_instance = $self->associated_class->instance_metaclass;
211 my $code = eval 'sub {'
212 . $meta_instance->inline_set_slot_value('$_[0]', "'$attr_name'", '$_[1]')
214 confess "Could not generate inline accessor because : $@" if $@;
219 sub generate_predicate_method {
221 my $attr_name = $self->name;
223 defined Class::MOP::Class->initialize(Scalar::Util::blessed($_[0]))
225 ->get_slot_value($_[0], $attr_name) ? 1 : 0;
229 sub generate_predicate_method_inline {
231 my $attr_name = $self->name;
232 my $meta_instance = $self->associated_class->instance_metaclass;
234 my $code = eval 'sub {'
235 . 'defined ' . $meta_instance->inline_get_slot_value('$_[0]', "'$attr_name'") . ' ? 1 : 0'
237 confess "Could not generate inline accessor because : $@" if $@;
242 sub process_accessors {
243 my ($self, $type, $accessor, $generate_as_inline_methods) = @_;
244 if (reftype($accessor)) {
245 (reftype($accessor) eq 'HASH')
246 || confess "bad accessor/reader/writer/predicate format, must be a HASH ref";
247 my ($name, $method) = %{$accessor};
248 return ($name, Class::MOP::Attribute::Accessor->wrap($method));
251 my $inline_me = ($generate_as_inline_methods && $self->associated_class->instance_metaclass->is_inlinable);
252 my $generator = $self->can('generate_' . $type . '_method' . ($inline_me ? '_inline' : ''));
254 || confess "There is no method generator for the type='$type'";
255 if (my $method = $self->$generator($self->name)) {
256 return ($accessor => Class::MOP::Attribute::Accessor->wrap($method));
258 confess "Could not create the '$type' method for " . $self->name . " because : $@";
262 sub install_accessors {
265 my $class = $self->associated_class;
268 $self->process_accessors('accessor' => $self->accessor(), $inline)
269 ) if $self->has_accessor();
272 $self->process_accessors('reader' => $self->reader(), $inline)
273 ) if $self->has_reader();
276 $self->process_accessors('writer' => $self->writer(), $inline)
277 ) if $self->has_writer();
280 $self->process_accessors('predicate' => $self->predicate(), $inline)
281 ) if $self->has_predicate();
287 my $_remove_accessor = sub {
288 my ($accessor, $class) = @_;
289 if (reftype($accessor) && reftype($accessor) eq 'HASH') {
290 ($accessor) = keys %{$accessor};
292 my $method = $class->get_method($accessor);
293 $class->remove_method($accessor)
294 if (blessed($method) && $method->isa('Class::MOP::Attribute::Accessor'));
297 sub remove_accessors {
299 $_remove_accessor->($self->accessor(), $self->associated_class()) if $self->has_accessor();
300 $_remove_accessor->($self->reader(), $self->associated_class()) if $self->has_reader();
301 $_remove_accessor->($self->writer(), $self->associated_class()) if $self->has_writer();
302 $_remove_accessor->($self->predicate(), $self->associated_class()) if $self->has_predicate();
308 package Class::MOP::Attribute::Accessor;
313 use Class::MOP::Method;
315 our $VERSION = '0.01';
317 our @ISA = ('Class::MOP::Method');
327 Class::MOP::Attribute - Attribute Meta Object
331 Class::MOP::Attribute->new('$foo' => (
332 accessor => 'foo', # dual purpose get/set accessor
333 predicate => 'has_foo' # predicate check for defined-ness
334 init_arg => '-foo', # class->new will look for a -foo key
335 default => 'BAR IS BAZ!' # if no -foo key is provided, use this
338 Class::MOP::Attribute->new('$.bar' => (
339 reader => 'bar', # getter
340 writer => 'set_bar', # setter
341 predicate => 'has_bar' # predicate check for defined-ness
342 init_arg => ':bar', # class->new will look for a :bar key
343 # no default value means it is undef
348 The Attribute Protocol is almost entirely an invention of this module,
349 and is completely optional to this MOP. This is because Perl 5 does not
350 have consistent notion of what is an attribute of a class. There are
351 so many ways in which this is done, and very few (if any) are
352 easily discoverable by this module.
354 So, all that said, this module attempts to inject some order into this
355 chaos, by introducing a consistent API which can be used to create
364 =item B<new ($name, ?%options)>
366 An attribute must (at the very least), have a C<$name>. All other
367 C<%options> are contained added as key-value pairs. Acceptable keys
374 This should be a string value representing the expected key in
375 an initialization hash. For instance, if we have an I<init_arg>
376 value of C<-foo>, then the following code will Just Work.
378 MyClass->meta->construct_instance(-foo => "Hello There");
380 In an init_arg is not assigned, it will automatically use the
385 The value of this key is the default value which
386 C<Class::MOP::Class::construct_instance> will initialize the
390 If the value is a simple scalar (string or number), then it can
391 be just passed as is. However, if you wish to initialize it with
392 a HASH or ARRAY ref, then you need to wrap that inside a CODE
395 Class::MOP::Attribute->new('@foo' => (
396 default => sub { [] },
401 Class::MOP::Attribute->new('%foo' => (
402 default => sub { {} },
405 If you wish to initialize an attribute with a CODE reference
406 itself, then you need to wrap that in a subroutine as well, like
409 Class::MOP::Attribute->new('&foo' => (
410 default => sub { sub { print "Hello World" } },
413 And lastly, if the value of your attribute is dependent upon
414 some other aspect of the instance structure, then you can take
415 advantage of the fact that when the I<default> value is a CODE
416 reference, it is passed the raw (unblessed) instance structure
417 as it's only argument. So you can do things like this:
419 Class::MOP::Attribute->new('$object_identity' => (
420 default => sub { Scalar::Util::refaddr($_[0]) },
423 This last feature is fairly limited as there is no gurantee of
424 the order of attribute initializations, so you cannot perform
425 any kind of dependent initializations. However, if this is
426 something you need, you could subclass B<Class::MOP::Class> and
427 this class to acheive it. However, this is currently left as
428 an exercise to the reader :).
432 The I<accessor>, I<reader>, I<writer> and I<predicate> keys can
433 contain either; the name of the method and an appropriate default
434 one will be generated for you, B<or> a HASH ref containing exactly one
435 key (which will be used as the name of the method) and one value,
436 which should contain a CODE reference which will be installed as
443 The I<accessor> is a standard perl-style read/write accessor. It will
444 return the value of the attribute, and if a value is passed as an argument,
445 it will assign that value to the attribute.
448 This method will properly handle the following code, by assigning an
449 C<undef> value to the attribute.
451 $object->set_something(undef);
455 This is a basic read-only accessor, it will just return the value of
460 This is a basic write accessor, it accepts a single argument, and
461 assigns that value to the attribute. This method does not intentially
462 return a value, however perl will return the result of the last
463 expression in the subroutine, which returns in this returning the
464 same value that it was passed.
467 This method will properly handle the following code, by assigning an
468 C<undef> value to the attribute.
470 $object->set_something();
474 This is a basic test to see if the value of the attribute is not
475 C<undef>. It will return true (C<1>) if the attribute's value is
476 defined, and false (C<0>) otherwise.
480 =item B<clone (%options)>
482 =item B<initialize_instance_slot ($instance, $params)>
486 =head2 Value management
490 =item set_value $instance, $value
492 Set the value without going through the accessor. Note that this may be done to
493 even attributes with just read only accessors.
495 =item get_value $instance
497 Return the value without going through the accessor. Note that this may be done
498 even to attributes with just write only accessors.
504 These are all basic read-only value accessors for the values
505 passed into C<new>. I think they are pretty much self-explanitory.
521 =item B<is_default_a_coderef>
523 =item B<default (?$instance)>
525 As noted in the documentation for C<new> above, if the I<default>
526 value is a CODE reference, this accessor will pass a single additional
527 argument C<$instance> into it and return the value.
531 Returns a list of slots required by the attribute. This is usually
532 just one, which is the name of the attribute.
536 =head2 Informational predicates
538 These are all basic predicate methods for the values passed into C<new>.
542 =item B<has_accessor>
548 =item B<has_predicate>
550 =item B<has_init_arg>
556 =head2 Class association
560 =item B<associated_class>
562 =item B<attach_to_class ($class)>
564 =item B<detach_from_class>
568 =item B<allocate_slots>
570 =item B<deallocate_slots>
574 =head2 Attribute Accessor generation
578 =item B<install_accessors>
580 This allows the attribute to generate and install code for it's own
581 I<accessor/reader/writer/predicate> methods. This is called by
582 C<Class::MOP::Class::add_attribute>.
584 This method will call C<process_accessors> for each of the possible
585 method types (accessor, reader, writer & predicate).
587 =item B<process_accessors ($type, $value)>
589 This takes a C<$type> (accessor, reader, writer or predicate), and
590 a C<$value> (the value passed into the constructor for each of the
591 different types). It will then either generate the method itself
592 (using the C<generate_*_method> methods listed below) or it will
593 use the custom method passed through the constructor.
597 =item B<generate_accessor_method>
599 =item B<generate_predicate_method>
601 =item B<generate_reader_method>
603 =item B<generate_writer_method>
609 =item B<generate_accessor_method_inline>
611 =item B<generate_predicate_method_inline>
613 =item B<generate_reader_method_inline>
615 =item B<generate_writer_method_inline>
619 =item B<remove_accessors>
621 This allows the attribute to remove the method for it's own
622 I<accessor/reader/writer/predicate>. This is called by
623 C<Class::MOP::Class::remove_attribute>.
633 This will return a B<Class::MOP::Class> instance which is related
636 It should also be noted that B<Class::MOP> will actually bootstrap
637 this module by installing a number of attribute meta-objects into
638 it's metaclass. This will allow this class to reap all the benifits
639 of the MOP when subclassing it.
645 Stevan Little E<lt>stevan@iinteractive.comE<gt>
647 =head1 COPYRIGHT AND LICENSE
649 Copyright 2006 by Infinity Interactive, Inc.
651 L<http://www.iinteractive.com>
653 This library is free software; you can redistribute it and/or modify
654 it under the same terms as Perl itself.