File Coverage

blib/lib/Mooish/Caller/Util.pm
Criterion Covered Total %
statement 77 92 83.7
branch 45 62 72.5
condition 12 23 52.1
subroutine 8 8 100.0
pod 2 2 100.0
total 144 187 77.0


line stmt bran cond sub pod time code
1             package Mooish::Caller::Util;
2              
3             our $DATE = '2015-07-30'; # DATE
4             our $VERSION = '0.06'; # VERSION
5              
6 1     1   1794 use 5.010001;
  1         4  
7 1     1   6 use warnings;
  1         1  
  1         32  
8 1     1   4 use strict;
  1         2  
  1         24  
9              
10 1     1   4 use Exporter qw(import);
  1         2  
  1         54  
11             our @EXPORT_OK = qw(get_constructor_caller
12             get_constructor_callers);
13              
14             sub _get_constructor_caller_or_callers {
15 1     1   4 no strict 'refs';
  1         1  
  1         765  
16              
17 26     26   47 my $routine = shift;
18              
19 26   100     116 my $start = $_[0] // 0;
20 26         30 my $with_args = $_[1];
21              
22 26         35 my @res;
23              
24             my $objsys;
25 0         0 my $is_build;
26 0         0 my $wrappers_done;
27              
28 26         29 my $i = 0;
29 26         25 my $j = 0;
30 26         26 my $skips = 0;
31 26         30 while (1) {
32 165         194 $i++;
33 165         161 my @caller;
34 165 50       246 if ($with_args) {
35             {
36 0         0 package DB;
37 0         0 @caller = caller($i);
38 0 0       0 $caller[11] = [@DB::args] if @caller;
39             }
40             } else {
41 165         367 @caller = caller($i);
42             }
43 165 100       2618 last unless @caller;
44              
45 155 100       267 if ($i == 1) {
46 26         27 my $subpkg;
47 26 100       123 if ($caller[3] =~ /(.+)::BUILD(ARGS)?\z/) {
48 24         50 $subpkg = $1;
49 24         47 $is_build = !$2;
50             } else {
51 2         25 die "$routine(): Not called directly inside BUILD/BUILDARGS";
52             }
53              
54 24 100       43 if ($is_build) {
55 14 50 33     88 if ($caller[0] eq 'Method::Generate::BuildAll' ||
    100          
    100          
    100          
56             $caller[0] eq 'Method::Generate::Constructor') {
57 0         0 $objsys = 'Moo';
58 0         0 next;
59             } elsif ($caller[0] eq 'Mo::build') {
60 4         5 $objsys = 'Mo';
61 4         9 next;
62             } elsif ($caller[0] eq 'Class::MOP::Method') {
63 4         5 $objsys = 'Moose';
64 4         10 next;
65 4         20 } elsif (grep {$_ eq "Mouse::Object"} @{"$subpkg\::ISA"}) {
  6         31  
66 4         7 $objsys = 'Mouse';
67 4         13 next;
68             } else {
69 2         24 die "$routine(): Unknown object system ".
70             "(only Mo/Moo/Moose/Mouse supported for BUILD)";
71             }
72             } else { # BUILDARGS
73 10 50       29 if ($caller[0] eq 'Moo::Object') {
    100          
    100          
74 0         0 $objsys = 'Moo';
75 0         0 next;
76             } elsif ($caller[0] eq 'Moose::Object') {
77 4         6 $objsys = 'Moose';
78 4         10 next;
79 4         14 } elsif (grep {$_ eq "Mouse::Object"} @{"$subpkg\::ISA"}) {
  6         21  
80 4         6 $objsys = 'Mouse';
81 4         11 next;
82             } else {
83 2         23 die "$routine(): Unknown object system ".
84             "(only Moo/Moose/Mouse supported for BUILDARGS)";
85             }
86             }
87             } else {
88 129 100       235 unless ($wrappers_done) {
89 44 100       69 if ($is_build) {
90 32 100       84 if ($objsys eq 'Mo') {
    50          
    100          
91 8 100       20 next if $caller[3] eq 'Mo::build::__ANON__';
92 4         5 $wrappers_done++;
93             } elsif ($objsys eq 'Moo') {
94 0 0 0     0 next if $caller[0] eq 'Moo::Object' ||
95             $caller[3] eq 'Moo::Object::new';
96 0         0 $wrappers_done++;
97             } elsif ($objsys eq 'Moose') {
98 20 100 100     99 next if $caller[0] eq 'Moose::Object' ||
      100        
99             $caller[0] eq 'Moose::Meta::Class' ||
100             $caller[3] eq 'Moose::Object::new';
101 4         5 $wrappers_done++;
102             } else { # Mouse
103 4 50       14 if ($] < 5.014) {
104 0 0 0     0 next if $skips++ < 1 && $caller[3] =~ /eval/;
105             }
106 4         9 $wrappers_done++;
107             }
108             } else { # BUILDARGS
109 12 50       30 if ($objsys eq 'Moo') {
    100          
110 0 0       0 next if $caller[3] eq 'Moo::Object::new';
111 0         0 $wrappers_done++;
112             } elsif ($objsys eq 'Moose') {
113 8 100       21 next if $caller[3] eq 'Moose::Object::new';
114 4         6 $wrappers_done++;
115             } else { # Mouse
116 4 50       9 if ($] < 5.014) {
117 0 0 0     0 next if $skips++ < 1 && $caller[3] =~ /eval/;
118             }
119 4         6 $wrappers_done++;
120             }
121             }
122             }
123             }
124              
125 105         125 $j++;
126 105         135 push @res, \@caller;
127 105 100 100     330 last if $routine eq 'get_constructor_caller' && $j > $start;
128             }
129              
130 20 100       39 if ($routine eq 'get_constructor_caller') {
131 10         155 return $res[$start];
132             } else {
133 10         14 splice(@res, 0, $start);
134 10         167 return @res;
135             }
136             }
137              
138             sub get_constructor_caller {
139 13     13 1 597212 unshift @_, "get_constructor_caller";
140 13         40 goto &_get_constructor_caller_or_callers;
141             }
142              
143             sub get_constructor_callers {
144 13     13 1 1598 unshift @_, "get_constructor_callers";
145 13         31 goto &_get_constructor_caller_or_callers;
146             }
147              
148             1;
149             # ABSTRACT: Get constructor caller from inside Mo/Moo/Moose/Mouse's BUILD/BUILDARGS
150              
151             __END__
152              
153             =pod
154              
155             =encoding UTF-8
156              
157             =head1 NAME
158              
159             Mooish::Caller::Util - Get constructor caller from inside Mo/Moo/Moose/Mouse's BUILD/BUILDARGS
160              
161             =head1 VERSION
162              
163             This document describes version 0.06 of Mooish::Caller::Util (from Perl distribution Mooish-Caller-Util), released on 2015-07-30.
164              
165             =head1 SYNOPSIS
166              
167             package MyClass;
168             use Moo; # or Mo 'build', or Moose, or Mouse
169             use Mooish::Util::Caller qw(get_constructor_caller get_constructor_callers);
170              
171             sub BUILD { # or BUILDARGS
172             $caller = get_constructor_caller();
173             say $caller->[3]; # subroutine name
174             }
175              
176             package main;
177             sub f1 { MyClass->new }
178             sub f2 { f1 }
179             f2; # prints 'main::f1'
180              
181             =head1 FUNCTIONS
182              
183             =head2 get_constructor_caller([ $start=0 [, $with_args] ]) => ARRAYREF
184              
185             Like C<[caller($start)]>, but skips Mo/Moo/Moose/Mouse wrappers. Result will be
186             like:
187              
188             # 0 1 2 3 4 5 6 7 8 9 10
189             [$package1, $filename1, $line1, $subroutine1, $hasargs1, $wantarray1, $evaltext1, $is_require1, $hints1, $bitmask1, $hinthash1],
190              
191             If C<$with_args> is true, will also return subroutine arguments in the 11th
192             element, produced by retrieving C<@DB::args>.
193              
194             =head2 get_constructor_callers([ $start=0 [, $with_args] ]) => LIST
195              
196             A convenience function to return the whole callers stack, akin to what is
197             produced by collecting result from C<get_constructor_caller($start+1)> up until
198             the last frame in caller stack. Result will be like:
199              
200             (
201             # for frame 0
202             # 0 1 2 3 4 5 6 7 8 9 10
203             [$package1, $filename1, $line1, $subroutine1, $hasargs1, $wantarray1, $evaltext1, $is_require1, $hints1, $bitmask1, $hinthash1],
204              
205             # for next frame
206             [$package2, $filename2, $line2, ...]
207              
208             ...
209             )
210              
211             If C<$with_args> is true, will also return subroutine arguments in the 11th
212             element for each frame, produced by retrieving C<@DB::args>.
213              
214             =head1 SEE ALSO
215              
216             =head1 HOMEPAGE
217              
218             Please visit the project's homepage at L<https://metacpan.org/release/Mooish-Caller-Util>.
219              
220             =head1 SOURCE
221              
222             Source repository is at L<https://github.com/perlancar/perl-Mooish-Caller-Util>.
223              
224             =head1 BUGS
225              
226             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Mooish-Caller-Util>
227              
228             When submitting a bug or request, please include a test-file or a
229             patch to an existing test-file that illustrates the bug or desired
230             feature.
231              
232             =head1 AUTHOR
233              
234             perlancar <perlancar@cpan.org>
235              
236             =head1 COPYRIGHT AND LICENSE
237              
238             This software is copyright (c) 2015 by perlancar@cpan.org.
239              
240             This is free software; you can redistribute it and/or modify it under
241             the same terms as the Perl 5 programming language system itself.
242              
243             =cut