File Coverage

blib/lib/Object/Annotate.pm
Criterion Covered Total %
statement 106 117 90.6
branch 23 36 63.8
condition 19 45 42.2
subroutine 18 21 85.7
pod 11 11 100.0
total 177 230 76.9


line stmt bran cond sub pod time code
1 2     2   115863 use warnings;
  2         6  
  2         107  
2 2     2   12 use strict;
  2         5  
  2         131  
3             package Object::Annotate;
4             {
5             $Object::Annotate::VERSION = '0.024';
6             }
7             # ABSTRACT: mix in logging-to-database to objects (deprecated)
8              
9 2     2   13 use Carp ();
  2         5  
  2         41  
10 2     2   1909 use UNIVERSAL::moniker 0.01;
  2         106  
  2         172  
11              
12              
13             # We'll store the constructed Class::DBI subclasses here.
14             # $class_for->{ $dsn }->{ $table } = $class
15             my $class_for = {};
16              
17             # We'll keep a counter, here, to use to form unique class names.
18             my $current_suffix = 0;
19              
20             # The "id" column isn't here because we want it first, always.
21             my %note_columns = (
22             mandatory => [ qw(class object_id created) ],
23             # I plan to use these values in the future. -- rjbs, 2006-01-13
24             # default => [ qw(event attr old_val new_val via comment expire_time) ],
25             default => [ qw(event attr old_val new_val via comment expire_time) ],
26             );
27              
28 2         26 use Sub::Exporter 0.92 -setup => {
29             groups => { annotator => \&setup_class },
30 2     2   16601 };
  2         30101  
