File Coverage

blib/lib/RapidApp/DBIC/Component/VirtualColumns.pm
Criterion Covered Total %
statement 87 100 87.0
branch 22 34 64.7
condition 8 21 38.1
subroutine 18 23 78.2
pod 13 14 92.8
total 148 192 77.0


line stmt bran cond sub pod time code
1             # ============================================================================
2             package RapidApp::DBIC::Component::VirtualColumns;
3             # ============================================================================
4 5     5   2276 use strict;
  5         14  
  5         128  
5 5     5   22 use warnings;
  5         11  
  5         128  
6              
7 5     5   26 use base qw(DBIx::Class);
  5         10  
  5         1123  
8              
9             our $VERSION = '1.03';
10              
11 0     0   0 sub _skip_namespace_frames { qr/^RapidApp::DBIC::Component/ }
12              
13             __PACKAGE__->mk_classdata('_virtual_columns');
14              
15             =encoding utf8
16              
17             =head1 NAME
18              
19             DBIx::Class::VirtualColumns - Add virtual columns to DBIx::Class schemata
20              
21             =head1 SYNOPSIS
22              
23             package Your::Schema::Class;
24             use strict;
25             use warnings;
26            
27             use base 'DBIx::Class';
28            
29             __PACKAGE__->load_components(
30             "VirtualColumns",
31             "PK",
32             "Core",
33             );
34            
35             __PACKAGE__->table("sometable");
36             __PACKAGE__->add_columns(qw/dbcol1 dbcol2/);
37             __PACKAGE__->add_virtual_columns(qw/vcol1 vcol2 vcol3/);
38            
39             # =========================================================
40             # Somewhere else
41            
42             my $item = $schema->resultset('Artist')->find($id);
43             $item->vcol1('test'); # Set 'test'
44             $item->get_column('vcol1'); # Return 'test'
45            
46             my $otheritem = $schema->resultset('Artist')->create({
47             dbcol1 => 'value1',
48             dbcol2 => 'value2',
49             vcol1 => 'value3',
50             vcol2 => 'value4',
51             });
52            
53             $otheritem->vcol1(); # Now is 'value3'
54            
55             # Get the column metadata just like for a regular DBIC column
56             my $info = $result_source->column_info('vcol1');
57              
58             =head1 DESCRIPTION
59              
60             This module allows to specify 'virtual columns' in DBIx::Class schema
61             classes. Virtual columns behave almost like regular columns but are not
62             stored in the database. They may be used to store temporary information in
63             the L<DBIx::Class::Row> object and without introducting an additional
64             interface.
65              
66             Most L<DBIx::Class> methods like C<set_column>, C<set_columns>, C<get_column>,
67             C<get_columns>, C<column_info>, ... will work with regular as well as
68             virtual columns.
69              
70             =head1 USAGE
71              
72             Use this module if you want to add 'virtual' columns to a DBIC class
73             which behave like real columns (e.g. if you want to use the C<set_column>,
74             C<get_column> methods)
75              
76             However if you only want to add non-column data to L<DBIx::Class::Row>
77             objects, then there are easier/better ways:
78              
79             __PACKAGE__->mk_group_accessors(simple => qw(foo bar baz));
80              
81             =head1 METHODS
82              
83             =head2 add_virtual_columns
84              
85             Adds virtual columns to the result source. If supplied key => hashref pairs,
86             uses the hashref as the column_info for that column. Repeated calls of this
87             method will add more columns, not replace them.
88              
89             $table->add_virtual_columns(qw/column1 column2/);
90             OR
91             $table->add_virtual_columns(column1 => \%column1_info, column2 => \%column2_info, ...);
92              
93             The column names given will be created as accessor methods on your
94             C<DBIx::Class::Row objects>, you can change the name of the accessor by
95             supplying an "accessor" in the column_info hash.
96              
97             The following options are currently recognised/used by
98             DBIx::Class::VirtualColumns:
99              
100             =over
101              
102             =item * accessor
103              
104             Use this to set the name of the accessor method for this column. If not set,
105             the name of the column will be used.
106              
107             =back
108              
109             =cut
110              
111             sub add_virtual_columns {
112 58     58 1 690 my $self = shift;
113 58         143 my @columns = @_;
114            
115 58 50       1005 $self->_virtual_columns( {} )
116             unless defined $self->_virtual_columns() ;
117            
118             # Add columns & accessors
119 58         1070 while (my $column = shift @columns) {
120 4 50       16 my $column_info = ref $columns[0] ? shift(@columns) : {};
121            
122             # Check column
123             $self->throw_exception("Cannot override existing column '$column' with virtual one")
124 4 50 33     19 if ($self->has_column($column) or exists $self->_virtual_columns->{$column});
125              
126 4         431 $self->_virtual_columns->{$column} = $column_info;
127            
128 4   33     71 my $accessor = $column_info->{accessor} || $column;
129            
130             # Add default acceccor
131 5     5   35 no strict 'refs';
  5         9  
  5         3575  
132 4         39 *{$self.'::'.$accessor} = sub {
133 1     1   3 my $self = shift;
134 1 50       9 return $self->get_column($column) unless @_;
135 0         0 $self->set_column($column, shift);
136 4         20 };
137            
138             }
139             }
140              
141             =head2 add_virtual_column
142              
143             Shortcut for L<add_virtual_columns>
144              
145             =cut
146              
147 0     0 1 0 sub add_virtual_column { shift->add_virtual_columns(@_) }
148              
149             =head2 has_any_column
150              
151             Returns true if the source has a virtual or regular column of this name,
152             false otherwise.
153              
154             =cut
155              
156             sub has_any_column {
157 0     0 1 0 my $self = shift;
158 0         0 my $column = shift;
159 0 0 0     0 return ($self->_virtual_columns->{$column} ||
160             $self->has_column($column)) ? 1:0;
161             }
162              
163             =head2 has_virtual_column
164              
165             Returns true if the source has a virtual column of this name, false otherwise.
166              
167             =cut
168              
169             sub has_virtual_column {
170 887     887 1 12058 my $self = shift;
171 887         1495 my $column = shift;
172 887 100       15208 return (exists $self->_virtual_columns->{$column}) ? 1:0
173             }
174              
175             =head2 remove_virtual_columns
176              
177             $table->remove_columns(qw/col1 col2 col3/);
178            
179             Removes virtual columns from the result source.
180              
181             =cut
182              
183             sub remove_virtual_columns {
184 0     0 1 0 my $self = shift;
185 0         0 my @columns = @_;
186            
187 0         0 foreach my $column (@columns) {
188 0         0 delete $self->_virtual_columns->{$column};
189             }
190             }
191              
192             =head2 remove_virtual_column
193              
194             Shortcut for L<remove_virtual_columns>
195              
196             =cut
197              
198 0     0 1 0 sub remove_virtual_column { shift->remove_virtual_columns(@_) }
199              
200             =head2 _virtual_filter
201              
202             Splits attributes for regular and virtual columns
203              
204             =cut
205              
206             sub _virtual_filter {
207 24     24   65 my ($self,$attrs) = @_;
208              
209 24 50       507 if ( !$self->_virtual_columns ) {
210 0         0 $self->_virtual_columns( {} );
211             }
212            
213 24         1100 my $virtual_attrs = {};
214 24         52 my $main_attrs = {};
215 24         89 foreach my $attr (keys %$attrs) {
216 45 100       753 if (exists $self->_virtual_columns->{$attr}) {
217 3         108 $virtual_attrs->{$attr} = $attrs->{$attr};
218             } else {
219 42         1322 $main_attrs->{$attr} = $attrs->{$attr};
220             }
221             }
222 24         81 return ($virtual_attrs,$main_attrs);
223             }
224              
225             =head2 new
226              
227             Overloaded method. L<DBIx::Class::Row/"new">
228              
229             =cut
230              
231             sub new {
232 13     13 1 43 my ( $class, $attrs ) = @_;
233            
234             # Split main and virtual values
235 13         121 my ($virtual_attrs,$main_attrs) = $class->_virtual_filter($attrs);
236              
237             # Call new method
238 13         53 my $return = $class->next::method($main_attrs);
239            
240             # Prefill localized data
241 13         468 $return->{_virtual_values} = {};
242            
243             # Set localized data
244 13         66 while ( my($key,$value) = each %$virtual_attrs ) {
245 1         4 $return->store_column($key,$value);
246             }
247            
248 13         408 return $return;
249             }
250              
251             =head2 get_column
252              
253             Overloaded method. L<DBIx::Class::Row/"get_colum">
254              
255             =cut
256              
257             sub get_column {
258 285     285 1 32440 my ($self, $column) = @_;
259              
260             # Check if a virtual colum has been requested
261 285 100 66     5274 if (defined $self->_virtual_columns
262             && exists $self->_virtual_columns->{$column}) {
263 2         174 return $self->{_virtual_values}{$column};
264             }
265              
266 283         25200 return $self->next::method($column);
267             }
268              
269             =head2 get_columns
270              
271             Overloaded method. L<DBIx::Class::Row/"get_colums">
272              
273             =cut
274              
275             sub get_columns {
276 32     32 1 374 my $self = shift;
277            
278 32 100       135 return $self->next::method(@_) unless $self->in_storage;
279 16         57 my %data = $self->next::method(@_);
280            
281 16 50       724 if (defined $self->_virtual_columns) {
282 16         922 foreach my $column (keys %{$self->_virtual_columns}) {
  16         337  
283 16         636 $data{$column} = $self->{_virtual_values}{$column};
284             }
285             }
286 16         118 return %data;
287             }
288              
289             =head2 store_column
290              
291             Overloaded method. L<DBIx::Class::Row/"store_column">
292              
293             =cut
294              
295             sub store_column {
296 119     119 1 1103 my ($self, $column, $value) = @_;
297              
298             # Check if a localized colum has been requested
299 119 100 66     2132 if (defined $self->_virtual_columns
300             && exists $self->_virtual_columns->{$column}) {
301 10         1174 return $self->{_virtual_values}{$column} = $value;
302             }
303              
304 109         9474 return $self->next::method($column, $value);
305             }
306              
307             =head2 set_column
308              
309             Overloaded method. L<DBIx::Class::Row/"set_column">
310              
311             =cut
312              
313             sub set_column {
314 76     76 1 772 my ($self, $column, $value) = @_;
315              
316 76 50 33     1461 if (defined $self->_virtual_columns
317             && exists $self->_virtual_columns->{$column}) {
318 0         0 return $self->{_virtual_values}{$column} = $value;
319             }
320 76         7040 return $self->next::method($column, $value);
321             }
322              
323             =head2 columns_info
324              
325             Overloaded method. L<DBIx::Class::ResultSource/columns_info>
326              
327             Additionally returns the HASH key 'virtual' which indicates if the requested
328             column is virtual or not.
329              
330             =cut
331              
332             # keep as compat shim for 0.0828xx DBIC
333             sub column_info :DBIC_method_is_indirect_sugar {
334 919     919 0 3318 $_[0]->columns_info([ $_[1] ])->{$_[1]};
335 5     5   476 }
  5         1001  
  5         41  
