8 use Data::Dump qw/dump/;
9 use File::Path qw/mkpath/;
12 use HTTP::Request::Common;
15 use lib qw(lib ../lib);
16 use Media::Type::Simple;
21 # 04/26/09 21:12:28 CEST Dobrica Pavlinusic <dpavlin@rot13.org>
23 my ( $command, $database, $design ) = @ARGV;
24 die "usage: $0 [push|pull] database design\n" unless $database && $design;
26 chdir $design || die "can't find $design: $!";
28 my $ua = LWP::UserAgent->new;
30 my $url = "http://localhost:5984/$database/_design/$design";
34 if ( $path =~ m{/} ) {
37 mkpath $dir if ! -e $dir;
42 my ( $path, $content ) = @_;
45 File::Slurp::write_file $path, $content;
46 print "$path ", -s $path, " bytes created\n";
49 sub write_attachment {
51 my $file = "_attachemnts/$path";
53 $ua->mirror( "$url/$path", $file );
54 print "detached $file ", -s $file, " bytes\n";
59 my ( $tree, $path ) = @_;
62 if ( $ref eq 'HASH' ) {
63 foreach my $child ( keys %$tree ) {
64 if ( $child eq '_attachments' ) {
65 write_attachment $_ foreach keys %{ $tree->{$child} };
67 unroll( $tree->{$child}, $path ? "$path/$child" : $child );
71 warn "UNSUPPORTED $path $ref ", dump( $tree );
72 write_file "$path.json", to_json $tree;
73 } elsif ( $ref eq '' ) {
75 if ( $tree =~ m[^\s*(function(.*){.*}|/\*|//|var)]is ) {
77 } elsif ( $tree =~ m{<%=.*%>} ) { # couchapp template
80 warn "# can't detect type of $path\n";
83 write_file $path, $tree;
88 if ( $command eq 'pull' ) {
91 my $response = $ua->get( $url );
92 die $response->status_line if $response->is_error;
94 my $json = $response->decoded_content;
95 write_file "../$database-$design.pull.js", $json;
97 unroll( from_json $json, '' );
99 } elsif ( $command eq 'push' ) {
103 find({ no_chdir => 1, wanted => sub {
104 my $path = $File::Find::name;
105 return unless -f $path;
111 if ( $path =~ m{_attachemnts/(.+)} ) {
114 my $content_type = 'text/plain';
115 $content_type = type_from_ext($1) if $filename =~ m{\.(\w+)$};
117 my $data = File::Slurp::read_file( $path );
118 $data = encode_base64( $data );
119 # XXX inline attachments must be single line
120 # XXX http://wiki.apache.org/couchdb/HTTP_Document_API
121 $data =~ s/[\n\r]+//gs;
122 $json->{_attachments}->{ $filename } = {
123 content_type => $content_type,
129 my $data = File::Slurp::read_file( $path );
130 $path =~ s[/]['}->{']g;
131 $path =~ s{\.\w+$}{};
132 my $code = "\$json->{'$path'} = \$data;";
134 die "ERROR in $code: $@" if $@;
135 # warn "## json = ",dump( $json );
138 if ( ! defined $json->{_id} ) {
139 warn "creating _id for document\n";
140 $json->{_id} = $$ . '-' . time();
142 delete( $json->{_rev} ) && warn "removing _rev from document\n";
144 print "push $database/_design/$design\n";
145 write_file "../$database-$design.push.js", to_json $json;
148 my $response = $ua->request(
149 HTTP::Request::Common::PUT(
151 'Content-Type' => 'application/json',
152 Content => to_json $json,
156 if ( $response->code == 409 ) {
157 warn "## update $url\n";
158 my $response = $ua->get( $url );
159 die $response->status_line if $response->is_error;
161 my $data = from_json $response->decoded_content;
162 $json->{$_} = $data->{$_} foreach ( '_rev', '_id' );
164 $response = $ua->request( HTTP::Request::Common::PUT($url, 'Content-Type' => 'application/json', Content => to_json $json ) );
165 die $response->status_line if $response->is_error;
166 warn "push updated $url\n";
168 die $response->status_line if $response->is_error;
169 warn "push new $url\n";
173 die "$0: unknown command $command";