Fix potential value for clear
[gitmo/Moose.git] / lib / Moose / Meta / Attribute / Native / Trait.pm
1
2 package Moose::Meta::Attribute::Native::Trait;
3 use Moose::Role;
4 use Moose::Util::TypeConstraints;
5
6 our $VERSION   = '1.14';
7 $VERSION = eval $VERSION;
8 our $AUTHORITY = 'cpan:STEVAN';
9
10 requires '_helper_type';
11
12 before '_process_options' => sub {
13     my ( $self, $name, $options ) = @_;
14
15     $self->_check_helper_type( $options, $name );
16
17     $options->{is} = $self->_default_is
18         if !exists $options->{is} && $self->can('_default_is');
19
20     $options->{default} = $self->_default_default
21         if !exists $options->{default} && $self->can('_default_default');
22 };
23
24 sub _check_helper_type {
25     my ( $self, $options, $name ) = @_;
26
27     my $type = $self->_helper_type;
28
29     $options->{isa} = $type
30         unless exists $options->{isa};
31
32     my $isa = Moose::Util::TypeConstraints::find_or_create_type_constraint(
33         $options->{isa} );
34
35     ( $isa->is_a_type_of($type) )
36         || confess
37         "The type constraint for $name must be a subtype of $type but it's a $isa";
38 }
39
40 before 'install_accessors' => sub { (shift)->_check_handles_values };
41
42 sub _check_handles_values {
43     my $self = shift;
44
45     my $method_constructors = $self->method_constructors;
46
47     my %handles = $self->_canonicalize_handles;
48
49     for my $original_method ( values %handles ) {
50         my $name = $original_method->[0];
51
52         my $accessor_class = $self->_native_accessor_class_for($name);
53
54         # XXX - bridge code
55         ( ( $accessor_class && $accessor_class->can('new') )
56                 || exists $method_constructors->{$name} )
57             || confess "$name is an unsupported method type - $accessor_class";
58     }
59 }
60
61 around '_canonicalize_handles' => sub {
62     shift;
63     my $self    = shift;
64     my $handles = $self->handles;
65
66     return unless $handles;
67
68     unless ( 'HASH' eq ref $handles ) {
69         $self->throw_error(
70             "The 'handles' option must be a HASH reference, not $handles");
71     }
72
73     return map {
74         my $to = $handles->{$_};
75         $to = [$to] unless ref $to;
76         $_ => $to
77     } keys %$handles;
78 };
79
80 around '_make_delegation_method' => sub {
81     my $next = shift;
82     my ( $self, $handle_name, $method_to_call ) = @_;
83
84     my ( $name, @curried_args ) = @$method_to_call;
85
86     my $accessor_class = $self->_native_accessor_class_for($name);
87
88     # XXX - bridge code
89     if ( $accessor_class && $accessor_class->can('new') ) {
90         return $accessor_class->new(
91             name              => $handle_name,
92             package_name      => $self->associated_class->name,
93             attribute         => $self,
94             curried_arguments => \@curried_args,
95             root_types        => [ $self->_root_types ],
96         );
97     }
98     # XXX - bridge code
99     else {
100         my $method_constructors = $self->method_constructors;
101
102         my $code = $method_constructors->{$name}->(
103             $self,
104             $self->get_read_method_ref,
105             $self->get_write_method_ref,
106         );
107
108         return $next->(
109             $self,
110             $handle_name,
111             sub {
112                 my $instance = shift;
113                 return $code->( $instance, @curried_args, @_ );
114             }
115         );
116     }
117 };
118
119 sub _root_types {
120     return $_[0]->_helper_type;
121 }
122
123 sub _native_accessor_class_for {
124     my ( $self, $suffix ) = @_;
125
126     return 'Moose::Meta::Method::Accessor::Native::' . $self->_native_type . '::' . $suffix;
127 }
128
129 sub _build_native_type {
130     my $self = shift;
131
132     for my $role_name ( map { $_->name } $self->meta->calculate_all_roles ) {
133         return $1 if $role_name =~ /::Native::Trait::(\w+)$/;
134     }
135
136     die "Cannot calculate native type for " . ref $self;
137 }
138
139 has '_native_type' => (
140     is      => 'ro',
141     isa     => 'Str',
142     lazy    => 1,
143     builder => '_build_native_type',
144 );
145
146 has 'method_constructors' => (
147     is      => 'ro',
148     isa     => 'HashRef',
149     lazy    => 1,
150     default => sub {
151         my $self = shift;
152         return +{}
153             unless $self->can('has_method_provider')
154                 && $self->has_method_provider;
155
156         # or grab them from the role/class
157         my $method_provider = $self->method_provider->meta;
158         return +{ map { $_->name => $_ }
159                 $method_provider->_get_local_methods };
160     },
161 );
162
163 no Moose::Role;
164 no Moose::Util::TypeConstraints;
165
166 1;
167
168 __END__
169
170 =head1 NAME
171
172 Moose::Meta::Attribute::Native::Trait - Base role for helpers
173
174 =head1 BUGS
175
176 See L<Moose/BUGS> for details on reporting bugs.
177
178 =head1 SEE ALSO
179
180 Documentation for Moose native traits starts at L<Moose::Meta::Attribute Native>
181
182 =head1 AUTHORS
183
184 Yuval Kogman
185
186 Shawn M Moore
187
188 Jesse Luehrs
189
190 =head1 COPYRIGHT AND LICENSE
191
192 Copyright 2007-2009 by Infinity Interactive, Inc.
193
194 L<http://www.iinteractive.com>
195
196 This library is free software; you can redistribute it and/or modify
197 it under the same terms as Perl itself.
198
199 =cut