File Coverage

blib/lib/Mojo/Collection/Role/Transform.pm
Criterion Covered Total %
statement 68 68 100.0
branch 26 26 100.0
condition 16 18 88.8
subroutine 16 16 100.0
pod 3 3 100.0
total 129 131 98.4


line stmt bran cond sub pod time code
1             package Mojo::Collection::Role::Transform;
2 5     5   5075 use Mojo::Base -role;
  5         14  
  5         42  
3 5     5   2749 use Carp ();
  5         14  
  5         6623  
4              
5             our $VERSION = '0.04';
6              
7             requires 'reduce';
8              
9 18     18 1 16713 sub hashify { _reduce(shift, \&_multi_key_hash_assign, {}, @_) }
10              
11             sub hashify_collect {
12 57     57 1 106468 my $self = shift;
13              
14 57 100 100     231 if (ref $_[0] eq 'HASH' and _parse_flatten_option(shift)) {
15 7         37 return _reduce($self, \&_multi_key_hash_collect_and_flatten, {}, @_)
16             }
17              
18 48         149 return _reduce($self, \&_multi_key_hash_collect, {}, @_);
19             }
20              
21             sub collect_by {
22 57     57 1 107317 my $self = shift;
23              
24 57 100 100     240 if (ref $_[0] eq 'HASH' and _parse_flatten_option(shift)) {
25 7         31 return _reduce($self, \&_collect_by_and_flatten, [Mojo::Collection->new, {}], @_)->[0];
26             }
27              
28 48         184 return _reduce($self, \&_collect_by, [Mojo::Collection->new, {}], @_)->[0];
29             }
30              
31             sub _multi_key_hash_assign {
32 23     23   70 my ($hash, $keys, $value, @extra_values) = @_;
33 23 100       75 Carp::confess 'multiple values returned from get_value sub when one is expected' if @extra_values;
34              
35 21         42 _create_leading_key_hashes($hash, $keys)->{$keys->[-1]} = $value;
36             }
37              
38             sub _multi_key_hash_collect {
39 105     105   379 my ($hash, $keys, @values) = @_;
40              
41 105   66     140 push @{ _create_leading_key_hashes($hash, $keys)->{$keys->[-1]} ||= Mojo::Collection->new }, @values;
  105         195  
42             }
43              
44             sub _multi_key_hash_collect_and_flatten {
45 18     18   106 my ($hash, $keys, @values) = @_;
46              
47             push
48 18   66     28 @{ _create_leading_key_hashes($hash, $keys)->{$keys->[-1]} ||= Mojo::Collection->new },
  18         35  
49             _flatten(@values);
50             }
51              
52             sub _collect_by {
53 105     105   300 my ($collection, $hash, $keys, @values) = (@{+shift}, @_);
  105         227  
54              
55 105         203 my $leading_hash = _create_leading_key_hashes($hash, $keys);
56 105 100       270 unless (exists $leading_hash->{$keys->[-1]}) {
57 89         232 push @$collection, $leading_hash->{$keys->[-1]} = Mojo::Collection->new;
58             }
59              
60 105         602 push @{ $leading_hash->{$keys->[-1]} }, @values;
  105         249  
61             }
62              
63             sub _collect_by_and_flatten {
64 18     18   96 my ($collection, $hash, $keys, @values) = (@{+shift}, @_);
  18         43  
65              
66 18         37 my $leading_hash = _create_leading_key_hashes($hash, $keys);
67 18 100       45 unless (exists $leading_hash->{$keys->[-1]}) {
68 11         29 push @$collection, $leading_hash->{$keys->[-1]} = Mojo::Collection->new;
69             }
70              
71 18         71 push @{ $leading_hash->{$keys->[-1]} }, _flatten(@values);
  18         43  
72             }
73              
74             sub _create_leading_key_hashes {
75 267     267   444 my ($hash, $keys) = @_;
76              
77 267         387 my $cur_hash = $hash;
78 267         675 for my $key (@$keys[0..$#$keys - 1]) {
79 123   100     563 $cur_hash = $cur_hash->{$key} ||= {};
80             }
81              
82 267         863 return $cur_hash;
83             }
84              
85             sub _reduce {
86 128     128   540 my ($self, $apply_key_and_value, $initial) = (shift, shift, shift);
87              
88 128 100       305 unless (@_) {
89 3         44 Carp::croak 'must provide get_keys sub';
90             }
91              
92 125         196 my $get_keys = shift;
93 125   100     309 my $get_keys_ref = ref $get_keys || 'scalar value';
94 125 100       354 Carp::croak qq{get_keys sub must be a subroutine, but was '$get_keys_ref'} unless $get_keys_ref eq 'CODE';
95              
96 116         169 my $get_value;
97 116 100       264 if (@_) {
98 58         108 $get_value = shift;
99 58   100     168 my $get_value_ref = ref $get_value || 'scalar value';
100 58 100       225 Carp::croak qq{get_value must be a subroutine if provided, but was '$get_value_ref'} if $get_value_ref ne 'CODE';
101             } else {
102 58     135   168 $get_value = sub { $_ };
  135         614  
103             }
104              
105             return $self->reduce(sub {
106 269     269   1234 local $_ = $b;
107 269         597 $apply_key_and_value->($a, [$get_keys->($b)], $get_value->($b));
108              
109 267         1422 $a;
110 107         534 }, $initial);
111             }
112              
113 92 100   92   212 sub _flatten { map { ref($_) ? _flatten(@$_) : $_ } @_ }
  100         323  
114              
115             sub _parse_flatten_option {
116 94     94   219 my ($options) = @_;
117 94 100       384 return unless %$options;
118              
119 30 100       133 Carp::confess 'only one option can be provided' if keys %$options > 1;
120              
121 28         62 my $flatten = delete $options->{flatten};
122              
123 28 100       81 Carp::confess 'unknown options provided: ' . Mojo::Util::dumper $options if %$options;
124              
125 26         98 return $flatten;
126             }
127              
128             1;
129             __END__