File Coverage

blib/lib/MooX/Traits/Util.pm
Criterion Covered Total %
statement 29 31 93.5
branch 10 12 83.3
condition n/a
subroutine 8 8 100.0
pod 3 3 100.0
total 50 54 92.5


line stmt bran cond sub pod time code
1 7     7   125873 use 5.006;
  7         24  
  7         431  
2 7     7   38 use strict;
  7         11  
  7         212  
3 7     7   34 use warnings;
  7         13  
  7         387  
4              
5 7 50   7   444 BEGIN { if ($] < 5.010000) { require UNIVERSAL::DOES } };
  0         0  
6              
7             package MooX::Traits::Util;
8              
9             our $AUTHORITY = 'cpan:TOBYINK';
10             our $VERSION = '0.005';
11              
12 7         46 use Exporter::Shiny qw(
13             new_class_with_traits
14             new_class_with_traits_one_by_one
15 7     7   8465 );
  7         30209  
16              
17             my @keepsies;
18             my $parameterize_role = sub
19             {
20             my ($class, $trait, $params) = @_;
21             return $trait unless @_ == 3;
22            
23             require Module::Runtime;
24             Module::Runtime::use_package_optimistically($trait);
25            
26             if ( $INC{'MooseX/Role/Parameterized.pm'} )
27             {
28             require Moose::Util;
29             my $meta = Moose::Util::find_meta($trait);
30             if ($meta->can('generate_role'))
31             {
32             my $generated = $meta->generate_role(parameters => $params);
33             push @keepsies, $generated; # prevent cleanup
34             return $generated->name;
35             }
36             }
37            
38             if ( $trait->can("make_variant") )
39             {
40             require Package::Variant;
41             return "Package::Variant"->build_variant_of(
42             $trait,
43             ref($params) eq q(ARRAY) ? @$params :
44             ref($params) eq q(HASH) ? %$params :
45             $$params,
46             );
47             }
48            
49             return $trait;
50             };
51              
52             my $looks_like_params = sub
53             {
54             my $thing = $_[0];
55             return !!0 if not ref $thing;
56             require Scalar::Util;
57             return !!0 if Scalar::Util::blessed($thing);
58             return !!1;
59             };
60              
61             sub resolve_traits
62             {
63 29     29 1 66 my ($class, @args) = @_;
64            
65 29 100       306 my $ns = $class->DOES('MooX::Traits') ? $class->_trait_namespace : undef;
66 29 100       263 $ns = defined($ns) ? "$ns\::" : "";
67            
68 29         302 my @traits;
69 29         132 while (@args)
70             {
71 35         2191 my $trait = shift(@args);
72 35 100       127 $trait = $trait =~ /\A\+(.+)\z/ ? $1 : "$ns$trait";
73            
74 35 100       116 push @traits => (
75             $looks_like_params->($args[0])
76             ? $parameterize_role->($class, $trait, shift(@args))
77             : $trait
78             );
79             }
80 29         3491 return @traits;
81             }
82              
83             my $toolage = sub
84             {
85             my $class = shift;
86            
87             if ($INC{"Moo.pm"} and $Moo::MAKERS{$class}{is_class})
88             {
89             require Moo::Role;
90             return "Moo::Role";
91             }
92            
93             if ($INC{"Moo/Role.pm"})
94             {
95             return "Moo::Role";
96             }
97            
98             "Role::Tiny";
99             };
100              
101             sub new_class_with_traits
102             {
103 29     29 1 15978 my ($class, @traits) = @_;
104 29         87 $class->$toolage->create_class_with_roles(
105             $class,
106             resolve_traits($class, @traits),
107             );
108             }
109              
110             sub new_class_with_traits_one_by_one
111             {
112 2     2 1 2641 my ($class, @traits) = @_;
113 2         7 while (@traits)
114             {
115 2         6 my @trait = shift(@traits);
116 2 50       6 push @trait, shift(@traits)
117             if $looks_like_params->($traits[0]);
118 2         6 $class = new_class_with_traits($class, @trait);
119             }
120 0           return $class;
121             }
122              
123             1;
124              
125             __END__
126              
127             =pod
128              
129             =encoding utf-8
130              
131             =for stopwords MooseX MouseX prepend metaclass
132              
133             =head1 NAME
134              
135             MooX::Traits::Util - non-role alternative to MooX::Traits
136              
137             =head1 SYNOPSIS
138              
139             Given some roles:
140              
141             package Role;
142             use Moo::Role;
143             has foo => ( is => 'ro', required => 1 );
144              
145             And a class:
146              
147             package Class;
148             use Moo;
149              
150             Apply the roles to the class:
151              
152             use MooX::Traits::Util -all;
153            
154             my $class = new_class_with_traits('Class', 'Role');
155              
156             Then use your customized class:
157              
158             my $object = $class->new( foo => 42 );
159             $object->isa('Class'); # true
160             $object->does('Role'); # true
161             $object->foo; # 42
162              
163             =head1 DESCRIPTION
164              
165             This module provides the functionality of L<MooX::Traits>, but it's an
166             exporter rather than a role.
167              
168             It's inspired by, but not compatible with L<MooseX::Traits::Util>. The
169             latter module is undocumented, and it's not entirely clear whether it's
170             intended to be consumed by end-users, or is an entirely internal API.
171              
172             This module exports nothing by default.
173              
174             =head2 Functions
175              
176             =over
177              
178             =item C<< new_class_with_traits( $class, @traits ) >>
179              
180             Return a new class name with the traits applied.
181              
182             This function is not quite compatible with the C<new_class_with_traits>
183             function provided by L<MooseX::Traits::Util>, in that the latter will
184             return a metaclass object.
185              
186             This function can be exported.
187              
188             =item C<< new_class_with_traits_one_by_one( $class, @traits ) >>
189              
190             Rather than applying the the traits simultaneously, the traits are
191             applied one at a time. It is roughly equivalent to:
192              
193             use List::Util qw(reduce);
194             use MooX::Traits::Util qw( new_class_with_traits );
195            
196             my $class = ...;
197             my @traits = ...;
198             my $new = reduce { new_class_with_traits($a, $b) } $class, @traits;
199              
200             Applying traits one by one has implications for method modifiers, and
201             for method conflict detection. B<< Use with caution. >>
202              
203             There is no equivalent functionality in L<MooseX::Traits::Util>.
204              
205             This function can be exported.
206              
207             =item C<< resolve_traits( $class, @traits ) >>
208              
209             This function returns a list of traits, but does not apply them to the
210             class. It honours the class' C<_trait_namespace> method (but only if
211             the class does the MooX::Traits role) and handles parameter hashrefs
212             for parameterizable roles. (That is, parameters are applied to the
213             role, and the list of traits returned by the function includes the
214             result of that application instead of including the original hashref.)
215              
216             This function is not quite compatible with the C<resolve_traits>
217             function provided by L<MooseX::Traits::Util>, in that the latter will
218             not handle parameter hashrefs, trusting Moose to do that.
219              
220             This function I<cannot> be exported.
221              
222             =back
223              
224             =head1 BUGS
225              
226             Please report any bugs to
227             L<http://rt.cpan.org/Dist/Display.html?Queue=MooX-Traits>.
228              
229             =head1 SEE ALSO
230              
231             L<MooX::Traits>.
232              
233             =head1 AUTHOR
234              
235             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
236              
237             =head1 COPYRIGHT AND LICENCE
238              
239             This software is copyright (c) 2014 by Toby Inkster.
240              
241             This is free software; you can redistribute it and/or modify it under
242             the same terms as the Perl 5 programming language system itself.
243              
244             =head1 DISCLAIMER OF WARRANTIES
245              
246             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
247             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
248             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
249