File Coverage

blib/lib/KinoSearch.pm
Criterion Covered Total %
statement 286 334 85.6
branch 36 62 58.0
condition 7 22 31.8
subroutine 101 116 87.0
pod 11 45 24.4
total 441 579 76.1


line stmt bran cond sub pod time code
1 168     168   8241 use strict;
  168         296  
  168         6453  
2 168     168   1061 use warnings;
  168         295  
  168         6471  
3              
4             package KinoSearch;
5              
6 168     168   6819 use 5.008003;
  168         793  
  168         8045  
7 168     168   1045 use Exporter;
  168         307  
  168         13270  
8              
9             our $VERSION = '0.315';
10             $VERSION = eval $VERSION;
11              
12 168     168   910 use XSLoader;
  168         345  
  168         6483  
13             # This loads a large number of disparate subs.
14 168     168   3358883 BEGIN { XSLoader::load( 'KinoSearch', '0.315' ) }
15              
16             BEGIN {
17 168     168   4391 push our @ISA, 'Exporter';
18 168         6032 our @EXPORT_OK = qw( to_clownfish to_perl kdump );
19             }
20              
21 168     168   869067 use KinoSearch::Autobinding;
  168         612  
  168         63877  
22              
23             sub kdump {
24 0     0 0 0 require Data::Dumper;
25 0         0 my $kdumper = Data::Dumper->new( [@_] );
26 0     0   0 $kdumper->Sortkeys( sub { return [ sort keys %{ $_[0] } ] } );
  0         0  
  0         0  
27 0         0 $kdumper->Indent(1);
28 0         0 warn $kdumper->Dump;
29             }
30              
31 0     0 1 0 sub error {$KinoSearch::Object::Err::error}
32              
33             {
34             package KinoSearch::Util::IndexFileNames;
35             BEGIN {
36 168     168   3237 push our @ISA, 'Exporter';
37 168         14802 our @EXPORT_OK = qw(
38             extract_gen
39             latest_snapshot
40             );
41             }
42             }
43              
44             {
45             package KinoSearch::Util::StringHelper;
46             BEGIN {
47 168     168   1167 push our @ISA, 'Exporter';
48 168         7572 our @EXPORT_OK = qw(
49             utf8_flag_on
50             utf8_flag_off
51             to_base36
52             from_base36
53             utf8ify
54             utf8_valid
55             cat_bytes
56             );
57             }
58             }
59              
60             {
61             # Temporary back compat.
62             package KinoSearch::Doc;
63 168     168   9915 BEGIN { our @ISA = qw( KinoSearch::Document::Doc ) }
64             package KinoSearch::Architecture;
65 168     168   8522 BEGIN { our @ISA = qw( KinoSearch::Plan::Architecture ) }
66             package KinoSearch::FieldType;
67 168     168   8814 BEGIN { our @ISA = qw( KinoSearch::Plan::FieldType ) }
68             package KinoSearch::FieldType::BlobType;
69 168     168   11141 BEGIN { our @ISA = qw( KinoSearch::Plan::BlobType ) }
70             package KinoSearch::FieldType::Float32Type;
71 168     168   9198 BEGIN { our @ISA = qw( KinoSearch::Plan::Float32Type ) }
72             package KinoSearch::FieldType::Float64Type;
73 168     168   15175 BEGIN { our @ISA = qw( KinoSearch::Plan::Float64Type ) }
74             package KinoSearch::FieldType::Int32Type;
75 168     168   9503 BEGIN { our @ISA = qw( KinoSearch::Plan::Int32Type ) }
76             package KinoSearch::FieldType::Int64Type;
77 168     168   9687 BEGIN { our @ISA = qw( KinoSearch::Plan::Int64Type ) }
78             package KinoSearch::FieldType::FullTextType;
79 168     168   9531 BEGIN { our @ISA = qw( KinoSearch::Plan::FullTextType ) }
80             package KinoSearch::FieldType::StringType;
81 168     168   9879 BEGIN { our @ISA = qw( KinoSearch::Plan::StringType ) }
82             package KinoSearch::Indexer;
83 168     168   10871 BEGIN { our @ISA = qw( KinoSearch::Index::Indexer ) }
84             package KinoSearch::Obj::BitVector;
85 168     168   10730 BEGIN { our @ISA = qw( KinoSearch::Object::BitVector ) }
86             package KinoSearch::QueryParser;
87 168     168   8592 BEGIN { our @ISA = qw( KinoSearch::Search::QueryParser ) }
88             package KinoSearch::Search::HitCollector;
89 168     168   11981 BEGIN { our @ISA = qw( KinoSearch::Search::Collector ) }
90             package KinoSearch::Search::HitCollector::BitCollector;
91 168     168   8679 BEGIN { our @ISA = qw( KinoSearch::Search::Collector::BitCollector ) }
92             package KinoSearch::Search::Searchable;
93 168     168   8804 BEGIN { our @ISA = qw( KinoSearch::Search::Searcher ) }
94             package KinoSearch::Search::Similarity;
95 168     168   9014 BEGIN { our @ISA = qw( KinoSearch::Index::Similarity ) }
96             package KinoSearch::Schema;
97 168     168   8316 BEGIN { our @ISA = qw( KinoSearch::Plan::Schema ) }
98             package KinoSearch::Searcher;
99 168     168   9757 BEGIN { our @ISA = qw( KinoSearch::Search::IndexSearcher ) }
100             package KinoSearch::Util::BitVector;
101 168     168   24409 BEGIN { our @ISA = qw( KinoSearch::Object::BitVector ) }
102             }
103              
104             {
105             package KinoSearch::Analysis::Inversion;
106              
107             our %new_PARAMS = (
108             # params
109             text => undef
110             );
111             }
112              
113             {
114             package KinoSearch::Analysis::Stemmer;
115             sub lazy_load_snowball {
116 45     45 0 579982 require Lingua::Stem::Snowball;
117 45         1275475 KinoSearch::Analysis::Stemmer::_copy_snowball_symbols();
118             }
119             }
120              
121             {
122             package KinoSearch::Analysis::Stopalizer;
123 168     168   3907 use KinoSearch qw( to_clownfish );
  168         328  
  168         87405  
124              
125             sub gen_stoplist {
126 2     2 0 82 my ( undef, $language ) = @_;
127 2         5551 require Lingua::StopWords;
128 2         928 $language = lc($language);
129 2 50       22 if ( $language =~ /^(?:da|de|en|es|fi|fr|hu|it|nl|no|pt|ru|sv)$/ ) {
130 2         12 my $stoplist
131             = Lingua::StopWords::getStopWords( $language, 'UTF-8' );
132 2         10026 return to_clownfish($stoplist);
133             }
134 0         0 return undef;
135             }
136             }
137              
138             {
139             package KinoSearch::Analysis::Token;
140              
141             our %new_PARAMS = (
142             text => undef,
143             start_offset => undef,
144             end_offset => undef,
145             pos_inc => 1,
146             boost => 1.0,
147             );
148             }
149              
150             {
151             package KinoSearch::Analysis::Tokenizer;
152              
153 480     480 0 307606 sub compile_token_re { return qr/$_[1]/ }
154              
155             sub new {
156 45     45 1 17064 my ( $either, %args ) = @_;
157 45         142 my $token_re = delete $args{token_re};
158 45 100       201 $args{pattern} = "$token_re" if $token_re;
159 45         979 return $either->_new(%args);
160             }
161             }
162              
163             {
164             package KinoSearch::Document::Doc;
165 168     168   245782 use Storable qw( nfreeze thaw );
  168         759574  
  168         21583  
166 168     168   246828 use bytes;
  168         2134  
  168         989  
167 168     168   5889 no bytes;
  168         388  
  168         805  
168              
169             use overload
170 168         1676 fallback => 1,
171 168     168   355688 '%{}' => \&get_fields;
  168         253959  
172              
173             sub serialize_fields {
174 2     2 0 7152 my ( $self, $outstream ) = @_;
175 2         23 my $buf = nfreeze( $self->get_fields );
176 2         270 $outstream->write_c32( bytes::length($buf) );
177 2         2576 $outstream->print($buf);
178             }
179              
180             sub deserialize_fields {
181 6     6 0 440 my ( $self, $instream ) = @_;
182 6         51 my $len = $instream->read_c32;
183 6         16 my $buf;
184 6         50 $instream->read( $buf, $len );
185 6         44 $self->set_fields( thaw($buf) );
186             }
187             }
188              
189             {
190             package KinoSearch::Object::I32Array;
191             our %new_PARAMS = ( ints => undef );
192             }
193              
194             {
195             package KinoSearch::Object::LockFreeRegistry;
196 0     0   0 sub DESTROY { } # leak all
197             }
198              
199             {
200             package KinoSearch::Object::Obj;
201 168     168   137843 use KinoSearch qw( to_clownfish to_perl );
  168         438  
  168         150008  
202 5     5 1 4821 sub load { return $_[0]->_load( to_clownfish( $_[1] ) ) }
203             }
204              
205             {
206             package KinoSearch::Object::VTable;
207              
208             sub find_parent_class {
209 91     91 0 408030 my ( undef, $package ) = @_;
210 168     168   1231 no strict 'refs';
  168         375  
  168         24072  
211 91         345 for my $parent ( @{"$package\::ISA"} ) {
  91         753  
212 91 50       2798 return $parent if $parent->isa('KinoSearch::Object::Obj');
213             }
214 0         0 return;
215             }
216              
217             sub novel_host_methods {
218 263     263 0 2780 my ( undef, $package ) = @_;
219 168     168   1621 no strict 'refs';
  168         416  
  168         36367  
220 263         402 my $stash = \%{"$package\::"};
  263         1759  
221 263         4091 my $methods = KinoSearch::Object::VArray->new(
222             capacity => scalar keys %$stash );
223 263         1840 while ( my ( $symbol, $glob ) = each %$stash ) {
224 1060 50       2700 next if ref $glob;
225 1060 100       5984 next unless *$glob{CODE};
226 351         5691 $methods->push( KinoSearch::Object::CharBuf->new($symbol) );
227             }
228 263         7795 return $methods;
229             }
230              
231             sub _register {
232 262     262   1009 my ( undef, %args ) = @_;
233 262         1546 my $singleton_class = $args{singleton}->get_name;
234 262         1419 my $parent_class = $args{parent}->get_name;
235 262 100       10275 if ( !$singleton_class->isa($parent_class) ) {
236 168     168   1541 no strict 'refs';
  168         387  
  168         26916  
237 3         8 push @{"$singleton_class\::ISA"}, $parent_class;
  3         0  
238             }
239             }
240              
241 168     168   1279 no warnings 'redefine';
  168         382  
  168         39336  
242 0     0   0 sub DESTROY { } # leak all
243             }
244              
245             {
246             package KinoSearch::Index::Indexer;
247              
248             sub new {
249 398     398 1 102820 my ( $either, %args ) = @_;
250 398         932 my $flags = 0;
251 398 100       1485 $flags |= CREATE if delete $args{'create'};
252 398 100       1508 $flags |= TRUNCATE if delete $args{'truncate'};
253 398         22291 return $either->_new( %args, flags => $flags );
254             }
255              
256             our %add_doc_PARAMS = ( doc => undef, boost => 1.0 );
257             }
258              
259             {
260             package KinoSearch::Index::IndexReader;
261 168     168   1211 use Carp;
  168         415  
  168         65204  
262              
263             sub new {
264 0     0 1 0 confess(
265             "IndexReader is an abstract class; use open() instead of new()");
266             }
267             sub lexicon {
268 0     0 0 0 my $self = shift;
269 0         0 my $lex_reader = $self->fetch("KinoSearch::Index::LexiconReader");
270 0 0       0 return $lex_reader->lexicon(@_) if $lex_reader;
271 0         0 return;
272             }
273             sub posting_list {
274 0     0 0 0 my $self = shift;
275 0         0 my $plist_reader
276             = $self->fetch("KinoSearch::Index::PostingListReader");
277 0 0       0 return $plist_reader->posting_list(@_) if $plist_reader;
278 0         0 return;
279             }
280 1     1 1 4727 sub offsets { shift->_offsets->to_arrayref }
281             }
282              
283             {
284             package KinoSearch::Index::PolyReader;
285 168     168   1271 use KinoSearch qw( to_clownfish );
  168         398  
  168         78770  
286              
287             sub try_read_snapshot {
288 473     473 0 59196 my ( undef, %args ) = @_;
289 473         1626 my ( $snapshot, $folder, $path ) = @args{qw( snapshot folder path )};
290 473         942 eval { $snapshot->read_file( folder => $folder, path => $path ); };
  473         7177  
291 473 50       1408 if ($@) { return KinoSearch::Object::CharBuf->new($@) }
  0         0  
292 473         5565 else { return undef }
293             }
294              
295             sub try_open_segreaders {
296 473     473 0 1056 my ( $self, $segments ) = @_;
297 473         3951 my $schema = $self->get_schema;
298 473         2303 my $folder = $self->get_folder;
299 473         2107 my $snapshot = $self->get_snapshot;
300 473         6860 my $seg_readers = KinoSearch::Object::VArray->new(
301             capacity => scalar @$segments );
302 473         3661 my $segs = to_clownfish($segments); # FIXME: Don't convert twice.
303 473         959 eval {
304             # Create a SegReader for each segment in the index.
305 473         794 my $num_segs = scalar @$segments;
306 473         1906 for ( my $seg_tick = 0; $seg_tick < $num_segs; $seg_tick++ ) {
307 1117         17686 my $seg_reader = KinoSearch::Index::SegReader->new(
308             schema => $schema,
309             folder => $folder,
310             segments => $segs,
311             seg_tick => $seg_tick,
312             snapshot => $snapshot,
313             );
314 1117         7327 $seg_readers->push($seg_reader);
315             }
316             };
317 473 50       1158 if ($@) {
318 0         0 return KinoSearch::Object::CharBuf->new($@);
319             }
320 473         32507 return $seg_readers;
321             }
322             }
323              
324             {
325             package KinoSearch::Index::Segment;
326 168     168   1119 use KinoSearch qw( to_clownfish );
  168         396  
  168         40545  
327             sub store_metadata {
328 8     8 1 42 my ( $self, %args ) = @_;
329 8         506 $self->_store_metadata( %args,
330             metadata => to_clownfish( $args{metadata} ) );
331             }
332             }
333              
334             {
335             package KinoSearch::Index::SegReader;
336              
337             sub try_init_components {
338 1117     1117 0 2127 my $self = shift;
339 1117         7847 my $arch = $self->get_schema->get_architecture;
340 1117         1961 eval { $arch->init_seg_reader($self); };
  1117         93551  
341 1117 50       17458 if ($@) { return KinoSearch::Object::CharBuf->new($@); }
  0         0  
342 1117         4737 return;
343             }
344             }
345              
346             {
347             package KinoSearch::Index::SortCache;
348             our %value_PARAMS = ( ord => undef, );
349             }
350              
351             {
352             package KinoSearch::Search::Compiler;
353 168     168   1122 use Carp;
  168         368  
  168         17466  
354 168     168   1070 use Scalar::Util qw( blessed );
  168         391  
  168         103828  
355              
356             sub new {
357 506     506 1 57748 my ( $either, %args ) = @_;
358 506 50       1175 if ( !defined $args{boost} ) {
359 0 0 0     0 confess("'parent' is not a Query")
360             unless ( blessed( $args{parent} )
361             and $args{parent}->isa("KinoSearch::Search::Query") );
362 0         0 $args{boost} = $args{parent}->get_boost;
363             }
364 506         7032 return $either->do_new(%args);
365             }
366             }
367              
368             {
369             package KinoSearch::Search::Query;
370              
371             sub make_compiler {
372 39     39 1 104083 my ( $self, %args ) = @_;
373 39 50       457 $args{boost} = $self->get_boost unless defined $args{boost};
374 39         1429 return $self->_make_compiler(%args);
375             }
376             }
377              
378             {
379             package KinoSearch::Search::SortRule;
380              
381             my %types = (
382             field => FIELD(),
383             score => SCORE(),
384             doc_id => DOC_ID(),
385             );
386              
387             sub new {
388 58     58 1 46053 my ( $either, %args ) = @_;
389 58   100     280 my $type = delete $args{type} || 'field';
390 58 50       182 confess("Invalid type: '$type'") unless defined $types{$type};
391 58         980 return $either->_new( %args, type => $types{$type} );
392             }
393             }
394              
395             {
396             package KinoSearch::Object::BitVector;
397 29     29 0 7329 sub to_arrayref { shift->to_array->to_arrayref }
398             }
399              
400             {
401             package KinoSearch::Object::ByteBuf;
402             {
403             # Override autogenerated deserialize binding.
404 168     168   1307 no warnings 'redefine';
  168         376  
  168         13887  
405 1     1 0 2328 sub deserialize { shift->_deserialize(@_) }
406             }
407             }
408              
409             {
410             package KinoSearch::Object::ViewByteBuf;
411 168     168   1254 use Carp;
  168         2115  
  168         20009  
412 0     0   0 sub new { confess "ViewByteBuf objects can only be created from C." }
413             }
414              
415             {
416             package KinoSearch::Object::CharBuf;
417              
418             {
419             # Defeat obscure bugs in the XS auto-generation by redefining clone()
420             # and deserialize(). (Because of how the typemap works for CharBuf*,
421             # the auto-generated methods return UTF-8 Perl scalars rather than
422             # actual CharBuf objects.)
423 168     168   1275 no warnings 'redefine';
  168         476  
  168         18611  
424 1     1 0 1781 sub clone { shift->_clone(@_) }
425 1     1 0 473 sub deserialize { shift->_deserialize(@_) }
426             }
427             }
428              
429             {
430             package KinoSearch::Object::ViewCharBuf;
431 168     168   1000 use Carp;
  168         471  
  168         16058  
432 0     0   0 sub new { confess "ViewCharBuf has no public constructor." }
433             }
434              
435             {
436             package KinoSearch::Object::ZombieCharBuf;
437 168     168   983 use Carp;
  168         346  
  168         23008  
438 0     0   0 sub new { confess "ZombieCharBuf objects can only be created from C." }
439 0     0   0 sub DESTROY { }
440             }
441              
442             {
443             package KinoSearch::Object::Err;
444 27     27 0 3878 sub do_to_string { shift->to_string }
445 168     168   1211 use Scalar::Util qw( blessed );
  168         352  
  168         12069  
446 168     168   1056 use Carp qw( confess longmess );
  168         1424  
  168         17770  
447             use overload
448 168         2095 '""' => \&do_to_string,
449 168     168   1953 fallback => 1;
  168         561  
450              
451             sub new {
452 2     2 1 24 my ( $either, $message ) = @_;
453 2         15 my ( undef, $file, $line ) = caller;
454 2         13 $message .= ", $file line $line\n";
455 2         178 return $either->_new(
456             mess => KinoSearch::Object::CharBuf->new($message) );
457             }
458              
459             sub do_throw {
460 25     25 0 35554 my $err = shift;
461 25         3587 $err->cat_mess( longmess() );
462 25         3887 die $err;
463             }
464              
465             our $error;
466             sub set_error {
467 165     165 0 14228 my $val = $_[1];
468 165 100       468 if ( defined $val ) {
469 94 50 33     1182 confess("Not a KinoSearch::Object::Err")
470             unless ( blessed($val)
471             && $val->isa("KinoSearch::Object::Err") );
472             }
473 165         925057 $error = $val;
474             }
475 129     129 0 19465 sub get_error {$error}
476             }
477              
478             {
479             package KinoSearch::Object::Hash;
480 168     168   63914 no warnings 'redefine';
  168         467  
  168         13752  
481 1     1 0 1508 sub deserialize { shift->_deserialize(@_) }
482             }
483              
484             {
485             package KinoSearch::Object::VArray;
486 168     168   964 no warnings 'redefine';
  168         401  
  168         21733  
487 0     0 0 0 sub clone { CORE::shift->_clone }
488 1     1 0 2956 sub deserialize { CORE::shift->_deserialize(@_) }
489             }
490              
491             {
492             package KinoSearch::Store::FileHandle;
493             BEGIN {
494 168     168   6914 push our @ISA, 'Exporter';
495 168         51340 our @EXPORT_OK = qw( build_fh_flags );
496             }
497              
498             sub build_fh_flags {
499 3     3 0 4 my $args = shift;
500 3         5 my $flags = 0;
501 3 50       24 $flags |= FH_CREATE if delete $args->{create};
502 3 50       16 $flags |= FH_READ_ONLY if delete $args->{read_only};
503 3 50       14 $flags |= FH_WRITE_ONLY if delete $args->{write_only};
504 3 50       13 $flags |= FH_EXCLUSIVE if delete $args->{exclusive};
505 3         8 return $flags;
506             }
507              
508             sub open {
509 0     0 0 0 my ( $either, %args ) = @_;
510 0   0     0 $args{flags} ||= 0;
511 0         0 $args{flags} |= build_fh_flags( \%args );
512 0         0 return $either->_open(%args);
513             }
514             }
515              
516             {
517             package KinoSearch::Store::FSFileHandle;
518              
519             sub open {
520 3     3 0 2141 my ( $either, %args ) = @_;
521 3   50     23 $args{flags} ||= 0;
522 3         10 $args{flags}
523             |= KinoSearch::Store::FileHandle::build_fh_flags( \%args );
524 3         259 return $either->_open(%args);
525             }
526             }
527              
528             {
529             package KinoSearch::Store::FSFolder;
530 168     168   250672 use File::Spec::Functions qw( rel2abs );
  168         196316  
  168         71113  
531 173     173 0 1964239 sub absolutify { return rel2abs( $_[1] ) }
532             }
533              
534             {
535             package KinoSearch::Store::RAMFileHandle;
536              
537             sub open {
538 0     0 0 0 my ( $either, %args ) = @_;
539 0   0     0 $args{flags} ||= 0;
540 0         0 $args{flags}
541             |= KinoSearch::Store::FileHandle::build_fh_flags( \%args );
542 0         0 return $either->_open(%args);
543             }
544             }
545              
546             {
547             package KinoSearch::Util::Debug;
548             BEGIN {
549 168     168   1709 push our @ISA, 'Exporter';
550 168         6665 our @EXPORT_OK = qw(
551             DEBUG
552             DEBUG_PRINT
553             DEBUG_ENABLED
554             ASSERT
555             set_env_cache
556             num_allocated
557             num_freed
558             num_globals
559             );
560             }
561             }
562              
563             {
564             package KinoSearch::Util::Json;
565 168     168   1225 use Scalar::Util qw( blessed );
  168         394  
  168         9811  
566 168     168   1108 use KinoSearch qw( to_clownfish );
  168         387  
  168         10252  
567 168     168   150854 use KinoSearch::Util::StringHelper qw( utf8_valid utf8_flag_on );
  168         651  
  168         13406  
568 168     168   270423 use JSON::XS qw();
  168         1544137  
  168         107717  
569              
570             my $json_encoder = JSON::XS->new->pretty(1)->canonical(1);
571              
572             sub slurp_json {
573 5012     5012 0 34103 my ( undef, %args ) = @_;
574 5012         6336 my $result;
575 5012 100       73566 my $instream = $args{folder}->open_in( $args{path} )
576             or return;
577 5009         20262 my $len = $instream->length;
578 5009         6451 my $json;
579 5009         22419 $instream->read( $json, $len );
580 5009 50       17046 if ( utf8_valid($json) ) {
581 5009         8815 utf8_flag_on($json);
582 5009         7074 $result = eval { to_clownfish( $json_encoder->decode($json) ) };
  5009         145436  
583             }
584             else {
585 0         0 $@ = "Invalid UTF-8";
586             }
587 5009 100 66     47053 if ( $@ or !$result ) {
588 1   50     9 KinoSearch::Object::Err->set_error(
589             KinoSearch::Object::Err->new( $@ || "Failed to decode JSON" )
590             );
591 1         12 return;
592             }
593 5008         339795 return $result;
594             }
595              
596             sub spew_json {
597 3131     3131 0 873315 my ( undef, %args ) = @_;
598 3131         4875 my $json = eval { $json_encoder->encode( $args{'dump'} ) };
  3131         44857  
599 3131 50       9103 if ( !defined $json ) {
600 0         0 KinoSearch::Object::Err->set_error(
601             KinoSearch::Object::Err->new($@) );
602 0         0 return 0;
603             }
604 3131         69033 my $outstream = $args{folder}->open_out( $args{path} );
605 3131 100       9543 return 0 unless $outstream;
606 3130         4337 eval {
607 3130         11094 $outstream->print($json);
608 3130         23477 $outstream->close;
609             };
610 3130 50       6799 if ($@) {
611 0         0 my $error;
612 0 0 0     0 if ( blessed($@) && $@->isa("KinoSearch::Object::Err") ) {
613 0         0 $error = $@;
614             }
615             else {
616 0         0 $error = KinoSearch::Object::Err->new($@);
617             }
618 0         0 KinoSearch::Object::Err->set_error($error);
619 0         0 return 0;
620             }
621 3130         1085111 return 1;
622             }
623              
624             sub to_json {
625 43     43 0 82 my ( undef, $dump ) = @_;
626 43         363 return $json_encoder->encode($dump);
627             }
628              
629             sub from_json {
630 41     41 0 1240 return to_clownfish( $json_encoder->decode( $_[1] ) );
631             }
632              
633 1     1 0 148 sub set_tolerant { $json_encoder->allow_nonref( $_[1] ) }
634             }
635              
636             {
637             package KinoSearch::Object::Host;
638             BEGIN {
639 168 50   168   3746 if ( !__PACKAGE__->isa('KinoSearch::Object::Obj') ) {
640 168         13688 push our @ISA, 'KinoSearch::Object::Obj';
641             }
642             }
643             }
644              
645             1;
646              
647             __END__