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   431168 use strict;
  82         321  
  82         2490  
3 82     82   422 use warnings;
  82         229  
  82         2326  
4 82     82   393 no warnings 'once';
  82         157  
  82         3190  
5              
6 82     82   3287 use Moo::_Utils qw(_getstash);
  82         162  
  82         4309  
7 82     82   33041 use Sub::Quote qw(quotify);
  82         344364  
  82         5377  
8 82     82   692 use Carp qw(croak);
  82         194  
  82         129815  
9              
10             our %TYPE_MAP;
11              
12             our $SETUP_DONE;
13              
14 90 100   90   181706 sub import { return if $SETUP_DONE; inject_all(); $SETUP_DONE = 1; }
  82         315  
  78         11359  
15              
16             sub inject_all {
17 82 100   82 0 684 croak "Can't inflate Moose metaclass with Moo::sification disabled"
18             if $Moo::sification::disabled;
19 78         5284 require Class::MOP;
20             inject_fake_metaclass_for($_)
21 78         911520 for grep $_ ne 'Moo::Object', keys %Moo::MAKERS;
22 78         68794 inject_fake_metaclass_for($_) for keys %Moo::Role::INFO;
23 78         82599 require Moose::Meta::Method::Constructor;
24 78         51507 @Moo::HandleMoose::FakeConstructor::ISA = 'Moose::Meta::Method::Constructor';
25 78         1859 @Moo::HandleMoose::FakeMeta::ISA = 'Moose::Meta::Method::Meta';
26             }
27              
28             sub maybe_reinject_fake_metaclass_for {
29 178     178 0 438 my ($name) = @_;
30 178         293 our %DID_INJECT;
31 178 100       2055 if (delete $DID_INJECT{$name}) {
32 8 100       30 unless ($Moo::Role::INFO{$name}) {
33 4         15 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 43351 my ($name) = @_;
41 240         1371 require Class::MOP;
42 240         31404 require Moo::HandleMoose::FakeMetaClass;
43 240         2125 Class::MOP::store_metaclass_by_name(
44             $name, bless({ name => $name }, 'Moo::HandleMoose::FakeMetaClass')
45             );
46 240         7388 require Moose::Util::TypeConstraints;
47 240 100       943599 if ($Moo::Role::INFO{$name}) {
48 74         349 Moose::Util::TypeConstraints::find_or_create_does_type_constraint($name);
49             } else {
50 166         847 Moose::Util::TypeConstraints::find_or_create_isa_type_constraint($name);
51             }
52             }
53              
54             {
55             package Moo::HandleMoose::FakeConstructor;
56              
57 16     16   64033 sub _uninlined_body { \&Moose::Object::new }
58             }
59              
60             sub inject_real_metaclass_for {
61 252     252 0 2647 my ($name) = @_;
62 252         382 our %DID_INJECT;
63 252 100       972 return Class::MOP::get_metaclass_by_name($name) if $DID_INJECT{$name};
64 138         5448 require Moose; require Moo; require Moo::Role; require Scalar::Util;
  138         1767024  
  138         15674  
  138         544  
65 138         465 require Sub::Defer;
66 138         777 Class::MOP::remove_metaclass_by_name($name);
67 138         930 my ($am_role, $am_class, $meta, $attr_specs, $attr_order) = do {
68 138 100       944 if (my $info = $Moo::Role::INFO{$name}) {
    100          
69 62 100       130 my @attr_info = @{$info->{attributes}||[]};
  62         439  
70 62         638 (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         292 my $specs = $cmaker->all_attribute_specs;
76             (0, 1, Moose::Meta::Class->initialize($name), $specs,
77 62         643 [ sort { $specs->{$a}{index} <=> $specs->{$b}{index} } keys %$specs ]
  56         20943  
78             );
79             } else {
80             # This codepath is used if $name does not exist in $Moo::MAKERS
81 14         96 (0, 0, Moose::Meta::Class->initialize($name), {}, [] )
82             }
83             };
84              
85             {
86 138         128995 local $DID_INJECT{$name} = 1;
  138         460  
87 138         635 foreach my $spec (values %$attr_specs) {
88 122 100       515 if (my $inflators = delete $spec->{moosify}) {
89 8         40 $_->($spec) for @$inflators;
90             }
91             }
92              
93             my %methods
94 138 100       1244 = %{($am_role ? 'Moo::Role' : 'Moo')->_concrete_methods_of($name)};
  138         1155  
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       5847 if (my $info = $Moo::Role::INFO{$name}) {
100 62         217 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         1045 for
106             grep $_ ne 'new',
107             keys %methods;
108 138         2216 my @attrs;
109             {
110             # This local is completely not required for roles but harmless
111 138         266 local @{_getstash($name)}{keys %methods};
  138         396  
  138         543  
112 138         310 my %seen_name;
113 138         441 foreach my $attr_name (@$attr_order) {
114 122         94746 $seen_name{$attr_name} = 1;
115 122         286 my %spec = %{$attr_specs->{$attr_name}};
  122         740  
116             my %spec_map = (
117 3544   66     11751 map { $_->name => $_->init_arg||$_->name }
118             (
119 3538         25490 (grep { $_->has_init_arg }
120             $meta->attribute_metaclass->meta->get_all_attributes),
121 8 100       180 grep { exists($_->{init_arg}) ? defined($_->init_arg) : 1 }
122             map {
123 6         36 my $meta = Moose::Util::resolve_metatrait_alias('Attribute', $_)
124             ->meta;
125 6         54569 map $meta->get_attribute($_), $meta->get_attribute_list
126 122 100       1594 } @{$spec{traits}||[]}
  122         1273  
127             )
128             );
129             # have to hard code this because Moose's role meta-model is lacking
130 122   100     830 $spec_map{traits} ||= 'traits';
131              
132 122 100 66     1270 $spec{is} = 'ro' if $spec{is} eq 'lazy' or $spec{is} eq 'rwp';
133 122         303 my $coerce = $spec{coerce};
134 122 100       559 if (my $isa = $spec{isa}) {
    100          
135 22         64 my $tc = $spec{isa} = do {
136 22 100       91 if (my $mapped = $TYPE_MAP{$isa}) {
137 16         154 my $type = $mapped->();
138 16 100 66     1370 unless ( Scalar::Util::blessed($type)
139             && $type->isa("Moose::Meta::TypeConstraint") ) {
140 4         622 croak "error inflating attribute '$attr_name' for package '$name': "
141             ."\$TYPE_MAP{$isa} did not return a valid type constraint'";
142             }
143 12 100       286 $coerce ? $type->create_child_type(name => $type->name) : $type;
144             } else {
145             Moose::Meta::TypeConstraint->new(
146 23     23   211064 constraint => sub { eval { &$isa; 1 } }
  23         82  
  11         2046  
147 6         79 );
148             }
149             };
150 18 100       13079 if ($coerce) {
151 8         92 $tc->coercion(Moose::Meta::TypeCoercion->new)
152             ->_compiled_type_coercion($coerce);
153 8         4199 $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   6752 constraint => sub { die "This is not going to work" },
159             inlined => sub {
160 10     10   16692 'my $r = $_[42]{'.$attr.'}; $_[42]{'.$attr.'} = 1; $r'
161             },
162 2         53 );
163 2         1033 $tc->coercion(Moose::Meta::TypeCoercion->new)
164             ->_compiled_type_coercion($coerce);
165 2         1120 $spec{isa} = $tc;
166 2         7 $spec{coerce} = 1;
167             }
168             %spec =
169 336         816 map { $spec_map{$_} => $spec{$_} }
170 118         429 grep { exists $spec_map{$_} }
  424         901  
171             keys %spec;
172 118         739 push @attrs, $meta->add_attribute($attr_name => %spec);
173             }
174 132 100       130109 foreach my $mouse (do { our %MOUSE; @{$MOUSE{$name}||[]} }) {
  132         287  
  132         281  
  132         1248  
175 4         46 foreach my $attr ($mouse->get_all_attributes) {
176 4         524 my %spec = %{$attr};
  4         40  
177 4         32 delete @spec{qw(
178             associated_class associated_methods __METACLASS__
179             provides curries
180             )};
181 4         13 my $attr_name = delete $spec{name};
182 4 100       29 next if $seen_name{$attr_name}++;
183 2         13 push @attrs, $meta->add_attribute($attr_name => %spec);
184             }
185             }
186             }
187 132         5082 foreach my $meth_name (keys %methods) {
188 452         14026 my $meth_code = $methods{$meth_name};
189 452         1215 $meta->add_method($meth_name, $meth_code);
190             }
191              
192 132 100       4697 if ($am_role) {
    100          
193 62         199 my $info = $Moo::Role::INFO{$name};
194 62         178 $meta->add_required_methods(@{$info->{requires}});
  62         761  
195 62         1570 foreach my $modifier (@{$info->{modifiers}}) {
  62         251  
196 8         531 my ($type, @args) = @$modifier;
197 8         12 my $code = pop @args;
198 8         17 $meta->${\"add_${type}_method_modifier"}($_, $code) for @args;
  10         330  
199             }
200             }
201             elsif ($am_class) {
202 56         156 foreach my $attr (@attrs) {
203 80         133 foreach my $method (@{$attr->associated_methods}) {
  80         232  
204 72         1556 $method->{body} = $name->can($method->name);
205             }
206             }
207             bless(
208 56         397 $meta->find_method_by_name('new'),
209             'Moo::HandleMoose::FakeConstructor',
210             );
211 56         11771 my $meta_meth;
212 56 100 100     191 if (
213             $meta_meth = $meta->find_method_by_name('meta')
214             and $meta_meth->body == \&Moo::Object::meta
215             ) {
216 46         8765 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         18695 require Method::Generate::DemolishAll;
222             }
223             $meta->add_role(Class::MOP::class_of($_))
224 132   100     782 for grep !/\|/ && $_ ne $name, # reject Foo|Bar and same-role-as-self
225 132         1347 keys %{$Moo::Role::APPLIED_TO{$name}}
226             }
227 132         1516 $DID_INJECT{$name} = 1;
228 132         5970 $meta;
229             }
230              
231             1;