File Coverage

blib/lib/Elastic/Model/TypeMap/Structured.pm
Criterion Covered Total %
statement 65 69 94.2
branch 24 26 92.3
condition 4 4 100.0
subroutine 16 17 94.1
pod n/a
total 109 116 93.9


line stmt bran cond sub pod time code
1             package Elastic::Model::TypeMap::Structured;
2             $Elastic::Model::TypeMap::Structured::VERSION = '0.51';
3 23     23   14937 use strict;
  23         50  
  23         866  
4 23     23   106 use warnings;
  23         38  
  23         728  
5              
6 23     23   110 use Elastic::Model::TypeMap::Base qw(:all);
  23         35  
  23         161  
7 23     23   156 use namespace::autoclean;
  23         35  
  23         200  
8              
9             #===================================
10             has_type 'MooseX::Meta::TypeConstraint::Structured',
11             #===================================
12                 deflate_via { _structured( 'deflator', @_ ) },
13                 inflate_via { _structured( 'inflator', @_ ) },
14                 map_via { _structured( 'mapper', @_ ) };
15              
16             #===================================
17             has_type 'MooseX::Types::Structured::Optional',
18             #===================================
19                 deflate_via { _content_handler( 'deflator', @_ ) },
20                 inflate_via { _content_handler( 'inflator', @_ ) },
21                 map_via { _content_handler( 'mapper', @_ ) };
22              
23             #===================================
24             has_type 'MooseX::Types::Structured::Tuple',
25             #===================================
26                 deflate_via { _deflate_tuple(@_) }, #
27                 inflate_via { _inflate_tuple(@_) }, #
28                 map_via { _map_dict( _tuple_to_dict(shift), @_ ) };
29              
30             #===================================
31             has_type 'MooseX::Types::Structured::Dict',
32             #===================================
33                 deflate_via {
34                 _flate_dict( 'deflator', { @{ shift->type_constraints || [] } }, @_ );
35                 },
36              
37                 inflate_via {
38                 _flate_dict( 'inflator', { @{ shift->type_constraints || [] } }, @_ );
39                 },
40              
41                 map_via {
42                 _map_dict( { @{ shift->type_constraints || [] } }, @_ );
43                 };
44              
45             #===================================
46             has_type 'MooseX::Types::Structured::Map',
47             #===================================
48                 deflate_via { _flate_map( 'deflator', @_ ) },
49                 inflate_via { _flate_map( 'inflator', @_ ) },
50                 map_via { type => 'object', enabled => 0 };
51              
52             #===================================
53             sub _deflate_tuple {
54             #===================================
55 6     6   25     my $dict = _tuple_to_dict(shift);
56 6 100       790     return '$val' unless %$dict;
57              
58 4         17     my $deflator = _flate_dict( 'deflator', $dict, @_ );
59              
60                 return sub {
61 5     5   563         my $array = shift;
62 5         9         my %hash;
63 5         15         @hash{ 0 .. $#{$array} } = @$array;
  5         25  
64 5         21         $deflator->( \%hash );
65 3         54     };
66             }
67              
68             #===================================
69             sub _inflate_tuple {
70             #===================================
71 6     6   22     my $dict = _tuple_to_dict(shift);
72 6 100       809     return '$val' unless %$dict;
73 4         11     my $inflator = _flate_dict( 'inflator', $dict, @_ );
74                 sub {
75 5     5   2005         my $hash = $inflator->(@_);
76 5         41         [ @{$hash}{ 0 .. keys(%$hash) - 1 } ];
  5         31  
77 3         73     };
78             }
79              
80             #===================================
81             sub _tuple_to_dict {
82             #===================================
83 18     18   29     my $i = 0;
84 18 100       25     return { map { $i++ => $_ } @{ shift->type_constraints || [] } };
  21         213  
  18         657  
85             }
86              
87             #===================================
88             sub _flate_dict {
89             #===================================
90 22     22   1150     my ( $type, $dict, $attr, $map ) = @_;
91              
92 22 100       85     return '$val' unless %$dict;
93              
94 18         36     my %flators;
95              
96 18         80     for my $key ( keys %$dict ) {
97 36   100     3837         my $flator = $map->find( $type, $dict->{$key}, $attr )
98                         || die "No $type found for key ($key)";
99              
100 32 100       196         $flators{$key}
101                         = ref $flator
102                         ? $flator
103                         : Eval::Closure::eval_closure(
104                         source => [ 'sub { my $val = $_[0];', $flator, '}' ] );
105                 }
106              
107                 sub {
108 24     24   3320         my $hash = shift;
109 46         1193         +{ map { $_ => $flators{$_}->( $hash->{$_} ) }
  46         99  
110 24         68             grep { exists $flators{$_} } keys %$hash
111                     };
112 14         3053     };
113             }
114              
115             #===================================
116             sub _map_dict {
117             #===================================
118 13     13   917     my ( $tcs, $attr, $map ) = @_;
119              
120 13 100       46     return ( type => 'object', enabled => 0 )
121                     unless %$tcs;
122              
123 9         16     my %properties;
124 9         45     for ( keys %$tcs ) {
125 18         63         my %key_mapping = $map->find( 'mapper', $tcs->{$_}, $attr );
126 18 100       68         die "Couldn't find mapping for key $_"
127                         unless %key_mapping;
128 16         45         $properties{$_} = \%key_mapping;
129                 }
130                 return (
131 7         77         type => 'object',
132                     dynamic => 'strict',
133                     properties => \%properties
134                 );
135             }
136              
137             #===================================
138             sub _flate_map {
139             #===================================
140 10     10   21     my ( $type, $tc, $attr, $map ) = @_;
141              
142 10   100     314     my $tcs = $tc->type_constraints || [];
143 10 100       1025     my $content_tc = $tcs->[1]
144                     or return \&_pass_through;
145              
146 6 100       41     my $content = $map->find( $type, $content_tc, $attr ) or return;
147              
148                 return sub {
149 0     0   0         my $hash = shift;
150 0         0         +{ map { $_ => $content->( $hash->{$_} ) } keys %$hash };
  0         0  
151                     }
152 4 50       11         if ref $content;
153              
154 4         50     'do { '
155                     . 'my $hash = $val; '
156                     . '+{map { '
157                     . 'my $key = $_; my $val = $hash->{$_}; '
158                     . '$key => '
159                     . $content
160                     . '} keys %$hash}}';
161              
162             }
163              
164             #===================================
165             sub _structured {
166             #===================================
167 45     45   88     my ( $type, $tc, $attr, $map ) = @_;
168 45         93     my $types = $type . 's';
169 45         1274     my $parent = $tc->parent;
170 45 50       341     if ( my $handler = $map->$types->{ $parent->name } ) {
171 45         441         return $handler->( $tc, $attr, $map );
172                 }
173 0         0     $map->find( $type, $parent, $attr );
174             }
175              
176             #===================================
177             sub _content_handler {
178             #===================================
179 24     24   50     my ( $type, $tc, $attr, $map ) = @_;
180 24 100       846     return $tc->can('type_parameter')
    100          
181                     ? $map->find( $type, $tc->type_parameter, $attr )
182                     : $type eq 'mapper' ? ( type => 'object', enabled => 0 )
183                     : '$val';
184             }
185              
186             #===================================
187 4     4   20 sub _pass_through { $_[0] }
188             #===================================
189              
190             1;
191              
192             =pod
193            
194             =encoding UTF-8
195            
196             =head1 NAME
197            
198             Elastic::Model::TypeMap::Structured - Type maps for MooseX::Types::Structured
199            
200             =head1 VERSION
201            
202             version 0.51
203            
204             =head1 DESCRIPTION
205            
206             L<Elastic::Model::TypeMap::Structured> provides mapping, inflation and deflation
207             for the L<MooseX::Types::Structured> type constraints.
208             It is loaded automatically byL<Elastic::Model::TypeMap::Default>.
209            
210             =head1 TYPES
211            
212             =head2 Optional
213            
214             Optional values are mapped, inflated and deflated according to their
215             content type, eg C<Optional[Int]>. An C<Optional> type with no
216             content type is mapped as C<<{ type => 'object', enabled => 'no' }>>
217             and the value would be passed through unaltered when deflating/inflating.
218            
219             =head2 Tuple
220            
221             Because array refs are interpreted by Elasticsearch as multiple values
222             of the same type, tuples are converted to hash refs whose keys are
223             the index number. For instance, a field C<foo> with C<Tuple[Int,Str]>
224             and value C<[5,'foo']> will be deflated to C<< { 0 => 5, 1 => 'foo' } >>.
225            
226             A tuple is mapped as an object, with:
227            
228             {
229             type => 'object',
230             dynamic => 'strict',
231             properties => \%properties
232             }
233            
234             The C<%properties> mapping depends on the content types. A C<Tuple> without
235             content types is mapped as C<<{ type => 'object', enabled => 'no' }>>
236             and the value would be passed through unaltered when deflating/inflating.
237            
238             =head2 Dict
239            
240             A C<Dict> is mapped as an object, with:
241            
242             {
243             type => 'object',
244             dynamic => 'strict',
245             properties => \%properties
246             }
247            
248             The C<%properties> mapping depends on the content types. A C<Dict> without
249             content types is mapped as C<<{ type => 'object', enabled => 'no' }>>
250             and the value would be passed through unaltered when deflating/inflating.
251            
252             =head2 Map
253            
254             It is not advisable to allow arbitrary key names in indexed hashes, as you
255             could end up generating many (and conflicting) field mappings. For this reason,
256             Maps are mapped as C<< { type => 'object', enabled => 0 } >>. In/deflation
257             depends on the content type (eg C<Map[Str,Int>]). A C<Map> without a content type
258             would pass through the value unaltered when inflating/deflatin.
259            
260             =head1 AUTHOR
261            
262             Clinton Gormley <drtech@cpan.org>
263            
264             =head1 COPYRIGHT AND LICENSE
265            
266             This software is copyright (c) 2015 by Clinton Gormley.
267            
268             This is free software; you can redistribute it and/or modify it under
269             the same terms as the Perl 5 programming language system itself.
270            
271             =cut
272              
273             __END__
274            
275             # ABSTRACT: Type maps for MooseX::Types::Structured
276            
277