Make prereq requirements explicit in code
[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($_);
23095f0a 132 $meta && $meta->can('_class_attribute_map')
133 ? %{ $meta->_class_attribute_map() }
9b2bd146 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
a5ed69bc 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
a5ed69bc 188sub _inline_get_class_slot_value {
bb70fe3a 189 my $self = shift;
190 my $name = shift;
191
a5ed69bc 192 return $self->_inline_class_slot_access($name);
bb70fe3a 193}
194
a5ed69bc 195sub _inline_set_class_slot_value {
bb70fe3a 196 my $self = shift;
197 my $name = shift;
198 my $val_name = shift;
199
a5ed69bc 200 return $self->_inline_class_slot_access($name) . ' = ' . $val_name;
bb70fe3a 201}
202
a5ed69bc 203sub _inline_is_class_slot_initialized {
9b2bd146 204 my $self = shift;
205 my $name = shift;
bb70fe3a 206
a5ed69bc 207 return 'exists ' . $self->_inline_class_slot_access($name);
bb70fe3a 208}
209
a5ed69bc 210sub _inline_deinitialize_class_slot {
9b2bd146 211 my $self = shift;
212 my $name = shift;
bb70fe3a 213
a5ed69bc 214 return 'delete ' . $self->_inline_class_slot_access($name);
bb70fe3a 215}
216
a5ed69bc 217sub _inline_weaken_class_slot_value {
9b2bd146 218 my $self = shift;
219 my $name = shift;
bb70fe3a 220
9b2bd146 221 return
222 'Scalar::Util::weaken( '
a5ed69bc 223 . $self->_inline_class_slot_access($name) . ')';
bb70fe3a 224}
225
bb70fe3a 2261;
7a4a3b1e 227
0d0bf8c3 228# ABSTRACT: A trait for classes with class attributes
229
7a4a3b1e 230__END__
231
232=pod
233
7a4a3b1e 234=head1 SYNOPSIS
235
236 for my $attr ( HasClassAttributes->meta()->get_all_class_attributes() )
237 {
238 print $attr->name();
239 }
240
241=head1 DESCRIPTION
242
243This role adds awareness of class attributes to a metaclass object. It
244provides a set of introspection methods that largely parallel the
245existing attribute methods, except they operate on class attributes.
246
247=head1 METHODS
248
249Every method provided by this role has an analogous method in
250C<Class::MOP::Class> or C<Moose::Meta::Class> for regular attributes.
251
252=head2 $meta->has_class_attribute($name)
253
254=head2 $meta->get_class_attribute($name)
255
256=head2 $meta->get_class_attribute_list()
257
7a4a3b1e 258These methods operate on the current metaclass only.
259
260=head2 $meta->add_class_attribute(...)
261
262This accepts the same options as the L<Moose::Meta::Attribute>
263C<add_attribute()> method. However, if an attribute is specified as
264"required" an error will be thrown.
265
266=head2 $meta->remove_class_attribute($name)
267
268If the named class attribute exists, it is removed from the class,
269along with its accessor methods.
270
271=head2 $meta->get_all_class_attributes()
272
b64c8efa 273This method returns a list of attribute objects for the class and all
7a4a3b1e 274its parent classes.
275
276=head2 $meta->find_class_attribute_by_name($name)
277
278This method looks at the class and all its parent classes for the
279named class attribute.
280
281=head2 $meta->get_class_attribute_value($name)
282
283=head2 $meta->set_class_attribute_value($name, $value)
284
285=head2 $meta->set_class_attribute_value($name)
286
287=head2 $meta->clear_class_attribute_value($name)
288
289These methods operate on the storage for class attribute values, which
290is attached to the metaclass object.
291
292There's really no good reason for you to call these methods unless
293you're doing some deep hacking. They are named as public methods
294solely because they are used by other meta roles and classes in this
295distribution.
296
7a4a3b1e 297=head1 BUGS
298
299See L<MooseX::ClassAttribute> for details.
300
7a4a3b1e 301=cut