File Coverage

blib/lib/Store/CouchDB.pm
Criterion Covered Total %
statement 49 446 10.9
branch 10 220 4.5
condition 3 75 4.0
subroutine 9 37 24.3
pod 23 23 100.0
total 94 801 11.7


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