line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DBIx::Class::Events; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# ABSTRACT: Store Events for your DBIC Results |
4
|
|
|
|
|
|
|
our $VERSION = '0.9.2'; # VERSION |
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
790990
|
use v5.10; |
|
1
|
|
|
|
|
9
|
|
7
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
16
|
|
8
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
32
|
|
9
|
1
|
|
|
1
|
|
25
|
use parent 'DBIx::Class'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
7
|
|
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
65
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
645
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( events_relationship => 'events' ); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub event { |
16
|
73
|
|
|
73
|
1
|
114251
|
my ($self, $event, $col_data) = @_; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# Just calling $object->event shouldn't work |
19
|
73
|
100
|
|
|
|
452
|
croak("Event is required") unless defined $event; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my %col_data = ( |
22
|
|
|
|
|
|
|
$self->event_defaults($event, $col_data), |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# Ignore unknown columns when we enter the event. |
25
|
|
|
|
|
|
|
# TODO: optimize the ->columns call |
26
|
71
|
|
|
|
|
242
|
map { $_ => $col_data->{$_} } |
27
|
72
|
|
|
|
|
197
|
grep { exists $col_data->{$_} } |
|
371
|
|
|
|
|
13854
|
|
28
|
|
|
|
|
|
|
$self->result_source |
29
|
|
|
|
|
|
|
->related_source( $self->events_relationship )->columns, |
30
|
|
|
|
|
|
|
); |
31
|
|
|
|
|
|
|
|
32
|
72
|
|
|
|
|
1266
|
return $self->create_related( $self->events_relationship, |
33
|
|
|
|
|
|
|
{ %col_data, event => $event } ); |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
72
|
1
|
|
sub event_defaults {} |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub state_at { |
39
|
17
|
|
|
17
|
1
|
46911
|
my ($self, $time_stamp, @args) = @_; |
40
|
|
|
|
|
|
|
|
41
|
17
|
100
|
|
|
|
66
|
if (ref $time_stamp) { |
42
|
4
|
|
|
|
|
13
|
my $dtf = $self->result_source->schema->storage->datetime_parser; |
43
|
4
|
|
|
|
|
198
|
$time_stamp = $dtf->format_datetime( $time_stamp ); |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
17
|
|
|
|
|
591
|
my $events = $self->search_related( $self->events_relationship ); |
47
|
17
|
|
|
|
|
7230
|
my $alias = $events->current_source_alias; |
48
|
|
|
|
|
|
|
$events = $events->search( { |
49
|
|
|
|
|
|
|
"$alias.event" => { in => [qw( insert update delete )] }, |
50
|
|
|
|
|
|
|
"$alias.triggered_on" => { '<=', $time_stamp }, |
51
|
|
|
|
|
|
|
}, |
52
|
|
|
|
|
|
|
{ |
53
|
|
|
|
|
|
|
select => [ "$alias.event", "$alias.details" ], |
54
|
|
|
|
|
|
|
order_by => [ |
55
|
17
|
|
|
|
|
223
|
map {"$alias.$_ desc"} 'triggered_on', |
|
34
|
|
|
|
|
205
|
|
56
|
|
|
|
|
|
|
$events->result_source->primary_columns |
57
|
|
|
|
|
|
|
], |
58
|
|
|
|
|
|
|
} )->search(@args); |
59
|
|
|
|
|
|
|
|
60
|
17
|
|
|
|
|
16992
|
my $event = $events->next; |
61
|
17
|
100
|
100
|
|
|
42035
|
return undef if !$event or $event->event eq 'delete'; |
62
|
|
|
|
|
|
|
|
63
|
11
|
|
|
|
|
173
|
my %state; |
64
|
11
|
|
|
|
|
25
|
while ($event) { |
65
|
22
|
100
|
|
|
|
1369
|
%state = ( %{ $event->details || {} }, %state ); |
|
22
|
|
|
|
|
407
|
|
66
|
|
|
|
|
|
|
|
67
|
22
|
100
|
|
|
|
8636
|
last if $event->event eq 'insert'; |
68
|
11
|
|
|
|
|
138
|
$event = $events->next; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
11
|
|
|
|
|
163
|
return \%state; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub insert { |
75
|
22
|
|
|
22
|
1
|
365743
|
my ( $class, @args ) = @_; |
76
|
|
|
|
|
|
|
|
77
|
22
|
|
|
|
|
86
|
my $self = $class->next::method(@args); |
78
|
|
|
|
|
|
|
|
79
|
22
|
|
|
|
|
38428
|
my %inserted = $self->get_columns; |
80
|
22
|
|
|
|
|
312
|
$self->event( insert => { details => \%inserted } ); |
81
|
|
|
|
|
|
|
|
82
|
22
|
|
|
|
|
86786
|
return $self; |
83
|
|
|
|
|
|
|
}; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub update { |
86
|
13
|
|
|
13
|
1
|
27107
|
my ( $self, @args ) = @_; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# Do this here instead of letting our parent do it |
89
|
|
|
|
|
|
|
# so that we can use get_dirty_columns. |
90
|
13
|
100
|
|
|
|
76
|
$self->set_inflated_columns(@args) if @args; |
91
|
|
|
|
|
|
|
|
92
|
13
|
|
|
|
|
372
|
my %changed = $self->get_dirty_columns; |
93
|
|
|
|
|
|
|
|
94
|
13
|
|
|
|
|
160
|
$self->next::method(); # we already set_inflated_columns |
95
|
|
|
|
|
|
|
|
96
|
13
|
100
|
|
|
|
13207
|
$self->event( update => { details => \%changed } ) if %changed; |
97
|
|
|
|
|
|
|
|
98
|
13
|
|
|
|
|
37173
|
return $self; |
99
|
|
|
|
|
|
|
}; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub delete { |
102
|
6
|
|
|
6
|
1
|
2173
|
my ( $self, @args ) = @_; |
103
|
|
|
|
|
|
|
|
104
|
6
|
|
|
|
|
39
|
my $ret = $self->next::method(@args); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# DBIx::Class::Row::delete has a special edge case for calling |
107
|
|
|
|
|
|
|
# delete as a class method, we however can't log it in that case. |
108
|
6
|
50
|
|
|
|
17413
|
if ( ref $self ) { |
109
|
6
|
|
|
|
|
33
|
my %deleted = $self->get_columns; |
110
|
6
|
|
|
|
|
101
|
$self->event( delete => { details => \%deleted } ); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
6
|
|
|
|
|
20117
|
return $ret; |
114
|
|
|
|
|
|
|
}; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
1; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=pod |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=encoding UTF-8 |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head1 NAME |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
DBIx::Class::Events - Store Events for your DBIC Results |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head1 VERSION |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
version 0.9.2 |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=head1 SYNOPSIS |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
my $artist |
133
|
|
|
|
|
|
|
= $schema->resultset('Artist')->create( { name => 'Dead Salmon' } ); |
134
|
|
|
|
|
|
|
$artist->events->count; # is now 1, an 'insert' event |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
$artist->change_name('Trout'); # add a name_change event |
137
|
|
|
|
|
|
|
$artist->update; # An update event, last_name_change_id and name |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# Find their previous name |
140
|
|
|
|
|
|
|
my $name_change = $artist->last_name_change; |
141
|
|
|
|
|
|
|
print $name_change->details->{old}, "\n"; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
See C<change_name> and C<last_name_change> example definitions |
144
|
|
|
|
|
|
|
in L</CONFIGURATION AND ENVIRONMENT>. |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# Three more name_change events and one update event |
147
|
|
|
|
|
|
|
$artist->change_name('Fried Trout'); |
148
|
|
|
|
|
|
|
$artist->change_name('Poached Trout in a White Wine Sauce'); |
149
|
|
|
|
|
|
|
$artist->change_name('Herring'); |
150
|
|
|
|
|
|
|
$artist->update; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# Look up all the band's previous names |
153
|
|
|
|
|
|
|
print "$_\n" |
154
|
|
|
|
|
|
|
for map { $_->details->{old} } |
155
|
|
|
|
|
|
|
$artist->events->search( { event => 'name_change' } ); |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
$artist->delete; # and then they break up. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# We can find out now when they broke up, if we remember their id. |
160
|
|
|
|
|
|
|
my $deleted_on |
161
|
|
|
|
|
|
|
= $schema->resultset('ArtistEvent') |
162
|
|
|
|
|
|
|
->single( { artistid => $artist->id, event => 'delete' } ) |
163
|
|
|
|
|
|
|
->triggered_on; |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Find the state of the band was just before the breakup. |
166
|
|
|
|
|
|
|
my $state_before_breakup |
167
|
|
|
|
|
|
|
= $artist->state_at( $deleted_on->subtract( seconds => 1 ) ); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# Maybe this is common, |
170
|
|
|
|
|
|
|
# so we have a column to link to who they used to be. |
171
|
|
|
|
|
|
|
my $previous_artist_id = delete $state_before_breakup->{artistid}; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# Then we can form a new band, linked to the old, |
174
|
|
|
|
|
|
|
# with the same values as the old band, but a new name. |
175
|
|
|
|
|
|
|
$artist = $schema->resultset('Artist')->create( { |
176
|
|
|
|
|
|
|
%{$state_before_breakup}, |
177
|
|
|
|
|
|
|
previousid => $previous_artist_id, |
178
|
|
|
|
|
|
|
name => 'Red Herring', |
179
|
|
|
|
|
|
|
} ); |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# After a few more name changes, split-ups, and getting back together, |
182
|
|
|
|
|
|
|
# we find an event we should have considered, but didn't. |
183
|
|
|
|
|
|
|
my $death_event |
184
|
|
|
|
|
|
|
= $artist->event( death => { details => { who => 'drummer' } } ); |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# but, we then go back and modify it to note that it was only a rumor |
187
|
|
|
|
|
|
|
$death_event->details->{only_a_rumour} = 1; |
188
|
|
|
|
|
|
|
$death_event->make_column_dirty('details'); # changing the hashref doesn't |
189
|
|
|
|
|
|
|
$death_event->update |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# And after even more new names and arguments, they split up again |
192
|
|
|
|
|
|
|
$artist->delete; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
See L</CONFIGURATION AND ENVIRONMENT> for how to set up the tables. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=head1 DESCRIPTION |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
A framework for capturing events that happen to a Result in a table, |
199
|
|
|
|
|
|
|
L</PRECONFIGURED EVENTS> are triggered automatically to track changes. |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
This is useful for both being able to see the history of things |
202
|
|
|
|
|
|
|
in the database as well as logging when events happen that |
203
|
|
|
|
|
|
|
can be looked up later. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Events can be used to track when things happen. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=over |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=item when a user on a website clicks a particular button |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=item when a recipe was prepared |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=item when a song was played |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=item anything that doesn't fit in the main table |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=back |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head1 CONFIGURATION AND ENVIRONMENT |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=head2 event_defaults |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
A method that returns an even-sized list of default values that will be used |
224
|
|
|
|
|
|
|
when creating a new event. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
my %defaults = $object->event_defaults( $event_type, \%col_data ); |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
The C<$event_type> is a string defining the "type" of event being created. |
229
|
|
|
|
|
|
|
The C<%col_data> is a reference to the parameters passed in. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
No default values, but if your database doesn't set a default for |
232
|
|
|
|
|
|
|
C<triggered_on>, you may want to set it to a C<< DateTime->now >> object. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 events_relationship |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
An class accessor that returns the relationship to get from your object |
237
|
|
|
|
|
|
|
to the relationship. |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
Default is C<events>, but you can override it: |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
__PACKAGE__->has_many( |
242
|
|
|
|
|
|
|
'cd_events' => |
243
|
|
|
|
|
|
|
( 'MyApp::Schema::Result::ArtistEvents', 'cdid' ), |
244
|
|
|
|
|
|
|
{ cascade_delete => 0 }, |
245
|
|
|
|
|
|
|
); |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
__PACKAGE__->events_relationship('cd_events'); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=head2 Tables |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=head3 Tracked Table |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
The table with events to be tracked in the L</Tracking Table>. |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
It requires the Component and L</events_relationship> in the Result class: |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
package MyApp::Schema::Result::Artist; |
258
|
|
|
|
|
|
|
use base qw( DBIx::Class::Core ); |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
...; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
__PACKAGE__->load_components( qw/ Events / ); |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# A different name can be used with the "events_relationship" attribute |
265
|
|
|
|
|
|
|
__PACKAGE__->has_many( |
266
|
|
|
|
|
|
|
'events' => ( 'MyApp::Schema::Result::ArtistEvent', 'artistid' ), |
267
|
|
|
|
|
|
|
{ cascade_delete => 0 }, |
268
|
|
|
|
|
|
|
); |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
You can also add custom events to track when something happens. For example, |
271
|
|
|
|
|
|
|
you can create a method to add events when an artist changes their name: |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
__PACKAGE__->add_column( |
274
|
|
|
|
|
|
|
last_name_change_id => { data_type => 'integer' } ); |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
__PACKAGE__->has_one( |
277
|
|
|
|
|
|
|
'last_name_change' => 'MyApp::Schema::Result::ArtistEvent', |
278
|
|
|
|
|
|
|
{ 'foreign.artisteventid' => 'self.last_name_change_id' }, |
279
|
|
|
|
|
|
|
{ cascade_delete => 0 }, |
280
|
|
|
|
|
|
|
); |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub change_name { |
283
|
|
|
|
|
|
|
my ( $self, $new_name ) = @_; |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
my $event = $self->event( name_change => |
286
|
|
|
|
|
|
|
{ details => { new => $new_name, old => $self->name } } ); |
287
|
|
|
|
|
|
|
$self->last_name_change( $event ); |
288
|
|
|
|
|
|
|
# $self->update; # be lazy and make our caller call ->update |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
$self->name( $new_name ); |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head3 Tracking Table |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
This table holds the events for the L</Tracked Table>. |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
The C<triggered_on> column must either provide a C<DEFAULT> value |
298
|
|
|
|
|
|
|
or you should add a default to L</event_defaults>. |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
package MyApp::Schema::Result::ArtistEvent; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
use warnings; |
303
|
|
|
|
|
|
|
use strict; |
304
|
|
|
|
|
|
|
use JSON; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
use base qw( DBIx::Class::Core ); |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
__PACKAGE__->load_components(qw/ InflateColumn::DateTime /); |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
__PACKAGE__->table('artist_event'); |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
__PACKAGE__->add_columns( |
313
|
|
|
|
|
|
|
artisteventid => { data_type => 'integer', is_auto_increment => 1 }, |
314
|
|
|
|
|
|
|
artistid => { data_type => 'integer' }, |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# The type of event |
317
|
|
|
|
|
|
|
event => { data_type => 'varchar' }, |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# Any other custom columns you want to store for each event. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
triggered_on => { |
322
|
|
|
|
|
|
|
data_type => 'datetime', |
323
|
|
|
|
|
|
|
default_value => \'NOW()', |
324
|
|
|
|
|
|
|
}, |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# Where we store freeform data about what happened |
327
|
|
|
|
|
|
|
details => { data_type => 'longtext' }, |
328
|
|
|
|
|
|
|
); |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
__PACKAGE__->set_primary_key('artisteventid'); |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# You should set up automatic inflation/deflation of the details column |
333
|
|
|
|
|
|
|
# as it is used this way by "state_at" and the insert/update/delete |
334
|
|
|
|
|
|
|
# events. Does not have to be JSON, just be able to serialize a hashref. |
335
|
|
|
|
|
|
|
{ |
336
|
|
|
|
|
|
|
my $json = JSON->new->utf8; |
337
|
|
|
|
|
|
|
__PACKAGE__->inflate_column( 'details' => { |
338
|
|
|
|
|
|
|
inflate => sub { $json->decode(shift) }, |
339
|
|
|
|
|
|
|
deflate => sub { $json->encode(shift) }, |
340
|
|
|
|
|
|
|
} ); |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
# A path back to the object that this event is for, |
344
|
|
|
|
|
|
|
# not required unlike the has_many "events" relationship above |
345
|
|
|
|
|
|
|
__PACKAGE__->belongs_to( |
346
|
|
|
|
|
|
|
'artist' => ( 'MyApp::Schema::Result::Artist', 'artistid' ) ); |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
You probably also want an index for searching for events: |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub sqlt_deploy_hook { |
351
|
|
|
|
|
|
|
my ( $self, $sqlt_table ) = @_; |
352
|
|
|
|
|
|
|
$sqlt_table->add_index( |
353
|
|
|
|
|
|
|
name => 'artist_event_idx', |
354
|
|
|
|
|
|
|
fields => [ "artistid", "triggered_on", "event" ], |
355
|
|
|
|
|
|
|
); |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=head1 PRECONFIGURED EVENTS |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
Automatically creates Events for actions that modify a row. |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
See the L</BUGS AND LIMITATIONS> of bulk modifications on events. |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=over |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=item insert |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
Logs all columns to the C<details> column, with an C<insert> event. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=item update |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
Logs dirty columns to the C<details> column, with an C<update> event. |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=item delete |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
Logs all columns to the C<details> column, with a C<delete> event. |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=back |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=head1 METHODS |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=head2 event |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Inserts a new event with L</event_defaults>: |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
my $new_event = $artist->event( $event => \%params ); |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
First, the L</event_defaults> method is called to build a list of values |
389
|
|
|
|
|
|
|
to set on the new event. This method is passed the C<$event> and a reference |
390
|
|
|
|
|
|
|
to C<%params>. |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
Then, the C<%params>, filtered for valid L</events_relationship> C<columns>, |
393
|
|
|
|
|
|
|
are added to the C<create_related> arguments, overriding the defaults. |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=head2 state_at |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
Takes a timestamp and returns the state of the thing at that timestamp as a |
398
|
|
|
|
|
|
|
hash reference. Can be either a correctly deflated string or a DateTime |
399
|
|
|
|
|
|
|
object that will be deflated with C<format_datetime>. |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
Returns undef if the object was not C<in_storage> at the timestamp. |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
my $state = $schema->resultset('Artist')->find( { name => 'David Bowie' } ) |
404
|
|
|
|
|
|
|
->state_at('2006-05-29 08:00'); |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
An idea is to use it to recreate an object as it was at that timestamp. |
407
|
|
|
|
|
|
|
Of course, default values that the database provides will not be included, |
408
|
|
|
|
|
|
|
unless the L</event_defaults> method accounts for that. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
my $resurrected_object |
411
|
|
|
|
|
|
|
= $object->result_source->new( $object->state_at($timestamp) ); |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
See ".. format a DateTime object for searching?" under L<DBIx::Class::Manual::FAQ/Searching> |
414
|
|
|
|
|
|
|
for details on formatting the timestamp. |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
You can pass additional L<search|DBIx::Class::ResultSet/search> conditions and |
417
|
|
|
|
|
|
|
attributes to this method. This is done in context of searching the events |
418
|
|
|
|
|
|
|
table: |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
my $state = $object->state_at($timestamp, \%search_cond, \%search_attrs); |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
There is no attempt to handle bulk updates or deletes. So, any changes to the |
425
|
|
|
|
|
|
|
database made by calling |
426
|
|
|
|
|
|
|
L<"update"|DBIx::Class::ResultSet/update> or L<"delete"|DBIx::Class::ResultSet/delete> |
427
|
|
|
|
|
|
|
will not create events the same as L<single row|DBIx::Class::Row> modifications. Use the |
428
|
|
|
|
|
|
|
L<"update_all"|DBIx::Class::ResultSet/update_all> or L<"delete_all"|DBIx::Class::ResultSet/delete_all> |
429
|
|
|
|
|
|
|
methods of the C<ResultSet> if you want these triggers. |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
There are three required columns on the L</events_relationship> table: |
432
|
|
|
|
|
|
|
C<event>, C<triggered_on>, and C<details>. We should eventually make those |
433
|
|
|
|
|
|
|
configurable. |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=head1 SEE ALSO |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=over |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=item L<DBIx::Class::AuditAny> |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=item L<DBIx::Class::AuditLog> |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=item L<DBIx::Class::Journal> |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=item L<DBIx::Class::PgLog> |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=back |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=head1 AUTHOR |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
Grant Street Group <developers@grantstreet.com> |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
This software is Copyright (c) 2018 - 2019 by Grant Street Group. |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
This is free software, licensed under: |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
The Artistic License 2.0 (GPL Compatible) |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head1 CONTRIBUTORS |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=for stopwords Andrew Fresh Brendan Byrd Justin Wheeler |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=over 4 |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=item * |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Andrew Fresh <andrew.fresh@grantstreet.com> |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=item * |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
Andrew Fresh <andrew+github@afresh1.com> |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=item * |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
Brendan Byrd <brendan.byrd@grantstreet.com> |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=item * |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Justin Wheeler <justin.wheeler@grantstreet.com> |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=back |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
=cut |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
__END__ |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
1; |