File Coverage

blib/lib/Teng/Row.pm
Criterion Covered Total %
statement 115 123 93.5
branch 37 44 84.0
condition 10 16 62.5
subroutine 21 21 100.0
pod 13 14 92.8
total 196 218 89.9


line stmt bran cond sub pod time code
1             package Teng::Row;
2 71     71   407 use strict;
  71         144  
  71         1772  
3 71     71   313 use warnings;
  71         429  
  71         1511  
4 71     71   315 use Carp ();
  71         116  
  71         74886  
5             our $AUTOLOAD;
6              
7             # inside-out
8             our %obj;
9              
10             sub new {
11 261     261 1 700 my ($class, $args) = @_;
12              
13 261         1727 my $self = bless {
14             # inflated values
15             _get_column_cached => {},
16             # values will be updated
17             _dirty_columns => {},
18             _autoload_column_cache => {},
19             %$args,
20             }, $class;
21              
22 261   100     1139 $self->{select_columns} ||= [keys %{$args->{row_data}}];
  128         717  
23 261   100     815 $self->{table} ||= $args->{teng}->schema->get_table($args->{table_name});
24              
25 261         900 $obj{$self+0} = delete $self->{teng};
26              
27 261         3012 $self;
28             }
29              
30             sub generate_column_accessor {
31 764     764 0 1466 my ($x, $col) = @_;
32              
33             return sub {
34 288     288   70444 my $self = shift;
35              
36             # setter is alias of set_column (not deflate column) for historical reason
37 288 100       829 return $self->set_column( $col => @_ ) if @_;
38              
39             # getter is alias of get (inflate column)
40 285         901 $self->get($col);
41 764         2662 };
42             }
43              
44 61     61 1 290 sub handle { $obj{$_[0]+0} }
45              
46             sub get {
47 285     285 1 588 my ($self, $col) = @_;
48              
49             # "Untrusted" means the row is set_column by scalarref.
50             # e.g.
51             # $row->set_column("date" => \"DATE()");
52 285 50       913 if ($self->{_untrusted_row_data}->{$col}) {
53 0         0 Carp::carp("${col}'s row data is untrusted. by your update query.");
54             }
55 285         489 my $cache = $self->{_get_column_cached};
56 285         483 my $data = $cache->{$col};
57 285 100       668 if (! $data) {
58 258 100       1025 $data = $cache->{$col} = $self->{table} ? $self->{table}->call_inflate($col, $self->get_column($col)) : $self->get_column($col);
59             }
60 285         1524 return $data;
61             }
62              
63             sub set {
64 25     25 1 1446 my ($self, $col, $val) = @_;
65 25         99 $self->set_column( $col => $self->{table}->call_deflate($col, $val) );
66 25         40 delete $self->{_get_column_cached}->{$col};
67 25         55 return $self;
68             }
69              
70             sub get_column {
71 379     379 1 2973 my ($self, $col) = @_;
72              
73 379 100       957 unless ( $col ) {
74 1         133 Carp::croak('please specify $col for first argument');
75             }
76              
77 378 100       2946 if ( exists $self->{row_data}->{$col} ) {
78 376 100       808 if (exists $self->{_dirty_columns}->{$col}) {
79 2         8 return $self->{_dirty_columns}->{$col};
80             } else {
81 374         1300 return $self->{row_data}->{$col};
82             }
83             } else {
84 2   50     131 Carp::croak("Specified column '$col' not found in row (query: " . ( $self->{sql} || 'unknown' ) . ")" );
85             }
86             }
87              
88             sub get_columns {
89 40     40 1 3801 my $self = shift;
90              
91 40         75 my %data;
92 40         64 for my $col ( @{$self->{select_columns}} ) {
  40         134  
93 113         272 $data{$col} = $self->get_column($col);
94             }
95 40         249 return \%data;
96             }
97              
98             sub set_column {
99 31     31 1 5870 my ($self, $col, $val) = @_;
100              
101 31 100 33     235 if ( defined $self->{row_data}->{$col}
      66        
102             && defined $val
103             && $self->{row_data}->{$col} eq $val ) {
104 3         8 return $val;
105             }
106              
107 28 100       74 if (ref($val) eq 'SCALAR') {
108 2         4 $self->{_untrusted_row_data}->{$col} = 1;
109             }
110              
111 28         57 delete $self->{_get_column_cached}->{$col};
112 28         87 $self->{_dirty_columns}->{$col} = $val;
113              
114 28         87 $val;
115             }
116              
117             sub set_columns {
118 1     1 1 4 my ($self, $args) = @_;
119              
120 1         3 for my $col (keys %$args) {
121 1         4 $self->set_column($col, $args->{$col});
122             }
123             }
124              
125             sub get_dirty_columns {
126 24     24 1 39 my $self = shift;
127 24         37 +{ %{ $self->{_dirty_columns} } };
  24         78  
128             }
129              
130             sub is_changed {
131 5     5 1 18 my $self = shift;
132 5         7 keys %{$self->{_dirty_columns}} > 0
  5         25  
133             }
134              
135             sub update {
136 29     29 1 4695 my ($self, $upd, $where) = @_;
137              
138 29 100       98 if (ref($self) eq 'Teng::Row') {
139 1         132 Carp::croak q{can't update from basic Teng::Row class.};
140             }
141              
142 28         55 my $table = $self->{table};
143 28         51 my $table_name = $self->{table_name};
144 28 50       82 if (! $table) {
145 0         0 Carp::croak( "Table definition for $table_name does not exist (Did you declare it in our schema?)" );
146             }
147              
148 28 100       99 if ($upd) {
149 23         73 for my $col (keys %$upd) {
150 24         85 $self->set($col => $upd->{$col});
151             }
152             }
153              
154 28 100       74 if ($where) {
155             $where = {
156             %$where,
157 2         5 %{ $self->_where_cond },
  2         7  
158             };
159             }
160             else {
161 26         79 $where = $self->_where_cond;
162             }
163              
164 24         75 $upd = $self->get_dirty_columns;
165 24 100       76 return 0 unless %$upd;
166              
167 20         70 my $bind_args = $self->handle->_bind_sql_type_to_args($table, $upd);
168 20         45 my $result = $self->handle->do_update($table_name, $bind_args, $where, 1);
169 20 50       95 if ($result > 0) {
170             $self->{row_data} = {
171 20         39 %{ $self->{row_data} },
  20         121  
172             %$upd,
173             };
174             }
175 20         62 $self->{_dirty_columns} = {};
176              
177 20         100 $result;
178             }
179              
180             sub delete {
181 15     15 1 6504 my $self = shift;
182              
183 15 100       62 if (ref($self) eq 'Teng::Row') {
184 1         59 Carp::croak q{can't delete from basic Teng::Row class.};
185             }
186              
187 14         54 $self->handle->delete($self->{table_name}, $self->_where_cond);
188             }
189              
190             sub refetch {
191 5     5 1 29 my ($self, $opt) = @_;
192 5         24 $self->handle->single($self->{table_name}, $self->_where_cond, $opt);
193             }
194              
195             # Generate a where clause to fetch this row itself.
196             sub _where_cond {
197 47     47   82 my $self = shift;
198              
199 47         89 my $table = $self->{table};
200 47         87 my $table_name = $self->{table_name};
201 47 50       168 unless ($table) {
202 0         0 Carp::croak("Unknown table: $table_name");
203             }
204              
205             # get target table pk
206 47         142 my $pk = $table->primary_keys;
207 47 100       249 unless ($pk) {
208 2         133 Carp::croak("$table_name has no primary key.");
209             }
210              
211             # multi primary keys
212 45 50       129 if ( ref $pk eq 'ARRAY' ) {
213 45 100       118 unless (@$pk) {
214 2         132 Carp::croak("$table_name has no primary key.");
215             }
216              
217 43         120 my %pks = map { $_ => 1 } @$pk;
  66         210  
218              
219 43 100       104 unless ( ( grep { exists $pks{ $_ } } @{$self->{select_columns}} ) == @$pk ) {
  131         319  
  43         97  
220 4         344 Carp::croak "can't get primary columns in your query.";
221             }
222              
223 39         92 return +{ map { $_ => $self->{row_data}->{$_} } @$pk };
  62         262  
224             } else {
225 0 0       0 unless (grep { $pk eq $_ } @{$self->{select_columns}}) {
  0         0  
  0         0  
226 0         0 Carp::croak "can't get primary column in your query.";
227             }
228              
229 0         0 return +{ $pk => $self->{row_data}->{$pk} };
230             }
231             }
232              
233             # for +columns option by some search methods
234             sub AUTOLOAD {
235 43     43   984 my $self = shift;
236 43         177 my($method) = ($AUTOLOAD =~ /([^:']+$)/);
237 43   33     161 ($self->{_autoload_column_cache}{$method} ||= $self->generate_column_accessor($method))->($self);
238             }
239              
240             ### don't autoload this
241             sub DESTROY {
242 261     261   101050 my $self = shift;
243 261         2197 delete $obj{$self+0};
244             };
245              
246             1;
247              
248             __END__
249             =head1 NAME
250              
251             Teng::Row - Teng's Row class
252              
253             =head1 METHODS
254              
255             =over
256              
257             =item $row = Teng::Row->new
258              
259             create new Teng::Row's instance
260              
261             =item $row->get($col)
262              
263             my $val = $row->get($column_name);
264              
265             # alias
266             my $val = $row->$column_name;
267              
268             get a column value from a row object.
269              
270             Note: This method inflates values.
271              
272             =item $row->set($col, $val)
273              
274             $row->set($col => $val);
275              
276             set column data.
277              
278             Note: This method deflates values.
279              
280             =item $row->get_column($column_name)
281              
282             my $val = $row->get_column($column_name);
283              
284             get a column value from a row object.
285              
286             Note: This method does not inflate values.
287              
288             =item $row->get_columns
289              
290             my $data = $row->get_columns;
291              
292             Does C<get_column>, for all column values.
293              
294             Note: This method does not inflate values.
295              
296             =item $row->set_columns(\%new_row_data)
297              
298             $row->set_columns({$col => $val});
299              
300             set columns data.
301              
302             Note: This method does not deflate values.
303              
304             =item $row->set_column($col => $val)
305              
306             $row->set_column($col => $val);
307              
308             # alias
309             $row->$col($val);
310              
311             set column data.
312              
313             Note: This method does not deflate values.
314              
315             =item $row->get_dirty_columns
316              
317             returns those that have been changed.
318              
319             =item $row->is_changed
320              
321             returns true, If the row object have a updated column.
322              
323             =item $row->update([$arg : HashRef, $where : HashRef])
324              
325             update is executed for instance record.
326              
327             It works by schema in which primary key exists.
328              
329             $row->update({name => 'tokuhirom'});
330             # or
331             $row->set({name => 'tokuhirom'});
332             $row->update;
333              
334             If C<$arg> is supplied, each pairs are passed to C<set()> method before update.
335              
336             If C<$where> is supplied, each pairs to be merged into default (primary keys) WHERE condition.
337             It is useful for optimistic lock.
338              
339             $row = $teng->single(table_name, {id => 1});
340             $result = $row->update({point => 2}, {point => 1});
341             # UPDATE table_name SET point = 2 WHERE id = 1 AND point = 1;
342              
343             =item $row->delete
344              
345             delete is executed for instance record.
346              
347             It works by schema in which primary key exists.
348              
349             =item my $refetched_row = $row->refetch([$opt:HashRef]);
350              
351             refetch record from database. get new row object.
352              
353             You can specify C<$opt> like C<< { for_update => 1} >> optionally, which is used to build query.
354              
355             =item $row->handle
356              
357             get Teng object.
358              
359             $row->handle->single('table', {id => 1});
360              
361             =back
362              
363             =head1 NOTE FOR COLUMN NAME METHOD
364              
365             Teng::Row has methods that have name from column name. For example, if a table has column named 'foo', Teng::Row instance of it has method 'foo'.
366              
367             This method has different behave for setter or getter as following:
368              
369             # (getter) is alias of $row->get('foo')
370             # so this method returns inflated value.
371             my $inflated_value = $row->foo;
372              
373             # (setter) is alias of $row->set_column('foo', $raw_value)
374             # so this method does not deflate the value. This only accepts raw value but inflated object.
375             $row->foo($raw_value);
376              
377             This behave is from historical reason. You should use column name methods with great caution, if you want to use this.
378              
379             =cut
380