31              
32              
33             sub new {
34 0     0 1 0 my ($self, $arg) = @_;
35 0 0       0 my $class = (ref $self) ? ref $self : $self;
36              
37 0         0 my $target
38             = sprintf '%s::Singularity::0x%08x', $class, ++$current_suffix;
39              
40 0         0 $self->setup_class($target, $arg);
41              
42 0         0 my $singularity = \do { undef };
  0         0  
43 0         0 bless $singularity => $target;
44             }
45              
46              
47              
48             sub setup_class {
49 3     3 1 1436 my ($self, $name, $arg, $col) = @_;
50              
51 3   33     15 $arg->{db}{dsn} ||= $self->default_dsn;
52 3   33     12 $arg->{db}{table} ||= $self->default_table;
53              
54 3   33     26 $arg->{db}{user} ||= $self->default_user;
55 3   33     17 $arg->{db}{pass} ||= $self->default_pass;
56              
57 3   33     15 $arg->{db}{sequence} ||= $self->_default_sequence;
58              
59 3 50 25     31 if ($arg->{noun} xor $arg->{verb}) {
    50 33        
60 0         0 Carp::croak 'you must supply either both or neither "noun" and "verb"';
61             } elsif (not ($arg->{noun} or $arg->{verb})) {
62 3         10 @$arg{qw(noun verb)} = qw(annotations annotate);
63             }
64              
65 3         8 my $class = $self->class_for($arg);
66              
67 3         7 my $obj_class = $arg->{obj_class};
68              
69 3   100     36 my %build_option = (
70             obj_class => $obj_class,
71             id_attr => $arg->{id_attr} || 'id',
72              
73             noun => $arg->{noun},
74             verb => $arg->{verb},
75             );
76              
77 3   50     40 my $annotator = $self->build_annotator({
78             %build_option,
79             columns => $arg->{columns},
80             set_time => ($arg->{db}{dsn} && (scalar $arg->{db}{dsn} =~ /SQLite/)),
81             });
82              
83             my $return = {
84 25     25   12667 "$arg->{noun}_class" => sub { $class },
85 3         27 $arg->{verb} => $annotator,
86             "search_$arg->{noun}" => $self->build_searcher(\%build_option),
87             };
88             }
89              
90              
91             sub class_for {
92 3     3 1 6 my ($self, $arg) = @_;
93              
94 3         7 my $dsn = $arg->{db}{dsn};
95 3         5 my $table = $arg->{db}{table};
96              
97 3         6 my $user = $arg->{db}{user};
98 3         4 my $pass = $arg->{db}{pass};
99              
100             # Try to find an already-constructed class.
101 3   66     35 my $class = ! $arg->{extra_setup}
102             && exists $class_for->{ $dsn }
103             && exists $class_for->{ $dsn }->{ $table }
104             && $class_for->{ $dsn }->{ $table };
105              
106 3 100       11 return $class if $class;
107              
108             # If we have no class built for this combination, build it.
109 1         6 $class = $self->construct_cdbi_class({
110             dsn => $dsn,
111             user => $user,
112             pass => $pass,
113             table => $table,
114             columns => $arg->{columns},
115             sequence => $arg->{db}{sequence},
116             base_class => $arg->{base_class},
117             });
118              
119 1 50       8 $arg->{extra_setup}->($class) if $arg->{extra_setup};
120              
121 1         5 return $class;
122             }
123              
124              
125 0     0 1 0 sub default_dsn { $ENV{OBJ_ANNOTATE_DSN}; }
126 0     0 1 0 sub default_table { $ENV{OBJ_ANNOTATE_TABLE}; }
127 3     3 1 14 sub default_user { $ENV{OBJ_ANNOTATE_USER}; }
128 3     3 1 10 sub default_pass { $ENV{OBJ_ANNOTATE_PASS}; }
129 1     1 1 4 sub default_base_class { 'Class::DBI' }
130              
131 3     3   7 sub _default_sequence { }
132              
133              
134             sub construct_cdbi_class {
135 1     1 1 3 my ($class, $arg) = @_;
136              
137 1         7 my $new_class
138             = sprintf '%s::Construct::0x%08x', __PACKAGE__, ++$current_suffix;
139              
140 1   33     7 $arg->{base_class} ||= $class->default_base_class;
141              
142 1 50       76 eval "require $arg->{base_class};" or die $@;
143 1         50590 do {
144 2     2   2373 no strict 'refs';
  2         4  
  2         1451  
145 1         4 @{$new_class . '::ISA'} = $arg->{base_class};
  1         31  
146             };
147              
148 1 50       6 if ($arg->{dsn}) {
149 1         12 $new_class->connection($arg->{dsn}, $arg->{user}, $arg->{pass});
150             }
151              
152 1         221 $new_class->table($arg->{table});
153              
154 1         47 my @columns = @{ $note_columns{mandatory} };
  1         6  
155 1 50       3 my @extra_columns = @{ $arg->{columns} || $note_columns{default} };
  1         15  
156 1         5 push @columns, @extra_columns;
157              
158 1         9 $new_class->columns(All => ('id', @columns));
159              
160 1 50       2296 $new_class->sequence($arg->{sequence}) if $arg->{sequence};
161              
162 1         6 $new_class->db_Main->{ AutoCommit } = 1;
163              
164 1   50     3672 return $class_for->{ $arg->{dsn} || '' }->{ $arg->{table} } = $new_class;
165             }
166              
167              
168             sub build_annotator {
169 3     3 1 6 my ($self, $arg) = @_;
170              
171 3         5 my $obj_class = $arg->{obj_class};
172 3         7 my $id_attr = $arg->{id_attr};
173 3         6 my $set_time = $arg->{set_time};
174              
175             my @columns
176 3 50       8 = $arg->{columns} ? @{ $arg->{columns} } : @{ $note_columns{default} };
  0         0  
  3         14  
177              
178 3         6 my $noun = $arg->{noun};
179              
180             my $annotator = sub {
181             # This $arg purposefully shadows the previous; I don't want to enclose
182             # those args. -- rjbs, 2006-01-05
183 9     9   10001 my ($self, $arg) = @_;
184 9   33     146 my $obj_class = $arg->{obj_class} || $self->moniker;
185              
186 9         504 my $id;
187 9 100       56 if (ref $id_attr) {
188 5         17 $id = $$id_attr;
189             } else {
190 4         31 $id = $self->$id_attr;
191 4 50       41 Carp::croak "couldn't get id for $self via $id_attr" unless $id;
192             }
193              
194             # build up only those attributes we declared
195 9         18 my %attr;
196 9         34 for (@columns) {
197 63 100       319 next unless exists $arg->{$_};
198 17         61 $attr{$_} = $arg->{$_};
199             }
200              
201 9 50       60 $attr{created} = time if $set_time;
202              
203 9         43 my $class_name_method = "$noun\_class";
204 9         51 my $request = $self->$class_name_method->create({
205             class => $obj_class,
206             object_id => $id,
207             %attr,
208             });
209              
210 9         186195 return $request;
211 3         28 };
212              
213 3         10 return $annotator;
214             }
215              
216              
217             sub build_searcher {
218 3     3 1 5 my ($self, $arg) = @_;
219              
220 3         6 my $obj_class = $arg->{obj_class};
221 3         7 my $id_attr = $arg->{id_attr};
222              
223 3         6 my $noun = $arg->{noun};
224              
225             my $searcher = sub {
226 10     10   17130 my ($self, $arg) = @_;
227 10   33     110 my $obj_class = $arg->{obj_class} || $self->moniker;
228 10   50     1127 $arg ||= {};
229              
230 10         19 my $id;
231 10 100       53 if (ref $id_attr) {
    100          
232 3         9 $id = $$id_attr;
233             } elsif (ref $self) {
234 5         31 $id = $self->$id_attr;
235 5 50       39 Carp::croak "couldn't get id for $self via $id_attr" unless $id;
236             }
237              
238 10         32 $arg->{class} = $obj_class;
239 10 100 66     70 $arg->{object_id} = $id if defined $id and not exists $arg->{object_id};
240              
241 10         90 my $class_name_method = "$noun\_class";
242 10         46 $self->$class_name_method->search(%$arg);
243             }
244 3         40 }
245              
246             '2. see footnote #1';
247              
248             __END__