File Coverage

blib/lib/App/Sqitch/X.pm
Criterion Covered Total %
statement 29 29 100.0
branch 4 4 100.0
condition n/a
subroutine 10 10 100.0
pod 2 2 100.0
total 45 45 100.0


line stmt bran cond sub pod time code
1             package App::Sqitch::X;
2              
3 55     55   228016 use 5.010;
  55         258  
4 55     55   367 use utf8;
  55         175  
  55         497  
5 55     55   31148 use Moo;
  55         471605  
  55         324  
6 55     55   111978 use Types::Standard qw(Str Int);
  55         4010648  
  55         635  
7 55     55   82033 use Sub::Exporter::Util ();
  55         562669  
  55         1676  
8 55     55   26777 use Throwable 0.200009;
  55         1410467  
  55         2758  
9 55     55   532 use Sub::Exporter -setup => [qw(hurl)];
  55         139  
  55         458  
10 55     55   26468 use overload '""' => 'as_string';
  55         154  
  55         428  
11              
12             our $VERSION = 'v1.4.0'; # VERSION
13              
14             has message => (
15             is => 'ro',
16             isa => Str,
17             required => 1,
18             );
19              
20             has exitval => (
21             is => 'ro',
22             isa => Int,
23             default => 2,
24             );
25              
26              
27             with qw(
28             Throwable
29             StackTrace::Auto
30             );
31              
32             has ident => (
33             is => 'ro',
34             isa => Str,
35             default => 'DEV'
36             );
37              
38             has '+previous_exception' => (init_arg => 'previous_exception')
39             if Throwable->VERSION < 0.200007;
40              
41             sub hurl {
42             @_ = (
43             __PACKAGE__,
44             # Always pass $@, as Throwable is unreliable about getting it thanks
45             # to hash randomization. Its workaround in v0.200006:
46             # https://github.com/rjbs/throwable/commit/596dfbafed970a30324dc21539d4edf2cbda767a
47             previous_exception => $@,
48 483 100   483 1 80361 ref $_[0] ? %{ $_[0] }
  34 100       270  
49             : @_ == 1 ? (message => $_[0])
50             : (ident => $_[0], message => $_[1])
51             );
52 483         5755 goto __PACKAGE__->can('throw');
53             }
54              
55             sub as_string {
56 54     54 1 109556 my $self = shift;
57 54         1274 join "\n", grep { defined } (
  162         999  
58             $self->message,
59             $self->previous_exception,
60             $self->stack_trace
61             );
62             }
63              
64             1;
65              
66             __END__
67              
68             =head1 Name
69              
70             App::Sqitch::X - Sqitch Exception class
71              
72             =head1 Synopsis
73              
74             use Locale::TextDomain;
75             use App::Sqitch::X qw(hurl);
76             open my $fh, '>', 'foo.txt' or hurl {
77             ident => 'io',
78             message => __x 'Cannot open {file}: {err}", file => 'foo.txt', err => $!,
79             };
80              
81             Developer:
82              
83             hurl 'Odd number of arguments passed to burf()' if @_ % 2;
84              
85             =head1 Description
86              
87             This module provides implements Sqitch exceptions. Exceptions may be thrown by
88             any part of the code, and, as long as a command is running, they will be
89             handled, showing the error message to the user.
90              
91             =head1 Interface
92              
93             =head2 Function
94              
95             =head3 C<hurl>
96              
97             Throws an exception. Pass the parameters as a hash reference, like so:
98              
99             use App::Sqitch::X qw(hurl);
100             open my $fh, '>', 'foo.txt' or hurl {
101             ident => 'io',
102             message => __x 'Cannot open {file}: {err}", file => 'foo.txt', err => $!,
103             };
104              
105             More simply, if all you need to pass are the C<ident> and C<message>
106             parameters, you can pass them as the only arguments to C<hurl()>:
107              
108             open my $fh, '>', 'foo.txt'
109             or hurl io => __x 'Cannot open {file}: {err}", file => 'foo.txt', err => $!
110              
111             For errors that should only happen during development (e.g., an invalid
112             parameter passed by some other library that should know better), you can omit
113             the C<ident>:
114              
115             hurl 'Odd number of arguments passed to burf()' if @_ % 2;
116              
117             In this case, the C<ident> will be C<DEV>, which you should not otherwise use.
118             Sqitch will emit a more detailed error message, including a stack trace, when
119             it sees C<DEV> exceptions.
120              
121             The supported parameters are:
122              
123             =over
124              
125             =item C<ident>
126              
127             A non-localized string identifying the type of exception.
128              
129             =item C<message>
130              
131             The exception message. Use L<Locale::TextDomain> to craft localized messages.
132              
133             =item C<exitval>
134              
135             Suggested exit value to use. Defaults to 2. This will be used if Sqitch
136             handles an exception while a command is running.
137              
138             =back
139              
140             =head2 Methods
141              
142             =head3 C<as_string>
143              
144             my $errstr = $x->as_string;
145              
146             Returns the stringified representation of the exception. This value is also
147             used for string overloading of the exception object, which means it is the
148             output shown for uncaught exceptions. Its contents are the concatenation of
149             the exception message, the previous exception (if any), and the stack trace.
150              
151             =head1 Handling Exceptions
152              
153             use L<Try::Tiny> to do exception handling, like so:
154              
155             use Try::Tiny;
156             try {
157             # ...
158             } catch {
159             die $_ unless eval { $_->isa('App::Sqitch::X') };
160             $sqitch->vent($x_->message);
161             if ($_->ident eq 'DEV') {
162             $sqitch->vent($_->stack_trace->as_string);
163             } else {
164             $sqitch->debug($_->stack_trace->as_string);
165             }
166             exit $_->exitval;
167             };
168              
169             Use the C<ident> attribute to determine what category of exception it is, and
170             take changes as appropriate.
171              
172             =head1 Author
173              
174             David E. Wheeler <david@justatheory.com>
175              
176             =head1 License
177              
178             Copyright (c) 2012-2023 iovation Inc., David E. Wheeler
179              
180             Permission is hereby granted, free of charge, to any person obtaining a copy
181             of this software and associated documentation files (the "Software"), to deal
182             in the Software without restriction, including without limitation the rights
183             to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
184             copies of the Software, and to permit persons to whom the Software is
185             furnished to do so, subject to the following conditions:
186              
187             The above copyright notice and this permission notice shall be included in all
188             copies or substantial portions of the Software.
189              
190             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
191             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
192             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
193             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
194             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
195             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
196             SOFTWARE.
197              
198             =cut
199