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   2347 use strict;
  5         11  
  5         133  
5 5     5   38 use warnings;
  5         10  
  5         126  
6              
7 5     5   24 use base qw(DBIx::Class);
  5         9  
  5         1137  
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 660 my $self = shift;
113 58         153 my @columns = @_;
114            
115 58 50       1019 $self->_virtual_columns( {} )
116             unless defined $self->_virtual_columns() ;
117            
118             # Add columns & accessors
119 58         988 while (my $column = shift @columns) {
120 4 50       19 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     15 if ($self->has_column($column) or exists $self->_virtual_columns->{$column});
125              
126 4         430 $self->_virtual_columns->{$column} = $column_info;
127            
128 4   33     87 my $accessor = $column_info->{accessor} || $column;
129            
130             # Add default acceccor
131 5     5   35 no strict 'refs';
  5         16  
  5         3706  
132 4         40 *{$self.'::'.$accessor} = sub {
133 1     1   3 my $self = shift;
134 1 50       7 return $self->get_column($column) unless @_;
135 0         0 $self->set_column($column, shift);
136 4         21 };
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 11174 my $self = shift;
171 887         1299 my $column = shift;
172 887 100       14770 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       503 if ( !$self->_virtual_columns ) {
210 0         0 $self->_virtual_columns( {} );
211             }
212            
213 24         1101 my $virtual_attrs = {};
214 24         61 my $main_attrs = {};
215 24         110 foreach my $attr (keys %$attrs) {
216 37 100       606 if (exists $self->_virtual_columns->{$attr}) {
217 1         31 $virtual_attrs->{$attr} = $attrs->{$attr};
218             } else {
219 36         1092 $main_attrs->{$attr} = $attrs->{$attr};
220             }
221             }
222 24         96 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 41 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         54 my $return = $class->next::method($main_attrs);
239            
240             # Prefill localized data
241 13         463 $return->{_virtual_values} = {};
242            
243             # Set localized data
244 13         72 while ( my($key,$value) = each %$virtual_attrs ) {
245 1         5 $return->store_column($key,$value);
246             }
247            
248 13         410 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 294     294 1 31829 my ($self, $column) = @_;
259              
260             # Check if a virtual colum has been requested
261 294 100 66     5104 if (defined $self->_virtual_columns
262             && exists $self->_virtual_columns->{$column}) {
263 2         182 return $self->{_virtual_values}{$column};
264             }
265              
266 292         25510 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 350 my $self = shift;
277            
278 32 100       132 return $self->next::method(@_) unless $self->in_storage;
279 16         50 my %data = $self->next::method(@_);
280            
281 16 50       714 if (defined $self->_virtual_columns) {
282 16         893 foreach my $column (keys %{$self->_virtual_columns}) {
  16         284  
283 16         611 $data{$column} = $self->{_virtual_values}{$column};
284             }
285             }
286 16         113 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 121     121 1 1211 my ($self, $column, $value) = @_;
297              
298             # Check if a localized colum has been requested
299 121 100 66     2228 if (defined $self->_virtual_columns
300             && exists $self->_virtual_columns->{$column}) {
301 10         1171 return $self->{_virtual_values}{$column} = $value;
302             }
303              
304 111         9859 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 80     80 1 832 my ($self, $column, $value) = @_;
315              
316 80 100 66     1515 if (defined $self->_virtual_columns
317             && exists $self->_virtual_columns->{$column}) {
318 2         192 return $self->{_virtual_values}{$column} = $value;
319             }
320 78         7112 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 3152 $_[0]->columns_info([ $_[1] ])->{$_[1]};
335 5     5   505 }
  5         1079  
  5         27  
336              
337             sub columns_info :DBIC_method_is_bypassable_resultsource_proxy {
338 919     919 1 1779 my( $self, $colnames ) = @_;
339              
340 919         1276 my %virt_cols;
341              
342 919 50       14783 if( my $vi = $self->_virtual_columns ) {
343             $virt_cols{$_} = {
344 127 50       885 %{ $vi->{$_} || {} },
345             virtual => 1,
346 919         20665 } 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       2760 { ! $virt_cols{$_} }
  919         2812  
352             @$colnames
353             ] if $colnames;
354             }
355              
356             return {
357 919   33     1498 %{ $self->next::method($colnames||()) },
  919         3417  
358             %virt_cols
359             };
360 5     5   2757 }
  5         11  
  5         25  
361              
362             =head2 update
363              
364             Overloaded method. L<DBIx::Class::Row/"update">
365              
366             =cut
367              
368             sub update {
369 11     11 1 131 my $self = shift;
370 11         31 my $attr = shift;
371            
372             # Filter localized values
373 11         67 my ($virtual_attrs,$main_attrs) = $self->_virtual_filter($attr);
374            
375             # Do regular update
376 11         45 $self->next::method($main_attrs);
377            
378 11 50       56174 if (scalar %{$virtual_attrs}) {
  11         48  
379 0         0 while ( my($column,$value) = each %$virtual_attrs ) {
380 0         0 $self->{_virtual_values}{$column} = $value;
381             }
382             }
383 11         39 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";