/[Search-Estraier]/trunk/lib/Search/Estraier.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/lib/Search/Estraier.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 47 by dpavlin, Fri Jan 6 01:51:28 2006 UTC revision 81 by dpavlin, Tue Jan 17 00:03:45 2006 UTC
# Line 4  use 5.008; Line 4  use 5.008;
4  use strict;  use strict;
5  use warnings;  use warnings;
6    
7  our $VERSION = '0.00';  our $VERSION = '0.04_1';
8    
9  =head1 NAME  =head1 NAME
10    
# Line 12  Search::Estraier - pure perl module to u Line 12  Search::Estraier - pure perl module to u
12    
13  =head1 SYNOPSIS  =head1 SYNOPSIS
14    
15    use Search::Estraier;  =head2 Simple indexer
16    my $est = new Search::Estraier();  
17            use Search::Estraier;
18    
19            # create and configure node
20            my $node = new Search::Estraier::Node;
21            $node->set_url("http://localhost:1978/node/test");
22            $node->set_auth("admin","admin");
23    
24            # create document
25            my $doc = new Search::Estraier::Document;
26    
27            # add attributes
28            $doc->add_attr('@uri', "http://estraier.gov/example.txt");
29            $doc->add_attr('@title', "Over the Rainbow");
30    
31            # add body text to document
32            $doc->add_text("Somewhere over the rainbow.  Way up high.");
33            $doc->add_text("There's a land that I heard of once in a lullaby.");
34    
35            die "error: ", $node->status,"\n" unless ($node->put_doc($doc));
36    
37    =head2 Simple searcher
38    
39            use Search::Estraier;
40    
41            # create and configure node
42            my $node = new Search::Estraier::Node;
43            $node->set_url("http://localhost:1978/node/test");
44            $node->set_auth("admin","admin");
45    
46            # create condition
47            my $cond = new Search::Estraier::Condition;
48    
49            # set search phrase
50            $cond->set_phrase("rainbow AND lullaby");
51    
52            my $nres = $node->search($cond, 0);
53            if (defined($nres)) {
54                    # for each document in results
55                    for my $i ( 0 ... $nres->doc_num - 1 ) {
56                            # get result document
57                            my $rdoc = $nres->get_doc($i);
58                            # display attribte
59                            print "URI: ", $rdoc->attr('@uri'),"\n";
60                            print "Title: ", $rdoc->attr('@title'),"\n";
61                            print $rdoc->snippet,"\n";
62                    }
63            } else {
64                    die "error: ", $node->status,"\n";
65            }
66    
67  =head1 DESCRIPTION  =head1 DESCRIPTION
68    
# Line 25  or Hyper Estraier development files on t Line 74  or Hyper Estraier development files on t
74  It is implemented as multiple packages which closly resamble Ruby  It is implemented as multiple packages which closly resamble Ruby
75  implementation. It also includes methods to manage nodes.  implementation. It also includes methods to manage nodes.
76    
77    There are few examples in C<scripts> directory of this distribution.
78    
79  =cut  =cut
80    
81  =head1 Inheritable common methods  =head1 Inheritable common methods
# Line 106  sub new { Line 157  sub new {
157                          } elsif ($line =~ m/^$/) {                          } elsif ($line =~ m/^$/) {
158                                  $in_text = 1;                                  $in_text = 1;
159                                  next;                                  next;
160                          } elsif ($line =~ m/^(.+)=(.+)$/) {                          } elsif ($line =~ m/^(.+)=(.*)$/) {
161                                  $self->{attrs}->{ $1 } = $2;                                  $self->{attrs}->{ $1 } = $2;
162                                  next;                                  next;
163                          }                          }
164    
165                          warn "draft ignored: $line\n";                          warn "draft ignored: '$line'\n";
166                  }                  }
167          }          }
168    
# Line 205  Returns array with attribute names from Line 256  Returns array with attribute names from
256    
257  sub attr_names {  sub attr_names {
258          my $self = shift;          my $self = shift;
259          croak "attr_names return array, not scalar" if (! wantarray);          return unless ($self->{attrs});
260            #croak "attr_names return array, not scalar" if (! wantarray);
261          return sort keys %{ $self->{attrs} };          return sort keys %{ $self->{attrs} };
262  }  }
263    
# Line 221  Returns value of an attribute. Line 273  Returns value of an attribute.
273  sub attr {  sub attr {
274          my $self = shift;          my $self = shift;
275          my $name = shift;          my $name = shift;
276            return unless (defined($name) && $self->{attrs});
277          return $self->{'attrs'}->{ $name };          return $self->{attrs}->{ $name };
278  }  }
279    
280    
# Line 236  Returns array with text sentences. Line 288  Returns array with text sentences.
288    
289  sub texts {  sub texts {
290          my $self = shift;          my $self = shift;
291          confess "texts return array, not scalar" if (! wantarray);          #confess "texts return array, not scalar" if (! wantarray);
292          return @{ $self->{dtexts} };          return @{ $self->{dtexts} } if ($self->{dtexts});
293  }  }
294    
295    
# Line 251  Return whole text as single scalar. Line 303  Return whole text as single scalar.
303    
304  sub cat_texts {  sub cat_texts {
305          my $self = shift;          my $self = shift;
306          return join(' ',@{ $self->{dtexts} });          return join(' ',@{ $self->{dtexts} }) if ($self->{dtexts});
307  }  }
308    
309    
# Line 460  Return search result attrs. Line 512  Return search result attrs.
512  sub attrs {  sub attrs {
513          my $self = shift;          my $self = shift;
514          #croak "attrs return array, not scalar" if (! wantarray);          #croak "attrs return array, not scalar" if (! wantarray);
515          return @{ $self->{attrs} };          return @{ $self->{attrs} } if ($self->{attrs});
516  }  }
517    
518    
# Line 524  sub new { Line 576  sub new {
576          my $self = {@_};          my $self = {@_};
577          bless($self, $class);          bless($self, $class);
578    
579          foreach my $f (qw/uri attrs snippet keywords/) {          croak "missing uri for ResultDocument" unless defined($self->{uri});
                 croak "missing $f for ResultDocument" unless defined($self->{$f});  
         }  
