File Coverage

blib/lib/MooseX/AttributeHelpers/Trait/Base.pm
Criterion Covered Total %
statement 39 39 100.0
branch 6 12 50.0
condition 1 3 33.3
subroutine 14 14 100.0
pod 2 2 100.0
total 62 70 88.5


line stmt bran cond sub pod time code
1             package MooseX::AttributeHelpers::Trait::Base;
2             # ABSTRACT: base role for helpers
3              
4 22     22   10545 use Moose::Role;
  22         32  
  22         112  
5 22     22   74566 use Moose::Util::TypeConstraints;
  22         36  
  22         190  
6              
7             our $VERSION = '0.25';
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             has 'curries' => (
19             is => 'ro',
20             isa => 'HashRef',
21             default => sub {{}}
22             );
23              
24             # these next two are the possible methods
25             # you can use in the 'provides' map.
26              
27             # provide a Class or Role which we can
28             # collect the method providers from
29              
30             # requires_attr 'method_provider'
31              
32             # or you can provide a HASH ref of anon subs
33             # yourself. This will also collect and store
34             # the methods from a method_provider as well
35             has 'method_constructors' => (
36             is => 'ro',
37             isa => 'HashRef',
38             lazy => 1,
39             default => sub {
40             my $self = shift;
41             return +{} unless $self->has_method_provider;
42             # or grab them from the role/class
43             my $method_provider = $self->method_provider->meta;
44             return +{
45             map {
46             $_ => $method_provider->get_method($_)
47             }
48             grep { $_ ne 'meta' } $method_provider->get_method_list
49             };
50             },
51             );
52              
53             ## Methods called prior to instantiation
54              
55             sub process_options_for_provides {
56 24     24 1 83 my ($self, $options) = @_;
57              
58 24 50       109 if (my $type = $self->helper_type) {
59             (exists $options->{isa})
60 24 50       82 || confess "You must define a type with the $type metaclass";
61              
62 24         59 my $isa = $options->{isa};
63              
64 24 50 33     161 unless (blessed($isa) && $isa->isa('Moose::Meta::TypeConstraint')) {
65 24         122 $isa = Moose::Util::TypeConstraints::find_or_create_type_constraint($isa);
66             }
67              
68 24 50       34548 ($isa->is_a_type_of($type))
69             || confess "The type constraint for a $type ($options->{isa}) must be a subtype of $type";
70             }
71             }
72              
73             before '_process_options' => sub {
74             my ($self, $name, $options) = @_;
75             $self->process_options_for_provides($options, $name);
76             };
77              
78             ## methods called after instantiation
79              
80             sub check_provides_values {
81 24     24 1 122 my $self = shift;
82              
83 24         809 my $method_constructors = $self->method_constructors;
84              
85 24         37 foreach my $key (keys %{$self->provides}) {
  24         717  
86 136 50       216 (exists $method_constructors->{$key})
87             || confess "$key is an unsupported method type";
88             }
89              
90 24         50 foreach my $key (keys %{$self->curries}) {
  24         725  
91 34 50       70 (exists $method_constructors->{$key})
92             || confess "$key is an unsupported method type";
93             }
94             }
95              
96             sub _curry {
97 30     30   34 my $self = shift;
98 30         35 my $code = shift;
99              
100 30         49 my @args = @_;
101             return sub {
102 30     30   12649 my $self = shift;
        34      
        26      
        26      
        12      
103 30         169 $code->($self, @args, @_)
104 30         103 };
105             }
106              
107             sub _curry_sub {
108 4     4   6 my $self = shift;
109 4         6 my $body = shift;
110 4         7 my $code = shift;
111              
112             return sub {
113 4     4   1995 my $self = shift;
114 4         20 $code->($self, $body, @_)
115 4         15 };
116             }
117              
118             after 'install_accessors' => sub {
119             my $attr = shift;
120             my $class = $attr->associated_class;
121              
122             # grab the reader and writer methods
123             # as well, this will be useful for
124             # our method provider constructors
125             my $attr_reader = $attr->get_read_method_ref;
126             my $attr_writer = $attr->get_write_method_ref;
127              
128              
129             # before we install them, lets
130             # make sure they are valid
131             $attr->check_provides_values;
132              
133             my $method_constructors = $attr->method_constructors;
134              
135             my $class_name = $class->name;
136              
137             while (my ($constructor, $constructed) = each %{$attr->curries}) {
138             my $method_code;
139             while (my ($curried_name, $curried_arg) = each(%$constructed)) {
140             if ($class->has_method($curried_name)) {
141             confess
142             "The method ($curried_name) already ".
143             "exists in class (" . $class->name . ")";
144             }
145             my $body = $method_constructors->{$constructor}->(
146             $attr,
147             $attr_reader,
148             $attr_writer,
149             );
150              
151             if (ref $curried_arg eq 'ARRAY') {
152             $method_code = $attr->_curry($body, @$curried_arg);
153             }
154             elsif (ref $curried_arg eq 'CODE') {
155             $method_code = $attr->_curry_sub($body, $curried_arg);
156             }
157             else {
158             confess "curries parameter must be ref type ARRAY or CODE";
159             }
160              
161             my $method = MooseX::AttributeHelpers::Meta::Method::Curried->wrap(
162             $method_code,
163             package_name => $class_name,
164             name => $curried_name,
165             );
166              
167             $attr->associate_method($method);
168             $class->add_method($curried_name => $method);
169             }
170             }
171              
172             foreach my $key (keys %{$attr->provides}) {
173              
174             my $method_name = $attr->provides->{$key};
175              
176             if ($class->has_method($method_name)) {
177             confess "The method ($method_name) already exists in class (" . $class->name . ")";
178             }
179              
180             my $method = MooseX::AttributeHelpers::Meta::Method::Provided->wrap(
181             $method_constructors->{$key}->(
182             $attr,
183             $attr_reader,
184             $attr_writer,
185             ),
186             package_name => $class_name,
187             name => $method_name,
188             );
189            
190             $attr->associate_method($method);
191             $class->add_method($method_name => $method);
192             }
193             };
194              
195             after 'remove_accessors' => sub {
196             my $attr = shift;
197             my $class = $attr->associated_class;
198              
199             # provides accessors
200             foreach my $key (keys %{$attr->provides}) {
201             my $method_name = $attr->provides->{$key};
202             my $method = $class->get_method($method_name);
203             $class->remove_method($method_name)
204             if blessed($method) &&
205             $method->isa('MooseX::AttributeHelpers::Meta::Method::Provided');
206             }
207              
208             # curries accessors
209             foreach my $key (keys %{$attr->curries}) {
210             my $method_name = $attr->curries->{$key};
211             my $method = $class->get_method($method_name);
212             $class->remove_method($method_name)
213             if blessed($method) &&
214             $method->isa('MooseX::AttributeHelpers::Meta::Method::Provided');
215             }
216             };
217              
218 22     22   45177 no Moose::Role;
  22         37  
  22         102  
