File Coverage

blib/lib/Moo/HandleMoose.pm
Criterion Covered Total %
statement 152 152 100.0
branch 52 52 100.0
condition 14 17 82.3
subroutine 15 15 100.0
pod 0 4 0.0
total 233 240 97.0


line stmt bran cond sub pod time code
1             package Moo::HandleMoose;
2 82     82   458516 use strict;
  82         207  
  82         2580  
3 82     82   481 use warnings;
  82         202  
  82         2269  
4 82     82   502 no warnings 'once';
  82         186  
  82         3536  
5              
6 82     82   3768 use Moo::_Utils qw(_getstash);
  82         203  
  82         4025  
7 82     82   30203 use Sub::Quote qw(quotify);
  82         372597  
  82         4838  
8 82     82   643 use Carp qw(croak);
  82         214  
  82         140185  
9              
10             our %TYPE_MAP;
11              
12             our $SETUP_DONE;
13              
14 90 100   90   176657 sub import { return if $SETUP_DONE; inject_all(); $SETUP_DONE = 1; }
  82         299  
  78         11603  
15              
16             sub inject_all {
17 82 100   82 0 722 croak "Can't inflate Moose metaclass with Moo::sification disabled"
18             if $Moo::sification::disabled;
19 78         5057 require Class::MOP;
20             inject_fake_metaclass_for($_)
21 78         990152 for grep $_ ne 'Moo::Object', keys %Moo::MAKERS;
22 78         75680 inject_fake_metaclass_for($_) for keys %Moo::Role::INFO;
23 78         89821 require Moose::Meta::Method::Constructor;
24 78         60143 @Moo::HandleMoose::FakeConstructor::ISA = 'Moose::Meta::Method::Constructor';
25 78         1839 @Moo::HandleMoose::FakeMeta::ISA = 'Moose::Meta::Method::Meta';
26             }
27              
28             sub maybe_reinject_fake_metaclass_for {
29 178     178 0 442 my ($name) = @_;
30 178         286 our %DID_INJECT;
31 178 100       1655 if (delete $DID_INJECT{$name}) {
32 8 100       33 unless ($Moo::Role::INFO{$name}) {
33 4         17 Moo->_constructor_maker_for($name)->install_delayed;
34             }
35 8         31 inject_fake_metaclass_for($name);
36             }
37             }
38              
39             sub inject_fake_metaclass_for {
40 240     240 0 50432 my ($name) = @_;
41 240         1336 require Class::MOP;
42 240         29604 require Moo::HandleMoose::FakeMetaClass;
43 240         2106 Class::MOP::store_metaclass_by_name(
44             $name, bless({ name => $name }, 'Moo::HandleMoose::FakeMetaClass')
45             );
46 240         7452 require Moose::Util::TypeConstraints;
47 240 100       1004070 if ($Moo::Role::INFO{$name}) {
48 74         373 Moose::Util::TypeConstraints::find_or_create_does_type_constraint($name);
49             } else {
50 166         766 Moose::Util::TypeConstraints::find_or_create_isa_type_constraint($name);
51             }
52             }
53              
54             {
55             package Moo::HandleMoose::FakeConstructor;
56              
57 16     16   69687 sub _uninlined_body { \&Moose::Object::new }
58             }
59              
60             sub inject_real_metaclass_for {
61 252     252 0 2627 my ($name) = @_;
62 252         416 our %DID_INJECT;
63 252 100       1001 return Class::MOP::get_metaclass_by_name($name) if $DID_INJECT{$name};
64 138         5360 require Moose; require Moo; require Moo::Role; require Scalar::Util;
  138         2031596  
  138         14972  
  138         560  
65 138         476 require Sub::Defer;
66 138         705 Class::MOP::remove_metaclass_by_name($name);
67 138         923 my ($am_role, $am_class, $meta, $attr_specs, $attr_order) = do {
68 138 100       939 if (my $info = $Moo::Role::INFO{$name}) {
    100          
69 62 100       125 my @attr_info = @{$info->{attributes}||[]};
  62         422  
70 62         598 (1, 0, Moose::Meta::Role->initialize($name),
71             { @attr_info },
72             [ @attr_info[grep !($_ % 2), 0..$#attr_info] ]
73             )
74             } elsif ( my $cmaker = Moo->_constructor_maker_for($name) ) {
75 62         267 my $specs = $cmaker->all_attribute_specs;
76             (0, 1, Moose::Meta::Class->initialize($name), $specs,
77 62         577 [ sort { $specs->{$a}{index} <=> $specs->{$b}{index} } keys %$specs ]
  64         22155  
78             );
79             } else {
80             # This codepath is used if $name does not exist in $Moo::MAKERS
81 14         103 (0, 0, Moose::Meta::Class->initialize($name), {}, [] )
82             }
83             };
84              
85             {
86 138         139027 local $DID_INJECT{$name} = 1;
  138         451  
87 138         633 foreach my $spec (values %$attr_specs) {
88 122 100       509 if (my $inflators = delete $spec->{moosify}) {
89 8         45 $_->($spec) for @$inflators;
90             }
91             }
92              
93             my %methods
94 138 100       1351 = %{($am_role ? 'Moo::Role' : 'Moo')->_concrete_methods_of($name)};
  138         3502  
95              
96             # if stuff gets added afterwards, _maybe_reset_handlemoose should
97             # trigger the recreation of the metaclass but we need to ensure the
98             # Moo::Role cache is cleared so we don't confuse Moo itself.
99 138 100       5012 if (my $info = $Moo::Role::INFO{$name}) {
100 62         1163 delete $info->{methods};
101             }
102              
103             # needed to ensure the method body is stable and get things named
104             $methods{$_} = Sub::Defer::undefer_sub($methods{$_})
105 138         1059 for
106             grep $_ ne 'new',
107             keys %methods;
108 138         2273 my @attrs;
109             {
110             # This local is completely not required for roles but harmless
111 138         253 local @{_getstash($name)}{keys %methods};
  138         406  
  138         480  
112 138         351 my %seen_name;
113 138         510 foreach my $attr_name (@$attr_order) {
114 122         89850 $seen_name{$attr_name} = 1;
115 122         232 my %spec = %{$attr_specs->{$attr_name}};
  122         660  
116             my %spec_map = (
117 3544   66     12079 map { $_->name => $_->init_arg||$_->name }
118             (
119 3538         27460 (grep { $_->has_init_arg }
120             $meta->attribute_metaclass->meta->get_all_attributes),
121 8 100       193 grep { exists($_->{init_arg}) ? defined($_->init_arg) : 1 }
122             map {
123 6         44 my $meta = Moose::Util::resolve_metatrait_alias('Attribute', $_)
124             ->meta;
125 6         56307 map $meta->get_attribute($_), $meta->get_attribute_list
126 122 100       1608 } @{$spec{traits}||[]}
  122         1157  
127             )
128             );
129             # have to hard code this because Moose's role meta-model is lacking
130 122   100     882 $spec_map{traits} ||= 'traits';
131              
132 122 100 66     836 $spec{is} = 'ro' if $spec{is} eq 'lazy' or $spec{is} eq 'rwp';
133 122         298 my $coerce = $spec{coerce};
134 122 100       503 if (my $isa = $spec{isa}) {
    100          
135 22         46 my $tc = $spec{isa} = do {
136 22 100       81 if (my $mapped = $TYPE_MAP{$isa}) {
137 16         122 my $type = $mapped->();
138 16 100 66     1370 unless ( Scalar::Util::blessed($type)
139             && $type->isa("Moose::Meta::TypeConstraint") ) {
140 4         730 croak "error inflating attribute '$attr_name' for package '$name': "
141             ."\$TYPE_MAP{$isa} did not return a valid type constraint'";
142             }
143 12 100       287 $coerce ? $type->create_child_type(name => $type->name) : $type;
144             } else {
145             Moose::Meta::TypeConstraint->new(
146 25     25   216413 constraint => sub { eval { &$isa; 1 } }
  25         92  
  13         1978  
147 6         52 );
148             }
149             };
150 18 100       12863 if ($coerce) {
151 8         73 $tc->coercion(Moose::Meta::TypeCoercion->new)
152             ->_compiled_type_coercion($coerce);
153 8         3925 $spec{coerce} = 1;
154             }
155             } elsif ($coerce) {
156 2         10 my $attr = quotify($attr_name);
157             my $tc = Moose::Meta::TypeConstraint->new(
158 2     2   5784 constraint => sub { die "This is not going to work" },
159             inlined => sub {
160 10     10   16050 'my $r = $_[42]{'.$attr.'}; $_[42]{'.$attr.'} = 1; $r'
161             },
162 2         48 );
163 2         989 $tc->coercion(Moose::Meta::TypeCoercion->new)
164             ->_compiled_type_coercion($coerce);
165 2         1167 $spec{isa} = $tc;
166 2         5 $spec{coerce} = 1;
167             }
168             %spec =
169 336         903 map { $spec_map{$_} => $spec{$_} }
170 118         428 grep { exists $spec_map{$_} }
  424         905  
171             keys %spec;
172 118         751 push @attrs, $meta->add_attribute($attr_name => %spec);
173             }
174 132 100       136278 foreach my $mouse (do { our %MOUSE; @{$MOUSE{$name}||[]} }) {
  132         270  
  132         305  
  132         1190  
175 4         31 foreach my $attr ($mouse->get_all_attributes) {
176 4         121 my %spec = %{$attr};
  4         34  
177 4         21 delete @spec{qw(
178             associated_class associated_methods __METACLASS__
179             provides curries
180             )};
181 4         11 my $attr_name = delete $spec{name};
182 4 100       24 next if $seen_name{$attr_name}++;
183 2         12 push @attrs, $meta->add_attribute($attr_name => %spec);
184             }
185             }
186             }
187 132         4924 foreach my $meth_name (keys %methods) {
188 452         13564 my $meth_code = $methods{$meth_name};
189 452         1245 $meta->add_method($meth_name, $meth_code);
190             }
191              
192 132 100       5998 if ($am_role) {
    100          
193 62         187 my $info = $Moo::Role::INFO{$name};
194 62         169 $meta->add_required_methods(@{$info->{requires}});
  62         422  
195 62         1739 foreach my $modifier (@{$info->{modifiers}}) {
  62         233  
196 8         580 my ($type, @args) = @$modifier;
197 8         17 my $code = pop @args;
198 8         17 $meta->${\"add_${type}_method_modifier"}($_, $code) for @args;
  10         360  
199             }
200             }
201             elsif ($am_class) {
202 56         180 foreach my $attr (@attrs) {
203 80         138 foreach my $method (@{$attr->associated_methods}) {
  80         241  
204 72         1459 $method->{body} = $name->can($method->name);
205             }
206             }
207             bless(
208 56         398 $meta->find_method_by_name('new'),
209             'Moo::HandleMoose::FakeConstructor',
210             );
211 56         12478 my $meta_meth;
212 56 100 100     210 if (
213             $meta_meth = $meta->find_method_by_name('meta')
214             and $meta_meth->body == \&Moo::Object::meta
215             ) {
216 46         9639 bless($meta_meth, 'Moo::HandleMoose::FakeMeta');
217             }
218             # a combination of Moo and Moose may bypass a Moo constructor but still
219             # use a Moo DEMOLISHALL. We need to make sure this is loaded before
220             # global destruction.
221 56         18292 require Method::Generate::DemolishAll;
222             }
223             $meta->add_role(Class::MOP::class_of($_))
224 132   100     840 for grep !/\|/ && $_ ne $name, # reject Foo|Bar and same-role-as-self
225 132         1439 keys %{$Moo::Role::APPLIED_TO{$name}}
226             }
227 132         1603 $DID_INJECT{$name} = 1;
228 132         5383 $meta;
229             }
230              
231             1;