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