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.52';
3 23     23   18727 use strict;
  23         58  
  23         707  
4 23     23   133 use warnings;
  23         48  
  23         782  
5              
6 23     23   133 use Elastic::Model::TypeMap::Base qw(:all);
  23         49  
  23         189  
7 23     23   150 use namespace::autoclean;
  23         52  
  23         189  
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   15     my $dict = _tuple_to_dict(shift);
56 6 100       634     return '$val' unless %$dict;
57              
58 4         11     my $deflator = _flate_dict( 'deflator', $dict, @_ );
59              
60                 return sub {
61 5     5   524         my $array = shift;
62 5         9         my %hash;
63 5         10         @hash{ 0 .. $#{$array} } = @$array;
  5         22  
64 5         17         $deflator->( \%hash );
65 3         40     };
66             }
67              
68             #===================================
69             sub _inflate_tuple {
70             #===================================
71 6     6   15     my $dict = _tuple_to_dict(shift);
72 6 100       625     return '$val' unless %$dict;
73 4         12     my $inflator = _flate_dict( 'inflator', $dict, @_ );
74                 sub {
75 5     5   1648         my $hash = $inflator->(@_);
76 5         42         [ @{$hash}{ 0 .. keys(%$hash) - 1 } ];
  5         31  
77 3         38     };
78             }
79              
80             #===================================
81             sub _tuple_to_dict {
82             #===================================
83 18     18   22     my $i = 0;
84 18 100       28     return { map { $i++ => $_ } @{ shift->type_constraints || [] } };
  21         158  
  18         617  
85             }
86              
87             #===================================
88             sub _flate_dict {
89             #===================================
90 22     22   1353     my ( $type, $dict, $attr, $map ) = @_;
91              
92 22 100       72     return '$val' unless %$dict;
93              
94 18         23     my %flators;
95              
96 18         55     for my $key ( keys %$dict ) {
97 36   100     2785         my $flator = $map->find( $type, $dict->{$key}, $attr )
98                         || die "No $type found for key ($key)";
99              
100 32 100       177         $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   3324         my $hash = shift;
109 46         967         +{ map { $_ => $flators{$_}->( $hash->{$_} ) }
110 24         58             grep { exists $flators{$_} } keys %$hash
  46         102  
111                     };
112 14         2631     };
113             }
114              
115             #===================================
116             sub _map_dict {
117             #===================================
118 13     13   1319     my ( $tcs, $attr, $map ) = @_;
119              
120 13 100       52     return ( type => 'object', enabled => 0 )
121                     unless %$tcs;
122              
123 9         13     my %properties;
124 9         29     for ( keys %$tcs ) {
125 18         77         my %key_mapping = $map->find( 'mapper', $tcs->{$_}, $attr );
126 18 100       78         die "Couldn't find mapping for key $_"
127                         unless %key_mapping;
128 16         58         $properties{$_} = \%key_mapping;
129                 }
130                 return (
131 7         58         type => 'object',
132                     dynamic => 'strict',
133                     properties => \%properties
134                 );
135             }
136              
137             #===================================
138             sub _flate_map {
139             #===================================
140 10     10   22     my ( $type, $tc, $attr, $map ) = @_;
141              
142 10   100     350     my $tcs = $tc->type_constraints || [];
143 10 100       1257     my $content_tc = $tcs->[1]
144                     or return \&_pass_through;
145              
146 6 100       54     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       15         if ref $content;
153              
154 4         40     '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   74     my ( $type, $tc, $attr, $map ) = @_;
168 45         84     my $types = $type . 's';
169 45         1424     my $parent = $tc->parent;
170 45 50       321     if ( my $handler = $map->$types->{ $parent->name } ) {
171 45         347         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   46     my ( $type, $tc, $attr, $map ) = @_;
180 24 100       923     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   23 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.52
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