Always load Mouse::Util first, which will be load Mouse::XS in the future
[gitmo/Mouse.git] / lib / Mouse / Meta / Attribute.pm
CommitLineData
306290e8 1package Mouse::Meta::Attribute;
c3398f5b 2use strict;
3use warnings;
4
6d28c5cf 5use Mouse::Util;
6
684db121 7use Mouse::Meta::TypeConstraint;
90fe520e 8use Mouse::Meta::Method::Accessor;
c3398f5b 9
10sub new {
2608b115 11 my ($class, $name, %options) = @_;
c3398f5b 12
2608b115 13 $options{name} = $name;
2e7e86c6 14
2608b115 15 $options{init_arg} = $name
16 unless exists $options{init_arg};
45959ffa 17
90fe520e 18 my $is = $options{is} ||= '';
19
20 if($is eq 'rw'){
21 $options{accessor} = $name if !exists $options{accessor};
22 }
23 elsif($is eq 'ro'){
24 $options{reader} = $name if !exists $options{reader};
25 }
c3398f5b 26
2608b115 27 bless \%options, $class;
c3398f5b 28}
29
90fe520e 30# readers
31
f6715552 32sub name { $_[0]->{name} }
33sub associated_class { $_[0]->{associated_class} }
90fe520e 34
35sub accessor { $_[0]->{accessor} }
36sub reader { $_[0]->{reader} }
37sub writer { $_[0]->{writer} }
38sub predicate { $_[0]->{predicate} }
39sub clearer { $_[0]->{clearer} }
40sub handles { $_[0]->{handles} }
41
f6715552 42sub _is_metadata { $_[0]->{is} }
43sub is_required { $_[0]->{required} }
44sub default { $_[0]->{default} }
45sub is_lazy { $_[0]->{lazy} }
46sub is_lazy_build { $_[0]->{lazy_build} }
f6715552 47sub is_weak_ref { $_[0]->{weak_ref} }
48sub init_arg { $_[0]->{init_arg} }
49sub type_constraint { $_[0]->{type_constraint} }
4c03ed87 50sub find_type_constraint {
51 Carp::carp("This method was deprecated");
52 $_[0]->type_constraint();
53}
f6715552 54sub trigger { $_[0]->{trigger} }
55sub builder { $_[0]->{builder} }
56sub should_auto_deref { $_[0]->{auto_deref} }
57sub should_coerce { $_[0]->{should_coerce} }
c3398f5b 58
90fe520e 59# predicates
60
61sub has_accessor { exists $_[0]->{accessor} }
62sub has_reader { exists $_[0]->{reader} }
63sub has_writer { exists $_[0]->{writer} }
f6715552 64sub has_predicate { exists $_[0]->{predicate} }
65sub has_clearer { exists $_[0]->{clearer} }
66sub has_handles { exists $_[0]->{handles} }
90fe520e 67
68sub has_default { exists $_[0]->{default} }
f6715552 69sub has_type_constraint { exists $_[0]->{type_constraint} }
70sub has_trigger { exists $_[0]->{trigger} }
71sub has_builder { exists $_[0]->{builder} }
eec1bb49 72
1bfebf5f 73sub _create_args {
74 $_[0]->{_create_args} = $_[1] if @_ > 1;
75 $_[0]->{_create_args}
76}
77
90fe520e 78sub accessor_metaclass { 'Mouse::Meta::Method::Accessor' }
79
c3398f5b 80sub create {
81 my ($self, $class, $name, %args) = @_;
82
90fe520e 83 $args{name} = $name;
181502b9 84 $args{associated_class} = $class;
1bfebf5f 85
93d190e0 86 %args = $self->canonicalize_args($name, %args);
1bbaa8ed 87 $self->validate_args($name, \%args);
45ea8620 88
32af3489 89 $args{should_coerce} = delete $args{coerce}
4188b837 90 if exists $args{coerce};
91
eec1bb49 92 if (exists $args{isa}) {
93 my $type_constraint = delete $args{isa};
684db121 94 $args{type_constraint}= Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($type_constraint);
eec1bb49 95 }
186657a9 96
2608b115 97 my $attribute = $self->new($name, %args);
1bfebf5f 98
724c77c0 99 $attribute->_create_args(\%args);
c3398f5b 100
724c77c0 101 $class->add_attribute($attribute);
b2500191 102
74be9f76 103 my $associated_methods = 0;
104
90fe520e 105 my $generator_class = $self->accessor_metaclass;
106 foreach my $type(qw(accessor reader writer predicate clearer handles)){
107 if(exists $attribute->{$type}){
108 my $installer = '_install_' . $type;
109 $generator_class->$installer($attribute, $attribute->{$type}, $class);
74be9f76 110 $associated_methods++;
c3398f5b 111 }
112 }
113
90fe520e 114 if($associated_methods == 0 && ($attribute->_is_metadata || '') ne 'bare'){
c8c1aeaf 115 Carp::cluck(qq{Attribute ($name) of class }.$class->name.qq{ has no associated methods (did you mean to provide an "is" argument?)});
a7d31de0 116
74be9f76 117 }
118
c3398f5b 119 return $attribute;
120}
121
93d190e0 122sub canonicalize_args {
123 my $self = shift;
124 my $name = shift;
125 my %args = @_;
126
127 if ($args{lazy_build}) {
128 $args{lazy} = 1;
129 $args{required} = 1;
130 $args{builder} = "_build_${name}"
131 if !exists($args{builder});
132 if ($name =~ /^_/) {
133 $args{clearer} = "_clear${name}" if !exists($args{clearer});
134 $args{predicate} = "_has${name}" if !exists($args{predicate});
135 }
136 else {
137 $args{clearer} = "clear_${name}" if !exists($args{clearer});
138 $args{predicate} = "has_${name}" if !exists($args{predicate});
139 }
140 }
141
142 return %args;
143}
144
8fd9e611 145sub validate_args {
146 my $self = shift;
147 my $name = shift;
1bbaa8ed 148 my $args = shift;
8fd9e611 149
fce211ae 150 $self->throw_error("You can not use lazy_build and default for the same attribute ($name)")
1bbaa8ed 151 if $args->{lazy_build} && exists $args->{default};
93d190e0 152
fce211ae 153 $self->throw_error("You cannot have lazy attribute ($name) without specifying a default value for it")
1bbaa8ed 154 if $args->{lazy}
155 && !exists($args->{default})
156 && !exists($args->{builder});
8fd9e611 157
fce211ae 158 $self->throw_error("References are not allowed as default values, you must wrap the default of '$name' in a CODE reference (ex: sub { [] } and not [])")
1bbaa8ed 159 if ref($args->{default})
160 && ref($args->{default}) ne 'CODE';
8fd9e611 161
fce211ae 162 $self->throw_error("You cannot auto-dereference without specifying a type constraint on attribute ($name)")
1bbaa8ed 163 if $args->{auto_deref} && !exists($args->{isa});
8fd9e611 164
fce211ae 165 $self->throw_error("You cannot auto-dereference anything other than a ArrayRef or HashRef on attribute ($name)")
1bbaa8ed 166 if $args->{auto_deref}
a3f4f68e 167 && $args->{isa} !~ /^(?:ArrayRef|HashRef)(?:\[.*\])?$/;
8fd9e611 168
506db557 169 if ($args->{trigger}) {
a08e715f 170 if (ref($args->{trigger}) eq 'HASH') {
fce211ae 171 $self->throw_error("HASH-based form of trigger has been removed. Only the coderef form of triggers are now supported.");
844fa049 172 }
506db557 173
fce211ae 174 $self->throw_error("Trigger must be a CODE ref on attribute ($name)")
a08e715f 175 if ref($args->{trigger}) ne 'CODE';
506db557 176 }
6c5498d0 177
8fd9e611 178 return 1;
179}
180
20e25eb9 181sub verify_against_type_constraint {
f55f60dd 182 my ($self, $value) = @_;
183 my $tc = $self->type_constraint;
184 return 1 unless $tc;
5aa30ced 185
f55f60dd 186 local $_ = $value;
187 return 1 if $tc->check($value);
5aa30ced 188
f55f60dd 189 $self->verify_type_constraint_error($self->name, $value, $tc);
b3b74cc6 190}
5aa30ced 191
b3b74cc6 192sub verify_type_constraint_error {
193 my($self, $name, $value, $type) = @_;
fce211ae 194 $self->throw_error("Attribute ($name) does not pass the type constraint because: " . $type->get_message($value));
5aa30ced 195}
196
8a7f2a8a 197sub coerce_constraint { ## my($self, $value) = @_;
198 my $type = $_[0]->{type_constraint}
199 or return $_[1];
684db121 200 return Mouse::Util::TypeConstraints->typecast_constraints($_[0]->associated_class->name, $_[0]->type_constraint, $_[1]);
4188b837 201}
202
af745d5a 203sub _canonicalize_handles {
204 my $self = shift;
205 my $handles = shift;
206
207 if (ref($handles) eq 'HASH') {
208 return %$handles;
209 }
210 elsif (ref($handles) eq 'ARRAY') {
211 return map { $_ => $_ } @$handles;
212 }
213 else {
fce211ae 214 $self->throw_error("Unable to canonicalize the 'handles' option with $handles");
af745d5a 215 }
216}
217
1bfebf5f 218sub clone_parent {
219 my $self = shift;
220 my $class = shift;
221 my $name = shift;
222 my %args = ($self->get_parent_args($class, $name), @_);
223
224 $self->create($class, $name, %args);
225}
226
227sub get_parent_args {
228 my $self = shift;
229 my $class = shift;
230 my $name = shift;
231
724c77c0 232 for my $super ($class->linearized_isa) {
bb733405 233 my $super_attr = $super->can("meta") && $super->meta->get_attribute($name)
1bfebf5f 234 or next;
235 return %{ $super_attr->_create_args };
236 }
237
fce211ae 238 $self->throw_error("Could not find an attribute by the name of '$name' to inherit from");
239}
240
241sub throw_error{
242 my $self = shift;
243
244 my $metaclass = (ref $self && $self->associated_class) || 'Mouse::Meta::Class';
245 $metaclass->throw_error(@_, depth => 1);
1bfebf5f 246}
247
c3398f5b 2481;
249
250__END__
251
252=head1 NAME
253
306290e8 254Mouse::Meta::Attribute - attribute metaclass
c3398f5b 255
256=head1 METHODS
257
306290e8 258=head2 new %args -> Mouse::Meta::Attribute
c3398f5b 259
306290e8 260Instantiates a new Mouse::Meta::Attribute. Does nothing else.
c3398f5b 261
306290e8 262=head2 create OwnerClass, AttributeName, %args -> Mouse::Meta::Attribute
c3398f5b 263
264Creates a new attribute in OwnerClass. Accessors and helper methods are
265installed. Some error checking is done.
266
267=head2 name -> AttributeName
268
181502b9 269=head2 associated_class -> OwnerClass
c3398f5b 270
ab27a55e 271=head2 is_required -> Bool
c3398f5b 272
ab27a55e 273=head2 default -> Item
c3398f5b 274
ab27a55e 275=head2 has_default -> Bool
276
277=head2 is_lazy -> Bool
278
279=head2 predicate -> MethodName | Undef
280
281=head2 has_predicate -> Bool
282
283=head2 clearer -> MethodName | Undef
284
285=head2 has_clearer -> Bool
c3398f5b 286
287=head2 handles -> { LocalName => RemoteName }
288
ab27a55e 289=head2 has_handles -> Bool
290
3645b316 291=head2 is_weak_ref -> Bool
c3398f5b 292
293=head2 init_arg -> Str
294
ab27a55e 295=head2 type_constraint -> Str
296
297=head2 has_type_constraint -> Bool
298
299=head2 trigger => CODE | Undef
300
301=head2 has_trigger -> Bool
302
303=head2 builder => MethodName | Undef
304
305=head2 has_builder -> Bool
306
93f08899 307=head2 is_lazy_build => Bool
308
0fff36e6 309=head2 should_auto_deref -> Bool
310
c3398f5b 311Informational methods.
312
20e25eb9 313=head2 verify_against_type_constraint Item -> 1 | ERROR
fb706f5c 314
315Checks that the given value passes this attribute's type constraint. Returns 1
316on success, otherwise C<confess>es.
317
93d190e0 318=head2 canonicalize_args Name, %args -> %args
319
320Canonicalizes some arguments to create. In particular, C<lazy_build> is
321canonicalized into C<lazy>, C<builder>, etc.
322
1bbaa8ed 323=head2 validate_args Name, \%args -> 1 | ERROR
93d190e0 324
325Checks that the arguments to create the attribute (ie those specified by
326C<has>) are valid.
327
f7b11a21 328=head2 clone_parent OwnerClass, AttributeName, %args -> Mouse::Meta::Attribute
329
330Creates a new attribute in OwnerClass, inheriting options from parent classes.
331Accessors and helper methods are installed. Some error checking is done.
332
333=head2 get_parent_args OwnerClass, AttributeName -> Hash
334
335Returns the options that the parent class of C<OwnerClass> used for attribute
336C<AttributeName>.
337
c3398f5b 338=cut
339