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   496227 use strict;
  82         367  
  82         2746  
3 82     82   510 use warnings;
  82         177  
  82         2429  
4 82     82   447 no warnings 'once';
  82         179  
  82         3673  
5              
6 82     82   4001 use Moo::_Utils qw(_getstash);
  82         200  
  82         4661  
7 82     82   34239 use Sub::Quote qw(quotify);
  82         379928  
  82         5886  
8 82     82   720 use Carp qw(croak);
  82         190  
  82         143019  
9              
10             our %TYPE_MAP;
11              
12             our $SETUP_DONE;
13              
14 90 100   90   182433 sub import { return if $SETUP_DONE; inject_all(); $SETUP_DONE = 1; }
  82         415  
  78         12464  
15              
16             sub inject_all {
17 82 100   82 0 750 croak "Can't inflate Moose metaclass with Moo::sification disabled"
18             if $Moo::sification::disabled;
19 78         5896 require Class::MOP;
20             inject_fake_metaclass_for($_)
21 78         1025674 for grep $_ ne 'Moo::Object', keys %Moo::MAKERS;
22 78         76096 inject_fake_metaclass_for($_) for keys %Moo::Role::INFO;
23 78         89215 require Moose::Meta::Method::Constructor;
24 78         60357 @Moo::HandleMoose::FakeConstructor::ISA = 'Moose::Meta::Method::Constructor';
25 78         2079 @Moo::HandleMoose::FakeMeta::ISA = 'Moose::Meta::Method::Meta';
26             }
27              
28             sub maybe_reinject_fake_metaclass_for {
29 178     178 0 473 my ($name) = @_;
30 178         305 our %DID_INJECT;
31 178 100       1940 if (delete $DID_INJECT{$name}) {
32 8 100       39 unless ($Moo::Role::INFO{$name}) {
33 4         17 Moo->_constructor_maker_for($name)->install_delayed;
34             }
35 8         33 inject_fake_metaclass_for($name);
36             }
37             }
38              
39             sub inject_fake_metaclass_for {
40 240     240 0 49281 my ($name) = @_;
41 240         1444 require Class::MOP;
42 240         35104 require Moo::HandleMoose::FakeMetaClass;
43 240         2319 Class::MOP::store_metaclass_by_name(
44             $name, bless({ name => $name }, 'Moo::HandleMoose::FakeMetaClass')
45             );
46 240         7872 require Moose::Util::TypeConstraints;
47 240 100       1047083 if ($Moo::Role::INFO{$name}) {
48 74         367 Moose::Util::TypeConstraints::find_or_create_does_type_constraint($name);
49             } else {
50 166         913 Moose::Util::TypeConstraints::find_or_create_isa_type_constraint($name);
51             }
52             }
53              
54             {
55             package Moo::HandleMoose::FakeConstructor;
56              
57 16     16   74657 sub _uninlined_body { \&Moose::Object::new }
58             }
59              
60             sub inject_real_metaclass_for {
61 252     252 0 2608 my ($name) = @_;
62 252         422 our %DID_INJECT;
63 252 100       1062 return Class::MOP::get_metaclass_by_name($name) if $DID_INJECT{$name};
64 138         6408 require Moose; require Moo; require Moo::Role; require Scalar::Util;
  138         2130727  
  138         16352  
  138         589  
65 138         520 require Sub::Defer;
66 138         823 Class::MOP::remove_metaclass_by_name($name);
67 138         1034 my ($am_role, $am_class, $meta, $attr_specs, $attr_order) = do {
68 138 100       1030 if (my $info = $Moo::Role::INFO{$name}) {
    100          
69 62 100       129 my @attr_info = @{$info->{attributes}||[]};
  62         428  
70 62         671 (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         331 my $specs = $cmaker->all_attribute_specs;
76             (0, 1, Moose::Meta::Class->initialize($name), $specs,
77 62         641 [ sort { $specs->{$a}{index} <=> $specs->{$b}{index} } keys %$specs ]
  60         23294  
78             );
79             } else {
80             # This codepath is used if $name does not exist in $Moo::MAKERS
81 14         116 (0, 0, Moose::Meta::Class->initialize($name), {}, [] )
82             }
83             };
84              
85             {
86 138         143929 local $DID_INJECT{$name} = 1;
  138         496  
87 138         653 foreach my $spec (values %$attr_specs) {
88 122 100       565 if (my $inflators = delete $spec->{moosify}) {
89 8         39 $_->($spec) for @$inflators;
90             }
91             }
92              
93             my %methods
94 138 100       1317 = %{($am_role ? 'Moo::Role' : 'Moo')->_concrete_methods_of($name)};
  138         1178  
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       4952 if (my $info = $Moo::Role::INFO{$name}) {
100 62         225 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         2212 for
106             grep $_ ne 'new',
107             keys %methods;
108 138         2591 my @attrs;
109             {
110             # This local is completely not required for roles but harmless
111 138         295 local @{_getstash($name)}{keys %methods};
  138         511  
  138         532  
112 138         377 my %seen_name;
113 138         508 foreach my $attr_name (@$attr_order) {
114 122         96884 $seen_name{$attr_name} = 1;
115 122         274 my %spec = %{$attr_specs->{$attr_name}};
  122         740  
116             my %spec_map = (
117 3544   66     12509 map { $_->name => $_->init_arg||$_->name }
118             (
119 3538         27954 (grep { $_->has_init_arg }
120             $meta->attribute_metaclass->meta->get_all_attributes),
121 8 100       186 grep { exists($_->{init_arg}) ? defined($_->init_arg) : 1 }
122             map {
123 6         38 my $meta = Moose::Util::resolve_metatrait_alias('Attribute', $_)
124             ->meta;
125 6         58903 map $meta->get_attribute($_), $meta->get_attribute_list
126 122 100       1748 } @{$spec{traits}||[]}
  122         1296  
127             )
128             );
129             # have to hard code this because Moose's role meta-model is lacking
130 122   100     909 $spec_map{traits} ||= 'traits';
131              
132 122 100 66     916 $spec{is} = 'ro' if $spec{is} eq 'lazy' or $spec{is} eq 'rwp';
133 122         350 my $coerce = $spec{coerce};
134 122 100       563 if (my $isa = $spec{isa}) {
    100          
135 22         67 my $tc = $spec{isa} = do {
136 22 100       90 if (my $mapped = $TYPE_MAP{$isa}) {
137 16         167 my $type = $mapped->();
138 16 100 66     1424 unless ( Scalar::Util::blessed($type)
139             && $type->isa("Moose::Meta::TypeConstraint") ) {
140 4         781 croak "error inflating attribute '$attr_name' for package '$name': "
141             ."\$TYPE_MAP{$isa} did not return a valid type constraint'";
142             }
143 12 100       281 $coerce ? $type->create_child_type(name => $type->name) : $type;
144             } else {
145             Moose::Meta::TypeConstraint->new(
146 25     25   235044 constraint => sub { eval { &$isa; 1 } }
  25         100  
  13         2256  
147 6         91 );
148             }
149             };
150 18 100       13035 if ($coerce) {
151 8         88 $tc->coercion(Moose::Meta::TypeCoercion->new)
152             ->_compiled_type_coercion($coerce);
153 8         4294 $spec{coerce} = 1;
154             }
155             } elsif ($coerce) {
156 2         12 my $attr = quotify($attr_name);
157             my $tc = Moose::Meta::TypeConstraint->new(
158 2     2   6305 constraint => sub { die "This is not going to work" },
159             inlined => sub {
160 10     10   18923 'my $r = $_[42]{'.$attr.'}; $_[42]{'.$attr.'} = 1; $r'
161             },
162 2         61 );
163 2         1107 $tc->coercion(Moose::Meta::TypeCoercion->new)
164             ->_compiled_type_coercion($coerce);
165 2         1225 $spec{isa} = $tc;
166 2         8 $spec{coerce} = 1;
167             }
168             %spec =
169 336         902 map { $spec_map{$_} => $spec{$_} }
170 118         426 grep { exists $spec_map{$_} }
  424         891  
171             keys %spec;
172 118         778 push @attrs, $meta->add_attribute($attr_name => %spec);
173             }
174 132 100       141971 foreach my $mouse (do { our %MOUSE; @{$MOUSE{$name}||[]} }) {
  132         283  
  132         289  
  132         1269  
175 4         43 foreach my $attr ($mouse->get_all_attributes) {
176 4         131 my %spec = %{$attr};
  4         35  
177 4         22 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       27 next if $seen_name{$attr_name}++;
183 2         14 push @attrs, $meta->add_attribute($attr_name => %spec);
184             }
185             }
186             }
187 132         5330 foreach my $meth_name (keys %methods) {
188 452         15204 my $meth_code = $methods{$meth_name};
189 452         1372 $meta->add_method($meth_name, $meth_code);
190             }
191              
192 132 100       4953 if ($am_role) {
    100          
193 62         181 my $info = $Moo::Role::INFO{$name};
194 62         169 $meta->add_required_methods(@{$info->{requires}});
  62         452  
195 62         1761 foreach my $modifier (@{$info->{modifiers}}) {
  62         231  
196 8         559 my ($type, @args) = @$modifier;
197 8         15 my $code = pop @args;
198 8         16 $meta->${\"add_${type}_method_modifier"}($_, $code) for @args;
  10         336  
199             }
200             }
201             elsif ($am_class) {
202 56         186 foreach my $attr (@attrs) {
203 80         150 foreach my $method (@{$attr->associated_methods}) {
  80         245  
204 72         1550 $method->{body} = $name->can($method->name);
205             }
206             }
207             bless(
208 56         371 $meta->find_method_by_name('new'),
209             'Moo::HandleMoose::FakeConstructor',
210             );
211 56         12691 my $meta_meth;
212 56 100 100     211 if (
213             $meta_meth = $meta->find_method_by_name('meta')
214             and $meta_meth->body == \&Moo::Object::meta
215             ) {
216 46         9745 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         19533 require Method::Generate::DemolishAll;
222             }
223             $meta->add_role(Class::MOP::class_of($_))
224 132   100     854 for grep !/\|/ && $_ ne $name, # reject Foo|Bar and same-role-as-self
225 132         1448 keys %{$Moo::Role::APPLIED_TO{$name}}
226             }
227 132         1529 $DID_INJECT{$name} = 1;
228 132         5543 $meta;
229             }
230              
231             1;