File Coverage

blib/lib/AnyEvent/Groonga.pm
Criterion Covered Total %
statement 58 166 34.9
branch 9 44 20.4
condition 3 23 13.0
subroutine 18 29 62.0
pod 2 2 100.0
total 90 264 34.0


line stmt bran cond sub pod time code
1             package AnyEvent::Groonga;
2 6     6   8829 use strict;
  6         10  
  6         217  
3 6     6   33 use warnings;
  6         11  
  6         147  
4 6     6   32 use Carp;
  6         8  
  6         550  
5 6     6   11698 use AnyEvent;
  6         49892  
  6         262  
6 6     6   7049 use AnyEvent::Util qw(run_cmd);
  6         97529  
  6         609  
7 6     6   7372 use AnyEvent::HTTP;
  6         155016  
  6         688  
8 6     6   9791 use AnyEvent::Groonga::Result;
  6         30  
  6         62  
9 6     6   10572 use File::Which qw(which);
  6         7464  
  6         517  
10 6     6   6225 use List::MoreUtils qw(any);
  6         8105  
  6         555  
11 6     6   8427 use URI;
  6         34694  
  6         226  
12 6     6   73 use URI::Escape;
  6         15  
  6         471  
13 6     6   14432 use JSON;
  6         87114  
  6         53  
14 6     6   7461 use Try::Tiny;
  6         11245  
  6         361  
15 6     6   44 use Encode;
  6         15  
  6         521  
16 6     6   34 use base qw(Class::Accessor::Fast);
  6         13  
  6         11786  
