handle the throw_exception bit. Drop DBIx::Class::Exception
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / InflateColumn.pm
1 package DBIx::Class::InflateColumn;
2
3 use strict;
4 use warnings;
5
6
7 use base qw/DBIx::Class::Row/;
8
9 =head1 NAME 
10
11 DBIx::Class::InflateColumn - Automatically create objects from column data
12
13 =head1 SYNOPSIS
14
15     # In your table classes
16     __PACKAGE__->inflate_column('column_name', {
17         inflate => sub { ... },
18         deflate => sub { ... },
19     });
20
21 =head1 DESCRIPTION
22
23 This component translates column data into objects, i.e. "inflating"
24 the column data. It also "deflates" objects into an appropriate format
25 for the database.
26
27 It can be used, for example, to automatically convert to and from
28 L<DateTime> objects for your date and time fields. 
29
30 =head1 METHODS
31
32 =head2 inflate_column
33
34 Instruct L<DBIx::Class> to inflate the given column. 
35
36 In addition to the column name, you must provide C<inflate> and
37 C<deflate> methods. The C<inflate> method is called when you access
38 the field, while the C<deflate> method is called when the field needs
39 to used by the database.
40
41 For example, if you have a table C<events> with a timestamp field
42 named C<insert_time>, you could inflate the column in the
43 corresponding table class using something like:
44
45     __PACKAGE__->inflate_column('insert_time', {
46         inflate => sub { DateTime::Format::Pg->parse_datetime(shift); },
47         deflate => sub { DateTime::Format::Pg->format_datetime(shift); },
48     });
49
50 (Replace L<DateTime::Format::Pg> with the appropriate module for your
51 database, or consider L<DateTime::Format::DBI>.)
52
53 In this example, calls to an event's C<insert_time> accessor return a
54 L<DateTime> object. This L<DateTime> object is later "deflated" when
55 used in the database layer.
56
57 =cut
58
59 sub inflate_column {
60   my ($self, $col, $attrs) = @_;
61   $self->throw_exception("No such column $col to inflate") unless $self->has_column($col);
62   $self->throw_exception("inflate_column needs attr hashref") unless ref $attrs eq 'HASH';
63   $self->column_info($col)->{_inflate_info} = $attrs;
64   $self->mk_group_accessors('inflated_column' => $col);
65   return 1;
66 }
67
68 sub _inflated_column {
69   my ($self, $col, $value) = @_;
70   return $value unless defined $value; # NULL is NULL is NULL
71   my $info = $self->column_info($col) || $self->throw_exception("No column info for $col");
72   return $value unless exists $info->{_inflate_info};
73   my $inflate = $info->{_inflate_info}{inflate};
74   $self->throw_exception("No inflator for $col") unless defined $inflate;
75   return $inflate->($value, $self);
76 }
77
78 sub _deflated_column {
79   my ($self, $col, $value) = @_;
80   return $value unless ref $value; # If it's not an object, don't touch it
81   my $info = $self->column_info($col) || $self->throw_exception("No column info for $col");
82   return $value unless exists $info->{_inflate_info};
83   my $deflate = $info->{_inflate_info}{deflate};
84   $self->throw_exception("No deflator for $col") unless defined $deflate;
85   return $deflate->($value, $self);
86 }
87
88 sub get_inflated_column {
89   my ($self, $col) = @_;
90   $self->throw_exception("$col is not an inflated column") unless
91     exists $self->column_info($col)->{_inflate_info};
92
93   return $self->{_inflated_column}{$col}
94     if exists $self->{_inflated_column}{$col};
95   return $self->{_inflated_column}{$col} =
96            $self->_inflated_column($col, $self->get_column($col));
97 }
98
99 sub set_inflated_column {
100   my ($self, $col, @rest) = @_;
101   my $ret = $self->_inflated_column_op('set', $col, @rest);
102   return $ret;
103 }
104
105 sub store_inflated_column {
106   my ($self, $col, @rest) = @_;
107   my $ret = $self->_inflated_column_op('store', $col, @rest);
108   return $ret;
109 }
110
111 sub _inflated_column_op {
112   my ($self, $op, $col, $obj) = @_;
113   my $meth = "${op}_column";
114   unless (ref $obj) {
115     delete $self->{_inflated_column}{$col};
116     return $self->$meth($col, $obj);
117   }
118
119   my $deflated = $self->_deflated_column($col, $obj);
120            # Do this now so we don't store if it's invalid
121
122   $self->{_inflated_column}{$col} = $obj;
123   $self->$meth($col, $deflated);
124   return $obj;
125 }
126
127 sub new {
128   my ($class, $attrs, @rest) = @_;
129   $attrs ||= {};
130   foreach my $key (keys %$attrs) {
131     if (ref $attrs->{$key}
132           && exists $class->column_info($key)->{_inflate_info}) {
133       $attrs->{$key} = $class->_deflated_column($key, $attrs->{$key});
134     }
135   }
136   return $class->next::method($attrs, @rest);
137 }
138
139 =head1 SEE ALSO
140
141 =over 4
142
143 =item L<DBIx::Class::Core> - This component is loaded as part of the
144       "core" L<DBIx::Class> components; generally there is no need to
145       load it directly
146
147 =back
148
149 =head1 AUTHOR
150
151 Matt S. Trout <mst@shadowcatsystems.co.uk>
152
153 =head1 CONTRIBUTORS
154
155 Daniel Westermann-Clark <danieltwc@cpan.org> (documentation)
156
157 =head1 LICENSE
158
159 You may distribute this code under the same terms as Perl itself.
160
161 =cut
162
163 1;