580    
581          $self ? return $self : return undef;          $self ? return $self : return undef;
582  }  }
# Line 645  Return number of documents Line 695  Return number of documents
695    
696  sub doc_num {  sub doc_num {
697          my $self = shift;          my $self = shift;
698          return $#{$self->{docs}};          return $#{$self->{docs}} + 1;
699  }  }
700    
701    
# Line 692  use Carp qw/carp croak confess/; Line 742  use Carp qw/carp croak confess/;
742  use URI;  use URI;
743  use MIME::Base64;  use MIME::Base64;
744  use IO::Socket::INET;  use IO::Socket::INET;
745    use URI::Escape qw/uri_escape/;
746    
747  =head1 Search::Estraier::Node  =head1 Search::Estraier::Node
748    
# Line 699  use IO::Socket::INET; Line 750  use IO::Socket::INET;
750    
751    my $node = new Search::HyperEstraier::Node;    my $node = new Search::HyperEstraier::Node;
752    
753    or optionally with C<url> as parametar
754    
755      my $node = new Search::HyperEstraier::Node( 'http://localhost:1978/node/test' );
756    
757    or in more verbose form
758    
759      my $node = new Search::HyperEstraier::Node(
760            url => 'http://localhost:1978/node/test',
761            debug => 1,
762            croak_on_error => 1
763      );
764    
765    with following arguments:
766    
767    =over 4
768    
769    =item url
770    
771    URL to node
772    
773    =item debug
774    
775    dumps a B<lot> of debugging output
776    
777    =item croak_on_error
778    
779    very helpful during development. It will croak on all errors instead of
780    silently returning C<-1> (which is convention of Hyper Estraier API in other
781    languages).
782    
783    =back
784    
785  =cut  =cut
786    
787  sub new {  sub new {
# Line 716  sub new { Line 799  sub new {
799          };          };
800          bless($self, $class);          bless($self, $class);
801    
802          if (@_) {          if ($#_ == 0) {
803                  $self->{debug} = shift;                  $self->{url} = shift;
804                  warn "## Node debug on\n";          } else {
805                    my $args = {@_};
806    
807                    %$self = ( %$self, @_ );
808    
809                    warn "## Node debug on\n" if ($self->{debug});
810          }          }
811    
812          $self ? return $self : return undef;          $self ? return $self : return undef;
# Line 866  sub out_doc_by_uri { Line 954  sub out_doc_by_uri {
954          return unless ($self->{url});          return unless ($self->{url});
955          $self->shuttle_url( $self->{url} . '/out_doc',          $self->shuttle_url( $self->{url} . '/out_doc',
956                  'application/x-www-form-urlencoded',                  'application/x-www-form-urlencoded',
957                  "uri=$uri",                  "uri=" . uri_escape($uri),
958                  undef                  undef
959          ) == 200;          ) == 200;
960  }  }
# Line 928  sub get_doc_by_uri { Line 1016  sub get_doc_by_uri {
1016  }  }
1017    
1018    
1019    =head2 get_doc_attr
1020    
1021    Retrieve the value of an atribute from object
1022    
1023      my $val = $node->get_doc_attr( document_id, 'attribute_name' ) or
1024            die "can't get document attribute";
1025    
1026    =cut
1027    
1028    sub get_doc_attr {
1029            my $self = shift;
1030            my ($id,$name) = @_;
1031            return unless ($id && $name);
1032            return $self->_fetch_doc( id => $id, attr => $name );
1033    }
1034    
1035    
1036    =head2 get_doc_attr_by_uri
1037    
1038    Retrieve the value of an atribute from object
1039    
1040      my $val = $node->get_doc_attr_by_uri( document_id, 'attribute_name' ) or
1041            die "can't get document attribute";
1042    
1043    =cut
1044    
1045    sub get_doc_attr_by_uri {
1046            my $self = shift;
1047            my ($uri,$name) = @_;
1048            return unless ($uri && $name);
1049            return $self->_fetch_doc( uri => $uri, attr => $name );
1050    }
1051    
1052    
1053  =head2 etch_doc  =head2 etch_doc
1054    
1055  Exctract document keywords  Exctract document keywords
# Line 936  Exctract document keywords Line 1058  Exctract document keywords
1058    
1059  =cut  =cut
1060    
1061  sub erch_doc {  sub etch_doc {
1062          my $self = shift;          my $self = shift;
1063          my $id = shift || return;          my $id = shift || return;
1064          return $self->_fetch_doc( id => $id, etch => 1 );          return $self->_fetch_doc( id => $id, etch => 1 );
# Line 987  C<etch_doc>, C<etch_doc_by_uri>. Line 1109  C<etch_doc>, C<etch_doc_by_uri>.
1109   my $doc = $node->_fetch_doc( id => 42, etch => 1 );   my $doc = $node->_fetch_doc( id => 42, etch => 1 );
1110   my $doc = $node->_fetch_doc( uri => 'file:///document/uri/42', etch => 1 );   my $doc = $node->_fetch_doc( uri => 'file:///document/uri/42', etch => 1 );
1111    
1112     # to get document attrubute add attr
1113     my $doc = $node->_fetch_doc( id => 42, attr => '@mdate' );
1114     my $doc = $node->_fetch_doc( uri => 'file:///document/uri/42', attr => '@mdate' );
1115    
1116   # more general form which allows implementation of   # more general form which allows implementation of
1117   # uri_to_id   # uri_to_id
1118   my $id = $node->_fetch_doc(   my $id = $node->_fetch_doc(
# Line 1011  sub _fetch_doc { Line 1137  sub _fetch_doc {
1137                  croak "id must be numberm not '$a->{id}'" unless ($a->{id} =~ m/^\d+$/);                  croak "id must be numberm not '$a->{id}'" unless ($a->{id} =~ m/^\d+$/);
1138                  $arg = 'id=' . $a->{id};                  $arg = 'id=' . $a->{id};
1139          } elsif ($a->{uri}) {          } elsif ($a->{uri}) {
1140                  $arg = 'uri=' . $a->{uri};                  $arg = 'uri=' . uri_escape($a->{uri});
1141          } else {          } else {
1142                  confess "unhandled argument. Need id or uri.";                  confess "unhandled argument. Need id or uri.";
1143          }          }
1144    
1145            if ($a->{attr}) {
1146                    $path = '/get_doc_attr';
1147                    $arg .= '&attr=' . uri_escape($a->{attr});
1148                    $a->{chomp_resbody} = 1;
1149            }
1150    
1151          my $rv = $self->shuttle_url( $self->{url} . $path,          my $rv = $self->shuttle_url( $self->{url} . $path,
1152                  'application/x-www-form-urlencoded',                  'application/x-www-form-urlencoded',
1153                  $arg,                  $arg,
# Line 1042  sub _fetch_doc { Line 1174  sub _fetch_doc {
1174  }  }
1175    
1176    
1177    =head2 name
1178    
1179      my $node_name = $node->name;
1180    
1181    =cut
1182    
1183    sub name {
1184            my $self = shift;
1185            $self->_set_info unless ($self->{name});
1186            return $self->{name};
1187    }
1188    
1189    
1190    =head2 label
1191    
1192      my $node_label = $node->label;
1193    
1194    =cut
1195    
1196    sub label {
1197            my $self = shift;
1198            $self->_set_info unless ($self->{label});
1199            return $self->{label};
1200    }
1201    
1202    
1203    =head2 doc_num
1204    
1205      my $documents_in_node = $node->doc_num;
1206    
1207    =cut
1208    
1209    sub doc_num {
1210            my $self = shift;
1211            $self->_set_info if ($self->{dnum} < 0);
1212            return $self->{dnum};
1213    }
1214    
1215    
1216    =head2 word_num
1217    
1218      my $words_in_node = $node->word_num;
1219    
1220    =cut
1221    
1222    sub word_num {
1223            my $self = shift;
1224            $self->_set_info if ($self->{wnum} < 0);
1225            return $self->{wnum};
1226    }
1227    
1228    
1229    =head2 size
1230    
1231      my $node_size = $node->size;
1232    
1233    =cut
1234    
1235    sub size {
1236            my $self = shift;
1237            $self->_set_info if ($self->{size} < 0);
1238            return $self->{size};
1239    }
1240    
1241    
1242    =head2 search
1243    
1244    Search documents which match condition
1245    
1246      my $nres = $node->search( $cond, $depth );
1247    
1248    C<$cond> is C<Search::Estraier::Condition> object, while <$depth> specifies
1249    depth for meta search.
1250    
1251    Function results C<Search::Estraier::NodeResult> object.
1252    
1253    =cut
1254    
1255    sub search {
1256            my $self = shift;
1257            my ($cond, $depth) = @_;
1258            return unless ($cond && defined($depth) && $self->{url});
1259            croak "cond mush be Search::Estraier::Condition, not '$cond->isa'" unless ($cond->isa('Search::Estraier::Condition'));
1260            croak "depth needs number, not '$depth'" unless ($depth =~ m/^\d+$/);
1261    
1262            my $resbody;
1263    
1264            my $rv = $self->shuttle_url( $self->{url} . '/search',
1265                    'application/x-www-form-urlencoded',
1266                    $self->cond_to_query( $cond, $depth ),
1267                    \$resbody,
1268            );
1269            return if ($rv != 200);
1270    
1271            my (@docs, $hints);
1272    
1273            my @lines = split(/\n/, $resbody);
1274            return unless (@lines);
1275    
1276            my $border = $lines[0];
1277            my $isend = 0;
1278            my $lnum = 1;
1279    
1280            while ( $lnum <= $#lines ) {
1281                    my $line = $lines[$lnum];
1282                    $lnum++;
1283    
1284                    #warn "## $line\n";
1285                    if ($line && $line =~ m/^\Q$border\E(:END)*$/) {
1286                            $isend = $1;
1287                            last;
1288                    }
1289    
1290                    if ($line =~ /\t/) {
1291                            my ($k,$v) = split(/\t/, $line, 2);
1292                            $hints->{$k} = $v;
1293                    }
1294            }
1295    
1296            my $snum = $lnum;
1297    
1298            while( ! $isend && $lnum <= $#lines ) {
1299                    my $line = $lines[$lnum];
1300                    #warn "# $lnum: $line\n";
1301                    $lnum++;
1302    
1303                    if ($line && $line =~ m/^\Q$border\E/) {
1304                            if ($lnum > $snum) {
1305                                    my $rdattrs;
1306                                    my $rdvector;
1307                                    my $rdsnippet;
1308                                    
1309                                    my $rlnum = $snum;
1310                                    while ($rlnum < $lnum - 1 ) {
1311                                            #my $rdline = $self->_s($lines[$rlnum]);
1312                                            my $rdline = $lines[$rlnum];
1313                                            $rlnum++;
1314                                            last unless ($rdline);
1315                                            if ($rdline =~ /^%/) {
1316                                                    $rdvector = $1 if ($rdline =~ /^%VECTOR\t(.+)$/);
1317                                            } elsif($rdline =~ /=/) {
1318                                                    $rdattrs->{$1} = $2 if ($rdline =~ /^(.+)=(.+)$/);
1319                                            } else {
1320                                                    confess "invalid format of response";
1321                                            }
1322                                    }
1323                                    while($rlnum < $lnum - 1) {
1324                                            my $rdline = $lines[$rlnum];
1325                                            $rlnum++;
1326                                            $rdsnippet .= "$rdline\n";
1327                                    }
1328                                    #warn Dumper($rdvector, $rdattrs, $rdsnippet);
1329                                    if (my $rduri = $rdattrs->{'@uri'}) {
1330                                            push @docs, new Search::Estraier::ResultDocument(
1331                                                    uri => $rduri,
1332                                                    attrs => $rdattrs,
1333                                                    snippet => $rdsnippet,
1334                                                    keywords => $rdvector,
1335                                            );
1336                                    }
1337                            }
1338                            $snum = $lnum;
1339                            #warn "### $line\n";
1340                            $isend = 1 if ($line =~ /:END$/);
1341                    }
1342    
1343            }
1344    
1345            if (! $isend) {
1346                    warn "received result doesn't have :END\n$resbody";
1347                    return;
1348            }
1349    
1350            #warn Dumper(\@docs, $hints);
1351    
1352            return new Search::Estraier::NodeResult( docs => \@docs, hints => $hints );
1353    }
1354    
1355    
1356    =head2 cond_to_query
1357    
1358    Return URI encoded string generated from Search::Estraier::Condition
1359    
1360      my $args = $node->cond_to_query( $cond, $depth );
1361    
1362    =cut
1363    
1364    sub cond_to_query {
1365            my $self = shift;
1366    
1367            my $cond = shift || return;
1368            croak "condition must be Search::Estraier::Condition, not '$cond->isa'" unless ($cond->isa('Search::Estraier::Condition'));
1369            my $depth = shift;
1370    
1371            my @args;
1372    
1373            if (my $phrase = $cond->phrase) {
1374                    push @args, 'phrase=' . uri_escape($phrase);
1375            }
1376    
1377            if (my @attrs = $cond->attrs) {
1378                    for my $i ( 0 .. $#attrs ) {
1379                            push @args,'attr' . ($i+1) . '=' . uri_escape( $attrs[$i] ) if ($attrs[$i]);
1380                    }
1381            }
1382    
1383            if (my $order = $cond->order) {
1384                    push @args, 'order=' . uri_escape($order);
1385            }
1386                    
1387            if (my $max = $cond->max) {
1388                    push @args, 'max=' . $max;
1389            } else {
1390                    push @args, 'max=' . (1 << 30);
1391            }
1392    
1393            if (my $options = $cond->options) {
1394                    push @args, 'options=' . $options;
1395            }
1396    
1397            push @args, 'depth=' . $depth if ($depth);
1398            push @args, 'wwidth=' . $self->{wwidth};
1399            push @args, 'hwidth=' . $self->{hwidth};
1400            push @args, 'awidth=' . $self->{awidth};
1401    
1402            return join('&', @args);
1403    }
1404    
1405    
1406  =head2 shuttle_url  =head2 shuttle_url
1407    
1408  This is method which uses C<IO::Socket::INET> to communicate with Hyper Estraier node  This is method which uses C<LWP::UserAgent> to communicate with Hyper Estraier node
1409  master.  master.
1410    
1411    my $rv = shuttle_url( $url, $content_type, \$req_body, \$resbody );    my $rv = shuttle_url( $url, $content_type, $req_body, \$resbody );
1412    
1413  C<$resheads> and C<$resbody> booleans controll if response headers and/or response  C<$resheads> and C<$resbody> booleans controll if response headers and/or response
1414  body will be saved within object.  body will be saved within object.
1415    
1416  =cut  =cut
1417    
1418    use LWP::UserAgent;
1419    
1420  sub shuttle_url {  sub shuttle_url {
1421          my $self = shift;          my $self = shift;
1422    
# Line 1074  sub shuttle_url { Line 1435  sub shuttle_url {
1435                  return -1;                  return -1;
1436          }          }
1437    
1438          my ($host,$port,$query) = ($url->host, $url->port, $url->path);          my $ua = LWP::UserAgent->new;
1439            $ua->agent( "Search-Estraier/$Search::Estraier::VERSION" );
1440    
1441          if ($self->{pxhost}) {          my $req;
1442                  ($host,$port) = ($self->{pxhost}, $self->{pxport});          if ($reqbody) {
1443                  $query = "http://$host:$port/$query";                  $req = HTTP::Request->new(POST => $url);
1444            } else {
1445                    $req = HTTP::Request->new(GET => $url);
1446          }          }
1447    
1448          $query .= '?' . $url->query if ($url->query && ! $reqbody);          $req->headers->header( 'Host' => $url->host . ":" . $url->port );
1449            $req->headers->header( 'Connection', 'close' );
1450            $req->headers->header( 'Authorization', 'Basic ' . $self->{auth} ) if ($self->{auth});
1451            $req->content_type( $content_type );
1452    
1453          my $headers;          warn $req->headers->as_string,"\n" if ($self->{debug});
1454    
1455          if ($reqbody) {          if ($reqbody) {
1456                  $headers .= "POST $query HTTP/1.0\r\n";                  warn "$reqbody\n" if ($self->{debug});
1457          } else {                  $req->content( $reqbody );
                 $headers .= "GET $query HTTP/1.0\r\n";  
1458          }          }
1459    
1460          $headers .= "Host: " . $url->host . ":" . $url->port . "\r\n";          my $res = $ua->request($req) || croak "can't make request to $url: $!";
         $headers .= "Connection: close\r\n";  
         $headers .= "User-Agent: Search-Estraier/$Search::Estraier::VERSION\r\n";  
         $headers .= "Content-Type: $content_type\r\n";  
         $headers .= "Authorization: Basic $self->{auth}\r\n";  
         my $len = 0;  
         {  
                 use bytes;  
                 $len = length($reqbody) if ($reqbody);  
         }  
         $headers .= "Content-Length: $len\r\n";  
         $headers .= "\r\n";  
   
         my $sock = IO::Socket::INET->new(  
                 PeerAddr        => $host,  
                 PeerPort        => $port,  
                 Proto           => 'tcp',  
                 Timeout         => $self->{timeout} || 90,  
         );  
   
         if (! $sock) {  
                 carp "can't open socket to $host:$port";  
                 return -1;  
         }  
1461    
1462          warn $headers if ($self->{debug});          warn "## response status: ",$res->status_line,"\n" if ($self->{debug});
1463    
1464          print $sock $headers or          ($self->{status}, $self->{status_message}) = split(/\s+/, $res->status_line, 2);
                 carp "can't send headers to network:\n$headers\n" and return -1;  
1465    
1466          if ($reqbody) {          if (! $res->is_success) {
1467                  warn "$reqbody\n" if ($self->{debug});                  if ($self->{croak_on_error}) {
1468                  print $sock $reqbody or                          croak("can't get $url: ",$res->status_line);
1469                          carp "can't send request body to network:\n$$reqbody\n" and return -1;                  } else {
1470                            return -1;
1471                    }
1472          }          }
1473    
1474          my $line = <$sock>;          $$resbody .= $res->content;
         chomp($line);  
         my ($schema, $res_status, undef) = split(/  */, $line, 3);  
         return if ($schema !~ /^HTTP/ || ! $res_status);  
   
         $self->{status} = $res_status;  
         warn "## response status: $res_status\n" if ($self->{debug});  
   
         # skip rest of headers  
         $line = <$sock>;  
         while ($line) {  
                 $line = <$sock>;  
                 $line =~ s/[\r\n]+$//;  
                 warn "## ", $line || 'NULL', " ##\n" if ($self->{debug});  
         };  
   
         # read body  
         $len = 0;  
         do {  
                 $len = read($sock, my $buf, 8192);  
                 $$resbody .= $buf if ($resbody);  
         } while ($len);  
1475    
1476          warn "## response body:\n$$resbody\n" if ($resbody && $self->{debug});          warn "## response body:\n$$resbody\n" if ($resbody && $self->{debug});
1477    
1478          return $self->{status};          return $self->{status};
1479  }  }
1480    
1481    
1482    =head2 set_snippet_width
1483    
1484    Set width of snippets in results
1485    
1486      $node->set_snippet_width( $wwidth, $hwidth, $awidth );
1487    
1488    C<$wwidth> specifies whole width of snippet. It's C<480> by default. If it's C<0> snippet
1489    is not sent with results. If it is negative, whole document text is sent instead of snippet.
1490    
1491    C<$hwidth> specified width of strings from beginning of string. Default
1492    value is C<96>. Negative or zero value keep previous value.
1493    
1494    C<$awidth> specifies width of strings around each highlighted word. It's C<96> by default.
1495    If negative of zero value is provided previous value is kept unchanged.
1496    
1497    =cut
1498    
1499    sub set_snippet_width {
1500            my $self = shift;
1501    
1502            my ($wwidth, $hwidth, $awidth) = @_;
1503            $self->{wwidth} = $wwidth;
1504            $self->{hwidth} = $hwidth if ($hwidth >= 0);
1505            $self->{awidth} = $awidth if ($awidth >= 0);
1506    }
1507    
1508    
1509    =head2 set_user
1510    
1511    Manage users of node
1512    
1513      $node->set_user( 'name', $mode );
1514    
1515    C<$mode> can be one of:
1516    
1517    =over 4
1518    
1519    =item 0
1520    
1521    delete account
1522    
1523    =item 1
1524    
1525    set administrative right for user
1526    
1527    =item 2
1528    
1529    set user account as guest
1530    
1531    =back
1532    
1533    Return true on success, otherwise false.
1534    
1535    =cut
1536    
1537    sub set_user {
1538            my $self = shift;
1539            my ($name, $mode) = @_;
1540    
1541            return unless ($self->{url});
1542            croak "mode must be number, not '$mode'" unless ($mode =~ m/^\d+$/);
1543    
1544            $self->shuttle_url( $self->{url} . '/_set_user',
1545                    'text/plain',
1546                    'name=' . uri_escape($name) . '&mode=' . $mode,
1547                    undef
1548            ) == 200;
1549    }
1550    
1551    
1552    =head2 set_link
1553    
1554    Manage node links
1555    
1556      $node->set_link('http://localhost:1978/node/another', 'another node label', $credit);
1557    
1558    If C<$credit> is negative, link is removed.
1559    
1560    =cut
1561    
1562    sub set_link {
1563            my $self = shift;
1564            my ($url, $label, $credit) = @_;
1565    
1566            return unless ($self->{url});
1567            croak "mode credit be number, not '$credit'" unless ($credit =~ m/^\d+$/);
1568    
1569            my $reqbody = 'url=' . uri_escape($url) . '&label=' . uri_escape($label);
1570            $reqbody .= '&credit=' . $credit if ($credit > 0);
1571    
1572            $self->shuttle_url( $self->{url} . '/_set_link',
1573                    'application/x-www-form-urlencoded',
1574                    $reqbody,
1575                    undef
1576            ) == 200;
1577    }
1578    
1579    
1580    =head1 PRIVATE METHODS
1581    
1582    You could call those directly, but you don't have to. I hope.
1583    
1584    =head2 _set_info
1585    
1586    Set information for node
1587    
1588      $node->_set_info;
1589    
1590    =cut
1591    
1592    sub _set_info {
1593            my $self = shift;
1594    
1595            $self->{status} = -1;
1596            return unless ($self->{url});
1597    
1598            my $resbody;
1599            my $rv = $self->shuttle_url( $self->{url} . '/inform',
1600                    'text/plain',
1601                    undef,
1602                    \$resbody,
1603            );
1604    
1605            return if ($rv != 200 || !$resbody);
1606    
1607            # it seems that response can have multiple line endings
1608            $resbody =~ s/[\r\n]+$//;
1609    
1610            ( $self->{name}, $self->{label}, $self->{dnum}, $self->{wnum}, $self->{size} ) =
1611                    split(/\t/, $resbody, 5);
1612    
1613    }
1614    
1615  ###  ###
1616    
1617  =head1 EXPORT  =head1 EXPORT

Legend:
Removed from v.47  
changed lines
  Added in v.81

  ViewVC Help
Powered by ViewVC 1.1.26