line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Class::ReluctantORM::OriginSupport; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
OriginSupport - Add support for Origin tracking to various CRO objects |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
package YourApp; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
Class::ReluctantORM->enable_origin_tracking(1); |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
my $ship = Ship->fetch(23); # Line 56 |
14
|
|
|
|
|
|
|
my $origin = $ship->origin_frame(); |
15
|
|
|
|
|
|
|
say $origin->{package}; # YourApp |
16
|
|
|
|
|
|
|
say $origin->{line}; # 56 |
17
|
|
|
|
|
|
|
say $origin->{file}; # whereever this is |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
@frames = $ship->origin_frame(); |
20
|
|
|
|
|
|
|
@frames = $ship->origin_frames(); # Plural alias provided |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
use Class::ReluctantORM::SQL; |
23
|
|
|
|
|
|
|
Class::ReluctantORM::SQL->enable_origin_tracking(1); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
my $sql = SQL->new( ... ); |
26
|
|
|
|
|
|
|
$origin = $sql->origin_frame(); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 DESCRIPTION |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
The problem with CRO's Reluctance feature is that when a FetchRequired exception is thrown, you have to go hunting for the location where the original fetch occured (which will be a different location than where the exception was thrown from). This mix-in superclass adds support for tracking the "origin" of each CRO object. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
The origin stack frame is the first stack frame that is from a package that is not from the Class::ReluctantORM tree. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
SQL objects also support Origins as a debugging feature. |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
Origin support adds to memory usage, but has no benefit in production (as you should in theory hit all FetchRequired exceptions in development). For this reason, origin tracking is disabled by default. |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 KNOWN USERS |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Currently these CRO modules use OriginSupport: |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=over 4 |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=item Class::ReluctantORM |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
This means all your model objects will support Origins. |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=item Class::ReluctantORM::SQL |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
This means each SQL statement will be traceable back to its point of origin. |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=back |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
Origin support is enable/disabled on an individual basis for these classes. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=cut |
57
|
|
|
|
|
|
|
|
58
|
1
|
|
|
1
|
|
7
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
59
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
70
|
|
60
|
1
|
|
|
1
|
|
689
|
use Class::ReluctantORM::Utilities qw(last_non_cro_stack_frame); |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
628
|
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $SVN_VERSION = 0; |
63
|
|
|
|
|
|
|
$SVN_VERSION = $1 if(q$LastChangedRevision: 27$ =~ /(\d+)/); |
64
|
|
|
|
|
|
|
our $VERSION = "0.4.${SVN_VERSION}"; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
our %ENABLED_FOR_CLASS = (); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=head2 Class::ReluctantORM->enable_origin_tracking($bool); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Based on the value of $bool, enables or disables origin tracking for all model objects. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
Disabled by default. |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=cut |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub enable_origin_tracking { |
77
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
78
|
0
|
|
|
|
|
|
my $flag = shift; |
79
|
0
|
|
|
|
|
|
$ENABLED_FOR_CLASS{$class} = $flag; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head2 $bool = $obj->is_origin_tracking_enabled(); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=head2 $bool = SomeClass->is_origin_tracking_enabled(); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
Returns a flag indicating whether origins are being tracked for this class. May be called as an instance method, but the check is performed at the class level. |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=cut |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub is_origin_tracking_enabled { |
91
|
0
|
|
|
0
|
1
|
|
my $inv = shift; |
92
|
0
|
|
|
|
|
|
my $enabled = grep { $inv->isa($_) } keys %ENABLED_FOR_CLASS; |
|
0
|
|
|
|
|
|
|
93
|
0
|
|
|
|
|
|
return $enabled; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head2 $frame = $obj->last_origin_frame(); |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Returns the last non-CRO frame from the last origin trace. A frame is a hashref representing info about the stack frame. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=cut |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub last_origin_frame { |
103
|
0
|
|
|
0
|
1
|
|
my $obj = shift; |
104
|
0
|
0
|
|
|
|
|
unless ($obj->is_origin_tracking_enabled) { return undef; } |
|
0
|
|
|
|
|
|
|
105
|
0
|
0
|
|
|
|
|
my @traces = @{$obj->get('_origin_traces') || []}; |
|
0
|
|
|
|
|
|
|
106
|
0
|
|
0
|
|
|
|
my $last_trace = $traces[-1] || []; |
107
|
0
|
|
|
|
|
|
my $last_frame_of_last_trace = $last_trace->[0]; |
108
|
0
|
|
|
|
|
|
return $last_frame_of_last_trace; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=head2 @frames = $obj->last_origin_trace(); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Returns all frames from the last origin trace. A frame is a hashref representing info about the stack frame. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=cut |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub last_origin_trace { |
118
|
0
|
|
|
0
|
1
|
|
my $obj = shift; |
119
|
0
|
0
|
|
|
|
|
unless ($obj->is_origin_tracking_enabled) { return (); } |
|
0
|
|
|
|
|
|
|
120
|
0
|
0
|
|
|
|
|
my @traces = @{$obj->get('_origin_traces') || []}; |
|
0
|
|
|
|
|
|
|
121
|
0
|
|
0
|
|
|
|
my $last_trace = $traces[-1] || []; |
122
|
0
|
|
|
|
|
|
return @{$last_trace}; |
|
0
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head2 @array_of_arrays = $obj->all_origin_traces(); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Returns an array of all traces. Each trace is an array of stack frames. |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
An object may have mor ethan one trace, because afterthought fetching causes multiple query origins. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=cut |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub all_origin_traces { |
134
|
0
|
|
|
0
|
1
|
|
my $obj = shift; |
135
|
0
|
0
|
|
|
|
|
unless ($obj->is_origin_tracking_enabled) { return (); } |
|
0
|
|
|
|
|
|
|
136
|
0
|
0
|
|
|
|
|
my @traces = @{$obj->get('_origin_traces') || []}; |
|
0
|
|
|
|
|
|
|
137
|
0
|
|
|
|
|
|
return @traces; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=head2 $obj->capture_origin(); |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
Called internally by CRO objects, this method records an origin point. |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=cut |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub capture_origin { |
147
|
0
|
|
|
0
|
1
|
|
my $obj = shift; |
148
|
0
|
0
|
|
|
|
|
unless ($obj->is_origin_tracking_enabled) { return; } |
|
0
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
|
150
|
0
|
|
|
|
|
|
my @trace = last_non_cro_stack_frame(); |
151
|
0
|
0
|
|
|
|
|
my @all_traces = @{$obj->get('_origin_traces') || []}; |
|
0
|
|
|
|
|
|
|
152
|
0
|
|
|
|
|
|
push @all_traces, \@trace; |
153
|
0
|
|
|
|
|
|
$obj->set('_origin_traces', \@all_traces); |
154
|
|
|
|
|
|
|
|
155
|
0
|
|
|
|
|
|
return $trace[0]; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
1; |