336              
337             sub columns_info :DBIC_method_is_bypassable_resultsource_proxy {
338 919     919 1 1857 my( $self, $colnames ) = @_;
339              
340 919         1268 my %virt_cols;
341              
342 919 50       14872 if( my $vi = $self->_virtual_columns ) {
343             $virt_cols{$_} = {
344 127 50       910 %{ $vi->{$_} || {} },
345             virtual => 1,
346 919         20409 } for keys %$vi;
347              
348             # We can not ask DBIC about virtual columns
349             # New arrayref so we do not destroy the supplied argument
350             $colnames = [ grep
351 919 50       2282 { ! $virt_cols{$_} }
  919         2842  
352             @$colnames
353             ] if $colnames;
354             }
355              
356             return {
357 919   33     1404 %{ $self->next::method($colnames||()) },
  919         3256  
358             %virt_cols
359             };
360 5     5   2553 }
  5         12  
  5         21  
361              
362             =head2 update
363              
364             Overloaded method. L<DBIx::Class::Row/"update">
365              
366             =cut
367              
368             sub update {
369 11     11 1 114 my $self = shift;
370 11         23 my $attr = shift;
371            
372             # Filter localized values
373 11         762 my ($virtual_attrs,$main_attrs) = $self->_virtual_filter($attr);
374            
375             # Do regular update
376 11         48 $self->next::method($main_attrs);
377            
378 11 100       60191 if (scalar %{$virtual_attrs}) {
  11         51  
379 2         10 while ( my($column,$value) = each %$virtual_attrs ) {
380 2         10 $self->{_virtual_values}{$column} = $value;
381             }
382             }
383 11         75 return $self;
384             }
385              
386             =head1 CAVEATS
387              
388             The best way to add non-column data to DBIC objects is to use
389             L<Class::Accessor::Grouped>.
390              
391             __PACKAGE__->mk_group_accessors(simple => qw(foo bar baz));
392              
393             Use L<DBIx::Class::VirtualColumns> only if you rely on L<DBIx::Class::Row>
394             methods like C<set_column>, C<get_column>, ...
395              
396             =head1 SUPPORT
397              
398             This module was just a proof of concept, and is not actively developed
399             anymore. Patches are still welcome though.
400              
401             Please report any bugs to
402             C<bug-dbix-class-virtualcolumns@rt.cpan.org>, or through the web interface at
403             L<http://rt.cpan.org/Public/Bug/Report.html?Queue=DBIx::Class::VirtualColumns>.
404             I will be notified, and then you'll automatically be notified of progress on
405             your report as I make changes.
406              
407             =head1 AUTHOR
408              
409             MaroÅ¡ Kollár
410             CPAN ID: MAROS
411             maros [at] k-1.com
412             L<http://www.revdev.at>
413              
414             =head1 ACKNOWLEDGEMENTS
415              
416             This module was written for Revdev L<http://www.revdev.at>, a nice litte
417             software company I run with Koki and Domm (L<http://search.cpan.org/~domm/>).
418              
419             =head1 COPYRIGHT
420              
421             DBIx::Class::VirtualColumns is Copyright (c) 2008 MaroÅ¡ Kollár
422             - L<http://www.revdev.at>
423              
424             This program is free software; you can redistribute it and/or modify it under
425             the same terms as Perl itself.
426              
427             The full text of the license can be found in the
428             LICENSE file included with this module.
429              
430             =cut
431              
432             "This ist virtually the end of the package";