Support Type in MMA::Native::Trait
[gitmo/Moose.git] / lib / Moose / Meta / Attribute / Native / Trait.pm
CommitLineData
e3c07b19 1
c466e58f 2package Moose::Meta::Attribute::Native::Trait;
e3c07b19 3use Moose::Role;
3cf2f9ec 4
b5ae7c00 5use Class::Load qw(load_class);
efa806d8 6use List::MoreUtils qw( any uniq );
34d6d196 7use Moose::Deprecated;
41647090 8use Moose::Util;
9use Moose::Util::TypeConstraints;
e3c07b19 10
2e069f5a 11requires '_helper_type';
e3c07b19 12
efa806d8 13has _used_default_is => (
14 is => 'rw',
15 isa => 'Bool',
16 default => 0,
17);
18
2edb73d9 19before '_process_options' => sub {
20 my ( $self, $name, $options ) = @_;
e3c07b19 21
2edb73d9 22 $self->_check_helper_type( $options, $name );
e3c07b19 23
3cf2f9ec 24 if ( !( any { exists $options->{$_} } qw( is reader writer accessor ) )
25 && $self->can('_default_is') ) {
26
34d6d196 27 $options->{is} = $self->_default_is;
28
efa806d8 29 $options->{_used_default_is} = 1;
34d6d196 30 }
31
3cf2f9ec 32 if (
33 !(
34 $options->{required}
b558f8a6 35 || any { exists $options->{$_} } qw( default builder lazy_build )
3cf2f9ec 36 )
37 && $self->can('_default_default')
38 ) {
39
34d6d196 40 $options->{default} = $self->_default_default;
41
42 Moose::Deprecated::deprecated(
43 feature => 'default default for Native Trait',
44 message =>
cab2e1d0 45 'Allowing a native trait to automatically supply a default is deprecated.'
557ae2ff 46 . ' You can avoid this warning by supplying a default, builder, or making the attribute required'
34d6d196 47 );
48 }
2edb73d9 49};
e3c07b19 50
efa806d8 51after 'install_accessors' => sub {
52 my $self = shift;
53
54 return unless $self->_used_default_is;
55
56 my @methods
57 = $self->_default_is eq 'rw'
58 ? qw( reader writer accessor )
59 : 'reader';
60
61 my $name = $self->name;
62 my $class = $self->associated_class->name;
63
64 for my $meth ( uniq grep {defined} map { $self->$_ } @methods ) {
65
66 my $message
67 = "The $meth method in the $class class was automatically created"
68 . " by the native delegation trait for the $name attribute."
69 . q{ This "default is" feature is deprecated.}
70 . q{ Explicitly set "is" or define accessor names to avoid this};
71
72 $self->associated_class->add_before_method_modifier(
73 $meth => sub {
74 Moose::Deprecated::deprecated(
75 feature => 'default is for Native Trait',
76 message =>$message,
77 );
78 }
79 );
80 }
81 };
82
2edb73d9 83sub _check_helper_type {
84 my ( $self, $options, $name ) = @_;
e3c07b19 85
2e069f5a 86 my $type = $self->_helper_type;
2edb73d9 87
2e069f5a 88 $options->{isa} = $type
2edb73d9 89 unless exists $options->{isa};
90
41647090 91 my $isa;
92 my $isa_name;
93
94 if (
95 Moose::Util::does_role(
96 $options->{isa}, 'Type::Constraint::Role::Interface'
97 )
98 ) {
99
100 $isa = $options->{isa};
101 require Type::Library::Builtins;
102 return if $isa->is_a_type_of( Type::Library::Builtins::t($type) );
103 $isa_name = $isa->name() || $isa->description();
104 }
105 else {
106 $isa = Moose::Util::TypeConstraints::find_or_create_type_constraint(
107 $options->{isa} );
108 return if $isa->is_a_type_of($type);
109 $isa_name = $isa->name();
110 }
2edb73d9 111
41647090 112 confess
113 "The type constraint for $name must be a subtype of $type but it's a $isa_name";
2edb73d9 114}
e3c07b19 115
2edb73d9 116before 'install_accessors' => sub { (shift)->_check_handles_values };
5404f169 117
2edb73d9 118sub _check_handles_values {
e3c07b19 119 my $self = shift;
120
5404f169 121 my %handles = $self->_canonicalize_handles;
e3c07b19 122
046c8b5e 123 for my $original_method ( values %handles ) {
5404f169 124 my $name = $original_method->[0];
f7fd22b6 125
ffc2e25f 126 my $accessor_class = $self->_native_accessor_class_for($name);
f7fd22b6 127
78aee58f 128 ( $accessor_class && $accessor_class->can('new') )
129 || confess
130 "$name is an unsupported method type - $accessor_class";
e3c07b19 131 }
5404f169 132}
e3c07b19 133
d4dc38ed 134around '_canonicalize_handles' => sub {
135 shift;
136 my $self = shift;
137 my $handles = $self->handles;
138
139 return unless $handles;
140
141 unless ( 'HASH' eq ref $handles ) {
142 $self->throw_error(
143 "The 'handles' option must be a HASH reference, not $handles");
144 }
145
b0b04f2b 146 return
147 map { $_ => $self->_canonicalize_handles_value( $handles->{$_} ) }
148 keys %$handles;
d4dc38ed 149};
150
b0b04f2b 151sub _canonicalize_handles_value {
152 my $self = shift;
153 my $value = shift;
154
155 if ( ref $value && 'ARRAY' ne ref $value ) {
156 $self->throw_error(
157 "All values passed to handles must be strings or ARRAY references, not $value"
158 );
159 }
160
161 return ref $value ? $value : [$value];
162}
163
18281451 164around '_make_delegation_method' => sub {
165 my $next = shift;
046c8b5e 166 my ( $self, $handle_name, $method_to_call ) = @_;
18281451 167
3c573ca4 168 my ( $name, @curried_args ) = @$method_to_call;
18281451 169
ffc2e25f 170 my $accessor_class = $self->_native_accessor_class_for($name);
18281451 171
78aee58f 172 die "Cannot find an accessor class for $name"
173 unless $accessor_class && $accessor_class->can('new');
174
175 return $accessor_class->new(
8b9641b8 176 name => $handle_name,
177 package_name => $self->associated_class->name,
178 delegate_to_method => $name,
179 attribute => $self,
180 is_inline => 1,
181 curried_arguments => \@curried_args,
182 root_types => [ $self->_root_types ],
78aee58f 183 );
18281451 184};
185
a6ae7438 186sub _root_types {
187 return $_[0]->_helper_type;
188}
189
ffc2e25f 190sub _native_accessor_class_for {
191 my ( $self, $suffix ) = @_;
192
8b9641b8 193 my $role
194 = 'Moose::Meta::Method::Accessor::Native::'
195 . $self->_native_type . '::'
196 . $suffix;
197
b5ae7c00 198 load_class($role);
8b9641b8 199 return Moose::Meta::Class->create_anon_class(
200 superclasses =>
201 [ $self->accessor_metaclass, $self->delegation_metaclass ],
202 roles => [$role],
203 cache => 1,
204 )->name;
f7fd22b6 205}
206
15715245 207sub _build_native_type {
208 my $self = shift;
209
210 for my $role_name ( map { $_->name } $self->meta->calculate_all_roles ) {
211 return $1 if $role_name =~ /::Native::Trait::(\w+)$/;
212 }
213
214 die "Cannot calculate native type for " . ref $self;
215}
216
217has '_native_type' => (
218 is => 'ro',
219 isa => 'Str',
220 lazy => 1,
221 builder => '_build_native_type',
222);
223
e3c07b19 224no Moose::Role;
225no Moose::Util::TypeConstraints;
226
2271;
228
ad46f524 229# ABSTRACT: Shared role for native delegation traits
e3c07b19 230
ad46f524 231__END__
e3c07b19 232
e3c07b19 233=head1 BUGS
234
d4048ef3 235See L<Moose/BUGS> for details on reporting bugs.
e3c07b19 236
1af5d116 237=head1 SEE ALSO
238
e132fd56 239Documentation for Moose native traits can be found in
240L<Moose::Meta::Attribute::Native>.
1af5d116 241
e3c07b19 242=cut