Update spelling list
[gitmo/MooseX-ClassAttribute.git] / lib / MooseX / ClassAttribute / Trait / Class.pm
CommitLineData
63fcc508 1package MooseX::ClassAttribute::Trait::Class;
bb70fe3a 2
3use strict;
4use warnings;
5
63fcc508 6use MooseX::ClassAttribute::Trait::Attribute;
bb70fe3a 7use Scalar::Util qw( blessed );
8
aa639029 9use namespace::autoclean;
bb70fe3a 10use Moose::Role;
11
63fcc508 12with 'MooseX::ClassAttribute::Trait::Mixin::HasClassAttributes';
9b2bd146 13
14has _class_attribute_values => (
aa639029 15 traits => ['Hash'],
16 is => 'ro',
17 isa => 'HashRef',
18 handles => {
19 'get_class_attribute_value' => 'get',
20 'set_class_attribute_value' => 'set',
21 'has_class_attribute_value' => 'exists',
22 'clear_class_attribute_value' => 'delete',
9b2bd146 23 },
aa639029 24 lazy => 1,
25 default => sub { $_[0]->_class_attribute_values_hashref() },
26 init_arg => undef,
9b2bd146 27);
28
deaffdd0 29around add_class_attribute => sub {
30 my $orig = shift;
bb70fe3a 31 my $self = shift;
deaffdd0 32 my $attr = (
33 blessed $_[0] && $_[0]->isa('Class::MOP::Attribute')
bb70fe3a 34 ? $_[0]
deaffdd0 35 : $self->_process_class_attribute(@_)
36 );
bb70fe3a 37
deaffdd0 38 $self->$orig($attr);
bb70fe3a 39
deaffdd0 40 return $attr;
41};
bb70fe3a 42
deaffdd0 43sub _post_add_class_attribute {
44 my $self = shift;
45 my $attr = shift;
bb70fe3a 46
deaffdd0 47 my $name = $attr->name();
bb70fe3a 48
9b2bd146 49 my $e = do {
50 local $@;
51 eval { $attr->install_accessors() };
52 $@;
53 };
bb70fe3a 54
9b2bd146 55 if ($e) {
bb70fe3a 56 $self->remove_attribute($name);
57 die $e;
58 }
deaffdd0 59}
bb70fe3a 60
deaffdd0 61sub _attach_class_attribute {
62 my ($self, $attribute) = @_;
63 $attribute->attach_to_class($self);
bb70fe3a 64}
65
66# It'd be nice if I didn't have to replicate this for class
67# attributes, since it's basically just a copy of
68# Moose::Meta::Class->_process_attribute
9b2bd146 69sub _process_class_attribute {
bb70fe3a 70 my $self = shift;
71 my $name = shift;
72 my @args = @_;
73
9b2bd146 74 @args = %{ $args[0] } if scalar @args == 1 && ref( $args[0] ) eq 'HASH';
bb70fe3a 75
9b2bd146 76 if ( $name =~ /^\+(.*)/ ) {
bb70fe3a 77 return $self->_process_inherited_class_attribute( $1, @args );
78 }
9b2bd146 79 else {
bb70fe3a 80 return $self->_process_new_class_attribute( $name, @args );
81 }
82}
83
9b2bd146 84sub _process_new_class_attribute {
bb70fe3a 85 my $self = shift;
86 my $name = shift;
87 my %p = @_;
88
9b2bd146 89 if ( $p{traits} ) {
63fcc508 90 push @{ $p{traits} }, 'MooseX::ClassAttribute::Trait::Attribute';
bb70fe3a 91 }
9b2bd146 92 else {
63fcc508 93 $p{traits} = ['MooseX::ClassAttribute::Trait::Attribute'];
bb70fe3a 94 }
95
96 return Moose::Meta::Attribute->interpolate_class_and_new( $name, %p );
97}
98
9b2bd146 99sub _process_inherited_class_attribute {
bb70fe3a 100 my $self = shift;
101 my $name = shift;
102 my %p = @_;
103
104 my $inherited_attr = $self->find_class_attribute_by_name($name);
105
9b2bd146 106 ( defined $inherited_attr )
107 || confess
108 "Could not find an attribute by the name of '$name' to inherit from";
bb70fe3a 109
110 return $inherited_attr->clone_and_inherit_options(%p);
111}
112
ad109c62 113around remove_class_attribute => sub {
114 my $orig = shift;
bb70fe3a 115 my $self = shift;
bb70fe3a 116
ad109c62 117 my $removed_attr = $self->$orig(@_)
118 or return;
bb70fe3a 119
120 $removed_attr->remove_accessors();
121 $removed_attr->detach_from_class();
122
123 return $removed_attr;
ad109c62 124};
bb70fe3a 125
9b2bd146 126sub get_all_class_attributes {
bb70fe3a 127 my $self = shift;
128
9b2bd146 129 my %attrs
130 = map {
131 my $meta = Class::MOP::class_of($_);
132 $meta && $meta->can('get_class_attribute_map')
133 ? %{ $meta->get_class_attribute_map() }
134 : ()
135 }
bb70fe3a 136 reverse $self->linearized_isa;
137
138 return values %attrs;
139}
140
9b2bd146 141sub compute_all_applicable_class_attributes {
142 warn
143 'The compute_all_applicable_class_attributes method has been deprecated.'
b64c8efa 144 . " Use get_all_class_attributes instead.\n";
145
146 shift->compute_all_applicable_class_attributes(@_);
147}
148
9b2bd146 149sub find_class_attribute_by_name {
bb70fe3a 150 my $self = shift;
151 my $name = shift;
152
9b2bd146 153 foreach my $class ( $self->linearized_isa() ) {
941ae03a 154 my $meta = Class::MOP::class_of($class)
155 or next;
bb70fe3a 156
157 return $meta->get_class_attribute($name)
9b2bd146 158 if $meta->can('has_class_attribute')
159 && $meta->has_class_attribute($name);
bb70fe3a 160 }
161
162 return;
163}
164
9b2bd146 165sub _class_attribute_values_hashref {
bb70fe3a 166 my $self = shift;
167
168 no strict 'refs';
169 return \%{ $self->_class_attribute_var_name() };
170}
171
9b2bd146 172sub _class_attribute_var_name {
bb70fe3a 173 my $self = shift;
174
175 return $self->name() . q'::__ClassAttributeValues';
176}
177
9b2bd146 178sub inline_class_slot_access {
bb70fe3a 179 my $self = shift;
180 my $name = shift;
181
9b2bd146 182 return
183 '$'
184 . $self->_class_attribute_var_name . '{"'
185 . quotemeta($name) . '"}';
bb70fe3a 186}
187
9b2bd146 188sub inline_get_class_slot_value {
bb70fe3a 189 my $self = shift;
190 my $name = shift;
191
192 return $self->inline_class_slot_access($name);
193}
194
9b2bd146 195sub inline_set_class_slot_value {
bb70fe3a 196 my $self = shift;
197 my $name = shift;
198 my $val_name = shift;
199
200 return $self->inline_class_slot_access($name) . ' = ' . $val_name;
201}
202
9b2bd146 203sub inline_is_class_slot_initialized {
204 my $self = shift;
205 my $name = shift;
bb70fe3a 206
207 return 'exists ' . $self->inline_class_slot_access($name);
208}
209
9b2bd146 210sub inline_deinitialize_class_slot {
211 my $self = shift;
212 my $name = shift;
bb70fe3a 213
214 return 'delete ' . $self->inline_class_slot_access($name);
215}
216
9b2bd146 217sub inline_weaken_class_slot_value {
218 my $self = shift;
219 my $name = shift;
bb70fe3a 220
9b2bd146 221 return
222 'Scalar::Util::weaken( '
223 . $self->inline_class_slot_access($name) . ')';
bb70fe3a 224}
225
bb70fe3a 2261;
7a4a3b1e 227
228__END__
229
230=pod
231
232=head1 NAME
233
63fcc508 234MooseX::ClassAttribute::Trait::Class - A metaclass role for classes with class attributes
7a4a3b1e 235
236=head1 SYNOPSIS
237
238 for my $attr ( HasClassAttributes->meta()->get_all_class_attributes() )
239 {
240 print $attr->name();
241 }
242
243=head1 DESCRIPTION
244
245This role adds awareness of class attributes to a metaclass object. It
246provides a set of introspection methods that largely parallel the
247existing attribute methods, except they operate on class attributes.
248
249=head1 METHODS
250
251Every method provided by this role has an analogous method in
252C<Class::MOP::Class> or C<Moose::Meta::Class> for regular attributes.
253
254=head2 $meta->has_class_attribute($name)
255
256=head2 $meta->get_class_attribute($name)
257
258=head2 $meta->get_class_attribute_list()
259
260=head2 $meta->get_class_attribute_map()
261
262These methods operate on the current metaclass only.
263
264=head2 $meta->add_class_attribute(...)
265
266This accepts the same options as the L<Moose::Meta::Attribute>
267C<add_attribute()> method. However, if an attribute is specified as
268"required" an error will be thrown.
269
270=head2 $meta->remove_class_attribute($name)
271
272If the named class attribute exists, it is removed from the class,
273along with its accessor methods.
274
275=head2 $meta->get_all_class_attributes()
276
b64c8efa 277This method returns a list of attribute objects for the class and all
7a4a3b1e 278its parent classes.
279
280=head2 $meta->find_class_attribute_by_name($name)
281
282This method looks at the class and all its parent classes for the
283named class attribute.
284
285=head2 $meta->get_class_attribute_value($name)
286
287=head2 $meta->set_class_attribute_value($name, $value)
288
289=head2 $meta->set_class_attribute_value($name)
290
291=head2 $meta->clear_class_attribute_value($name)
292
293These methods operate on the storage for class attribute values, which
294is attached to the metaclass object.
295
296There's really no good reason for you to call these methods unless
297you're doing some deep hacking. They are named as public methods
298solely because they are used by other meta roles and classes in this
299distribution.
300
ead2b556 301=head2 $meta->inline_class_slot_access($name)
7a4a3b1e 302
ead2b556 303=head2 $meta->inline_get_class_slot_value($name)
7a4a3b1e 304
ead2b556 305=head2 $meta->inline_set_class_slot_value($name, $val_name)
7a4a3b1e 306
ead2b556 307=head2 $meta->inline_is_class_slot_initialized($name)
7a4a3b1e 308
ead2b556 309=head2 $meta->inline_deinitialize_class_slot($name)
7a4a3b1e 310
ead2b556 311=head2 $meta->inline_weaken_class_slot_value($name)
7a4a3b1e 312
313These methods return code snippets for inlining.
314
315There's really no good reason for you to call these methods unless
316you're doing some deep hacking. They are named as public methods
317solely because they are used by other meta roles and classes in this
318distribution.
319
320=head1 AUTHOR
321
322Dave Rolsky, C<< <autarch@urth.org> >>
323
324=head1 BUGS
325
326See L<MooseX::ClassAttribute> for details.
327
328=head1 COPYRIGHT & LICENSE
329
330Copyright 2007-2008 Dave Rolsky, All Rights Reserved.
331
332This program is free software; you can redistribute it and/or modify
333it under the same terms as Perl itself.
334
335=cut