File Coverage

blib/lib/Store/CouchDB.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Store::CouchDB;
2              
3 1     1   50239 use Moo;
  1         7762  
  1         4  
4              
5             # ABSTRACT: Store::CouchDB - a simple CouchDB driver
6              
7             our $VERSION = '4.1'; # VERSION
8              
9 1     1   1406 use MooX::Types::MooseLike::Base qw(:all);
  1         4693  
  1         310  
10 1     1   472 use experimental 'smartmatch';
  1         2474  
  1         6  
11 1     1   574 use JSON;
  1         9992  
  1         5  
12 1     1   545 use LWP::Protocol::Net::Curl;
  0            
  0            
13             use LWP::UserAgent;
14             use URI;
15             use URI::QueryParam;
16             use URI::Escape;
17             use Carp;
18             use Data::Dump 'dump';
19              
20             # the following GET parameter keys have to be JSON encoded according to the
21             # couchDB API documentation. http://docs.couchdb.org/en/latest/api/
22             my @JSON_KEYS = qw(
23             doc_ids
24             key
25             keys
26             startkey
27             start_key
28             endkey
29             end_key
30             );
31              
32              
33             has 'debug' => (
34             is => 'rw',
35             isa => Bool,
36             default => sub { 0 },
37             lazy => 1,
38             );
39              
40              
41             has 'host' => (
42             is => 'rw',
43             isa => Str,
44             required => 1,
45             default => sub { 'localhost' },
46             );
47              
48              
49             has 'port' => (
50             is => 'rw',
51             isa => Int,
52             required => 1,
53             default => sub { 5984 },
54             );
55              
56              
57             has 'ssl' => (
58             is => 'rw',
59             isa => Bool,
60             default => sub { 0 },
61             lazy => 1,
62             );
63              
64              
65             has 'db' => (
66             is => 'rw',
67             isa => Str,
68             predicate => 'has_db',
69             );
70              
71              
72             has 'user' => (
73             is => 'rw',
74             isa => Str,
75             );
76              
77              
78             has 'pass' => (
79             is => 'rw',
80             isa => Str,
81             );
82              
83              
84             has 'method' => (
85             is => 'rw',
86             required => 1,
87             default => sub { 'GET' },
88             );
89              
90              
91             has 'error' => (
92             is => 'rw',
93             predicate => 'has_error',
94             clearer => 'clear_error',
95             );
96              
97              
98             has 'purge_limit' => (
99             is => 'rw',
100             isa => Int,
101             default => sub { 5000 },
102             );
103              
104              
105             has 'timeout' => (
106             is => 'rw',
107             isa => Int,
108             default => sub { 30 },
109             );
110              
111              
112             has 'json' => (
113             is => 'rw',
114             isa =>
115             sub { JSON->new->utf8->allow_nonref->allow_blessed->convert_blessed },
116             default => sub {
117             JSON->new->utf8->allow_nonref->allow_blessed->convert_blessed;
118             },
119             );
120              
121              
122             has 'agent' => (
123             is => 'rw',
124             lazy => 1,
125             required => 1,
126             builder => '_build_agent',
127             );
128              
129             sub _build_agent {
130             my ($self) = @_;
131              
132             return LWP::UserAgent->new(
133             agent => __PACKAGE__ . $Store::CouchDB::VERSION,
134             timeout => $self->timeout,
135             keep_alive => 1,
136             );
137             }
138              
139              
140             sub get_doc {
141             my ($self, $data) = @_;
142              
143             unless (ref $data eq 'HASH') {
144             $data = { id => $data };
145             }
146              
147             $self->_check_db($data);
148              
149             unless ($data->{id}) {
150             carp 'Document ID not defined';
151             return;
152             }
153              
154             my $path = $self->db . '/' . $data->{id};
155             my $rev;
156             $rev = 'rev=' . $data->{rev} if (exists $data->{rev} and $data->{rev});
157             my $params = $self->_uri_encode($data->{opts});
158             if ($rev or $params) {
159             $path .= '?';
160             $path .= $rev . '&' if $rev;
161             $path .= $params . '&' if $params;
162             chop $path;
163             }
164              
165             $self->method('GET');
166              
167             return $self->_call($path);
168             }
169              
170              
171             sub head_doc {
172             my ($self, $data) = @_;
173              
174             unless (ref $data eq 'HASH') {
175             $data = { id => $data };
176             }
177              
178             $self->_check_db($data);
179              
180             unless ($data->{id}) {
181             carp 'Document ID not defined';
182             return;
183             }
184              
185             my $path = $self->db . '/' . $data->{id};
186              
187             $self->method('HEAD');
188             my $rev = $self->_call($path);
189              
190             $rev =~ s/"//g if $rev;
191              
192             return $rev;
193             }
194              
195              
196             sub all_docs {
197             my ($self, $data) = @_;
198              
199             $self->_check_db($data);
200              
201             my $path = $self->db . '/_all_docs';
202             my $params = $self->_uri_encode($data);
203             $path .= '?' . $params if $params;
204              
205             $self->method('GET');
206             my $res = $self->_call($path);
207              
208             return
209             unless exists $res->{rows}
210             and ref $res->{rows} eq 'ARRAY'
211             and $res->{rows}->[0];
212              
213             return @{ $res->{rows} };
214             }
215              
216              
217             sub get_design_docs {
218             my ($self, $data) = @_;
219              
220             $self->_check_db($data);
221              
222             my $path = $self->db
223             . '/_all_docs?descending=true&startkey="_design0"&endkey="_design"';
224             my $params = $self->_uri_encode($data);
225             $path .= '&' . $params if $params;
226              
227             $self->method('GET');
228             my $res = $self->_call($path);
229              
230             return
231             unless exists $res->{rows}
232             and ref $res->{rows} eq 'ARRAY'
233             and $res->{rows}->[0];
234              
235             return @{ $res->{rows} }
236             if (ref $data eq 'HASH' and $data->{include_docs});
237              
238             my @design;
239             foreach my $design (@{ $res->{rows} }) {
240             my (undef, $name) = split(/\//, $design->{key}, 2);
241             push(@design, $name);
242             }
243              
244             return @design;
245             }
246              
247              
248             sub put_doc {
249             my ($self, $data) = @_;
250              
251             unless (exists $data->{doc} and ref $data->{doc} eq 'HASH') {
252             carp "Document not defined";
253             return;
254             }
255              
256             $self->_check_db($data);
257              
258             my $path;
259             if (exists $data->{doc}->{_id} and defined $data->{doc}->{_id}) {
260             $self->method('PUT');
261             $path = $self->db . '/' . $data->{doc}->{_id};
262             }
263             else {
264             $self->method('POST');
265             $path = $self->db;
266             }
267              
268             my $params = $self->_uri_encode($data->{opts});
269             $path .= '?' . $params if $params;
270             my $res = $self->_call($path, undef, $data->{doc});
271              
272             # update revision in original doc for convenience
273             $data->{doc}->{_rev} = $res->{rev} if exists $res->{rev};
274              
275             return ($res->{id}, $res->{rev}) if wantarray;
276             return $res->{id};
277             }
278              
279              
280             sub del_doc {
281             my ($self, $data) = @_;
282              
283             unless (ref $data eq 'HASH') {
284             $data = { id => $data };
285             }
286              
287             my $id = $data->{id} || $data->{_id};
288             my $rev = $data->{rev} || $data->{_rev};
289              
290             unless ($id) {
291             carp 'Document ID not defined';
292             return;
293             }
294              
295             $self->_check_db($data);
296              
297             # get doc revision if missing
298             unless ($rev) {
299             $rev = $self->head_doc($id);
300             }
301              
302             # stop if doc doesn't exist
303             unless ($rev) {
304             carp "Document does not exist";
305             return;
306             }
307              
308             my $path = $self->db . '/' . $id . '?rev=' . $rev;
309             my $params = $self->_uri_encode($data->{opts});
310             $path .= $params if $params;
311              
312             $self->method('DELETE');
313             my $res = $self->_call($path);
314              
315             return ($res->{id}, $res->{rev}) if wantarray;
316             return $res->{rev};
317             }
318              
319              
320             sub update_doc {
321             my ($self, $data) = @_;
322              
323             unless (ref $data eq 'HASH'
324             and exists $data->{doc}
325             and ref $data->{doc} eq 'HASH')
326             {
327             carp "Document not defined";
328             return;
329             }
330              
331             unless (exists $data->{doc}->{_id} and defined $data->{doc}->{_id}) {
332             carp "Document ID not defined";
333             return;
334             }
335              
336             unless (exists $data->{doc}->{_rev} and defined $data->{doc}->{_rev}) {
337             carp "Document revision not defined";
338             return;
339             }
340              
341             $self->_check_db($data);
342              
343             my $rev = $self->head_doc($data->{doc}->{_id});
344             unless ($rev) {
345             carp "Document does not exist";
346             return;
347             }
348              
349             return $self->put_doc($data);
350             }
351              
352              
353             sub copy_doc {
354             my ($self, $data) = @_;
355              
356             unless (ref $data eq 'HASH') {
357             $data = { id => $data };
358             }
359              
360             unless ($data->{id}) {
361             carp "Document ID not defined";
362             return;
363             }
364              
365             # as long as CouchDB does not support automatic document name creation
366             # for the copy command we copy the ugly way ...
367             my $doc = $self->get_doc($data);
368              
369             unless ($doc) {
370             carp "Document does not exist";
371             return;
372             }
373              
374             delete $doc->{_id};
375             delete $doc->{_rev};
376              
377             return $self->put_doc({ doc => $doc });
378             }
379              
380              
381             sub show_doc {
382             my ($self, $data) = @_;
383              
384             $self->_check_db($data);
385              
386             unless ($data->{show}) {
387             carp 'show not defined';
388             return;
389             }
390              
391             my $path = $self->_make_path($data);
392              
393             $self->method('GET');
394              
395             return $self->_call($path);
396             }
397              
398              
399             sub get_view {
400             my ($self, $data) = @_;
401              
402             unless ($data->{view}) {
403             carp "View not defined";
404             return;
405             }
406              
407             $self->_check_db($data);
408              
409             my $path = $self->_make_path($data);
410             $self->method('GET');
411             my $res = $self->_call($path, 'accept_stale');
412              
413             # fallback lookup for broken data consistency due to the way earlier
414             # versions of this module where handling (or not) input data that had been
415             # stringified by dumpers or otherwise internally
416             # e.g. numbers were stored as strings which will be used as keys eventually
417             unless ($res->{rows}->[0]) {
418             $path = $self->_make_path($data, 'compat');
419             $res = $self->_call($path, 'accept_stale');
420             }
421              
422             return unless $res->{rows}->[0];
423              
424             my $c = 0;
425             my $result = {};
426             foreach my $doc (@{ $res->{rows} }) {
427             if ($doc->{doc}) {
428             $result->{ $doc->{key} || $c } = $doc->{doc};
429             }
430             else {
431             next unless exists $doc->{value};
432             if (ref $doc->{key} eq 'ARRAY') {
433             $self->_hash($result, $doc->{value}, @{ $doc->{key} });
434             }
435             else {
436             # TODO debug why this crashes from time to time
437             #$doc->{value}->{id} = $doc->{id};
438             $result->{ $doc->{key} || $c } = $doc->{value};
439             }
440             }
441             $c++;
442             }
443              
444             return $result;
445             }
446              
447              
448             sub get_post_view {
449             my ($self, $data) = @_;
450              
451             unless ($data->{view}) {
452             carp 'View not defined';
453             return;
454             }
455             unless ($data->{opts}) {
456             carp 'No options defined - use "get_view" instead';
457             return;
458             }
459              
460             $self->_check_db($data);
461              
462             my $opts;
463             if ($data->{opts}) {
464             $opts = delete $data->{opts};
465             }
466              
467             my $path = $self->_make_path($data);
468             $self->method('POST');
469             my $res = $self->_call($path, 'accept_stale', $opts);
470              
471             my $result;
472             foreach my $doc (@{ $res->{rows} }) {
473             next unless exists $doc->{value};
474             $doc->{value}->{id} = $doc->{id};
475             $result->{ $doc->{key} } = $doc->{value};
476             }
477              
478             return $result;
479             }
480              
481              
482             sub get_view_array {
483             my ($self, $data) = @_;
484              
485             unless ($data->{view}) {
486             carp 'View not defined';
487             return;
488             }
489              
490             $self->_check_db($data);
491              
492             my $path = $self->_make_path($data);
493             $self->method('GET');
494             my $res = $self->_call($path, 'accept_stale');
495              
496             # fallback lookup for broken data consistency due to the way earlier
497             # versions of this module where handling (or not) input data that had been
498             # stringified by dumpers or otherwise internally
499             # e.g. numbers were stored as strings which will be used as keys eventually
500             unless ($res->{rows}->[0]) {
501             $path = $self->_make_path($data, 'compat');
502             $res = $self->_call($path, 'accept_stale');
503             }
504              
505             my @result;
506             foreach my $doc (@{ $res->{rows} }) {
507             if ($doc->{doc}) {
508             push(@result, $doc->{doc});
509             }
510             else {
511             next unless exists $doc->{value};
512             if (ref($doc->{value}) eq 'HASH') {
513             $doc->{value}->{id} = $doc->{id};
514             push(@result, $doc->{value});
515             }
516             else {
517             push(@result, $doc);
518             }
519             }
520             }
521              
522             return @result;
523             }
524              
525              
526             sub get_array_view {
527             my ($self, $data) = @_;
528              
529             unless ($data->{view}) {
530             carp "View not defined";
531             return;
532             }
533              
534             $self->_check_db($data);
535              
536             my $path = $self->_make_path($data);
537             $self->method('GET');
538             my $res = $self->_call($path, 'accept_stale');
539              
540             # fallback lookup for broken data consistency due to the way earlier
541             # versions of this module where handling (or not) input data that had been
542             # stringified by dumpers or otherwise internally
543             # e.g. numbers were stored as strings which will be used as keys eventually
544             unless ($res->{rows}->[0]) {
545             $path = $self->_make_path($data, 'compat');
546             $res = $self->_call($path, 'accept_stale');
547             }
548              
549             my $result;
550             foreach my $doc (@{ $res->{rows} }) {
551             if ($doc->{doc}) {
552             push(@{$result}, $doc->{doc});
553             }
554             else {
555             next unless exists $doc->{value};
556             if (ref($doc->{value}) eq 'HASH') {
557             $doc->{value}->{id} = $doc->{id};
558             push(@{$result}, $doc->{value});
559             }
560             else {
561             push(@{$result}, $doc);
562             }
563             }
564             }
565              
566             return $result;
567             }
568              
569              
570             sub list_view {
571             my ($self, $data) = @_;
572              
573             unless ($data->{list}) {
574             carp "List not defined";
575             return;
576             }
577              
578             unless ($data->{view}) {
579             carp "View not defined";
580             return;
581             }
582              
583             $self->_check_db($data);
584              
585             my $path = $self->_make_path($data);
586              
587             $self->method('GET');
588              
589             return $self->_call($path, 'accept_stale');
590             }
591              
592              
593             sub changes {
594             my ($self, $data) = @_;
595              
596             $self->_check_db($data);
597              
598             $self->method('GET');
599              
600             my $path = $self->db . '/_changes';
601             my $params = $self->_uri_encode($data);
602             $path .= '?' . $params if $params;
603             my $res = $self->_call($path);
604              
605             return $res;
606             }
607              
608              
609             sub purge {
610             my ($self, $data) = @_;
611              
612             $self->_check_db($data);
613              
614             my $path = $self->db . '/_changes?limit=' . $self->purge_limit . '&since=0';
615             $self->method('GET');
616             my $res = $self->_call($path);
617              
618             return unless $res->{results}->[0];
619              
620             my @del;
621             my $resp;
622              
623             $self->method('POST');
624             foreach my $_del (@{ $res->{results} }) {
625             next
626             unless (exists $_del->{deleted}
627             and ($_del->{deleted} eq 'true' or $_del->{deleted} == 1));
628              
629             my $opts = { $_del->{id} => [ $_del->{changes}->[0]->{rev} ], };
630             $resp->{ $_del->{seq} } =
631             $self->_call($self->db . '/_purge', undef, $opts);
632             }
633              
634             return $resp;
635             }
636              
637              
638             sub compact {
639             my ($self, $data) = @_;
640              
641             $self->_check_db($data);
642              
643             my $res;
644             if ($data->{purge}) {
645             $res->{purge} = $self->purge();
646             }
647              
648             if ($data->{view_compact}) {
649             $self->method('POST');
650             $res->{view_compact} = $self->_call($self->db . '/_view_cleanup');
651             my @design = $self->get_design_docs();
652             $self->method('POST');
653             foreach my $doc (@design) {
654             $res->{ $doc . '_compact' } =
655             $self->_call($self->db . '/_compact/' . $doc);
656             }
657             }
658              
659             $self->method('POST');
660             $res->{compact} = $self->_call($self->db . '/_compact');
661              
662             return $res;
663             }
664              
665              
666             sub put_file {
667             my ($self, $data) = @_;
668              
669             unless ($data->{file}) {
670             carp 'File content not defined';
671             return;
672             }
673             unless ($data->{filename}) {
674             carp 'File name not defined';
675             return;
676             }
677              
678             $self->_check_db($data);
679              
680             my $id = $data->{id} || $data->{doc}->{_id};
681             my $rev = $data->{rev} || $data->{doc}->{_rev};
682              
683             if (!$rev && $id) {
684             $rev = $self->head_doc($id);
685             $self->_log("put_file(): rev $rev") if $self->debug;
686             }
687              
688             # create a new doc if required
689             ($id, $rev) = $self->put_doc({ doc => {} }) unless $id;
690              
691             my $path = $self->db . '/' . $id . '/' . $data->{filename} . '?rev=' . $rev;
692              
693             $self->method('PUT');
694             $data->{content_type} ||= 'text/plain';
695             my $res = $self->_call($path, undef, $data->{file}, $data->{content_type});
696              
697             return ($res->{id}, $res->{rev}) if wantarray;
698             return $res->{id};
699             }
700              
701              
702             sub get_file {
703             my ($self, $data) = @_;
704              
705             $self->_check_db($data);
706              
707             unless ($data->{id}) {
708             carp "Document ID not defined";
709             return;
710             }
711             unless ($data->{filename}) {
712             carp "File name not defined";
713             return;
714             }
715              
716             my $path = join('/', $self->db, $data->{id}, $data->{filename});
717              
718             $self->method('GET');
719              
720             return $self->_call($path);
721             }
722              
723              
724             sub del_file {
725             my ($self, $data) = @_;
726              
727             unless ($data->{id}) {
728             carp "Document ID not defined";
729             return;
730             }
731             unless ($data->{filename}) {
732             carp 'File name not defined';
733             return;
734             }
735              
736             $self->_check_db($data);
737              
738             my $id = $data->{id};
739             my $rev = $data->{rev};
740              
741             if ($id && !$rev) {
742             $rev = $self->head_doc($id);
743             $self->_log("delete_file(): rev $rev") if $self->debug;
744             }
745              
746             my $path = $self->db . '/' . $id . '/' . $data->{filename} . '?rev=' . $rev;
747             $self->method('DELETE');
748             my $res = $self->_call($path);
749              
750             return ($res->{id}, $res->{rev}) if wantarray;
751             return $res->{id};
752             }
753              
754              
755             sub config {
756             my ($self, $data) = @_;
757              
758             foreach my $key (keys %{$data}) {
759             $self->$key($data->{$key}) or confess "$key not defined as property!";
760             }
761             return $self;
762             }
763              
764              
765             sub create_db {
766             my ($self, $db) = @_;
767              
768             if ($db) {
769             $self->db($db);
770             }
771              
772             $self->method('PUT');
773             my $res = $self->_call($self->db);
774              
775             return $res;
776             }
777              
778              
779             sub delete_db {
780             my ($self, $db) = @_;
781              
782             if ($db) {
783             $self->db($db);
784             }
785              
786             $self->method('DELETE');
787             my $res = $self->_call($self->db);
788              
789             return $res;
790             }
791              
792              
793             sub all_dbs {
794             my ($self) = @_;
795              
796             $self->method('GET');
797             my $res = $self->_call('_all_dbs');
798              
799             return @{ $res || [] };
800             }
801              
802             sub _check_db {
803             my ($self, $data) = @_;
804              
805             if ( ref $data eq 'HASH'
806             and exists $data->{dbname}
807             and defined $data->{dbname})
808             {
809             $self->db($data->{dbname});
810             return;
811             }
812              
813             unless ($self->has_db) {
814             carp 'database not defined! you must set $sc->db("some_database")';
815             return;
816             }
817              
818             return;
819             }
820              
821             sub _uri_encode {
822             my ($self, $options, $compat) = @_;
823              
824             return unless (ref $options eq 'HASH');
825              
826             # make sure stringified keys and values return their original state
827             # because otherwise JSON will encode numbers as strings
828             my $opts = eval dump $options; ## no critic
829              
830             my $path = '';
831             foreach my $key (keys %$opts) {
832             my $value = $opts->{$key};
833              
834             if ($key ~~ @JSON_KEYS) {
835              
836             # backwards compatibility with key, startkey, endkey as strings
837             $value .= '' if ($compat && !ref($value));
838              
839             # only JSON encode URI parameter value if necessary and required by
840             # documentation. see http://docs.couchdb.org/en/latest/api/
841             $value = $self->json->encode($value);
842             }
843              
844             $value = uri_escape($value);
845             $path .= $key . '=' . $value . '&';
846             }
847              
848             # remove last '&'
849             chop($path);
850              
851             return $path;
852             }
853              
854             sub _make_path {
855             my ($self, $data, $compat) = @_;
856              
857             my ($design, $view, $show, $list);
858              
859             if (exists $data->{view}) {
860             $data->{view} =~ s/^\///;
861             ($design, $view) = split(/\//, $data->{view}, 2);
862             }
863              
864             if (exists $data->{show}) {
865             $data->{show} =~ s/^\///;
866             ($design, $show) = split(/\//, $data->{show}, 2);
867             }
868              
869             $list = $data->{list} if exists $data->{list};
870              
871             my $path = $self->db . "/_design/${design}";
872             if ($list) {
873             $path .= "/_list/${list}/${view}";
874             }
875             elsif ($show) {
876             $path .= "/_show/${show}";
877             $path .= '/' . $data->{id} if defined $data->{id};
878             }
879             elsif ($view) {
880             $path .= "/_view/${view}";
881             }
882              
883             if (keys %{ $data->{opts} }) {
884             my $params = $self->_uri_encode($data->{opts}, $compat);
885             $path .= '?' . $params if $params;
886             }
887              
888             return $path;
889             }
890              
891             sub _build_uri {
892             my ($self, $path) = @_;
893              
894             my $uri = $self->ssl ? 'https' : 'http';
895             $uri .= '://' . $self->host . ':' . $self->port;
896             $uri .= '/' . $path;
897             $uri = URI->new($uri);
898             $uri->userinfo($self->user . ':' . $self->pass)
899             if ($self->user and $self->pass);
900              
901             return $uri;
902             }
903              
904             sub _call {
905             my ($self, $path, $accept_stale, $content, $ct) = @_;
906              
907             binmode(STDERR, ":encoding(UTF-8)") if $self->debug;
908              
909             # cleanup old error
910             $self->clear_error if $self->has_error;
911              
912             my $uri = $self->_build_uri($path);
913              
914             $self->_log($self->method . ": $uri") if $self->debug;
915              
916             my $req = HTTP::Request->new();
917             $req->method($self->method);
918             $req->uri($uri);
919              
920             if ($content) {
921              
922             # make sure stringified keys and values return their original state
923             # because otherwise JSON will encode numbers as strings for example
924             my $c = eval dump $content; ## no critic
925              
926             # ensure couchDB _id is a string as required
927             # TODO: if support for _bulk_doc API is added we also need to make
928             # sure every document ID is a string!
929             if (ref $c eq 'HASH' && !defined $ct) {
930             $c->{_id} .= '' if exists $c->{_id};
931             }
932              
933             if ($self->debug) {
934             $self->_log('Payload: ' . $self->_dump($content));
935             }
936              
937             $req->content((
938             $ct
939             ? $content
940             : $self->json->encode($c)));
941             }
942              
943             $self->agent->default_header('Content-Type' => $ct || "application/json");
944             my $res = $self->agent->request($req);
945              
946             if ($self->method eq 'HEAD' and $res->header('ETag')) {
947             $self->_log('Revision: ' . $res->header('ETag')) if $self->debug;
948             return $res->header('ETag');
949             }
950              
951             # retry with stale=update_after in case of a timeout
952             if ($accept_stale and $res->status_line eq '500 read timeout') {
953             $uri->query_param_append(stale => 'update_after');
954             $req->uri($uri);
955             $res = $self->agent->request($req);
956             }
957              
958             # try JSON decoding response content all the time
959             my $result;
960             eval { $result = $self->json->decode($res->content) };
961             unless ($@) {
962             $self->_log('Result: ' . $self->_dump($result)) if $self->debug;
963             }
964              
965             if ($res->is_success) {
966             return $result if $result;
967              
968             if ($self->debug) {
969             my $dc = $res->decoded_content;
970             chomp $dc;
971             $self->_log('Result: ' . $self->_dump($dc));
972             }
973              
974             return {
975             file => $res->decoded_content,
976             content_type => [ $res->content_type ]->[0],
977             };
978             }
979             else {
980             $self->error($res->status_line . ': ' . $res->content);
981             }
982              
983             return;
984             }
985              
986             sub _hash {
987             my ($self, $head, $val, @tail) = @_;
988              
989             if ($#tail == 0) {
990             return $head->{ shift(@tail) } = $val;
991             }
992             else {
993             return $self->_hash($head->{ shift(@tail) } //= {}, $val, @tail);
994             }
995             }
996              
997             sub _dump {
998             my ($self, $obj) = @_;
999              
1000             my %options;
1001             if ($self->debug) {
1002             $options{colored} = 1;
1003             }
1004             else {
1005             $options{colored} = 0;
1006             $options{multiline} = 0;
1007             }
1008              
1009             require Data::Printer;
1010             Data::Printer->import(%options) unless __PACKAGE__->can('np');
1011              
1012             my $dump;
1013             if (ref $obj) {
1014             $dump = np($obj, %options);
1015             }
1016             else {
1017             $dump = np(\$obj, %options);
1018             }
1019              
1020             return $dump;
1021             }
1022              
1023             sub _log {
1024             my ($self, $msg) = @_;
1025              
1026             print STDERR __PACKAGE__ . ': ' . $msg . $/;
1027              
1028             return;
1029             }
1030              
1031              
1032             1; # End of Store::CouchDB
1033              
1034             __END__