object key modifier works okay
[dbsrgits/DBIx-Class-ResultSet-WithMetaData.git] / lib / DBIx / Class / ResultSet / WithMetaData.pm
CommitLineData
b8e6d226 1package DBIx::Class::ResultSet::WithMetaData;
2
3use strict;
4use warnings;
5
6use Data::Alias;
7use Moose;
79b18b81 8use Method::Signatures::Simple;
b8e6d226 9extends 'DBIx::Class::ResultSet';
10
11has '_row_info' => (
683ececb 12 is => 'rw',
13 isa => 'HashRef'
b8e6d226 14);
15
16has 'was_row' => (
683ececb 17 is => 'rw',
18 isa => 'Int'
b8e6d226 19);
20
b51d39c8 21has 'id_cols' => (
683ececb 22 is => 'rw',
23 isa => 'ArrayRef',
b51d39c8 24);
683ececb 25
b9495a50 26has '_hash_modifiers' => (
27 is => 'rw',
28 isa => 'ArrayRef',
29);
30
31has '_key_modifiers' => (
f1690863 32 is => 'rw',
33 isa => 'ArrayRef',
34);
35
9432d8bf 36has '_object_hash_modifiers' => (
37 is => 'rw',
38 isa => 'ArrayRef',
39);
40
41has '_object_key_modifiers' => (
42 is => 'rw',
43 isa => 'ArrayRef',
44);
45
683ececb 46=head1 VERSION
47
6419b987 48Version 1.000002
683ececb 49
50=cut
51
6419b987 52our $VERSION = '1.000002';
683ececb 53
54=head1 NAME
55
56DBIx::Class::ResultSet::WithMetaData
57
58=head1 SYNOPSIS
59
60 package MyApp::Schema::ResultSet::ObjectType;
61
62 use Moose;
63 use MooseX::Method::Signatures;
64 extends 'DBIx::Class::ResultSet::WithMetaData;
65
66 method with_substr () {
50e9dd04 67 return $self->_with_meta_key(
68 substr => sub {
69 return substr(shift->{name}, 0, 3);
70 }
71 );
683ececb 72 }
73
74 ...
75
76
77 # then somewhere else
78
d1d2fc5e 79 my $object_type_arrayref = $object_type_rs->with_substr->display();
683ececb 80
81 # [{
82 # 'artistid' => '1',
83 # 'name' => 'Caterwauler McCrae',
84 # 'substr' => 'Cat'
85 # },
86 # {
87 # 'artistid' => '2',
88 # 'name' => 'Random Boy Band',
89 # 'substr' => 'Ran'
90 # },
91 # {
92 # 'artistid' => '3',
93 # 'name' => 'We Are Goth',
94 # 'substr' => 'We '
95 # }]
96
97=head1 DESCRIPTION
98
99Attach metadata to rows by chaining ResultSet methods together. When the ResultSet is
cbce6bed 100flattened to an ArrayRef the metadata is merged with the row hashes to give
683ececb 101a combined 'hash-plus-other-stuff' representation.
102
103=head1 METHODS
104
105=cut
b51d39c8 106
b8e6d226 107sub new {
683ececb 108 my $self = shift;
b51d39c8 109
683ececb 110 my $new = $self->next::method(@_);
9432d8bf 111 foreach my $key (qw/_row_info was_row id_cols _key_modifiers _hash_modifiers _object_key_modifiers _object_hash_modifiers/) {
683ececb 112 alias $new->{$key} = $new->{attrs}{$key};
113 }
b8e6d226 114
683ececb 115 unless ($new->_row_info) {
116 $new->_row_info({});
117 }
b8e6d226 118
b9495a50 119 unless ($new->_key_modifiers) {
120 $new->_key_modifiers([]);
121 }
122 unless ($new->_hash_modifiers) {
123 $new->_hash_modifiers([]);
f1690863 124 }
9432d8bf 125 unless ($new->_object_key_modifiers) {
126 $new->_object_key_modifiers([]);
127 }
128 unless ($new->_object_hash_modifiers) {
129 $new->_object_hash_modifiers([]);
130 }
f1690863 131
683ececb 132 unless ($new->id_cols && scalar(@{$new->id_cols})) {
133 $new->id_cols([sort $new->result_source->primary_columns]);
134 }
b8e6d226 135
683ececb 136 return $new;
b8e6d226 137}
138
683ececb 139=head2 display
140
141=over 4
142
143=item Arguments: none
144
145=item Return Value: ArrayRef
146
147=back
148
149 $arrayref_of_row_hashrefs = $rs->display();
150
151This method uses L<DBIx::Class::ResultClass::HashRefInflator> to convert all
cbce6bed 152rows in the ResultSet to HashRefs. Then the subrefs that were added via
153L</_with_meta_key> or L</_with_meta_hash> are run for each row and the
154resulting data merged with them.
683ececb 155
156=cut
157
b8e6d226 158method display () {
159 my $rs = $self->search({});
4cac0ed6 160# $rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
161 $rs->result_class('DBIx::Class::WithMetaData::Inflator');
b8e6d226 162 my @rows;
4cac0ed6 163 foreach my $row_rep ($rs->all) {
9432d8bf 164 # the custom inflator inflates to a arrayref with two versions of the row in it - hash and obj
4cac0ed6 165 my ($row, $row_obj) = @{$row_rep};
f1690863 166 # THIS BLOCK IS DEPRECATED
683ececb 167 if (my $info = $self->row_info_for(id => $self->_mk_id(row => $row))) {
168 $row = { %{$row}, %{$info} };
169 }
f1690863 170
b9495a50 171 foreach my $modifier (@{$rs->_hash_modifiers}) {
172 my $row_hash = $modifier->($row);
173 if (ref $row_hash ne 'HASH') {
174 die 'modifier subref (added via build_metadata) did not return hashref';
175 }
176
177 # simple merge for now, potentially needs to be more complex
178 $row->{$_} = $row_hash->{$_} for keys %{$row_hash};
179 }
180
fbe6d90c 181 foreach my $modifier (@{$rs->_object_hash_modifiers}) {
182 my $row_hash = $modifier->($row, $row_obj);
183 if (ref $row_hash ne 'HASH') {
184 die 'modifier subref (added via build_metadata) did not return hashref';
185 }
186
187 # simple merge for now, potentially needs to be more complex
188 $row->{$_} = $row_hash->{$_} for keys %{$row_hash};
189 }
190
b9495a50 191 foreach my $params (@{$rs->_key_modifiers}) {
192 my $modifier = $params->{modifier};
193 my $key = $params->{key};
194
195 if (my $val = $modifier->($row)) {
196 $row->{$key} = $val;
f1690863 197 }
f1690863 198 }
9432d8bf 199
200 foreach my $params (@{$rs->_object_key_modifiers}) {
201 my $modifier = $params->{modifier};
202 my $key = $params->{key};
203
204 if (my $val = $modifier->($row, $row_obj)) {
205 $row->{$key} = $val;
206 }
207 }
683ececb 208 push(@rows, $row);
209 }
b8e6d226 210
211 return ($self->was_row) ? $rows[0] : \@rows;
212}
213
b9495a50 214=head2 _with_meta_key
f1690863 215
216=over 4
217
b9495a50 218=item Arguments: key_name => subref($row_hash)
f1690863 219
220=item Return Value: ResultSet
221
222=back
223
b9495a50 224 $self->_with_meta_key( substr => sub ($row) {
225 return substr(shift->{name}, 0, 3);
f1690863 226 });
227
b9495a50 228This method allows you populate a certain key for each row hash at L</display> time.
f1690863 229
230=cut
231
b9495a50 232method _with_meta_key ($key, $modifier) {
233 my $rs = $self->search({});
234 unless ($key) {
235 die 'build_metadata called without key';
236 }
237
f1690863 238 unless ($modifier && (ref $modifier eq 'CODE')) {
239 die 'build_metadata called without modifier param';
240 }
241
b9495a50 242 push( @{$rs->_key_modifiers}, { key => $key, modifier => $modifier });
9432d8bf 243 return $rs;
244}
245
246=head2 _with_object_meta_key
247
248=over 4
249
250=item Arguments: key_name => subref($row_hash, $row_obj)
251
252=item Return Value: ResultSet
253
254=back
255
256 $self->_with_object_meta_key( substr => sub {
257 my ($row_hash, $row_obj) = @_;
258 return substr($row_obj->row_method, 0, 3);
259 });
260
261The same as L</_with_meta_key> but the subref gets the row object
262as well as the row hash. This should only be used when you need to
263access row methods as it's slower to inflate objects.
264
265=cut
266
267method _with_object_meta_key ($key, $modifier) {
268 my $rs = $self->search({});
269 unless ($key) {
270 die '_with_object_meta_key called without key';
271 }
272
273 unless ($modifier && (ref $modifier eq 'CODE')) {
274 die '_with_object_meta_key called without modifier param';
275 }
276
277 push( @{$rs->_object_key_modifiers}, { key => $key, modifier => $modifier });
b9495a50 278 return $rs;
f1690863 279}
280
b9495a50 281=head2 _with_meta_hash
282
283=over 4
284
285=item Arguments: subref($row_hash)
286
287=item Return Value: ResultSet
288
289=back
290
291 $self->_with_meta_hash( sub ($row) {
292 my $row = shift;
293 my $return_hash = { substr => substr($row->{name}, 0, 3), substr2 => substr($row->{name}, 0, 4) };
294 return $return_hash;
295 });
296
297Use this method when you want to populate multiple keys of the hash at the same time. If you just want to
298populate one key, use L</_with_meta_key>.
299
300=cut
301
302method _with_meta_hash ($modifier) {
303 my $rs = $self->search({});
304 unless ($modifier && (ref $modifier eq 'CODE')) {
305 die 'build_metadata called without modifier param';
306 }
307
308 push( @{$rs->_hash_modifiers}, $modifier );
309 return $rs;
310}
f1690863 311
fbe6d90c 312=head2 _with_object_meta_hash
313
314=over 4
315
316=item Arguments: subref($row_hash, $row_object)
317
318=item Return Value: ResultSet
319
320=back
321
322 $self->_with_object_meta_hash( sub {
323 my ($row_hash, $row_object) = @_;
324
325 my $return_hash = { substr => substr($row_object->name, 0, 3), substr2 => substr($row_hash->{name}, 0, 4) };
326 return $return_hash;
327 });
328
329Like L</_with_meta_hash> but the subref gets the row object
330as well as the row hash. This should only be used when you need to
331access row methods as it's slower to inflate objects.
332
333=cut
334
335method _with_object_meta_hash ($modifier) {
336 my $rs = $self->search({});
337 unless ($modifier && (ref $modifier eq 'CODE')) {
338 die 'build_metadata called without modifier param';
339 }
340
341 push( @{$rs->_object_hash_modifiers}, $modifier );
342 return $rs;
343}
344
f1690863 345=head2 add_row_info (DEPRECATED)
683ececb 346
347=over 4
348
349=item Arguments: row => DBIx::Class::Row object, info => HashRef to attach to the row
350
351=item Return Value: ResultSet
352
353=back
354
355 $rs = $rs->add_row_info(row => $row, info => { dates => [qw/mon weds fri/] } );
356
f1690863 357DEPRECATED - this method is quite slow as it requires that you iterate through
358the resultset each time you want to add metadata. Replaced by L</build_metadata>.
683ececb 359
360=cut
361
79b18b81 362method add_row_info (%opts) {
363 my ($row, $id, $info) = map { $opts{$_} } qw/row id info/;
f1690863 364
365 warn 'DEPRECATED - add_row_info is deprecated in favour of build_metadata';
683ececb 366 if ($row) {
367 $id = $self->_mk_id(row => { $row->get_columns });
368 }
2aec43ca 369
370 unless ($row || $self->find($id)) {
683ececb 371 die 'invalid id passed to add_row_info';
372 }
373
374 if (my $existing = $self->_row_info->{$id}) {
375 $info = { %{$existing}, %{$info} };
376 }
377
378 $self->_row_info->{$id} = $info;
b8e6d226 379}
380
f1690863 381# DEPRECATED
79b18b81 382method row_info_for (%opts) {
383 my $id = $opts{id};
683ececb 384 return $self->_row_info->{$id};
b8e6d226 385}
386
f1690863 387# DEPRECATED
79b18b81 388method _mk_id (%opts) {
389 my $row = $opts{row};
683ececb 390 return join('-', map { $row->{$_} } @{$self->id_cols});
b51d39c8 391}
b8e6d226 392
024c1044 393=head1 AUTHOR
394
395 Luke Saunders <luke.saunders@gmail.com>
396
397=head1 THANKS
398
399As usual, thanks to Matt S Trout for the sanity check.
400
401=head1 LICENSE
402
403 This library is free software under the same license as perl itself
404
405=cut
406
b8e6d226 4071;