File Coverage

blib/lib/Elastic/Model/TypeMap/Objects.pm
Criterion Covered Total %
statement 46 60 76.6
branch 16 22 72.7
condition 18 21 85.7
subroutine 10 12 83.3
pod n/a
total 90 115 78.2


line stmt bran cond sub pod time code
1             package Elastic::Model::TypeMap::Objects;
2             $Elastic::Model::TypeMap::Objects::VERSION = '0.52';
3 23     23   18015 use strict;
  23         56  
  23         720  
4 23     23   127 use warnings;
  23         48  
  23         694  
5 23     23   129 use Elastic::Model::TypeMap::Base qw(:all);
  23         49  
  23         192  
6 23     23   153 use Scalar::Util qw(reftype weaken);
  23         49  
  23         1549  
7 23     23   131 use Moose::Util qw(does_role);
  23         48  
  23         199  
8 23     23   4706 use namespace::autoclean;
  23         52  
  23         165  
9              
10             #===================================
11             has_type 'Moose::Meta::TypeConstraint::Class',
12             #===================================
13                 deflate_via { _deflate_class(@_) },
14                 inflate_via { _inflate_class(@_) },
15                 map_via { _map_class(@_) };
16              
17             # TODO: Moose role
18              
19             #===================================
20             has_type 'Moose::Meta::TypeConstraint::Role',
21             #===================================
22                 deflate_via {undef}, inflate_via {undef};
23              
24             #===================================
25             has_type 'Object',
26             #===================================
27                 deflate_via {
28                 sub {
29                     my $obj = shift;
30                     my $ref = ref $obj;
31              
32                     die "$ref does not provide a deflate() method"
33                         unless $obj->can('deflate');
34                     return { $ref => $obj->deflate };
35                 };
36                 },
37              
38                 inflate_via {
39                 sub {
40                     my ( $class, $data ) = %{ shift() };
41                     my $inflated = $class->inflate($data);
42                     return bless $inflated, $class;
43                     }
44                 },
45              
46                 map_via { type => 'object', enabled => 0 };
47              
48             #===================================
49             sub _deflate_class {
50             #===================================
51 2     2   9     my ( $tc, $attr, $map ) = @_;
52              
53 2         88     my $class = $tc->name;
54 2 50       27     my $attrs = _class_attrs( $map, $class, $attr )
55                     or return;
56              
57 0         0     return $map->class_deflator( $class, $attrs );
58             }
59              
60             #===================================
61             sub _inflate_class {
62             #===================================
63 2     2   11     my ( $tc, $attr, $map ) = @_;
64              
65 2         105     my $class = $tc->name;
66 2 50       34     if ( $map->model->knows_class($class) ) {
67 0         0         my $model = $map->model;
68 0         0         weaken $model;
69                     return sub {
70 0     0   0             my $hash = shift;
71 0 0       0             die "Missing UID\n" unless $hash->{uid};
72 0         0             my $uid = Elastic::Model::UID->new( %{ $hash->{uid} },
  0         0  
73                             from_store => 1 );
74 0         0             return $model->get_doc( uid => $uid );
75 0         0         };
76                 }
77              
78 2 50       10     my $attrs = _class_attrs( $map, $class, $attr )
79                     or return;
80              
81 0         0     my $attr_inflator = $map->class_inflator( $class, $attrs );
82              
83                 return sub {
84 0     0   0         my $hash = shift;
85 0         0         my $obj = Class::MOP::class_of($class)
86                         ->get_meta_instance->create_instance;
87 0         0         $attr_inflator->( $obj, $hash );
88 0         0     };
89             }
90              
91             #===================================
92             sub _map_class {
93             #===================================
94 29     29   53     my ( $tc, $attr, $map ) = @_;
95              
96 29 100 100     1319     return ( type => 'object', enabled => 0 )
      66        
97                     if $attr->can('has_enabled')
98                     && $attr->has_enabled
99                     && !$attr->enabled;
100              
101 27         830     my $class = $tc->name;
102 27 100       186     my $attrs = _class_attrs( $map, $class, $attr )
103                     or return;
104              
105 23         119     return $map->class_mapping( $class, $attrs );
106             }
107              
108             #===================================
109             sub _class_attrs {
110             #===================================
111 31     31   65     my ( $map, $class, $attr ) = @_;
112              
113 31   66     143     $class = $map->model->class_for($class) || $class;
114              
115 31         120     my $meta = Class::MOP::class_of($class);
116 31 100 66     482     return unless $meta && $meta->isa('Moose::Meta::Class');
117              
118 23         36     my %attrs;
119              
120 23   100     936     my $inc = $attr->can('include_attrs') && $attr->include_attrs;
121 23   100     912     my $exc = $attr->can('exclude_attrs') && $attr->exclude_attrs;
122              
123                 my @inc_attr = $inc
124                     ? map {
125 23 50       108         $meta->find_attribute_by_name($_)
  2 100       8  
126                         or die "Unknown attribute ($_) in class $class"
127                     } @$inc
128                     : $meta->get_all_attributes;
129              
130 37         183     %attrs = map { $_->name => $_ }
131 23   100     966         grep { !( $_->can('exclude') && $_->exclude ) } @inc_attr;
  46         1336  
132              
133             # TODO: does it ever make sense to remove the UID field?
134 23 100       90     if ( my $uid = $meta->find_attribute_by_name('uid') ) {
135 5         354         $attrs{uid} = $uid;
136                 }
137              
138 23 100       845     delete @attrs{@$exc} if $exc;
139              
140 23         93     return \%attrs;
141             }
142              
143             1;
144              
145             # ABSTRACT: Type maps for objects and Moose classes
146              
147             __END__
148            
149             =pod
150            
151             =encoding UTF-8
152            
153             =head1 NAME
154            
155             Elastic::Model::TypeMap::Objects - Type maps for objects and Moose classes
156            
157             =head1 VERSION
158            
159             version 0.52
160            
161             =head1 DESCRIPTION
162            
163             L<Elastic::Model::TypeMap::Objects> provides mapping, inflation and deflation
164             for Moose-based classes and objects.
165             It is loaded automatically by L<Elastic::Model::TypeMap::Default>.
166            
167             =head1 TYPES
168            
169             =head2 Moose classes
170            
171             has 'bar' => (
172             is => 'rw,
173             isa => 'Bar'
174             );
175            
176             If C<Bar> is a Moose class, then its attributes will be introspected and
177             the mapping will look like:
178            
179             {
180             type => 'object',
181             dynamic => 'strict',
182             properties => {
183             ... mapping for Bar's attributes...
184             }
185             }
186            
187             By default, all attributes are included. You can control the attribute list
188             with:
189            
190             has 'bar' => (
191             is => 'rw,
192             isa => 'Bar',
193             include_attrs => [], # no attributes
194             | include_attrs => ['foo','bar'] # just 'foo' and 'bar'
195             | exclude_attrs => ['foo','bar'] # all except 'foo' and 'bar'
196             );
197            
198             You can control the mapping for individual attributes in Moose classes with
199             the L<Elastic::Model::Trait::Field> trait:
200            
201             package Bar;
202            
203             use Moose;
204            
205             has 'foo' => (
206             is => 'rw,
207             isa => 'Str',
208             trait => ['Elastic::Model::Trait::Field']
209             );
210            
211             =head2 Elastic::Doc classes
212            
213             Elastic::Doc classes work in exactly the same way as other Moose classes except
214            
215             =over
216            
217             =item *
218            
219             You don't need to specify the L<Elastic::Model::Trait::Field> trait - it is
220             added automatically.
221            
222             =item *
223            
224             The L<UID|Elastic::Model::UID> field is always included, unless you specifically
225             list it in C<exclude_attrs>.
226            
227             =back
228            
229             By default, all the attributes of an Elastic::Doc class will be included.
230             For instance, if we have two classes: C<User> and C<Post>, and the C<Post> class
231             has a C<user> attribute. Because all the attributes of the C<$user> are
232             also indexed in the C<$post> object, you can search for C<Posts>
233             which have been written by a C<User> whose name is C<"john">.
234            
235             This also means that if a C<User> updates their name, then you need
236             to reindex all of their C<Posts>.
237            
238             If you don't want to include any attributes, then you can just specify:
239             C<< include_attrs => [] >>. The L<UID|Elastic::Model::UID> will still be indexed,
240             meaning that you can still do:
241            
242             $user_name = $post->user->name;
243            
244             =head2 Moose Roles and non-Moose classes
245            
246             Moose roles and non-Moose classes must provide
247             L<custom mappings, deflators and inflators|Elastic::Manual::Attributes/CUSTOM MAPPING, INFLATION AND DEFLATION>
248            
249             =head1 AUTHOR
250            
251             Clinton Gormley <drtech@cpan.org>
252            
253             =head1 COPYRIGHT AND LICENSE
254            
255             This software is copyright (c) 2015 by Clinton Gormley.
256            
257             This is free software; you can redistribute it and/or modify it under
258             the same terms as the Perl 5 programming language system itself.
259            
260             =cut
261