Add tests for introspection methods
[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 =
151 map { %{ Class::MOP::Class->initialize($_)->get_class_attribute_map } }
152 reverse $self->linearized_isa;
153
154 return values %attrs;
155}
156
157sub find_class_attribute_by_name
158{
159 my $self = shift;
160 my $name = shift;
161
162 foreach my $class ( $self->linearized_isa() )
163 {
164 my $meta = Class::MOP::Class->initialize($class);
165
166 return $meta->get_class_attribute($name)
167 if $meta->has_class_attribute($name);
168 }
169
170 return;
171}
172
173sub _class_attribute_values_hashref
174{
175 my $self = shift;
176
177 no strict 'refs';
178 return \%{ $self->_class_attribute_var_name() };
179}
180
181sub _class_attribute_var_name
182{
183 my $self = shift;
184
185 return $self->name() . q'::__ClassAttributeValues';
186}
187
188sub inline_class_slot_access
189{
190 my $self = shift;
191 my $name = shift;
192
193 return '$' . $self->_class_attribute_var_name . '{' . $name . '}';
194}
195
196sub inline_get_class_slot_value
197{
198 my $self = shift;
199 my $name = shift;
200
201 return $self->inline_class_slot_access($name);
202}
203
204sub inline_set_class_slot_value
205{
206 my $self = shift;
207 my $name = shift;
208 my $val_name = shift;
209
210 return $self->inline_class_slot_access($name) . ' = ' . $val_name;
211}
212
213sub inline_is_class_slot_initialized
214{
215 my $self = shift;
216 my $name = shift;
217
218 return 'exists ' . $self->inline_class_slot_access($name);
219}
220
221sub inline_deinitialize_class_slot
222{
223 my $self = shift;
224 my $name = shift;
225
226 return 'delete ' . $self->inline_class_slot_access($name);
227}
228
229sub inline_weaken_class_slot_value
230{
231 my $self = shift;
232 my $name = shift;
233
234 return 'Scalar::Util::weaken( ' . $self->inline_class_slot_access($name) . ')';
235}
236
237no Moose::Role;
238
2391;