File Coverage

blib/lib/DBIx/NoSQL/Model.pm
Criterion Covered Total %
statement 121 140 86.4
branch 26 46 56.5
condition 2 3 66.6
subroutine 26 31 83.8
pod 0 21 0.0
total 175 241 72.6


line stmt bran cond sub pod time code
1             package DBIx::NoSQL::Model;
2             our $AUTHORITY = 'cpan:YANICK';
3             $DBIx::NoSQL::Model::VERSION = '0.0021';
4 8     8   29 use strict;
  8         79  
  8         186  
5 8     8   25 use warnings;
  8         8  
  8         205  
6              
7 8     8   23 use Moose;
  8         7  
  8         98  
8 8     8   39656 use Clone qw/ clone /;
  8         15066  
  8         1411  
9 8     8   3407 use Digest::SHA qw/ sha1_hex /;
  8         16935  
  8         508  
10 8     8   3126 use Data::GUID;
  8         27386  
  8         26  
11              
12             has store => qw/ is ro required 1 weak_ref 1 /, handles => [qw/ storage /];
13             has name => qw/ reader name writer _name required 1 /;
14              
15             has inflate => qw/ accessor _inflate isa Maybe[CodeRef] /;
16             has deflate => qw/ accessor _deflate isa Maybe[CodeRef] /;
17 0     0 0 0 sub inflator { return shift->_inflate( @_ ) }
18 0     0 0 0 sub deflator { return shift->_deflate( @_ ) }
19              
20             has wrap => qw/ accessor _wrap /;
21 16     16 0 568 sub wrapper { return shift->_wrap( @_ ) }
22              
23             has field_map => qw/ is ro lazy_build 1 isa HashRef /;
24 11     11   233 sub _build_field_map { {} }
25             sub field {
26 14     14 0 3914 require DBIx::NoSQL::Model::Field;
27 14         32 my $self = shift;
28 14         20 my $name = shift;
29              
30 14 50       39 return $self->field_map->{ $name } unless @_;
31              
32 14 50       363 die "Already have field ($name)" if $self->field_map->{ $name };
33 14         93 my $field = $self->field_map->{ $name } = DBIx::NoSQL::Model::Field->new( name => $name );
34 14         56 $field->setup( $self, @_ );
35 14         48 return $field;
36             }
37             has _field2column_map => qw/ is ro /, default => sub { {} };
38             has _field2inflate_map => qw/ is ro /, default => sub { {} };
39             has _field2deflate_map => qw/ is ro /, default => sub { {} };
40              
41             sub _store_set {
42 58     58   94 my $self = shift;
43 58         1488 return $self->store->schema->resultset( '__Store__' );
44             }
45              
46             sub _find {
47 11     11   21 my $self = shift;
48 11         18 my $key = shift;
49              
50 11         39 my $result = $self->_store_set->find(
51             { __model__ => $self->name, __key__ => $key },
52             { key => 'primary' },
53             );
54              
55 11         28969 return $result;
56             }
57              
58             sub create {
59 0     0 0 0 my $self = shift;
60 0         0 return $self->create_object( @_ );
61             }
62              
63             sub create_object {
64 16     16 0 111 my $self = shift;
65 16         32 my $target = shift;
66              
67 16         56 my $data = $self->deserialize( $target );
68 16         59 my $entity = $self->inflate( $data );
69 16         57 my $object = $self->wrap( $entity );
70              
71 16         113 return $object;
72             }
73              
74             sub create_entity {
75 0     0 0 0 my $self = shift;
76 0         0 my $target = shift;
77              
78 0         0 my $data = $self->deserialize( $target );
79 0         0 my $entity = $self->inflate( $data );
80              
81 0         0 return $entity;
82             }
83              
84             sub create_data {
85 5     5 0 8 my $self = shift;
86 5         8 my $target = shift;
87              
88 5         12 my $data = $self->deserialize( $target );
89              
90 5         50 return $data;
91             }
92              
93             sub set {
94 33     33 0 1797 my $self = shift;
95 33         63 my $key = shift;
96 33         53 my $target = shift;
97              
98 33         59 my ( $entity, $data, $value );
99              
100 33 100 66     162 if (ref($key) and !$target) {
101 1         3 $target = $key;
102 1         11 $key = Data::GUID->new->as_string;
103             }
104              
105 33 50       858 if ( blessed $target ) {
106 0         0 $entity = $self->unwrap( $target );
107 0         0 $target = $entity;
108             }
109              
110 33 50       121 if ( ref $target ) {
111 33         124 $data = $self->deflate( $target );
112 33         69 $target = $data;
113             }
114              
115 33         119 $value = $self->serialize( $target );
116              
117 33         125 $self->_store_set->update_or_create(
118             { __model__ => $self->name, __key__ => $key, __value__ => $value },
119             { key => 'primary' },
120             );
121              
122 33 100       44721537 if ( $self->searchable ) {
123 18         90 $self->index->update( $key => $data );
124             }
125 33         6036942 return $key;
126             }
127              
128             sub exists {
129 10     10 0 16 my $self = shift;
130 10         18 my $key = shift;
131              
132 10         34 return $self->_store_set->search({ __key__ => $key })->count;
133             }
134              
135             sub get {
136 10     10 0 22 my $self = shift;
137 10         20 my $key = shift;
138              
139 10         40 my $result = $self->_find( $key );
140              
141 10 100       179 return unless $result;
142              
143 9         121 return $self->create_object( $result->get_column( '__value__' ) );
144             }
145              
146             sub delete {
147 1     1 0 2 my $self = shift;
148 1         1 my $key = shift;
149              
150 1         4 my $result = $self->_find( $key );
151 1 50       12 if ( $result ) {
152 1         11 $result->delete;
153             }
154              
155 1 50       269067 if ( $self->searchable ) {
156 1         6 $self->index->delete( $key );
157             }
158             }
159              
160             sub wrap {
161 16     16 0 25 my $self = shift;
162 16         21 my $entity = shift;
163              
164 16 50       54 if ( my $wrapper = $self->wrapper ) {
165 0 0       0 if ( ref $wrapper eq 'CODE' ) {
166 0         0 return $wrapper->( $entity );
167             }
168             else {
169 0         0 return $wrapper->new( _entity => $entity );
170             }
171             }
172              
173 16         160 return $entity;
174             }
175              
176             sub unwrap {
177 0     0 0 0 my $self = shift;
178 0         0 my $target = shift;
179              
180 0 0       0 return $target->_entity if blessed $target;
181 0         0 return $target;
182             }
183              
184             sub inflate {
185 16     16 0 29 my $self = shift;
186 16         25 my $data = shift;
187              
188 16         158 my $entity = clone $data;
189            
190 16         32 while( my ( $field, $inflator ) = each %{ $self->_field2inflate_map } ) {
  18         1411  
191 2 50       21 $entity->{ $field } = $inflator->( $entity->{ $field } ) if defined $entity->{ $field };
192             }
193              
194 16         32 return $entity;
195             }
196              
197             sub deflate {
198 33     33 0 59 my $self = shift;
199 33         53 my $target = shift;
200              
201 33         69 my $data = {};
202              
203 33         70 while( my ( $field, $deflator ) = each %{ $self->_field2deflate_map } ) {
  35         1086  
204 2 50       13 $data->{ $field } = $deflator->( $target->{ $field } ) if defined $target->{ $field };
205             }
206              
207 33         185 while( my ( $key, $value ) = each %$target ) {
208 45 100       135 next if exists $data->{ $key };
209 43 50       233 $data->{ $key } = ref $value ? clone $value : $value;
210             }
211              
212 33         74 return $data;
213             }
214              
215             sub deserialize {
216 29     29 0 52 my $self = shift;
217 29         54 my $value = shift;
218              
219 29 50       92 return $value if ref $value;
220              
221 29         1077 my $data = $self->store->json->decode( $value );
222 29         68 return $data;
223             }
224              
225             sub serialize {
226 33     33 0 60 my $self = shift;
227 33         52 my $data = shift;
228              
229 33 50       115 return $data if ! ref $data;
230              
231 33         907 my $value = $self->store->json->encode( $data );
232 33         81 return $value;
233             }
234              
235             has searchable => qw/ is rw isa Bool default 1 /;
236              
237             has index => qw/ reader _index lazy_build 1 /;
238             sub _build_index {
239 11     11   4832 require DBIx::NoSQL::Model::Index;
240 11         31 my $self = shift;
241 11 50       277 return unless $self->searchable;
242 11         129 return DBIx::NoSQL::Model::Index->new( model => $self );
243             }
244              
245             sub index {
246 50     50 0 1828 my $self = shift;
247 50 100       1264 return $self->_index unless @_;
248 10         21 my $field_name = shift;
249 10         27 return $self->field( $field_name => ( index => 1, @_ ) );
250             }
251              
252             sub reindex {
253 2     2 0 3 my $self = shift;
254              
255 2 50       49 return unless $self->searchable;
256              
257 2 50       8 if ( ! $self->index ) {
258 0         0 $self->clear_index;
259             }
260              
261 2         5 return $self->index->reindex;
262             }
263              
264             sub search {
265 16     16 0 26 my $self = shift;
266              
267 16 50       459 die "Trying to search on an unsearchable (unindexed) model" unless $self->searchable;
268              
269 16         70 return $self->index->search( @_ );
270             }
271              
272             1;
273              
274             __END__
275              
276             =pod
277              
278             =encoding UTF-8
279              
280             =head1 NAME
281              
282             DBIx::NoSQL::Model
283              
284             =head1 VERSION
285              
286             version 0.0021
287              
288             =head1 AUTHORS
289              
290             =over 4
291              
292             =item *
293              
294             Robert Krimen <robertkrimen@gmail.com>
295              
296             =item *
297              
298             Yanick Champoux <yanick@cpan.org>
299              
300             =back
301              
302             =head1 COPYRIGHT AND LICENSE
303              
304             This software is copyright (c) 2017 by Robert Krimen.
305              
306             This is free software; you can redistribute it and/or modify it under
307             the same terms as the Perl 5 programming language system itself.
308              
309             =cut