Commit | Line | Data |
b8e6d226 |
1 | package DBIx::Class::ResultSet::WithMetaData; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | |
6 | use Data::Alias; |
7 | use Moose; |
79b18b81 |
8 | use Method::Signatures::Simple; |
b8e6d226 |
9 | extends 'DBIx::Class::ResultSet'; |
10 | |
11 | has '_row_info' => ( |
683ececb |
12 | is => 'rw', |
13 | isa => 'HashRef' |
b8e6d226 |
14 | ); |
15 | |
16 | has 'was_row' => ( |
683ececb |
17 | is => 'rw', |
18 | isa => 'Int' |
b8e6d226 |
19 | ); |
20 | |
b51d39c8 |
21 | has 'id_cols' => ( |
683ececb |
22 | is => 'rw', |
23 | isa => 'ArrayRef', |
b51d39c8 |
24 | ); |
683ececb |
25 | |
b9495a50 |
26 | has '_hash_modifiers' => ( |
27 | is => 'rw', |
28 | isa => 'ArrayRef', |
29 | ); |
30 | |
31 | has '_key_modifiers' => ( |
f1690863 |
32 | is => 'rw', |
33 | isa => 'ArrayRef', |
34 | ); |
35 | |
9432d8bf |
36 | has '_object_hash_modifiers' => ( |
37 | is => 'rw', |
38 | isa => 'ArrayRef', |
39 | ); |
40 | |
41 | has '_object_key_modifiers' => ( |
42 | is => 'rw', |
43 | isa => 'ArrayRef', |
44 | ); |
45 | |
683ececb |
46 | =head1 VERSION |
47 | |
6419b987 |
48 | Version 1.000002 |
683ececb |
49 | |
50 | =cut |
51 | |
6419b987 |
52 | our $VERSION = '1.000002'; |
683ececb |
53 | |
54 | =head1 NAME |
55 | |
56 | DBIx::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 | |
99 | Attach metadata to rows by chaining ResultSet methods together. When the ResultSet is |
cbce6bed |
100 | flattened to an ArrayRef the metadata is merged with the row hashes to give |
683ececb |
101 | a combined 'hash-plus-other-stuff' representation. |
102 | |
103 | =head1 METHODS |
104 | |
105 | =cut |
b51d39c8 |
106 | |
b8e6d226 |
107 | sub 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 | |
151 | This method uses L<DBIx::Class::ResultClass::HashRefInflator> to convert all |
cbce6bed |
152 | rows in the ResultSet to HashRefs. Then the subrefs that were added via |
153 | L</_with_meta_key> or L</_with_meta_hash> are run for each row and the |
154 | resulting data merged with them. |
683ececb |
155 | |
156 | =cut |
157 | |
b8e6d226 |
158 | method 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 |
228 | This method allows you populate a certain key for each row hash at L</display> time. |
f1690863 |
229 | |
230 | =cut |
231 | |
b9495a50 |
232 | method _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 | |
261 | The same as L</_with_meta_key> but the subref gets the row object |
262 | as well as the row hash. This should only be used when you need to |
263 | access row methods as it's slower to inflate objects. |
264 | |
265 | =cut |
266 | |
267 | method _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 | |
297 | Use this method when you want to populate multiple keys of the hash at the same time. If you just want to |
298 | populate one key, use L</_with_meta_key>. |
299 | |
300 | =cut |
301 | |
302 | method _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 | |
329 | Like L</_with_meta_hash> but the subref gets the row object |
330 | as well as the row hash. This should only be used when you need to |
331 | access row methods as it's slower to inflate objects. |
332 | |
333 | =cut |
334 | |
335 | method _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 |
357 | DEPRECATED - this method is quite slow as it requires that you iterate through |
358 | the resultset each time you want to add metadata. Replaced by L</build_metadata>. |
683ececb |
359 | |
360 | =cut |
361 | |
79b18b81 |
362 | method 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 |
382 | method row_info_for (%opts) { |
383 | my $id = $opts{id}; |
683ececb |
384 | return $self->_row_info->{$id}; |
b8e6d226 |
385 | } |
386 | |
f1690863 |
387 | # DEPRECATED |
79b18b81 |
388 | method _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 | |
399 | As 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 |
407 | 1; |