File Coverage

blib/lib/Trickster/Exception.pm
Criterion Covered Total %
statement 55 72 76.3
branch 2 4 50.0
condition 5 19 26.3
subroutine 21 26 80.7
pod 0 9 0.0
total 83 130 63.8


line stmt bran cond sub pod time code
1             package Trickster::Exception;
2              
3 7     7   409699 use strict;
  7         19  
  7         275  
4 7     7   36 use warnings;
  7         12  
  7         373  
5 7     7   110 use v5.14;
  7         24  
6              
7 7     7   66 use overload '""' => 'as_string', fallback => 1;
  7         26  
  7         53  
8              
9             sub new {
10 4     4 0 770 my ($class, %args) = @_;
11            
12             return bless {
13             message => $args{message} || 'An error occurred',
14             status => $args{status} || 500,
15             details => $args{details},
16 4   50     37 stack_trace => $args{stack_trace} || _get_stack_trace(),
      50        
      33        
17             timestamp => time,
18             }, $class;
19             }
20              
21             sub throw {
22 3     3 0 2395 my ($class, @args) = @_;
23 3         12 die $class->new(@args);
24             }
25              
26 2     2 0 43 sub message { shift->{message} }
27 4     4 0 942 sub status { shift->{status} }
28 1     1 0 8 sub details { shift->{details} }
29 1     1 0 6 sub stack_trace { shift->{stack_trace} }
30 0     0 0 0 sub timestamp { shift->{timestamp} }
31              
32             sub as_string {
33 4     4 0 12 my ($self) = @_;
34 4         41 return $self->{message};
35             }
36              
37             sub as_hash {
38 2     2 0 6 my ($self) = @_;
39            
40             return {
41             error => $self->{message},
42             status => $self->{status},
43 2 50       33 ($self->{details} ? (details => $self->{details}) : ()),
44             };
45             }
46              
47             sub _get_stack_trace {
48 4     4   7 my @trace;
49 4         8 my $i = 1;
50            
51 4         37 while (my @caller = caller($i++)) {
52 22         94 push @trace, {
53             package => $caller[0],
54             filename => $caller[1],
55             line => $caller[2],
56             subroutine => $caller[3],
57             };
58 22 50       137 last if $i > 20; # Limit stack depth
59             }
60            
61 4         55 return \@trace;
62             }
63              
64             # Predefined exception types
65             package Trickster::Exception::NotFound;
66 7     7   4042 use parent 'Trickster::Exception';
  7         17  
  7         60  
67             sub new {
68 2     2   9 my ($class, %args) = @_;
69 2   50     11 $args{message} ||= 'Resource not found';
70 2         4 $args{status} = 404;
71 2         27 return $class->SUPER::new(%args);
72             }
73              
74             package Trickster::Exception::BadRequest;
75 7     7   1242 use parent 'Trickster::Exception';
  7         21  
  7         31  
76             sub new {
77 1     1   31 my ($class, %args) = @_;
78 1   50     6 $args{message} ||= 'Bad request';
79 1         2 $args{status} = 400;
80 1         10 return $class->SUPER::new(%args);
81             }
82              
83             package Trickster::Exception::Unauthorized;
84 7     7   1132 use parent 'Trickster::Exception';
  7         16  
  7         34  
85             sub new {
86 0     0     my ($class, %args) = @_;
87 0   0       $args{message} ||= 'Unauthorized';
88 0           $args{status} = 401;
89 0           return $class->SUPER::new(%args);
90             }
91              
92             package Trickster::Exception::Forbidden;
93 7     7   1037 use parent 'Trickster::Exception';
  7         33  
  7         30  
94             sub new {
95 0     0     my ($class, %args) = @_;
96 0   0       $args{message} ||= 'Forbidden';
97 0           $args{status} = 403;
98 0           return $class->SUPER::new(%args);
99             }
100              
101             package Trickster::Exception::MethodNotAllowed;
102 7     7   1247 use parent 'Trickster::Exception';
  7         15  
  7         38  
103             sub new {
104 0     0     my ($class, %args) = @_;
105 0   0       $args{message} ||= 'Method not allowed';
106 0           $args{status} = 405;
107 0           return $class->SUPER::new(%args);
108             }
109              
110             package Trickster::Exception::InternalServerError;
111 7     7   1025 use parent 'Trickster::Exception';
  7         13  
  7         30  
112             sub new {
113 0     0     my ($class, %args) = @_;
114 0   0       $args{message} ||= 'Internal server error';
115 0           $args{status} = 500;
116 0           return $class->SUPER::new(%args);
117             }
118              
119             1;
120              
121             __END__