File Coverage

blib/lib/RapidApp/DBIC/Component/VirtualColumns.pm
Criterion Covered Total %
statement 86 100 86.0
branch 22 34 64.7
condition 9 21 42.8
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   2737 use strict;
  5         15  
  5         144  
5 5     5   32 use warnings;
  5         10  
  5         136  
6              
7 5     5   27 use base qw(DBIx::Class);
  5         13  
  5         1362  
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 735 my $self = shift;
113 58         141 my @columns = @_;
114            
115 58 50       1080 $self->_virtual_columns( {} )
116             unless defined $self->_virtual_columns() ;
117            
118             # Add columns & accessors
119 58         1110 while (my $column = shift @columns) {
120 4 50       18 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         558 $self->_virtual_columns->{$column} = $column_info;
127            
128 4   33     92 my $accessor = $column_info->{accessor} || $column;
129            
130             # Add default acceccor
131 5     5   41 no strict 'refs';
  5         13  
  5         4230  
132 4         44 *{$self.'::'.$accessor} = sub {
133 1     1   4 my $self = shift;
134 1 50       9 return $self->get_column($column) unless @_;
135 0         0 $self->set_column($column, shift);
136 4         23 };
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 13658 my $self = shift;
171 887         1604 my $column = shift;
172 887 100       17756 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   84 my ($self,$attrs) = @_;
208              
209 24 50       633 if ( !$self->_virtual_columns ) {
210 0         0 $self->_virtual_columns( {} );
211             }
212            
213 24         1331 my $virtual_attrs = {};
214 24         54 my $main_attrs = {};
215 24         174 foreach my $attr (keys %$attrs) {
216 37 100       667 if (exists $self->_virtual_columns->{$attr}) {
217 1         30 $virtual_attrs->{$attr} = $attrs->{$attr};
218             } else {
219 36         1228 $main_attrs->{$attr} = $attrs->{$attr};
220             }
221             }
222 24         97 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 44 my ( $class, $attrs ) = @_;
233            
234             # Split main and virtual values
235 13         129 my ($virtual_attrs,$main_attrs) = $class->_virtual_filter($attrs);
236              
237             # Call new method
238 13         66 my $return = $class->next::method($main_attrs);
239            
240             # Prefill localized data
241 13         544 $return->{_virtual_values} = {};
242            
243             # Set localized data
244 13         76 while ( my($key,$value) = each %$virtual_attrs ) {
245 1         4 $return->store_column($key,$value);
246             }
247            
248 13         484 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 298     298 1 39080 my ($self, $column) = @_;
259              
260             # Check if a virtual colum has been requested
261 298 100 66     6383 if (defined $self->_virtual_columns
262             && exists $self->_virtual_columns->{$column}) {
263 2         216 return $self->{_virtual_values}{$column};
264             }
265              
266 296         31728 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 433 my $self = shift;
277            
278 32 100       215 return $self->next::method(@_) unless $self->in_storage;
279 16         60 my %data = $self->next::method(@_);
280            
281 16 50       883 if (defined $self->_virtual_columns) {
282 16         1132 foreach my $column (keys %{$self->_virtual_columns}) {
  16         346  
283 16         761 $data{$column} = $self->{_virtual_values}{$column};
284             }
285             }
286 16         148 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 123     123 1 1360 my ($self, $column, $value) = @_;
297              
298             # Check if a localized colum has been requested
299 123 100 66     2790 if (defined $self->_virtual_columns
300             && exists $self->_virtual_columns->{$column}) {
301 10         1311 return $self->{_virtual_values}{$column} = $value;
302             }
303              
304 113         11855 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 82     82 1 1012 my ($self, $column, $value) = @_;
315              
316 82 100 66     1833 if (defined $self->_virtual_columns
317             && exists $self->_virtual_columns->{$column}) {
318 2         236 return $self->{_virtual_values}{$column} = $value;
319             }
320 80         8846 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 730     730 0 3259 $_[0]->columns_info([ $_[1] ])->{$_[1]};
335 5     5   602 }
  5         1337  
  5         53  
336              
337             sub columns_info :DBIC_method_is_bypassable_resultsource_proxy {
338 730     730 1 1721 my( $self, $colnames ) = @_;
339              
340 730         1218 my %virt_cols;
341              
342 730 50       13753 if( my $vi = $self->_virtual_columns ) {
343             $virt_cols{$_} = {
344 113 50       1031 %{ $vi->{$_} || {} },
345             virtual => 1,
346 730         16638 } 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 730 50       2139 { ! $virt_cols{$_} }
  730         2550  
352             @$colnames
353             ] if $colnames;
354             }
355              
356             return {
357 730   33     1390 %{ $self->next::method($colnames||()) },
  730         3008  
358             %virt_cols
359             };
360 5     5   3142 }
  5         16  
  5         26  
361              
362             =head2 update
363              
364             Overloaded method. L<DBIx::Class::Row/"update">
365              
366             =cut
367              
368             sub update {
369 11     11 1 134 my $self = shift;
370 11         26 my $attr = shift;
371            
372             # Filter localized values
373 11         79 my ($virtual_attrs,$main_attrs) = $self->_virtual_filter($attr);
374            
375             # Do regular update
376 11         53 $self->next::method($main_attrs);
377            
378 11 50       71646 if (scalar %{$virtual_attrs}) {
  11         67  
379 0         0 while ( my($column,$value) = each %$virtual_attrs ) {
380 0         0 $self->{_virtual_values}{$column} = $value;
381             }
382             }
383 11         50 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";