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   5103 use Mojo::Base -role;
  5         11  
  5         41  
3 5     5   2349 use Carp ();
  5         10  
  5         6545  
4              
5             our $VERSION = '0.01';
6              
7             requires 'reduce';
8              
9 17     17 1 15341 sub hashify { _reduce(shift, \&_multi_key_hash_assign, {}, @_) }
10              
11             sub hashify_collect {
12 53     53 1 94161 my $self = shift;
13              
14 53 100 100     224 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 44         135 return _reduce($self, \&_multi_key_hash_collect, {}, @_);
19             }
20              
21             sub collect_by {
22 57     57 1 105862 my $self = shift;
23              
24 57 100 100     264 if (ref $_[0] eq 'HASH' and _parse_flatten_option(shift)) {
25 7         35 return _reduce($self, \&_collect_by_and_flatten, [Mojo::Collection->new, {}], @_)->[0];
26             }
27              
28 48         183 return _reduce($self, \&_collect_by, [Mojo::Collection->new, {}], @_)->[0];
29             }
30              
31             sub _multi_key_hash_assign {
32 20     20   68 my ($hash, $keys, $value, @extra_values) = @_;
33 20 100       66 Carp::confess 'multiple values returned from get_value sub when one is expected' if @extra_values;
34              
35 18         36 _create_leading_key_hashes($hash, $keys)->{$keys->[-1]} = $value;
36             }
37              
38             sub _multi_key_hash_collect {
39 93     93   382 my ($hash, $keys, @values) = @_;
40              
41 93   66     136 push @{ _create_leading_key_hashes($hash, $keys)->{$keys->[-1]} ||= Mojo::Collection->new }, @values;
  93         166  
42             }
43              
44             sub _multi_key_hash_collect_and_flatten {
45 18     18   93 my ($hash, $keys, @values) = @_;
46              
47             push
48 18   66     29 @{ _create_leading_key_hashes($hash, $keys)->{$keys->[-1]} ||= Mojo::Collection->new },
  18         34  
49             _flatten(@values);
50             }
51              
52             sub _collect_by {
53 105     105   296 my ($collection, $hash, $keys, @values) = (@{+shift}, @_);
  105         259  
54              
55 105         204 my $leading_hash = _create_leading_key_hashes($hash, $keys);
56 105 100       268 unless (exists $leading_hash->{$keys->[-1]}) {
57 89         214 push @$collection, $leading_hash->{$keys->[-1]} = Mojo::Collection->new;
58             }
59              
60 105         557 push @{ $leading_hash->{$keys->[-1]} }, @values;
  105         285  
61             }
62              
63             sub _collect_by_and_flatten {
64 18     18   98 my ($collection, $hash, $keys, @values) = (@{+shift}, @_);
  18         44  
65              
66 18         37 my $leading_hash = _create_leading_key_hashes($hash, $keys);
67 18 100       48 unless (exists $leading_hash->{$keys->[-1]}) {
68 11         28 push @$collection, $leading_hash->{$keys->[-1]} = Mojo::Collection->new;
69             }
70              
71 18         77 push @{ $leading_hash->{$keys->[-1]} }, _flatten(@values);
  18         43  
72             }
73              
74             sub _create_leading_key_hashes {
75 252     252   433 my ($hash, $keys) = @_;
76              
77 252         392 my $cur_hash = $hash;
78 252         626 for my $key (@$keys[0..$#$keys - 1]) {
79 93   100     423 $cur_hash = $hash->{$key} ||= {};
80             }
81              
82 252         794 return $cur_hash;
83             }
84              
85             sub _reduce {
86 123     123   538 my ($self, $apply_key_and_value, $initial) = (shift, shift, shift);
87              
88 123 100       317 unless (@_) {
89 3         36 Carp::croak 'must provide get_keys sub';
90             }
91              
92 120         190 my $get_keys = shift;
93 120   100     307 my $get_keys_ref = ref $get_keys || 'scalar value';
94 120 100       339 Carp::croak qq{get_keys sub must be a subroutine, but was '$get_keys_ref'} unless $get_keys_ref eq 'CODE';
95              
96 111         162 my $get_value;
97 111 100       283 if (@_) {
98 58         129 $get_value = shift;
99 58   100     146 my $get_value_ref = ref $get_value || 'scalar value';
100 58 100       231 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 53     120   152 $get_value = sub { $_ };
  120         554  
103             }
104              
105             return $self->reduce(sub {
106 254     254   1134 local $_ = $b;
107 254         578 $apply_key_and_value->($a, [$get_keys->($b)], $get_value->($b));
108              
109 252         1314 $a;
110 102         550 }, $initial);
111             }
112              
113 92 100   92   229 sub _flatten { map { ref($_) ? _flatten(@$_) : $_ } @_ }
  100         293  
114              
115             sub _parse_flatten_option {
116 91     91   186 my ($options) = @_;
117 91 100       392 return unless %$options;
118              
119 30 100       120 Carp::confess 'only one option can be provided' if keys %$options > 1;
120              
121 28         68 my $flatten = delete $options->{flatten};
122              
123 28 100       74 Carp::confess 'unknown options provided: ' . Mojo::Util::dumper $options if %$options;
124              
125 26         100 return $flatten;
126             }
127              
128             1;
129             __END__