219 22     22   3183 no Moose::Util::TypeConstraints;
  22         25  
  22         86  
220              
221             1;
222              
223             __END__
224              
225             =pod
226              
227             =encoding UTF-8
228              
229             =head1 NAME
230              
231             MooseX::AttributeHelpers::Trait::Base - base role for helpers
232              
233             =head1 VERSION
234              
235             version 0.25
236              
237             =head1 METHODS
238              
239             =head2 check_provides_values
240              
241             Confirms that provides (and curries) has all valid possibilities in it.
242              
243             =head2 process_options_for_provides
244              
245             Ensures that the type constraint (C<isa>) matches the helper type.
246              
247             =head1 SUPPORT
248              
249             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-AttributeHelpers>
250             (or L<bug-MooseX-AttributeHelpers@rt.cpan.org|mailto:bug-MooseX-AttributeHelpers@rt.cpan.org>).
251              
252             There is also a mailing list available for users of this distribution, at
253             L<http://lists.perl.org/list/moose.html>.
254              
255             There is also an irc channel available for users of this distribution, at
256             L<C<#moose> on C<irc.perl.org>|irc://irc.perl.org/#moose>.
257              
258             =head1 AUTHORS
259              
260             Stevan Little <stevan@iinteractive.com>
261              
262             Yuval Kogman
263              
264             Shawn M Moore
265              
266             Jesse Luehrs
267              
268             =head1 COPYRIGHT AND LICENSE
269              
270             This software is copyright (c) 2007 by Stevan Little and Infinity Interactive, Inc.
271              
272             This is free software; you can redistribute it and/or modify it under
273             the same terms as the Perl 5 programming language system itself.
274              
275             =cut