Bump to 0.14
[gitmo/MooseX-AttributeHelpers.git] / lib / MooseX / AttributeHelpers / Base.pm
CommitLineData
d26633fc 1
2package MooseX::AttributeHelpers::Base;
3use Moose;
8ba40fb0 4use Moose::Util::TypeConstraints;
d26633fc 5
7a93b96e 6our $VERSION = '0.14';
38430345 7$VERSION = eval $VERSION;
d26633fc 8our $AUTHORITY = 'cpan:STEVAN';
9
10extends 'Moose::Meta::Attribute';
11
8e3fab6d 12# this is the method map you define ...
d26633fc 13has 'provides' => (
9810162d 14 is => 'ro',
15 isa => 'HashRef',
16 default => sub {{}}
d26633fc 17);
18
c43a2317 19has 'curries' => (
20 is => 'ro',
21 isa => 'HashRef',
22 default => sub {{}}
23);
24
999f34a9 25# these next two are the possible methods
8e3fab6d 26# you can use in the 'provides' map.
27
999f34a9 28# provide a Class or Role which we can
29# collect the method providers from
8e3fab6d 30has 'method_provider' => (
31 is => 'ro',
32 isa => 'ClassName',
33 predicate => 'has_method_provider',
34);
35
36# or you can provide a HASH ref of anon subs
37# yourself. This will also collect and store
999f34a9 38# the methods from a method_provider as well
8e3fab6d 39has 'method_constructors' => (
40 is => 'ro',
41 isa => 'HashRef',
42 lazy => 1,
43 default => sub {
44 my $self = shift;
45 return +{} unless $self->has_method_provider;
46 # or grab them from the role/class
47 my $method_provider = $self->method_provider->meta;
48 return +{
999f34a9 49 map {
8e3fab6d 50 $_ => $method_provider->get_method($_)
51 } $method_provider->get_method_list
999f34a9 52 };
e295d072 53 },
8e3fab6d 54);
55
999f34a9 56# extend the parents stuff to make sure
8881a8d3 57# certain bits are now required ...
4e6acc23 58has '+default' => (required => 1);
d26633fc 59has '+type_constraint' => (required => 1);
60
8ba40fb0 61## Methods called prior to instantiation
62
63sub helper_type { () }
d26633fc 64
8ba40fb0 65sub process_options_for_provides {
d26633fc 66 my ($self, $options) = @_;
999f34a9 67
8ba40fb0 68 if (my $type = $self->helper_type) {
69 (exists $options->{isa})
999f34a9 70 || confess "You must define a type with the $type metaclass";
8ba40fb0 71
999f34a9 72 my $isa = $options->{isa};
8ba40fb0 73
74 unless (blessed($isa) && $isa->isa('Moose::Meta::TypeConstraint')) {
999f34a9 75 $isa = Moose::Util::TypeConstraints::find_or_create_type_constraint($isa);
8ba40fb0 76 }
77
78 ($isa->is_a_type_of($type))
79 || confess "The type constraint for a $type ($options->{isa}) must be a subtype of $type";
80 }
d26633fc 81}
82
83before '_process_options' => sub {
88aaf2bd 84 my ($self, $name, $options) = @_;
38abf787 85 $self->process_options_for_provides($options, $name);
d26633fc 86};
87
8ba40fb0 88## methods called after instantiation
89
96c2370b 90# this confirms that provides (and curries) has
8ba40fb0 91# all valid possibilities in it
92sub check_provides_values {
93 my $self = shift;
999f34a9 94
8ba40fb0 95 my $method_constructors = $self->method_constructors;
999f34a9 96
8ba40fb0 97 foreach my $key (keys %{$self->provides}) {
98 (exists $method_constructors->{$key})
99 || confess "$key is an unsupported method type";
100 }
96c2370b 101
102 foreach my $key (keys %{$self->curries}) {
103 (exists $method_constructors->{$key})
104 || confess "$key is an unsupported method type";
105 }
8ba40fb0 106}
107
c43a2317 108sub _curry {
109 my $self = shift;
110 my $code = shift;
111
c43a2317 112 my @args = @_;
96c2370b 113 return sub {
114 my $self = shift;
115 $code->($self, @args, @_)
116 };
c43a2317 117}
118
696d4dc7 119sub _curry_sub {
120 my $self = shift;
121 my $body = shift;
122 my $code = shift;
123
96c2370b 124 return sub {
125 my $self = shift;
126 $code->($self, $body, @_)
127 };
696d4dc7 128}
129
d26633fc 130after 'install_accessors' => sub {
131 my $attr = shift;
132 my $class = $attr->associated_class;
999f34a9 133
457dc4fb 134 # grab the reader and writer methods
999f34a9 135 # as well, this will be useful for
457dc4fb 136 # our method provider constructors
9a976497 137 my $attr_reader = $attr->get_read_method_ref;
138 my $attr_writer = $attr->get_write_method_ref;
999f34a9 139
d26633fc 140
88aaf2bd 141 # before we install them, lets
142 # make sure they are valid
999f34a9 143 $attr->check_provides_values;
88aaf2bd 144
d26633fc 145 my $method_constructors = $attr->method_constructors;
999f34a9 146
dc4333e9 147 my $class_name = $class->name;
148
3656a0d7 149 while (my ($constructor, $constructed) = each %{$attr->curries}) {
150 my $method_code;
696d4dc7 151 while (my ($curried_name, $curried_arg) = each(%$constructed)) {
152 if ($class->has_method($curried_name)) {
153 confess
154 "The method ($curried_name) already ".
155 "exists in class (" . $class->name . ")";
3656a0d7 156 }
696d4dc7 157 my $body = $method_constructors->{$constructor}->(
158 $attr,
159 $attr_reader,
160 $attr_writer,
161 );
162
163 if (ref $curried_arg eq 'ARRAY') {
164 $method_code = $attr->_curry($body, @$curried_arg);
165 }
166 elsif (ref $curried_arg eq 'CODE') {
167 $method_code = $attr->_curry_sub($body, $curried_arg);
168 }
169 else {
170 confess "curries parameter must be ref type HASH or CODE";
171 }
172
173 my $method = MooseX::AttributeHelpers::Meta::Method::Curried->wrap(
174 $method_code,
175 package_name => $class_name,
176 name => $curried_name,
177 );
178
179 $attr->associate_method($method);
180 $class->add_method($curried_name => $method);
c43a2317 181 }
c43a2317 182 }
183
d26633fc 184 foreach my $key (keys %{$attr->provides}) {
999f34a9 185
186 my $method_name = $attr->provides->{$key};
187
9a976497 188 if ($class->has_method($method_name)) {
189 confess "The method ($method_name) already exists in class (" . $class->name . ")";
999f34a9 190 }
191
192 my $method = MooseX::AttributeHelpers::Meta::Method::Provided->wrap(
193 $method_constructors->{$key}->(
194 $attr,
195 $attr_reader,
196 $attr_writer,
dc4333e9 197 ),
198 package_name => $class_name,
199 name => $method_name,
d26633fc 200 );
999f34a9 201
202 $attr->associate_method($method);
203 $class->add_method($method_name => $method);
204 }
205};
206
207after 'remove_accessors' => sub {
208 my $attr = shift;
209 my $class = $attr->associated_class;
96c2370b 210
211 # provides accessors
999f34a9 212 foreach my $key (keys %{$attr->provides}) {
213 my $method_name = $attr->provides->{$key};
214 my $method = $class->get_method($method_name);
215 $class->remove_method($method_name)
216 if blessed($method) &&
217 $method->isa('MooseX::AttributeHelpers::Meta::Method::Provided');
d26633fc 218 }
96c2370b 219
220 # curries accessors
221 foreach my $key (keys %{$attr->curries}) {
222 my $method_name = $attr->curries->{$key};
223 my $method = $class->get_method($method_name);
224 $class->remove_method($method_name)
225 if blessed($method) &&
226 $method->isa('MooseX::AttributeHelpers::Meta::Method::Provided');
227 }
d26633fc 228};
229
230no Moose;
8ba40fb0 231no Moose::Util::TypeConstraints;
d26633fc 232
2331;
234
235__END__
236
237=pod
238
239=head1 NAME
240
5431dff2 241MooseX::AttributeHelpers::Base - Base class for attribute helpers
999f34a9 242
d26633fc 243=head1 DESCRIPTION
244
5431dff2 245Documentation to come.
e295d072 246
247=head1 ATTRIBUTES
248
5431dff2 249=over 4
250
251=item B<provides>
e295d072 252
c6f9edf8 253=item B<curries>
254
5431dff2 255=item B<method_provider>
e295d072 256
5431dff2 257=item B<method_constructors>
258
259=back
e295d072 260
261=head1 EXTENDED ATTRIBUTES
262
5431dff2 263=over 4
264
4e6acc23 265=item B<default>
e295d072 266
4e6acc23 267C<default> is now required.
e295d072 268
5431dff2 269=item B<type_constraint>
e295d072 270
271C<type_constraint> is now required.
272
5431dff2 273=back
274
d26633fc 275=head1 METHODS
276
5431dff2 277=over 4
278
b91f57af 279=item B<meta>
280
5431dff2 281=item B<helper_type>
282
283=item B<check_provides_values>
284
285=item B<has_default>
286
287=item B<has_method_provider>
288
289=item B<has_type_constraint>
290
291=item B<install_accessors>
292
999f34a9 293=item B<remove_accessors>
294
5431dff2 295=item B<process_options_for_provides>
296
297=back
e295d072 298
d26633fc 299=head1 BUGS
300
999f34a9 301All complex software has bugs lurking in it, and this module is no
d26633fc 302exception. If you find a bug please either email me, or add the bug
303to cpan-RT.
304
305=head1 AUTHOR
306
307Stevan Little E<lt>stevan@iinteractive.comE<gt>
308
309=head1 COPYRIGHT AND LICENSE
310
99c62fb8 311Copyright 2007-2008 by Infinity Interactive, Inc.
d26633fc 312
313L<http://www.iinteractive.com>
314
315This library is free software; you can redistribute it and/or modify
316it under the same terms as Perl itself.
317
8a9cea9b 318=cut