Tidy all 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 {
3e9e5aef 62 my ( $self, $attribute ) = @_;
deaffdd0 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
3e9e5aef 129 my %attrs = map {
9b2bd146 130 my $meta = Class::MOP::class_of($_);
23095f0a 131 $meta && $meta->can('_class_attribute_map')
132 ? %{ $meta->_class_attribute_map() }
9b2bd146 133 : ()
134 }
bb70fe3a 135 reverse $self->linearized_isa;
136
137 return values %attrs;
138}
139
9b2bd146 140sub compute_all_applicable_class_attributes {
141 warn
142 'The compute_all_applicable_class_attributes method has been deprecated.'
b64c8efa 143 . " Use get_all_class_attributes instead.\n";
144
145 shift->compute_all_applicable_class_attributes(@_);
146}
147
9b2bd146 148sub find_class_attribute_by_name {
bb70fe3a 149 my $self = shift;
150 my $name = shift;
151
9b2bd146 152 foreach my $class ( $self->linearized_isa() ) {
941ae03a 153 my $meta = Class::MOP::class_of($class)
154 or next;
bb70fe3a 155
156 return $meta->get_class_attribute($name)
9b2bd146 157 if $meta->can('has_class_attribute')
3e9e5aef 158 && $meta->has_class_attribute($name);
bb70fe3a 159 }
160
161 return;
162}
163
9b2bd146 164sub _class_attribute_values_hashref {
bb70fe3a 165 my $self = shift;
166
167 no strict 'refs';
168 return \%{ $self->_class_attribute_var_name() };
169}
170
9b2bd146 171sub _class_attribute_var_name {
bb70fe3a 172 my $self = shift;
173
174 return $self->name() . q'::__ClassAttributeValues';
175}
176
a5ed69bc 177sub _inline_class_slot_access {
bb70fe3a 178 my $self = shift;
179 my $name = shift;
180
9b2bd146 181 return
182 '$'
183 . $self->_class_attribute_var_name . '{"'
184 . quotemeta($name) . '"}';
bb70fe3a 185}
186
a5ed69bc 187sub _inline_get_class_slot_value {
bb70fe3a 188 my $self = shift;
189 my $name = shift;
190
a5ed69bc 191 return $self->_inline_class_slot_access($name);
bb70fe3a 192}
193
a5ed69bc 194sub _inline_set_class_slot_value {
bb70fe3a 195 my $self = shift;
196 my $name = shift;
197 my $val_name = shift;
198
a5ed69bc 199 return $self->_inline_class_slot_access($name) . ' = ' . $val_name;
bb70fe3a 200}
201
a5ed69bc 202sub _inline_is_class_slot_initialized {
9b2bd146 203 my $self = shift;
204 my $name = shift;
bb70fe3a 205
a5ed69bc 206 return 'exists ' . $self->_inline_class_slot_access($name);
bb70fe3a 207}
208
a5ed69bc 209sub _inline_deinitialize_class_slot {
9b2bd146 210 my $self = shift;
211 my $name = shift;
bb70fe3a 212
a5ed69bc 213 return 'delete ' . $self->_inline_class_slot_access($name);
bb70fe3a 214}
215
a5ed69bc 216sub _inline_weaken_class_slot_value {
9b2bd146 217 my $self = shift;
218 my $name = shift;
bb70fe3a 219
9b2bd146 220 return
221 'Scalar::Util::weaken( '
a5ed69bc 222 . $self->_inline_class_slot_access($name) . ')';
bb70fe3a 223}
224
bb70fe3a 2251;
7a4a3b1e 226
0d0bf8c3 227# ABSTRACT: A trait for classes with class attributes
228
7a4a3b1e 229__END__
230
231=pod
232
7a4a3b1e 233=head1 SYNOPSIS
234
235 for my $attr ( HasClassAttributes->meta()->get_all_class_attributes() )
236 {
237 print $attr->name();
238 }
239
240=head1 DESCRIPTION
241
242This role adds awareness of class attributes to a metaclass object. It
243provides a set of introspection methods that largely parallel the
244existing attribute methods, except they operate on class attributes.
245
246=head1 METHODS
247
248Every method provided by this role has an analogous method in
249C<Class::MOP::Class> or C<Moose::Meta::Class> for regular attributes.
250
251=head2 $meta->has_class_attribute($name)
252
253=head2 $meta->get_class_attribute($name)
254
255=head2 $meta->get_class_attribute_list()
256
7a4a3b1e 257These methods operate on the current metaclass only.
258
259=head2 $meta->add_class_attribute(...)
260
261This accepts the same options as the L<Moose::Meta::Attribute>
262C<add_attribute()> method. However, if an attribute is specified as
263"required" an error will be thrown.
264
265=head2 $meta->remove_class_attribute($name)
266
267If the named class attribute exists, it is removed from the class,
268along with its accessor methods.
269
270=head2 $meta->get_all_class_attributes()
271
b64c8efa 272This method returns a list of attribute objects for the class and all
7a4a3b1e 273its parent classes.
274
275=head2 $meta->find_class_attribute_by_name($name)
276
277This method looks at the class and all its parent classes for the
278named class attribute.
279
280=head2 $meta->get_class_attribute_value($name)
281
282=head2 $meta->set_class_attribute_value($name, $value)
283
284=head2 $meta->set_class_attribute_value($name)
285
286=head2 $meta->clear_class_attribute_value($name)
287
288These methods operate on the storage for class attribute values, which
289is attached to the metaclass object.
290
291There's really no good reason for you to call these methods unless
292you're doing some deep hacking. They are named as public methods
293solely because they are used by other meta roles and classes in this
294distribution.
295
7a4a3b1e 296=head1 BUGS
297
298See L<MooseX::ClassAttribute> for details.
299
7a4a3b1e 300=cut