bump version to 0.23
[gitmo/MooseX-AttributeHelpers.git] / lib / MooseX / AttributeHelpers / Trait / Base.pm
CommitLineData
3b5070d1 1
2package MooseX::AttributeHelpers::Trait::Base;
3use Moose::Role;
4use Moose::Util::TypeConstraints;
5
2e74144c 6our $VERSION = '0.23';
38430345 7$VERSION = eval $VERSION;
3b5070d1 8our $AUTHORITY = 'cpan:STEVAN';
9
10requires 'helper_type';
11
12# this is the method map you define ...
13has 'provides' => (
14 is => 'ro',
15 isa => 'HashRef',
16 default => sub {{}}
17);
18
c43a2317 19has 'curries' => (
20 is => 'ro',
21 isa => 'HashRef',
22 default => sub {{}}
23);
3b5070d1 24
25# these next two are the possible methods
26# you can use in the 'provides' map.
27
28# provide a Class or Role which we can
29# collect the method providers from
dbd51f30 30
31# requires_attr 'method_provider'
3b5070d1 32
33# or you can provide a HASH ref of anon subs
34# yourself. This will also collect and store
35# the methods from a method_provider as well
36has 'method_constructors' => (
37 is => 'ro',
38 isa => 'HashRef',
39 lazy => 1,
40 default => sub {
41 my $self = shift;
42 return +{} unless $self->has_method_provider;
43 # or grab them from the role/class
44 my $method_provider = $self->method_provider->meta;
45 return +{
46 map {
47 $_ => $method_provider->get_method($_)
1b8bf95a 48 }
49 grep { $_ ne 'meta' } $method_provider->get_method_list
3b5070d1 50 };
51 },
52);
53
3b5070d1 54## Methods called prior to instantiation
55
56sub process_options_for_provides {
57 my ($self, $options) = @_;
58
59 if (my $type = $self->helper_type) {
60 (exists $options->{isa})
61 || confess "You must define a type with the $type metaclass";
62
63 my $isa = $options->{isa};
64
65 unless (blessed($isa) && $isa->isa('Moose::Meta::TypeConstraint')) {
66 $isa = Moose::Util::TypeConstraints::find_or_create_type_constraint($isa);
67 }
68
69 ($isa->is_a_type_of($type))
70 || confess "The type constraint for a $type ($options->{isa}) must be a subtype of $type";
71 }
72}
73
74before '_process_options' => sub {
75 my ($self, $name, $options) = @_;
76 $self->process_options_for_provides($options, $name);
77};
78
79## methods called after instantiation
80
3b5070d1 81sub check_provides_values {
82 my $self = shift;
83
84 my $method_constructors = $self->method_constructors;
85
86 foreach my $key (keys %{$self->provides}) {
87 (exists $method_constructors->{$key})
88 || confess "$key is an unsupported method type";
89 }
96c2370b 90
91 foreach my $key (keys %{$self->curries}) {
92 (exists $method_constructors->{$key})
93 || confess "$key is an unsupported method type";
94 }
8ba40fb0 95}
96
c43a2317 97sub _curry {
98 my $self = shift;
99 my $code = shift;
100
c43a2317 101 my @args = @_;
96c2370b 102 return sub {
103 my $self = shift;
104 $code->($self, @args, @_)
105 };
c43a2317 106}
107
696d4dc7 108sub _curry_sub {
109 my $self = shift;
110 my $body = shift;
111 my $code = shift;
112
96c2370b 113 return sub {
114 my $self = shift;
115 $code->($self, $body, @_)
116 };
3b5070d1 117}
118
119after 'install_accessors' => sub {
120 my $attr = shift;
121 my $class = $attr->associated_class;
122
123 # grab the reader and writer methods
124 # as well, this will be useful for
125 # our method provider constructors
126 my $attr_reader = $attr->get_read_method_ref;
127 my $attr_writer = $attr->get_write_method_ref;
128
129
130 # before we install them, lets
131 # make sure they are valid
132 $attr->check_provides_values;
133
134 my $method_constructors = $attr->method_constructors;
135
136 my $class_name = $class->name;
137
3656a0d7 138 while (my ($constructor, $constructed) = each %{$attr->curries}) {
139 my $method_code;
696d4dc7 140 while (my ($curried_name, $curried_arg) = each(%$constructed)) {
141 if ($class->has_method($curried_name)) {
142 confess
143 "The method ($curried_name) already ".
144 "exists in class (" . $class->name . ")";
3656a0d7 145 }
696d4dc7 146 my $body = $method_constructors->{$constructor}->(
147 $attr,
148 $attr_reader,
149 $attr_writer,
150 );
151
152 if (ref $curried_arg eq 'ARRAY') {
153 $method_code = $attr->_curry($body, @$curried_arg);
154 }
155 elsif (ref $curried_arg eq 'CODE') {
156 $method_code = $attr->_curry_sub($body, $curried_arg);
157 }
158 else {
a78379c8 159 confess "curries parameter must be ref type ARRAY or CODE";
696d4dc7 160 }
161
162 my $method = MooseX::AttributeHelpers::Meta::Method::Curried->wrap(
163 $method_code,
164 package_name => $class_name,
165 name => $curried_name,
166 );
a78379c8 167
696d4dc7 168 $attr->associate_method($method);
169 $class->add_method($curried_name => $method);
c43a2317 170 }
c43a2317 171 }
172
3b5070d1 173 foreach my $key (keys %{$attr->provides}) {
174
175 my $method_name = $attr->provides->{$key};
176
177 if ($class->has_method($method_name)) {
178 confess "The method ($method_name) already exists in class (" . $class->name . ")";
179 }
180
181 my $method = MooseX::AttributeHelpers::Meta::Method::Provided->wrap(
182 $method_constructors->{$key}->(
183 $attr,
184 $attr_reader,
185 $attr_writer,
186 ),
187 package_name => $class_name,
188 name => $method_name,
189 );
190
191 $attr->associate_method($method);
192 $class->add_method($method_name => $method);
193 }
194};
195
196after 'remove_accessors' => sub {
197 my $attr = shift;
198 my $class = $attr->associated_class;
96c2370b 199
200 # provides accessors
3b5070d1 201 foreach my $key (keys %{$attr->provides}) {
202 my $method_name = $attr->provides->{$key};
203 my $method = $class->get_method($method_name);
204 $class->remove_method($method_name)
205 if blessed($method) &&
206 $method->isa('MooseX::AttributeHelpers::Meta::Method::Provided');
207 }
96c2370b 208
209 # curries accessors
210 foreach my $key (keys %{$attr->curries}) {
211 my $method_name = $attr->curries->{$key};
212 my $method = $class->get_method($method_name);
213 $class->remove_method($method_name)
214 if blessed($method) &&
215 $method->isa('MooseX::AttributeHelpers::Meta::Method::Provided');
216 }
3b5070d1 217};
218
219no Moose::Role;
220no Moose::Util::TypeConstraints;
221
2221;
223
4115eceb 224__END__
225
226=head1 NAME
227
228MooseX::AttributeHelpers::Trait::Base - base role for helpers
229
230=head1 METHODS
231
232=head2 check_provides_values
233
234Confirms that provides (and curries) has all valid possibilities in it.
235
236=head2 process_options_for_provides
237
238Ensures that the type constraint (C<isa>) matches the helper type.
239
240=head1 BUGS
241
242All complex software has bugs lurking in it, and this module is no
243exception. If you find a bug please either email me, or add the bug
244to cpan-RT.
245
246=head1 AUTHORS
247
248Yuval Kogman
249
250Shawn M Moore
251
252Jesse Luehrs
253
254=head1 COPYRIGHT AND LICENSE
255
9c5d164e 256Copyright 2007-2009 by Infinity Interactive, Inc.
4115eceb 257
258L<http://www.iinteractive.com>
259
260This library is free software; you can redistribute it and/or modify
261it under the same terms as Perl itself.
262
263=cut