File Coverage

blib/lib/Mite/Trait/HasConstructor.pm
Criterion Covered Total %
statement 48 49 97.9
branch 8 10 80.0
condition n/a
subroutine 11 11 100.0
pod n/a
total 67 70 95.7


line stmt bran cond sub pod time code
1 109     109   2752 use 5.010001;
  109         433  
2 109     109   717 use strict;
  109         286  
  109         11926  
3 109     109   673 use warnings;
  109         319  
  109         5914  
4              
5             package Mite::Trait::HasConstructor;
6 109     109   827 use Mite::Miteception -role, -all;
  109         322  
  109         979  
7              
8             our $AUTHORITY = 'cpan:TOBYINK';
9             our $VERSION = '0.012000';
10              
11             requires qw(
12             linear_isa
13             _get_parent
14             _compile_meta
15             );
16              
17             around compilation_stages => sub {
18             my ( $next, $self ) = ( shift, shift );
19              
20             # Check if we are inheriting from a Mite class in this project
21             my $inherit_from_mite = do {
22             # First parent
23             my $first_isa = do {
24             my @isa = $self->linear_isa;
25             shift @isa;
26             shift @isa;
27             };
28             !! ( $first_isa and $self->_get_parent( $first_isa ) );
29             };
30              
31             my @stages = $self->$next( @_ );
32              
33             # Need a constructor if we're not inheriting from Mite,
34             # or if we define any new attributes.
35             push @stages, '_compile_new'
36             if !$inherit_from_mite
37             || keys %{ $self->attributes };
38              
39             # Only need these stages if not already inheriting from Mite
40             push @stages, qw(
41             _compile_buildall_method
42             ) unless $inherit_from_mite;
43              
44             return @stages;
45             };
46              
47             sub _compile_new {
48 104     104   346 my $self = shift;
49 104         549 my @vars = ('$class', '$self', '$args', '$meta');
50              
51 104         750 return sprintf <<'CODE', $self->_compile_meta(@vars), $self->_compile_bless(@vars), $self->_compile_buildargs(@vars), $self->_compile_init_attributes(@vars), $self->_compile_buildall(@vars, '$no_build'), $self->_compile_strict_constructor(@vars);
52             # Standard Moose/Moo-style constructor
53             sub new {
54             my $class = ref($_[0]) ? ref(shift) : shift;
55             my $meta = %s;
56             my $self = %s;
57             my $args = %s;
58             my $no_build = delete $args->{__no_BUILD__};
59              
60             %s
61              
62             # Call BUILD methods
63             %s
64              
65             # Unrecognized parameters
66             %s
67              
68             return $self;
69             }
70             CODE
71             }
72              
73             sub _compile_bless {
74 104     104   494 my ( $self, $classvar, $selfvar, $argvar, $metavar ) = @_;
75              
76 104         357 my $simple_bless = "bless {}, $classvar";
77              
78             # Force parents to be loaded
79 104         708 $self->parents;
80              
81             # First parent with &new
82 104         305 my ( $first_isa ) = do {
83 104         587 my @isa = $self->linear_isa;
84 104         390 shift @isa;
85 109     109   927 no strict 'refs';
  109         2428  
  109         59439  
86 104         465 grep +(defined &{$_.'::new'}), @isa;
  22         216  
87             };
88              
89             # If we're not inheriting from anything with a constructor: simple case
90 104 100       975 $first_isa or return $simple_bless;
91              
92             # Inheriting from a Mite class in this project: simple case
93 2 50       9 my $first_parent = $self->_get_parent( $first_isa )
94             and return $simple_bless;
95              
96             # Inheriting from a Moose/Moo/Mite/Class::Tiny class:
97             # call buildargs
98             # set $args->{__no_BUILD__}
99             # call parent class constructor
100 2 100       51 if ( $first_isa->can( 'BUILDALL' ) ) {
101 1         15 return sprintf 'do { my %s = %s; %s->{__no_BUILD__} = 1; %s->SUPER::new( %s ) }',
102             $argvar, $self->_compile_buildargs($classvar, $selfvar, $argvar, $metavar), $argvar, $classvar, $argvar;
103             }
104              
105             # Inheriting from some random class
106             # call FOREIGNBUILDARGS if it exists
107             # pass return value or @_ to parent class constructor
108 1         11 return sprintf '%s->SUPER::new( %s->{HAS_FOREIGNBUILDARGS} ? %s->FOREIGNBUILDARGS( @_ ) : @_ )',
109             $classvar, $metavar, $classvar;
110             }
111              
112             sub _compile_buildargs {
113 105     105   453 my ( $self, $classvar, $selfvar, $argvar, $metavar ) = @_;
114 105         960 return sprintf '%s->{HAS_BUILDARGS} ? %s->BUILDARGS( @_ ) : { ( @_ == 1 ) ? %%{$_[0]} : @_ }',
115             $metavar, $classvar;
116             }
117              
118             sub _compile_strict_constructor {
119 104     104   434 my ( $self, $classvar, $selfvar, $argvar, $metavar ) = @_;
120              
121             my @allowed =
122 132         634 grep { defined $_ }
123 129         544 map { ( $_->init_arg, $_->_all_aliases ) }
124 104         319 values %{ $self->all_attributes };
  104         2832  
125 104         347 my $check = do {
126 104         469 local $Type::Tiny::AvoidCallbacks = 1;
127 104         732 my $enum = Enum->of( @allowed );
128 104 100       135256 $enum->can( '_regexp' ) # not part of official API
129             ? sprintf( '/\\A%s\\z/', $enum->_regexp )
130             : $enum->inline_check( '$_' );
131             };
132              
133 104         30931 my $code = sprintf 'my @unknown = grep not( %s ), keys %%{%s}; @unknown and %s( "Unexpected keys in constructor: " . join( q[, ], sort @unknown ) );',
134             $check, $argvar, $self->_function_for_croak;
135 104 50       951 if ( my $autolax = $self->autolax ) {
136 0         0 $code = "if ( $autolax ) { $code }";
137             }
138 104         5425 return $code;
139             }
140              
141             sub _compile_buildall {
142 104     104   598 my ( $self, $classvar, $selfvar, $argvar, $metavar, $nobuildvar ) = @_;
143 104         984 return sprintf '%s->BUILDALL( %s ) if ( ! %s and @{ %s->{BUILD} || [] } );',
144             $selfvar, $argvar, $nobuildvar, $metavar;
145             }
146              
147             sub _compile_buildall_method {
148 90     90   299 my $self = shift;
149              
150 90         576 return sprintf <<'CODE', $self->_compile_meta( '$class', '$_[0]', '$_[1]', '$meta' ),
151             # Used by constructor to call BUILD methods
152             sub BUILDALL {
153             my $class = ref( $_[0] );
154             my $meta = %s;
155             $_->( @_ ) for @{ $meta->{BUILD} || [] };
156             }
157             CODE
158             }
159              
160             1;