17              
18             our $VERSION = '0.08';
19              
20             __PACKAGE__->mk_accessors($_)
21             for qw( protocol host port groonga_path database_path command_list debug);
22              
23             sub new {
24 8     8 1 2130 my $class = shift;
25 8   50     47 my $self = $class->SUPER::new(
26             { protocol => 'gqtp',
27             host => 'localhost',
28             port => '10041',
29             groonga_path => which("groonga") || undef,
30             database_path => undef,
31             command_list => [
32             qw(
33             cache_limit
34             check
35             clearlock
36             column_create
37             column_list
38             column_remove
39             define_selector
40             defrag
41             delete
42             dump
43             load
44             log_level
45             log_put
46             log_reopen
47             quit
48             select
49             shutdown
50             status
51             suggest
52             table_create
53             table_list
54             table_remove
55             view_add
56             )
57             ],
58             @_
59             }
60             );
61 8         2067 return $self;
62             }
63              
64             sub call {
65 4     4 1 187 my $self = shift;
66 4         8 my $command = shift;
67 4         5 my $args_ref = shift;
68              
69             croak( $command . " is not supported command" )
70 4 100   71   13 unless any { $command eq $_ } @{ $self->{command_list} };
  71         198  
  4         25  
71              
72 3 50       13 if ( $self->protocol eq 'http' ) {
    100          
    100          
73 0         0 return $self->_post_to_http_server( $command, $args_ref );
74             }
75             elsif ( $self->protocol eq 'gqtp' ) {
76 1 50 33     14 croak("can not find gronnga_path")
77             if !$self->groonga_path
78             or !-e $self->groonga_path;
79 0         0 return $self->_post_to_gqtp_server( $command, $args_ref );
80             }
81             elsif ( $self->protocol eq 'local_db' ) {
82 1 50 33     20 croak("can not find gronnga_path")
83             if !$self->groonga_path
84             or !-e $self->groonga_path;
85 0 0 0     0 croak("can not find database_path")
86             if !$self->database_path
87             or !-e $self->database_path;
88 0         0 return $self->_post_to_local_db( $command, $args_ref );
89             }
90             else {
91 1         18 croak( $self->protocol . " is not supported protocol" );
92 0           return undef;
93             }
94             }
95              
96             sub _set_timeout {
97 0     0     my $self = shift;
98 0           my $cv = shift;
99 0           my $timeout = shift;
100 0           AnyEvent->now_update;
101 0           my $timer;
102             $timer = AnyEvent->timer(
103             after => $timeout,
104             cb => sub {
105 0     0     my $data = [ [ 0, undef, undef, ], ['timeout'] ];
106 0           my $result = AnyEvent::Groonga::Result->new( data => $data );
107 0           $cv->send($result);
108 0           undef $timer;
109             },
110 0           );
111             }
112              
113             sub _post_to_http_server {
114 0     0     my $self = shift;
115 0           my $command = shift;
116 0           my $args_ref = shift;
117              
118 0           my $url = $self->_generate_groonga_url( $command, $args_ref );
119              
120 0           my $cv = AnyEvent->condvar;
121              
122 0 0         $self->_set_timeout( $cv, $args_ref->{timeout} ) if $args_ref->{timeout};
123              
124             http_get(
125             $url,
126             sub {
127 0     0     my $json = shift;
128 0           my $result;
129             try {
130 0           my $data = JSON->new->utf8->decode($json);
131 0           $result = AnyEvent::Groonga::Result->new(
132             posted_command => $command,
133             data => $data
134             );
135             }
136             catch {
137 0           $result = $_;
138 0           };
139 0           $cv->send($result);
140             }
141 0           );
142              
143 0           return $cv;
144             }
145              
146             sub _post_to_gqtp_server {
147 0     0     my $self = shift;
148 0           my $command = shift;
149 0           my $args_ref = shift;
150              
151 0           my $groonga_command
152             = $self->_generate_groonga_command( $command, $args_ref );
153              
154 0           my $cv = AnyEvent->condvar;
155              
156 0 0         $self->_set_timeout( $cv, $args_ref->{timeout} ) if $args_ref->{timeout};
157              
158 0           my $cmd_cv = run_cmd $groonga_command,
159             '>' => \my $stdout,
160             '2>' => \my $stderr;
161              
162             $cmd_cv->cb(
163             sub {
164 0     0     my $json = $stdout;
165 0           my $result;
166             try {
167 0           my $data = JSON->new->utf8->decode($json);
168 0           $result = AnyEvent::Groonga::Result->new(
169             posted_command => $command,
170             data => $data
171             );
172             }
173             catch {
174 0           $result = $_;
175 0           };
176 0           $cv->send($result);
177             }
178 0           );
179              
180 0           return $cv;
181             }
182              
183             sub _post_to_local_db {
184 0     0     my $self = shift;
185 0           my $command = shift;
186 0           my $args_ref = shift;
187              
188             # just a proxy!
189 0           return $self->_post_to_gqtp_server( $command, $args_ref );
190             }
191              
192             sub _generate_groonga_url {
193 0     0     my $self = shift;
194 0           my $command = shift;
195 0           my $args_ref = shift;
196              
197 0           my $uri = URI->new;
198 0           $uri->scheme("http");
199 0           $uri->host( $self->host );
200 0           $uri->port( $self->port );
201 0           $uri->path( "d/" . $command );
202              
203 0           my @array;
204 0           while ( my ( $key, $value ) = each %$args_ref ) {
205 0 0 0       if ( $command eq 'load' && $key eq 'values' ) {
    0          
206 0           $value = $self->_load_filter($value);
207             }
208             elsif ( ref $value eq 'ARRAY' ) {
209 0           $value = join( ",", @$value );
210             }
211 0           $key = uri_escape($key);
212 0           $value = uri_escape($value);
213 0           push @array, $key . '=' . $value;
214             }
215 0           $uri->query( join( "&", @array ) );
216              
217 0           return $uri->as_string;
218             }
219              
220             sub _generate_groonga_command {
221 0     0     my $self = shift;
222 0           my $command = shift;
223 0           my $args_ref = shift;
224              
225 0           my $groonga_command;
226              
227 0 0         if ( $self->protocol eq 'gqtp' ) {
228 0           $groonga_command = join( " ",
229             $self->groonga_path, '-p', $self->port, '-c', $self->host );
230             }
231             else {
232 0           $groonga_command
233             = join( " ", $self->groonga_path, $self->database_path );
234             }
235              
236 0           $groonga_command .= ' "' . $command . ' ';
237              
238 0           my @array;
239 0           while ( my ( $key, $value ) = each %$args_ref ) {
240 0 0 0       if ( $command eq 'load' && $key eq 'values' ) {
    0 0        
    0 0        
241 0           $value = $self->_load_filter($value);
242             }
243             elsif (
244             $command eq 'select'
245             && ( $key eq 'query'
246             || $key eq 'filter'
247             || $key eq 'sortby'
248             || $key eq 'scorer' )
249             )
250             {
251 0 0         if ( ref $value eq 'ARRAY' ) {
252 0           $value = join( ",", @$value );
253             }
254 0           $value = $self->_select_filter($value);
255             }
256             elsif ( ref $value eq 'ARRAY' ) {
257 0           $value = join( ",", @$value );
258             }
259 0           $key = '--' . $key;
260 0           push @array, ( $key, $value );
261             }
262 0           $groonga_command .= join( " ", @array ) . '"';
263 0 0         warn($groonga_command) if $self->debug;
264 0           return $groonga_command;
265             }
266              
267             sub _select_filter {
268 0     0     my $self = shift;
269 0           my $data = shift;
270 0 0         $data = decode( "utf8", $data ) unless utf8::is_utf8($data);
271 0           $data =~ /(^|[^\\])"|'/;
272 0 0         if ($1) {
273 0           $data =~ s/(^|[^\\])"|'/$1\\"/g;
274             }
275             else {
276 0           $data =~ s/(^|[^\\])"|'/\\"/g;
277             }
278 0           return '\'' . $data . '\'';
279             }
280              
281             sub _load_filter {
282 0     0     my $self = shift;
283 0           my $data = shift;
284 0           my $json = JSON->new->latin1->encode($data);
285 0 0         if ( $self->protocol ne 'http' ) {
286 0           $json =~ s/\\/\\\\\\\\/g;
287 0           $json =~ s/'/\\'/g;
288 0           $json =~ s/"/\\"/g;
289             }
290 0 0         if ( ref $data ne 'ARRAY' ) {
291 0           $json = '[' . $json . ']';
292             }
293 0 0         $json = '\'' . $json . '\'' if $self->protocol ne 'http';
294 0           return $json;
295             }
296              
297             1;
298             __END__