File Coverage

blib/lib/Call/Context.pm
Criterion Covered Total %
statement 24 24 100.0
branch 8 8 100.0
condition 2 3 66.6
subroutine 8 8 100.0
pod 2 2 100.0
total 44 45 97.7


line stmt bran cond sub pod time code
1             package Call::Context;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Call::Context - Sanity-check calling context
8              
9             =head1 SYNOPSIS
10              
11             use Call::Context;
12              
13             sub gives_a_list {
14              
15             # Will die() if the context is not list.
16             Call::Context::must_be_list();
17              
18             return (1, 2, 3);
19             }
20              
21             gives_a_list(); # die()s: incorrect context (void)
22              
23             my $v = gives_a_list(); # die()s: incorrect context (scalar)
24              
25             my @list = gives_a_list(); # lives
26              
27             #----------------------------------------------------------------------
28              
29             sub scalar_is_bad {
30              
31             # Will die() if the context is scalar.
32             Call::Context::must_not_be_scalar();
33              
34             return (1, 2, 3);
35             }
36              
37             scalar_is_bad(); # die()s: incorrect context (void)
38              
39             my $v = scalar_is_bad(); # die()s: incorrect context (scalar)
40              
41             my @list = scalar_is_bad(); # lives
42              
43             =head1 DESCRIPTION
44              
45             If your function only expects to return a list, then a call in some other
46             context is, by definition, an error. The problem is that, depending on how
47             the function is written, it may actually do something expected in testing, but
48             then in production act differently.
49              
50             =head1 FUNCTIONS
51              
52             =head2 must_be_list()
53              
54             Cs if the calling function is itself called outside list context.
55             (See the SYNOPSIS for examples.)
56              
57             =head2 must_not_be_scalar()
58              
59             Cs if the calling function is itself called in scalar context.
60             (See the SYNOPSIS for examples.)
61              
62             =head1 EXCEPTIONS
63              
64             This module throws instances of C. C is
65             overloaded to stringify; however, to keep memory usage low, C is not
66             loaded until instantiation.
67              
68             =head1 REPOSITORY
69              
70             https://github.com/FGasper/p5-Call-Context
71              
72             =head1 LICENSE
73              
74             This module is licensed under the MIT License.
75              
76             =cut
77              
78 3     3   376525 use strict;
  3         6  
  3         127  
79 3     3   30 use warnings;
  3         6  
  3         1465  
80              
81             our $VERSION = '0.05';
82              
83             my $_OVERLOADED_X;
84              
85             sub must_be_list {
86 3     3 1 244620 return _must_be_list(0);
87             }
88              
89             sub must_not_be_scalar {
90 3 100   3 1 226796 return if !defined( (caller 1)[5] );
91 2         5 return _must_be_list(1);
92             }
93              
94             sub _must_be_list {
95 5 100   5   32 return if (caller 2)[5]; #wantarray
96              
97 3   66 2   254 $_OVERLOADED_X ||= eval q{
  2         17  
  2         5  
  2         15  
98             package Call::Context::X;
99             use overload ( q<""> => \\&_spew );
100             1;
101             };
102              
103 3         11 die Call::Context::X->_new($_[0]);
104             }
105              
106             #----------------------------------------------------------------------
107              
108             package Call::Context::X;
109              
110             #Not to be instantiated except from Call::Context!
111              
112             sub _new {
113 3     3   10 my ($class, $accept_void_yn) = @_;
114              
115 3         16 my ($sub, $ctx) = (caller 3)[3, 5];
116 3         35 my (undef, $cfilename, $cline, $csub) = caller 4;
117              
118 3 100       11 if ($accept_void_yn) {
119 1         32 return bless \"$sub called in scalar context from $csub (line $cline of $cfilename)", $class;
120             }
121              
122 2 100       8 $ctx = defined($ctx) ? 'scalar' : 'void';
123              
124 2         22 return bless \"$sub called in non-list ($ctx) context from $csub (line $cline of $cfilename)", $class;
125             }
126              
127 7     7   1142 sub _spew { ${ $_[0] } }
  7         128  
128              
129             1;