File Coverage

blib/lib/Class/ReluctantORM/OriginSupport.pm
Criterion Covered Total %
statement 9 47 19.1
branch 0 16 0.0
condition 0 4 0.0
subroutine 3 9 33.3
pod 6 6 100.0
total 18 82 21.9


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;