File Coverage

blib/lib/Class/DBI/Relationship/HasVariant.pm
Criterion Covered Total %
statement 9 54 16.6
branch 0 20 0.0
condition 0 3 0.0
subroutine 3 9 33.3
pod 2 2 100.0
total 14 88 15.9


line stmt bran cond sub pod time code
1             package Class::DBI::Relationship::HasVariant;
2              
3 1     1   21422 use strict;
  1         2  
  1         35  
4 1     1   4 use warnings;
  1         1  
  1         27  
5              
6 1     1   5 use base qw(Class::DBI::Relationship);
  1         5  
  1         829  
7              
8             =head1 NAME
9              
10             Class::DBI::Relationship::HasVariant - columns with varying types
11              
12             =head1 VERSION
13              
14             version 0.02
15              
16             $Id: HasVariant.pm,v 1.3 2004/10/12 16:53:07 rjbs Exp $
17              
18             =cut
19              
20             our $VERSION = '0.020';
21              
22             =head1 SYNOPSIS
23              
24             Using a class to transform values:
25              
26             package Music::Track::Attribute;
27             use base qw(Music::DBI);
28              
29             Music::Track::Attribute->add_relationship_type(
30             has_variant =>
31             'Class::DBI::Relationship::HasVariant'
32             );
33              
34             Music::Track::Attribute->table("trackattributes");
35              
36             Music::Track::Attribute->has_variant(
37             attr_value => 'Music::Track::Attribute::Transformer',
38             inflate => 'inflate',
39             deflate => 'deflate'
40             );
41              
42             Using subs (this is a wildly contrived example):
43              
44             Boolean::Stored->has_variant(
45             boolean => undef,
46             deflate => sub {
47             return undef if ($_[0] and $_[0] == 0);
48             return 1 if $_[0];
49             return 0;
50             }
51             );
52              
53             =head1 DESCRIPTION
54              
55             The C relationship in Class::DBI works like this:
56              
57             __PACKAGE__->has_a($columnname => $class, %options);
58              
59             The column is inflated into an instance of the named class, using methods from
60             the options or default methods. The inflated value must be of class C<$class>,
61             or an exception is thrown.
62              
63             The C relationship allows one column to inflate to different
64             types. If a class is given, it is not used for type checking, but for finding
65             a transformation method.
66              
67             =head2 EXAMPLES
68              
69             __PACKAGE__->has_variant(
70             variant => 'Variant::Auto',
71             inflate => 'inflate',
72             deflate => 'deflate'
73             );
74              
75             This example will pass the value of the "variant" column to Variant::Auto's
76             C<> method before returning it, and to its C<> method before
77             storing it.
78              
79             __PACKAGE__->has_variant(
80             variant => undef,
81             inflate => sub {
82             return ($_[0] % 2) ? Oddity->new($_[0]) : Normal->new($_[0])
83             }
84             deflate => sub { $_[0]->isa('Oddity') ? $_[0]->value : $_[0]->number }
85             );
86              
87             The above example will inflate odd numbers to Oddity objects and other values
88             to Normals. Oddities are deflated with the C<> methods, and others with
89             the C<> method.
90              
91             =cut
92              
93             sub remap_arguments {
94 0     0 1   my $proto = shift;
95 0           my $class = shift;
96 0 0         $class->_invalid_object_method('has_a()') if ref $class;
97 0 0         my $column = $class->find_column(+shift)
98             or return $class->_croak("has_variant needs a valid column");
99 0           my $a_class = shift;
100 0           my %meths = @_;
101 0           return ($class, $column, $a_class, \%meths);
102             }
103              
104             sub triggers {
105 0     0 1   my $self = shift;
106            
107 0 0         $self->class->_require_class($self->foreign_class) ## no critic Private
108             if $self->foreign_class;
109              
110 0           my $column = $self->accessor;
111             return (
112 0           select => $self->_inflator,
113             "after_set_$column" => $self->_inflator,
114             deflate_for_create => $self->_deflator(1),
115             deflate_for_update => $self->_deflator,
116             );
117             }
118              
119             sub _inflator {
120 0     0     my $self = shift;
121 0           my $col = $self->accessor;
122              
123             return sub {
124 0     0     my $self = shift;
125 0 0         defined(my $value = $self->_attrs($col)) or return;
126 0           my $meta = $self->meta_info(has_variant => $col);
127 0           my ($a_class, %meths) = ($meta->foreign_class, %{ $meta->args });
  0            
128              
129             my $get_new_value = sub {
130 0           my ($inflator, $value, $transform_class, $obj) = @_;
131 0 0         my $new_value =
132             (ref $inflator eq 'CODE')
133             ? $inflator->($value, $obj)
134             : $transform_class->$inflator($value, $obj);
135 0           return $new_value;
136 0           };
137              
138             # If we have a custom inflate ...
139 0 0         if (exists $meths{'inflate'}) {
140 0           $value = $get_new_value->($meths{'inflate'}, $value, $a_class, $self);
141 0           return $self->_attribute_store($col, $value);
142             } else {
143 0           return $value;
144             }
145              
146 0           $self->_croak("can't inflate column $col");
147 0           };
148             }
149              
150             sub _deflator {
151 0     0     my ($self, $always) = @_;
152 0           my $col = $self->accessor;
153              
154             return sub {
155 0     0     my $self = shift;
156 0 0         defined(my $value = $self->_attrs($col)) or return;
157 0           my $meta = $self->meta_info(has_variant => $col);
158 0           my ($a_class, %meths) = ($meta->foreign_class, %{ $meta->args });
  0            
159              
160             my $deflate_value = sub {
161 0           my ($deflator, $value, $transform_class, $obj) = @_;
162 0 0         my $new_value =
163             (ref $deflator eq 'CODE')
164             ? $deflator->($value, $obj)
165             : $transform_class->$deflator($value, $obj);
166 0           return $new_value;
167 0           };
168              
169 0 0         if (exists $meths{'deflate'}) {
170 0           my $value = $deflate_value->($meths{'deflate'}, $value, $a_class, $self);
171 0 0 0       return $self->_attribute_store($col => $value)
172             if ($always or $self->{__Changed}->{$col});
173 0           return;
174             }
175              
176 0           $self->_croak("can't deflate column $col");
177 0           };
178             }
179              
180             =head1 WARNINGS
181              
182             My understanding of the Class::DBI internals isn't beyond question, and I
183             expect that I've done something foolish inside here. I've tried to compensate
184             for my naivety with testing, but stupidy may have leaked through. Feedback is
185             welcome.
186              
187             =head1 AUTHOR
188              
189             Ricardo SIGNES >>
190              
191             =head2 COPYRIGHT
192              
193             (C) 2004, Ricardo SIGNES, and released under the same terms as Perl itself.
194              
195             =cut
196              
197             1;