File Coverage

blib/lib/DBIx/QuickORM/Connection/Transaction.pm
Criterion Covered Total %
statement 109 118 92.3
branch 46 64 71.8
condition 14 23 60.8
subroutine 19 21 90.4
pod 0 13 0.0
total 188 239 78.6


line stmt bran cond sub pod time code
1             package DBIx::QuickORM::Connection::Transaction;
2 24     24   202 use strict;
  24         69  
  24         1020  
3 24     24   146 use warnings;
  24         52  
  24         5078  
4              
5             our $VERSION = '0.000019';
6              
7 24     24   183 use Carp qw/croak confess/;
  24         73  
  24         6542  
8              
9 24         325 use DBIx::QuickORM::Util::HashBase qw{
10             <id
11             +savepoint
12              
13             +on_success
14             +on_fail
15             +on_completion
16              
17             verbose
18              
19             <result
20             <errors
21             <trace
22              
23             <rolled_back
24             <committed
25              
26             <in_destroy
27             +finalize
28              
29             no_last
30 24     24   195 };
  24         62  
31              
32 0 0   0 0 0 sub is_savepoint { $_[0]->{+SAVEPOINT} ? 1 : 0 }
33              
34             sub init {
35 27     27 0 48 my $self = shift;
36              
37 27 50       98 croak "A transaction ID is required" unless $self->{+ID};
38              
39 27         88 $self->{+RESULT} = undef;
40              
41 27 100       85 $self->{+ON_SUCCESS} = [$self->{+ON_SUCCESS}] if 'CODE' eq ref($self->{+ON_SUCCESS});
42 27 100       74 $self->{+ON_FAIL} = [$self->{+ON_FAIL}] if 'CODE' eq ref($self->{+ON_FAIL});
43 27 100       92 $self->{+ON_COMPLETION} = [$self->{+ON_COMPLETION}] if 'CODE' eq ref($self->{+ON_COMPLETION});
44             }
45              
46 3     3 0 15 sub complete { defined $_[0]->{+RESULT} }
47              
48             sub state {
49 0     0 0 0 my $self = shift;
50 0 0       0 return 'committed' if $self->{+COMMITTED};
51 0 0       0 return 'rolled_back' if $self->{+ROLLED_BACK};
52 0 0       0 return 'complete' if $self->{+RESULT};
53 0         0 return 'active';
54             }
55              
56             {
57 24     24   216 no warnings 'once';
  24         100  
  24         13467  
58             *abort = \&rollback;
59             }
60             sub rollback {
61 6     6 0 493 my $self = shift;
62 6         24 my ($why) = @_;
63              
64 6 50 66     49 if ($self->{+VERBOSE} || !$why) {
65 6         26 my @caller = caller;
66 6         23 my $trace = "$caller[1] line $caller[2]";
67              
68 6 100       24 if (my $verbose = $self->{+VERBOSE}) {
69 2 50       11 my $name = length($verbose) > 1 ? $verbose : $self->{+ID};
70 2 100       47 warn "Transaction '$name' rolled back in $trace" . ($why ? " ($why)" : ".") . "\n";
71             }
72              
73 6 100       47 if ($why) {
74 1 50       8 $why .= " in $trace" unless $why =~ m/\n$/;
75             }
76             else {
77 5         15 $why = $trace;
78             }
79             }
80              
81 6         20 $self->{+ROLLED_BACK} = $why;
82              
83 6 100       22 $self->finalize(1, $why) if $self->{+FINALIZE};
84              
85 6 100       20 return if $self->{+NO_LAST};
86              
87 24     24   204 no warnings 'exiting';
  24         76  
  24         13272  
88 5         28 last QORM_TRANSACTION;
89             };
90              
91             sub commit {
92 3     3 0 736 my $self = shift;
93 3         7053 my ($why) = @_;
94              
95 3 50 66     24 if ($self->{+VERBOSE} || !$why) {
96 3         13 my @caller = caller;
97 3         9 my $trace = "$caller[1] line $caller[2]";
98              
99 3 100       11 if (my $verbose = $self->{+VERBOSE}) {
100 2 50       7 my $name = length($verbose) > 1 ? $verbose : $self->{+ID};
101 2 100       38 warn "Transaction '$name' committed in $trace" . ($why ? " ($why)" : ".") . "\n";
102             }
103              
104 3 100       17 if ($why) {
105 1 50       7 $why .= " in $trace" unless $why =~ m/\n$/;
106             }
107             else {
108 2         21 $why = $trace;
109             }
110             }
111              
112 3         10 $self->{+COMMITTED} = $why;
113              
114 3 100       17 $self->finalize(1) if $self->{+FINALIZE};
115              
116 3 100       13 return if $self->{+NO_LAST};
117              
118 24     24   215 no warnings 'exiting';
  24         73  
  24         34423  
119 2         8 last QORM_TRANSACTION;
120             }
121              
122             sub terminate {
123 27     27 0 55 my $self = shift;
124 27         78 my ($res, $err) = @_;
125              
126 27 100       132 $self->{+RESULT} = $res ? 1 : 0;
127 27 100       116 $self->{+ERRORS} = $res ? undef : $err;
128              
129 27 100       79 my $todo = $res ? $self->{+ON_SUCCESS} : $self->{+ON_FAIL};
130 27   100     58 $todo = [@{$todo // []}, @{$self->{+ON_COMPLETION} // []}];
  27   100     132  
  27         142  
131              
132 27         93 delete $self->{+ON_SUCCESS};
133 27         57 delete $self->{+ON_FAIL};
134 27         45 delete $self->{+ON_COMPLETION};
135 27         69 delete $self->{+SAVEPOINT};
136              
137 27 100 66     159 return (1, undef) unless $todo && @$todo;
138              
139 4         7 my ($out, $out_err) = (1, undef);
140 4         9 for my $cb (@$todo) {
141 8         9 local $@;
142 8 50       12 eval { $cb->($self); 1 } and next;
  8         19  
  8         31  
143 0   0     0 push @{$out_err //= []} => $@;
  0         0  
144 0         0 $out = 0;
145             }
146              
147 4         13 return ($out, $out_err);
148             }
149              
150             sub add_success_callback {
151 1     1 0 9 my $self = shift;
152 1         2 my ($cb) = @_;
153 1   50     2 push @{$self->{+ON_SUCCESS} //= []} => $cb;
  1         8  
154             }
155              
156             sub add_fail_callback {
157 1     1 0 5 my $self = shift;
158 1         3 my ($cb) = @_;
159 1   50     2 push @{$self->{+ON_FAIL} //= []} => $cb;
  1         7  
160             }
161              
162             sub add_completion_callback {
163 1     1 0 7 my $self = shift;
164 1         2 my ($cb) = @_;
165 1   50     2 push @{$self->{+ON_COMPLETION} //= []} => $cb;
  1         5  
166             }
167              
168             sub throw {
169 3     3 0 8 my $self = shift;
170 3         10 my ($err) = @_;
171              
172 3   50     12 my $trace = $self->{+TRACE} // [qw/unknown unknown unknown/];
173 3         15 $err = "Transaction error in transaction started in $trace->[1] line $trace->[2]: $err";
174 3 50       8 $err = "[In DESTROY] $err" if $self->{+IN_DESTROY};
175              
176 3         1611 confess $err;
177             }
178              
179             sub set_finalize {
180 3     3 0 7 my $self = shift;
181 3         6 my ($cb) = @_;
182              
183 3         9 $self->{+FINALIZE} = $cb;
184             }
185              
186             sub finalize {
187 2     2 0 7 my $self = shift;
188 2         6 my ($ok, $err) = @_;
189 2 50       8 my $cb = delete $self->{+FINALIZE} or croak "Nothing to finalize!";
190 2         12 $cb->($self, $ok, $err);
191 2         24 return $ok;
192             }
193              
194             sub DESTROY {
195 27     27   5628 my $self = shift;
196 27         106 my @caller = caller;
197 27 100       268 my $finalize = $self->{+FINALIZE} or return;
198 1         3 $self->{+IN_DESTROY} = 1;
199 1         5 $finalize->($self, 1, "Transaction fell out of scope");
200             }
201              
202             1;