9ab3f6efecf431d317168e675782106621bf7776
[gitmo/MooseX-AttributeHelpers.git] / lib / MooseX / AttributeHelpers / Trait / Base.pm
1
2 package MooseX::AttributeHelpers::Trait::Base;
3 use Moose::Role;
4 use Moose::Util::TypeConstraints;
5
6 our $VERSION   = '0.04';
7 our $AUTHORITY = 'cpan:STEVAN';
8
9 requires 'helper_type';
10
11 # this is the method map you define ...
12 has 'provides' => (
13     is      => 'ro',
14     isa     => 'HashRef',
15     default => sub {{}}
16 );
17
18
19 # these next two are the possible methods
20 # you can use in the 'provides' map.
21
22 # provide a Class or Role which we can
23 # collect the method providers from
24 has '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
32 # the methods from a method_provider as well
33 has '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 +{
43             map {
44                 $_ => $method_provider->get_method($_)
45             } $method_provider->get_method_list
46         };
47     },
48 );
49
50 # extend the parents stuff to make sure
51 # certain bits are now required ...
52 has '+$!default'       => (required => 1);
53 has '+type_constraint' => (required => 1);
54
55 ## Methods called prior to instantiation
56
57 sub process_options_for_provides {
58     my ($self, $options) = @_;
59
60     if (my $type = $self->helper_type) {
61         (exists $options->{isa})
62             || confess "You must define a type with the $type metaclass";
63
64         my $isa = $options->{isa};
65
66         unless (blessed($isa) && $isa->isa('Moose::Meta::TypeConstraint')) {
67             $isa = Moose::Util::TypeConstraints::find_or_create_type_constraint($isa);
68         }
69
70         ($isa->is_a_type_of($type))
71             || confess "The type constraint for a $type ($options->{isa}) must be a subtype of $type";
72     }
73 }
74
75 before '_process_options' => sub {
76     my ($self, $name, $options) = @_;
77     $self->process_options_for_provides($options, $name);
78 };
79
80 ## methods called after instantiation
81
82 # this confirms that provides has
83 # all valid possibilities in it
84 sub check_provides_values {
85     my $self = shift;
86
87     my $method_constructors = $self->method_constructors;
88
89     foreach my $key (keys %{$self->provides}) {
90         (exists $method_constructors->{$key})
91             || confess "$key is an unsupported method type";
92     }
93 }
94
95 after 'install_accessors' => sub {
96     my $attr  = shift;
97     my $class = $attr->associated_class;
98
99     # grab the reader and writer methods
100     # as well, this will be useful for
101     # our method provider constructors
102     my $attr_reader = $attr->get_read_method_ref;
103     my $attr_writer = $attr->get_write_method_ref;
104
105
106     # before we install them, lets
107     # make sure they are valid
108     $attr->check_provides_values;
109
110     my $method_constructors = $attr->method_constructors;
111
112     my $class_name = $class->name;
113
114     foreach my $key (keys %{$attr->provides}) {
115
116         my $method_name = $attr->provides->{$key};
117
118         if ($class->has_method($method_name)) {
119             confess "The method ($method_name) already exists in class (" . $class->name . ")";
120         }
121
122         my $method = MooseX::AttributeHelpers::Meta::Method::Provided->wrap(
123             $method_constructors->{$key}->(
124                 $attr,
125                 $attr_reader,
126                 $attr_writer,
127             ),
128             package_name => $class_name,
129             name => $method_name,
130         );
131         
132         $attr->associate_method($method);
133         $class->add_method($method_name => $method);
134     }
135 };
136
137 after 'remove_accessors' => sub {
138     my $attr  = shift;
139     my $class = $attr->associated_class;
140     foreach my $key (keys %{$attr->provides}) {
141         my $method_name = $attr->provides->{$key};
142         my $method = $class->get_method($method_name);
143         $class->remove_method($method_name)
144             if blessed($method) &&
145                $method->isa('MooseX::AttributeHelpers::Meta::Method::Provided');
146     }
147 };
148
149 no Moose::Role;
150 no Moose::Util::TypeConstraints;
151
152 1;
153