Just haven't commited this yet, and it has a lot of work in it.
[gitmo/MooseX-AttributeHelpers.git] / lib / MooseX / AttributeHelpers / Base.pm
CommitLineData
d26633fc 1
2package MooseX::AttributeHelpers::Base;
3use Moose;
8ba40fb0 4use Moose::Util::TypeConstraints;
8683383a 5use MooseX::AttributeHelpers::Meta::Method::Provided;
d26633fc 6
999f34a9 7our $VERSION = '0.04';
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
999f34a9 19# these next two are the possible methods
8e3fab6d 20# you can use in the 'provides' map.
21
999f34a9 22# provide a Class or Role which we can
23# collect the method providers from
8e3fab6d 24has 'method_provider' => (
25 is => 'ro',
26 isa => 'ClassName',
27 predicate => 'has_method_provider',
28);
29
30# or you can provide a HASH ref of anon subs
31# yourself. This will also collect and store
999f34a9 32# the methods from a method_provider as well
8e3fab6d 33has 'method_constructors' => (
34 is => 'ro',
35 isa => 'HashRef',
36 lazy => 1,
37 default => sub {
38 my $self = shift;
39 return +{} unless $self->has_method_provider;
40 # or grab them from the role/class
41 my $method_provider = $self->method_provider->meta;
42 return +{
999f34a9 43 map {
8e3fab6d 44 $_ => $method_provider->get_method($_)
45 } $method_provider->get_method_list
999f34a9 46 };
e295d072 47 },
8e3fab6d 48);
49
999f34a9 50# extend the parents stuff to make sure
8881a8d3 51# certain bits are now required ...
d26633fc 52has '+$!default' => (required => 1);
53has '+type_constraint' => (required => 1);
54
8ba40fb0 55## Methods called prior to instantiation
56
8683383a 57# (overridden by Sugar or plain subclasses)
58sub helper_type {()}
59sub default_options {}
60sub auto_provide {0}
d26633fc 61
8ba40fb0 62sub process_options_for_provides {
d26633fc 63 my ($self, $options) = @_;
999f34a9 64
8683383a 65 if (my $defaults = $self->default_options) {
66 foreach my $key (keys %$defaults) {
67 $options->{$key} = $defaults->{$key}
68 unless exists $options->{$key};
69 }
70 }
71
8ba40fb0 72 if (my $type = $self->helper_type) {
8683383a 73 $options->{isa} = $type unless exists $options->{isa};
8ba40fb0 74
999f34a9 75 my $isa = $options->{isa};
8ba40fb0 76
77 unless (blessed($isa) && $isa->isa('Moose::Meta::TypeConstraint')) {
999f34a9 78 $isa = Moose::Util::TypeConstraints::find_or_create_type_constraint($isa);
8ba40fb0 79 }
80
81 ($isa->is_a_type_of($type))
82 || confess "The type constraint for a $type ($options->{isa}) must be a subtype of $type";
83 }
d26633fc 84}
85
86before '_process_options' => sub {
88aaf2bd 87 my ($self, $name, $options) = @_;
38abf787 88 $self->process_options_for_provides($options, $name);
d26633fc 89};
90
8ba40fb0 91## methods called after instantiation
92
999f34a9 93# this confirms that provides has
8ba40fb0 94# all valid possibilities in it
95sub check_provides_values {
96 my $self = shift;
999f34a9 97
8ba40fb0 98 my $method_constructors = $self->method_constructors;
999f34a9 99
8ba40fb0 100 foreach my $key (keys %{$self->provides}) {
101 (exists $method_constructors->{$key})
102 || confess "$key is an unsupported method type";
103 }
8683383a 104
105 my $provides = $self->provides;
106 if (keys %$provides == 0 and $self->auto_provide) {
107 my $attr_name = $self->name;
108
109 foreach my $method (keys %$method_constructors) {
110 $provides->{$method} = "${method}_${attr_name}";
111 }
112 }
8ba40fb0 113}
114
d26633fc 115after 'install_accessors' => sub {
116 my $attr = shift;
117 my $class = $attr->associated_class;
999f34a9 118
457dc4fb 119 # grab the reader and writer methods
999f34a9 120 # as well, this will be useful for
457dc4fb 121 # our method provider constructors
9a976497 122 my $attr_reader = $attr->get_read_method_ref;
123 my $attr_writer = $attr->get_write_method_ref;
999f34a9 124
d26633fc 125
88aaf2bd 126 # before we install them, lets
127 # make sure they are valid
999f34a9 128 $attr->check_provides_values;
88aaf2bd 129
d26633fc 130 my $method_constructors = $attr->method_constructors;
999f34a9 131
d26633fc 132 foreach my $key (keys %{$attr->provides}) {
999f34a9 133
134 my $method_name = $attr->provides->{$key};
135
9a976497 136 if ($class->has_method($method_name)) {
137 confess "The method ($method_name) already exists in class (" . $class->name . ")";
999f34a9 138 }
139
140 my $method = MooseX::AttributeHelpers::Meta::Method::Provided->wrap(
141 $method_constructors->{$key}->(
142 $attr,
143 $attr_reader,
144 $attr_writer,
8f7951c9 145 )
d26633fc 146 );
999f34a9 147
148 $attr->associate_method($method);
149 $class->add_method($method_name => $method);
150 }
151};
152
153after 'remove_accessors' => sub {
154 my $attr = shift;
155 my $class = $attr->associated_class;
156 foreach my $key (keys %{$attr->provides}) {
157 my $method_name = $attr->provides->{$key};
158 my $method = $class->get_method($method_name);
159 $class->remove_method($method_name)
160 if blessed($method) &&
161 $method->isa('MooseX::AttributeHelpers::Meta::Method::Provided');
d26633fc 162 }
163};
164
165no Moose;
8ba40fb0 166no Moose::Util::TypeConstraints;
d26633fc 167
1681;
169
170__END__
171
172=pod
173
174=head1 NAME
175
5431dff2 176MooseX::AttributeHelpers::Base - Base class for attribute helpers
999f34a9 177
d26633fc 178=head1 DESCRIPTION
179
8683383a 180This class is what you inherit from when you want to make a new
181AttributeHelper. Unless you are doing something quite fancy, your needs
182should be met by L<MooseX::AttributeHelpers::Sugar>, which has a nice, terse
183syntax and some convenience, but you should still subclass this class.
e295d072 184
185=head1 ATTRIBUTES
186
5431dff2 187=over 4
188
189=item B<provides>
e295d072 190
8683383a 191This is the map of metaclass methods to methods that will be installed in your
192class, e.g. add => 'add_to_number'.
193
5431dff2 194=item B<method_provider>
e295d072 195
8683383a 196The name of a class or role to be used as source material for the above map.
197In the above example, the method provider's "add" method would be used to
198construct a method to install into the attribute holder's class.
199
5431dff2 200=item B<method_constructors>
201
8683383a 202You can optionally supply a hashref of names to subs instead of a class to be
203used as method constructors, but by default this is pulled from
204method_provider.
205
5431dff2 206=back
e295d072 207
208=head1 EXTENDED ATTRIBUTES
209
5431dff2 210=over 4
211
212=item B<$!default>
e295d072 213
214C<$!default> is now required.
215
5431dff2 216=item B<type_constraint>
e295d072 217
218C<type_constraint> is now required.
219
5431dff2 220=back
221
d26633fc 222=head1 METHODS
223
5431dff2 224=over 4
225
b91f57af 226=item B<meta>
227
8683383a 228=item B<auto_provide>
229
230If this method returns a true value, all available method constructors will be
231provided in the format $method_$attribute_name e.g. inc_counter. This is
232intended to be overridden in subclasses.
233
234=item B<default_options>
235
236Returns a Maybe[Hashref] of attribution specifications to fill in if they are
237not overridden by the implementing attribute. This is intended to be
238overridden in subclasses.
239
5431dff2 240=item B<helper_type>
241
8683383a 242This forces all attributes using this metaclass to be a subtype of
243helper_type. This is intended to be overridden in subclasses.
5431dff2 244
8683383a 245=item B<check_provides_values>
5431dff2 246
247=item B<has_method_provider>
248
5431dff2 249=item B<install_accessors>
250
999f34a9 251=item B<remove_accessors>
252
5431dff2 253=item B<process_options_for_provides>
254
255=back
e295d072 256
d26633fc 257=head1 BUGS
258
999f34a9 259All complex software has bugs lurking in it, and this module is no
d26633fc 260exception. If you find a bug please either email me, or add the bug
261to cpan-RT.
262
263=head1 AUTHOR
264
265Stevan Little E<lt>stevan@iinteractive.comE<gt>
266
267=head1 COPYRIGHT AND LICENSE
268
99c62fb8 269Copyright 2007-2008 by Infinity Interactive, Inc.
d26633fc 270
271L<http://www.iinteractive.com>
272
273This library is free software; you can redistribute it and/or modify
274it under the same terms as Perl itself.
275
8a9cea9b 276=cut