blob: 5f1583018b3c25e3d042fad36a13c943f69f191e (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
package FixMyStreet::DB::RABXColumn;
use strict;
use warnings;
use IO::String;
use RABX;
=head1 NAME
FixMyStreet::DB::RABXColumn
=head2 DESCRIPTION
This is a helper component that will setup the RABX serialisation for some
fields. This is useful for when you want to persist some data structure such as
hashrefs etc.
This code will also change the default FilterColumn behaviour so that whenever
your set a column, or specify a RABX'd column in an ->update the value is saved
to the database. The default behaviour is to check if the value is already set,
and for hashrefs this means that changes to the contents are missed as it is
still the same hashref.
By putting all this code in one place there is also much less repetition.
=cut
# Store which columns are RABX cols.
# $RABX_COLUMNS{$class}{$col} = 1
my %RABX_COLUMNS = ();
sub _get_class_identifier {
my $class = ref $_[0] || $_[0];
$class =~ s/.*?(\w+)$/$1/;
return $class;
}
=head1 METHODS
=head2 rabx_column
# In one of your ::Result:: modules
__PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn");
__PACKAGE__->rabx_column('data');
This sets up the filtering to and from the database, and also changes the
set_filtered_column behaviour to not trust the cache.
=cut
sub rabx_column {
my ($class, $col) = @_;
# Apply the filtering for this column
$class->filter_column(
$col => {
filter_from_storage => sub {
my $self = shift;
my $ser = shift;
return undef unless defined $ser;
utf8::encode($ser) if utf8::is_utf8($ser);
my $h = new IO::String($ser);
return RABX::wire_rd($h);
},
filter_to_storage => sub {
my $self = shift;
my $data = shift;
my $ser = '';
my $h = new IO::String($ser);
RABX::wire_wr( $data, $h );
return $ser;
},
}
);
# store that this column is a RABX column.
$RABX_COLUMNS{ _get_class_identifier($class) }{$col} = 1;
}
sub set_filtered_column {
my ($self, $col, $val) = @_;
my $class = ref $self;
# because filtered objects may be expensive to marshall for storage there
# is a cache that attempts to detect if they have changed or not. For us
# this cache breaks things and our marshalling is cheap, so clear it when
# trying set a column.
delete $self->{_filtered_column}{$col}
if $RABX_COLUMNS{ _get_class_identifier($class) }{$col};
return $self->next::method($col, $val);
}
1;
|