File Coverage

blib/lib/Teng/Row.pm
Criterion Covered Total %
statement 123 131 93.8
branch 41 48 85.4
condition 19 22 86.3
subroutine 21 21 100.0
pod 13 14 92.8
total 217 236 91.9


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