/[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 30 by dpavlin, Thu Jan 5 15:33:48 2006 UTC revision 136 by dpavlin, Tue May 9 14:03:36 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.06_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                    url => 'http://localhost:1978/node/test',
22                    user => 'admin',
23                    passwd => 'admin'
24            );
25    
26            # create document
27            my $doc = new Search::Estraier::Document;
28    
29            # add attributes
30            $doc->add_attr('@uri', "http://estraier.gov/example.txt");
31            $doc->add_attr('@title', "Over the Rainbow");
32    
33            # add body text to document
34            $doc->add_text("Somewhere over the rainbow.  Way up high.");
35            $doc->add_text("There's a land that I heard of once in a lullaby.");
36    
37            die "error: ", $node->status,"\n" unless (eval { $node->put_doc($doc) });
38    
39    =head2 Simple searcher
40    
41            use Search::Estraier;
42    
43            # create and configure node
44            my $node = new Search::Estraier::Node(
45                    url => 'http://localhost:1978/node/test',
46                    user => 'admin',
47                    passwd => 'admin',
48                    croak_on_error => 1,
49            );
50    
51            # create condition
52            my $cond = new Search::Estraier::Condition;
53    
54            # set search phrase
55            $cond->set_phrase("rainbow AND lullaby");
56    
57            my $nres = $node->search($cond, 0);
58    
59            if (defined($nres)) {
60                    print "Got ", $nres->hits, " results\n";
61    
62                    # for each document in results
63                    for my $i ( 0 ... $nres->doc_num - 1 ) {
64                            # get result document
65                            my $rdoc = $nres->get_doc($i);
66                            # display attribte
67                            print "URI: ", $rdoc->attr('@uri'),"\n";
68                            print "Title: ", $rdoc->attr('@title'),"\n";
69                            print $rdoc->snippet,"\n";
70                    }
71            } else {
72                    die "error: ", $node->status,"\n";
73            }
74    
75  =head1 DESCRIPTION  =head1 DESCRIPTION
76    
# Line 25  or Hyper Estraier development files on t Line 82  or Hyper Estraier development files on t
82  It is implemented as multiple packages which closly resamble Ruby  It is implemented as multiple packages which closly resamble Ruby
83  implementation. It also includes methods to manage nodes.  implementation. It also includes methods to manage nodes.
84    
85    There are few examples in C<scripts> directory of this distribution.
86    
87  =cut  =cut
88    
89    =head1 Inheritable common methods
90    
91    This methods should really move somewhere else.
92    
93  =head2 _s  =head2 _s
94    
95  Remove multiple whitespaces from string, as well as whitespaces at beginning or end  Remove multiple whitespaces from string, as well as whitespaces at beginning or end
# Line 37  Remove multiple whitespaces from string, Line 100  Remove multiple whitespaces from string,
100  =cut  =cut
101    
102  sub _s {  sub _s {
103          my $text = $_[1] || return;          my $text = $_[1];
104            return unless defined($text);
105          $text =~ s/\s\s+/ /gs;          $text =~ s/\s\s+/ /gs;
106          $text =~ s/^\s+//;          $text =~ s/^\s+//;
107          $text =~ s/\s+$//;          $text =~ s/\s+$//;
# Line 56  our @ISA = qw/Search::Estraier/; Line 120  our @ISA = qw/Search::Estraier/;
120  This class implements Document which is collection of attributes  This class implements Document which is collection of attributes
121  (key=value), vectors (also key value) display text and hidden text.  (key=value), vectors (also key value) display text and hidden text.
122    
123    
124  =head2 new  =head2 new
125    
126  Create new document, empty or from draft.  Create new document, empty or from draft.
# Line 101  sub new { Line 166  sub new {
166                          } elsif ($line =~ m/^$/) {                          } elsif ($line =~ m/^$/) {
167                                  $in_text = 1;                                  $in_text = 1;
168                                  next;                                  next;
169                          } elsif ($line =~ m/^(.+)=(.+)$/) {                          } elsif ($line =~ m/^(.+)=(.*)$/) {
170                                  $self->{attrs}->{ $1 } = $2;                                  $self->{attrs}->{ $1 } = $2;
171                                  next;                                  next;
172                          }                          }
173    
174                          warn "draft ignored: $line\n";                          warn "draft ignored: '$line'\n";
175                  }                  }
176          }          }
177    
# Line 175  sub add_hidden_text { Line 240  sub add_hidden_text {
240          push @{ $self->{htexts} }, $self->_s($text);          push @{ $self->{htexts} }, $self->_s($text);
241  }  }
242    
243    
244  =head2 id  =head2 id
245    
246  Get the ID number of document. If the object has never been registred, C<-1> is returned.  Get the ID number of document. If the object has never been registred, C<-1> is returned.
# Line 188  sub id { Line 254  sub id {
254          return $self->{id};          return $self->{id};
255  }  }
256    
257    
258  =head2 attr_names  =head2 attr_names
259    
260  Returns array with attribute names from document object.  Returns array with attribute names from document object.
# Line 198  Returns array with attribute names from Line 265  Returns array with attribute names from
265    
266  sub attr_names {  sub attr_names {
267          my $self = shift;          my $self = shift;
268          croak "attr_names return array, not scalar" if (! wantarray);          return unless ($self->{attrs});
269            #croak "attr_names return array, not scalar" if (! wantarray);
270          return sort keys %{ $self->{attrs} };          return sort keys %{ $self->{attrs} };
271  }  }
272    
# Line 214  Returns value of an attribute. Line 282  Returns value of an attribute.
282  sub attr {  sub attr {
283          my $self = shift;          my $self = shift;
284          my $name = shift;          my $name = shift;
285            return unless (defined($name) && $self->{attrs});
286          return $self->{'attrs'}->{ $name };          return $self->{attrs}->{ $name };
287  }  }
288    
289    
# Line 229  Returns array with text sentences. Line 297  Returns array with text sentences.
297    
298  sub texts {  sub texts {
299          my $self = shift;          my $self = shift;
300          confess "texts return array, not scalar" if (! wantarray);          #confess "texts return array, not scalar" if (! wantarray);
301          return @{ $self->{dtexts} };          return @{ $self->{dtexts} } if ($self->{dtexts});
302  }  }
303    
304    
305  =head2 cat_texts  =head2 cat_texts
306    
307  Return whole text as single scalar.  Return whole text as single scalar.
# Line 243  Return whole text as single scalar. Line 312  Return whole text as single scalar.
312    
313  sub cat_texts {  sub cat_texts {
314          my $self = shift;          my $self = shift;
315          return join(' ',@{ $self->{dtexts} });          return join(' ',@{ $self->{dtexts} }) if ($self->{dtexts});
316  }  }
317    
318    
319  =head2 dump_draft  =head2 dump_draft
320    
321  Dump draft data from document object.  Dump draft data from document object.
# Line 259  sub dump_draft { Line 329  sub dump_draft {
329          my $draft;          my $draft;
330    
331          foreach my $attr_name (sort keys %{ $self->{attrs} }) {          foreach my $attr_name (sort keys %{ $self->{attrs} }) {
332                  $draft .= $attr_name . '=' . $self->{attrs}->{$attr_name} . "\n";                  next unless defined(my $v = $self->{attrs}->{$attr_name});
333                    $draft .= $attr_name . '=' . $v . "\n";
334          }          }
335    
336          if ($self->{kwords}) {          if ($self->{kwords}) {
# Line 272  sub dump_draft { Line 343  sub dump_draft {
343    
344          $draft .= "\n";          $draft .= "\n";
345    
346          $draft .= join("\n", @{ $self->{dtexts} }) . "\n";          $draft .= join("\n", @{ $self->{dtexts} }) . "\n" if ($self->{dtexts});
347          $draft .= "\t" . join("\n\t", @{ $self->{htexts} }) . "\n";          $draft .= "\t" . join("\n\t", @{ $self->{htexts} }) . "\n" if ($self->{htexts});
348    
349          return $draft;          return $draft;
350  }  }
351    
352    
353  =head2 delete  =head2 delete
354    
355  Empty document object  Empty document object
# Line 306  sub delete { Line 378  sub delete {
378    
379  package Search::Estraier::Condition;  package Search::Estraier::Condition;
380    
381  use Carp qw/confess croak/;  use Carp qw/carp confess croak/;
382    
383  use Search::Estraier;  use Search::Estraier;
384  our @ISA = qw/Search::Estraier/;  our @ISA = qw/Search::Estraier/;
# Line 330  sub new { Line 402  sub new {
402          $self ? return $self : return undef;          $self ? return $self : return undef;
403  }  }
404    
405    
406  =head2 set_phrase  =head2 set_phrase
407    
408    $cond->set_phrase('search phrase');    $cond->set_phrase('search phrase');
# Line 341  sub set_phrase { Line 414  sub set_phrase {
414          $self->{phrase} = $self->_s( shift );          $self->{phrase} = $self->_s( shift );
415  }  }
416    
417    
418  =head2 add_attr  =head2 add_attr
419    
420    $cond->add_attr('@URI STRINC /~dpavlin/');    $cond->add_attr('@URI STRINC /~dpavlin/');
# Line 353  sub add_attr { Line 427  sub add_attr {
427          push @{ $self->{attrs} }, $self->_s( $attr );          push @{ $self->{attrs} }, $self->_s( $attr );
428  }  }
429    
430    
431  =head2 set_order  =head2 set_order
432    
433    $cond->set_order('@mdate NUMD');    $cond->set_order('@mdate NUMD');
# Line 364  sub set_order { Line 439  sub set_order {
439          $self->{order} = shift;          $self->{order} = shift;
440  }  }
441    
442    
443  =head2 set_max  =head2 set_max
444    
445    $cond->set_max(42);    $cond->set_max(42);
# Line 373  sub set_order { Line 449  sub set_order {
449  sub set_max {  sub set_max {
450          my $self = shift;          my $self = shift;
451          my $max = shift;          my $max = shift;
452          croak "set_max needs number" unless ($max =~ m/^\d+$/);          croak "set_max needs number, not '$max'" unless ($max =~ m/^\d+$/);
453          $self->{max} = $max;          $self->{max} = $max;
454  }  }
455    
456    
457  =head2 set_options  =head2 set_options
458    
459    $cond->set_options( SURE => 1 );    $cond->set_options( 'SURE' );
460    
461      $cond->set_options( qw/AGITO NOIDF SIMPLE/ );
462    
463    Possible options are:
464    
465    =over 8
466    
467    =item SURE
468    
469    check every N-gram
470    
471    =item USUAL
472    
473    check every second N-gram
474    
475    =item FAST
476    
477    check every third N-gram
478    
479    =item AGITO
480    
481    check every fourth N-gram
482    
483    =item NOIDF
484    
485    don't perform TF-IDF tuning
486    
487    =item SIMPLE
488    
489    use simplified query phrase
490    
491    =back
492    
493    Skipping N-grams will speed up search, but reduce accuracy. Every call to C<set_options> will reset previous
494    options;
495    
496    This option changed in version C<0.04> of this module. It's backwards compatibile.
497    
498  =cut  =cut
499    
500  my $options = {  my $options = {
         # check N-gram keys skipping by three  
501          SURE => 1 << 0,          SURE => 1 << 0,
         # check N-gram keys skipping by two  
502          USUAL => 1 << 1,          USUAL => 1 << 1,
         # without TF-IDF tuning  
503          FAST => 1 << 2,          FAST => 1 << 2,
         # with the simplified phrase  
504          AGITO => 1 << 3,          AGITO => 1 << 3,
         # check every N-gram key  
505          NOIDF => 1 << 4,          NOIDF => 1 << 4,
         # check N-gram keys skipping by one  
506          SIMPLE => 1 << 10,          SIMPLE => 1 << 10,
507  };  };
508    
509  sub set_options {  sub set_options {
510          my $self = shift;          my $self = shift;
511          my $option = shift;          my $opt = 0;
512          confess "unknown option" unless ($options->{$option});          foreach my $option (@_) {
513          $self->{options} ||= $options->{$option};                  my $mask;
514                    unless ($mask = $options->{$option}) {
515                            if ($option eq '1') {
516                                    next;
517                            } else {
518                                    croak "unknown option $option";
519                            }
520                    }
521                    $opt += $mask;
522            }
523            $self->{options} = $opt;
524  }  }
525    
526    
527  =head2 phrase  =head2 phrase
528    
529  Return search phrase.  Return search phrase.
# Line 418  sub phrase { Line 537  sub phrase {
537          return $self->{phrase};          return $self->{phrase};
538  }  }
539    
540    
541  =head2 order  =head2 order
542    
543  Return search result order.  Return search result order.
# Line 431  sub order { Line 551  sub order {
551          return $self->{order};          return $self->{order};
552  }  }
553    
554    
555  =head2 attrs  =head2 attrs
556    
557  Return search result attrs.  Return search result attrs.
# Line 442  Return search result attrs. Line 563  Return search result attrs.
563  sub attrs {  sub attrs {
564          my $self = shift;          my $self = shift;
565          #croak "attrs return array, not scalar" if (! wantarray);          #croak "attrs return array, not scalar" if (! wantarray);
566          return @{ $self->{attrs} };          return @{ $self->{attrs} } if ($self->{attrs});
567  }  }
568    
569    
570  =head2 max  =head2 max
571    
572  Return maximum number of results.  Return maximum number of results.
# Line 460  sub max { Line 582  sub max {
582          return $self->{max};          return $self->{max};
583  }  }
584    
585    
586  =head2 options  =head2 options
587    
588  Return options for this condition.  Return options for this condition.
# Line 476  sub options { Line 599  sub options {
599  }  }
600    
601    
602    =head2 set_skip
603    
604    Set number of skipped documents from beginning of results
605    
606      $cond->set_skip(42);
607    
608    Similar to C<offset> in RDBMS.
609    
610    =cut
611    
612    sub set_skip {
613            my $self = shift;
614            $self->{skip} = shift;
615    }
616    
617    =head2 skip
618    
619    Return skip for this condition.
620    
621      print $cond->skip;
622    
623    =cut
624    
625    sub skip {
626            my $self = shift;
627            return $self->{skip};
628    }
629    
630    
631  package Search::Estraier::ResultDocument;  package Search::Estraier::ResultDocument;
632    
633  use Carp qw/croak/;  use Carp qw/croak/;
# Line 504  sub new { Line 656  sub new {
656          my $self = {@_};          my $self = {@_};
657          bless($self, $class);          bless($self, $class);
658    
659          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});  
         }  
660    
661          $self ? return $self : return undef;          $self ? return $self : return undef;
662  }  }
663    
664    
665  =head2 uri  =head2 uri
666    
667  Return URI of result document  Return URI of result document
# Line 539  sub attr_names { Line 690  sub attr_names {
690          return sort keys %{ $self->{attrs} };          return sort keys %{ $self->{attrs} };
691  }  }
692    
693    
694  =head2 attr  =head2 attr
695    
696  Returns value of an attribute.  Returns value of an attribute.
# Line 553  sub attr { Line 705  sub attr {
705          return $self->{attrs}->{ $name };          return $self->{attrs}->{ $name };
706  }  }
707    
708    
709  =head2 snippet  =head2 snippet
710    
711  Return snippet from result document  Return snippet from result document
# Line 566  sub snippet { Line 719  sub snippet {
719          return $self->{snippet};          return $self->{snippet};
720  }  }
721    
722    
723  =head2 keywords  =head2 keywords
724    
725  Return keywords from result document  Return keywords from result document
# Line 610  sub new { Line 764  sub new {
764          $self ? return $self : return undef;          $self ? return $self : return undef;
765  }  }
766    
767    
768  =head2 doc_num  =head2 doc_num
769    
770  Return number of documents  Return number of documents
771    
772    print $res->doc_num;    print $res->doc_num;
773    
774    This will return real number of documents (limited by C<max>).
775    If you want to get total number of hits, see C<hits>.
776    
777  =cut  =cut
778    
779  sub doc_num {  sub doc_num {
780          my $self = shift;          my $self = shift;
781          return $#{$self->{docs}};          return $#{$self->{docs}} + 1;
782  }  }
783    
784    
785  =head2 get_doc  =head2 get_doc
786    
787  Return single document  Return single document
# Line 636  Returns undef if document doesn't exist. Line 795  Returns undef if document doesn't exist.
795  sub get_doc {  sub get_doc {
796          my $self = shift;          my $self = shift;
797          my $num = shift;          my $num = shift;
798          croak "expect number as argument" unless ($num =~ m/^\d+$/);          croak "expect number as argument, not '$num'" unless ($num =~ m/^\d+$/);
799          return undef if ($num < 0 || $num > $self->{docs});          return undef if ($num < 0 || $num > $self->{docs});
800          return $self->{docs}->[$num];          return $self->{docs}->[$num];
801  }  }
802    
803    
804  =head2 hint  =head2 hint
805    
806  Return specific hint from results.  Return specific hint from results.
807    
808    print $rec->hint( 'VERSION' );    print $res->hint( 'VERSION' );
809    
810  Possible hints are: C<VERSION>, C<NODE>, C<HIT>, C<HINT#n>, C<DOCNUM>, C<WORDNUM>,  Possible hints are: C<VERSION>, C<NODE>, C<HIT>, C<HINT#n>, C<DOCNUM>, C<WORDNUM>,
811  C<TIME>, C<LINK#n>, C<VIEW>.  C<TIME>, C<LINK#n>, C<VIEW>.
# Line 658  sub hint { Line 818  sub hint {
818          return $self->{hints}->{$key};          return $self->{hints}->{$key};
819  }  }
820    
821    =head2 hints
822    
823    More perlish version of C<hint>. This one returns hash.
824    
825      my %hints = $res->hints;
826    
827    =cut
828    
829    sub hints {
830            my $self = shift;
831            return $self->{hints};
832    }
833    
834    =head2 hits
835    
836    Syntaxtic sugar for total number of hits for this query
837    
838      print $res->hits;
839    
840    It's same as
841    
842      print $res->hint('HIT');
843    
844    but shorter.
845    
846    =cut
847    
848    sub hits {
849            my $self = shift;
850            return $self->{hints}->{'HIT'} || 0;
851    }
852    
853  package Search::Estraier::Node;  package Search::Estraier::Node;
854    
855  use Carp qw/croak/;  use Carp qw/carp croak confess/;
856    use URI;
857    use MIME::Base64;
858    use IO::Socket::INET;
859    use URI::Escape qw/uri_escape/;
860    
861  =head1 Search::Estraier::Node  =head1 Search::Estraier::Node
862    
# Line 669  use Carp qw/croak/; Line 864  use Carp qw/croak/;
864    
865    my $node = new Search::HyperEstraier::Node;    my $node = new Search::HyperEstraier::Node;
866    
867    or optionally with C<url> as parametar
868    
869      my $node = new Search::HyperEstraier::Node( 'http://localhost:1978/node/test' );
870    
871    or in more verbose form
872    
873      my $node = new Search::HyperEstraier::Node(
874            url => 'http://localhost:1978/node/test',
875            user => 'admin',
876            passwd => 'admin'
877            debug => 1,
878            croak_on_error => 1
879      );
880    
881    with following arguments:
882    
883    =over 4
884    
885    =item url
886    
887    URL to node
888    
889    =item user
890    
891    specify username for node server authentication
892    
893    =item passwd
894    
895    password for authentication
896    
897    =item debug
898    
899    dumps a B<lot> of debugging output
900    
901    =item croak_on_error
902    
903    very helpful during development. It will croak on all errors instead of
904    silently returning C<-1> (which is convention of Hyper Estraier API in other
905    languages).
906    
907    =back
908    
909  =cut  =cut
910    
911  sub new {  sub new {
912          my $class = shift;          my $class = shift;
913          my $self = {          my $self = {
914                  pxport => -1,                  pxport => -1,
915                  timeout => -1,                  timeout => 0,   # this used to be -1
                 dnum => -1,  
                 wnum => -1,  
                 size => -1.0,  
916                  wwidth => 480,                  wwidth => 480,
917                  hwidth => 96,                  hwidth => 96,
918                  awidth => 96,                  awidth => 96,
919                  status => -1,                  status => -1,
920          };          };
921    
922          bless($self, $class);          bless($self, $class);
923    
924            if ($#_ == 0) {
925                    $self->{url} = shift;
926            } else {
927                    %$self = ( %$self, @_ );
928    
929                    $self->set_auth( $self->{user}, $self->{passwd} ) if ($self->{user});
930    
931                    warn "## Node debug on\n" if ($self->{debug});
932            }
933    
934            $self->{inform} = {
935                    dnum => -1,
936                    wnum => -1,
937                    size => -1.0,
938            };
939    
940          $self ? return $self : return undef;          $self ? return $self : return undef;
941  }  }
942    
943    
944  =head2 set_url  =head2 set_url
945    
946  Specify URL to node server  Specify URL to node server
# Line 702  sub set_url { Line 954  sub set_url {
954          $self->{url} = shift;          $self->{url} = shift;
955  }  }
956    
957    
958  =head2 set_proxy  =head2 set_proxy
959    
960  Specify proxy server to connect to node server  Specify proxy server to connect to node server
# Line 713  Specify proxy server to connect to node Line 966  Specify proxy server to connect to node
966  sub set_proxy {  sub set_proxy {
967          my $self = shift;          my $self = shift;
968          my ($host,$port) = @_;          my ($host,$port) = @_;
969          croak "proxy port must be number" unless ($port =~ m/^\d+$/);          croak "proxy port must be number, not '$port'" unless ($port =~ m/^\d+$/);
970          $self->{pxhost} = $host;          $self->{pxhost} = $host;
971          $self->{pxport} = $port;          $self->{pxport} = $port;
972  }  }
973    
974    
975  =head2 set_timeout  =head2 set_timeout
976    
977  Specify timeout of connection in seconds  Specify timeout of connection in seconds
# Line 729  Specify timeout of connection in seconds Line 983  Specify timeout of connection in seconds
983  sub set_timeout {  sub set_timeout {
984          my $self = shift;          my $self = shift;
985          my $sec = shift;          my $sec = shift;
986          croak "timeout must be number" unless ($sec =~ m/^\d+$/);          croak "timeout must be number, not '$sec'" unless ($sec =~ m/^\d+$/);
987          $self->{timeout} = $sec;          $self->{timeout} = $sec;
988  }  }
989    
 package Search::Estraier::Master;  
990    
991  use Carp;  =head2 set_auth
992    
993    Specify name and password for authentication to node server.
994    
995      $node->set_auth('clint','eastwood');
996    
997    =cut
998    
999    sub set_auth {
1000            my $self = shift;
1001            my ($login,$passwd) = @_;
1002            my $basic_auth = encode_base64( "$login:$passwd" );
1003            chomp($basic_auth);
1004            $self->{auth} = $basic_auth;
1005    }
1006    
1007    
1008    =head2 status
1009    
1010    Return status code of last request.
1011    
1012      print $node->status;
1013    
1014    C<-1> means connection failure.
1015    
1016    =cut
1017    
1018    sub status {
1019            my $self = shift;
1020            return $self->{status};
1021    }
1022    
1023    
1024    =head2 put_doc
1025    
1026    Add a document
1027    
1028      $node->put_doc( $document_draft ) or die "can't add document";
1029    
1030    Return true on success or false on failture.
1031    
1032    =cut
1033    
1034    sub put_doc {
1035            my $self = shift;
1036            my $doc = shift || return;
1037            return unless ($self->{url} && $doc->isa('Search::Estraier::Document'));
1038            $self->shuttle_url( $self->{url} . '/put_doc',
1039                    'text/x-estraier-draft',
1040                    $doc->dump_draft,
1041                    undef
1042            ) == 200;
1043    }
1044    
1045    
1046    =head2 out_doc
1047    
1048    Remove a document
1049    
1050      $node->out_doc( document_id ) or "can't remove document";
1051    
1052    Return true on success or false on failture.
1053    
1054    =cut
1055    
1056    sub out_doc {
1057            my $self = shift;
1058            my $id = shift || return;
1059            return unless ($self->{url});
1060            croak "id must be number, not '$id'" unless ($id =~ m/^\d+$/);
1061            $self->shuttle_url( $self->{url} . '/out_doc',
1062                    'application/x-www-form-urlencoded',
1063                    "id=$id",
1064                    undef
1065            ) == 200;
1066    }
1067    
1068    
1069    =head2 out_doc_by_uri
1070    
1071    Remove a registrated document using it's uri
1072    
1073      $node->out_doc_by_uri( 'file:///document/uri/42' ) or "can't remove document";
1074    
1075    Return true on success or false on failture.
1076    
1077    =cut
1078    
1079    sub out_doc_by_uri {
1080            my $self = shift;
1081            my $uri = shift || return;
1082            return unless ($self->{url});
1083            $self->shuttle_url( $self->{url} . '/out_doc',
1084                    'application/x-www-form-urlencoded',
1085                    "uri=" . uri_escape($uri),
1086                    undef
1087            ) == 200;
1088    }
1089    
1090    
1091    =head2 edit_doc
1092    
1093    Edit attributes of a document
1094    
1095      $node->edit_doc( $document_draft ) or die "can't edit document";
1096    
1097    Return true on success or false on failture.
1098    
1099    =cut
1100    
1101    sub edit_doc {
1102            my $self = shift;
1103            my $doc = shift || return;
1104            return unless ($self->{url} && $doc->isa('Search::Estraier::Document'));
1105            $self->shuttle_url( $self->{url} . '/edit_doc',
1106                    'text/x-estraier-draft',
1107                    $doc->dump_draft,
1108                    undef
1109            ) == 200;
1110    }
1111    
1112    
1113    =head2 get_doc
1114    
1115    Retreive document
1116    
1117      my $doc = $node->get_doc( document_id ) or die "can't get document";
1118    
1119    Return true on success or false on failture.
1120    
1121    =cut
1122    
1123    sub get_doc {
1124            my $self = shift;
1125            my $id = shift || return;
1126            return $self->_fetch_doc( id => $id );
1127    }
1128    
1129    
1130    =head2 get_doc_by_uri
1131    
1132    Retreive document
1133    
1134      my $doc = $node->get_doc_by_uri( 'file:///document/uri/42' ) or die "can't get document";
1135    
1136    Return true on success or false on failture.
1137    
1138    =cut
1139    
1140    sub get_doc_by_uri {
1141            my $self = shift;
1142            my $uri = shift || return;
1143            return $self->_fetch_doc( uri => $uri );
1144    }
1145    
1146    
1147    =head2 get_doc_attr
1148    
1149    Retrieve the value of an atribute from object
1150    
1151      my $val = $node->get_doc_attr( document_id, 'attribute_name' ) or
1152            die "can't get document attribute";
1153    
1154    =cut
1155    
1156    sub get_doc_attr {
1157            my $self = shift;
1158            my ($id,$name) = @_;
1159            return unless ($id && $name);
1160            return $self->_fetch_doc( id => $id, attr => $name );
1161    }
1162    
1163    
1164    =head2 get_doc_attr_by_uri
1165    
1166  =head1 Search::Estraier::Master  Retrieve the value of an atribute from object
1167    
1168  Controll node master. This requires user with administration priviledges.    my $val = $node->get_doc_attr_by_uri( document_id, 'attribute_name' ) or
1169            die "can't get document attribute";
1170    
1171  =cut  =cut
1172    
1173  {  sub get_doc_attr_by_uri {
1174          package RequestAgent;          my $self = shift;
1175          our @ISA = qw(LWP::UserAgent);          my ($uri,$name) = @_;
1176            return unless ($uri && $name);
1177            return $self->_fetch_doc( uri => $uri, attr => $name );
1178    }
1179    
1180    
1181    =head2 etch_doc
1182    
1183    Exctract document keywords
1184    
1185      my $keywords = $node->etch_doc( document_id ) or die "can't etch document";
1186    
1187    =cut
1188    
1189    sub etch_doc {
1190            my $self = shift;
1191            my $id = shift || return;
1192            return $self->_fetch_doc( id => $id, etch => 1 );
1193    }
1194    
1195    =head2 etch_doc_by_uri
1196    
1197    Retreive document
1198    
1199      my $keywords = $node->etch_doc_by_uri( 'file:///document/uri/42' ) or die "can't etch document";
1200    
1201    Return true on success or false on failture.
1202    
1203    =cut
1204    
1205    sub etch_doc_by_uri {
1206            my $self = shift;
1207            my $uri = shift || return;
1208            return $self->_fetch_doc( uri => $uri, etch => 1 );
1209    }
1210    
1211    
1212    =head2 uri_to_id
1213    
1214    Get ID of document specified by URI
1215    
1216      my $id = $node->uri_to_id( 'file:///document/uri/42' );
1217    
1218    This method won't croak, even if using C<croak_on_error>.
1219    
1220    =cut
1221    
1222    sub uri_to_id {
1223            my $self = shift;
1224            my $uri = shift || return;
1225            return $self->_fetch_doc( uri => $uri, path => '/uri_to_id', chomp_resbody => 1, croak_on_error => 0 );
1226    }
1227    
1228    
1229    =head2 _fetch_doc
1230    
1231          sub new {  Private function used for implementing of C<get_doc>, C<get_doc_by_uri>,
1232                  my $self = LWP::UserAgent::new(@_);  C<etch_doc>, C<etch_doc_by_uri>.
1233                  $self->agent("Search-Estraier/$Search::Estraer::VERSION");  
1234                  $self;   # this will decode received draft into Search::Estraier::Document object
1235     my $doc = $node->_fetch_doc( id => 42 );
1236     my $doc = $node->_fetch_doc( uri => 'file:///document/uri/42' );
1237    
1238     # to extract keywords, add etch
1239     my $doc = $node->_fetch_doc( id => 42, etch => 1 );
1240     my $doc = $node->_fetch_doc( uri => 'file:///document/uri/42', etch => 1 );
1241    
1242     # to get document attrubute add attr
1243     my $doc = $node->_fetch_doc( id => 42, attr => '@mdate' );
1244     my $doc = $node->_fetch_doc( uri => 'file:///document/uri/42', attr => '@mdate' );
1245    
1246     # more general form which allows implementation of
1247     # uri_to_id
1248     my $id = $node->_fetch_doc(
1249            uri => 'file:///document/uri/42',
1250            path => '/uri_to_id',
1251            chomp_resbody => 1
1252     );
1253    
1254    =cut
1255    
1256    sub _fetch_doc {
1257            my $self = shift;
1258            my $a = {@_};
1259            return unless ( ($a->{id} || $a->{uri}) && $self->{url} );
1260    
1261            my ($arg, $resbody);
1262    
1263            my $path = $a->{path} || '/get_doc';
1264            $path = '/etch_doc' if ($a->{etch});
1265    
1266            if ($a->{id}) {
1267                    croak "id must be numberm not '$a->{id}'" unless ($a->{id} =~ m/^\d+$/);
1268                    $arg = 'id=' . $a->{id};
1269            } elsif ($a->{uri}) {
1270                    $arg = 'uri=' . uri_escape($a->{uri});
1271            } else {
1272                    confess "unhandled argument. Need id or uri.";
1273            }
1274    
1275            if ($a->{attr}) {
1276                    $path = '/get_doc_attr';
1277                    $arg .= '&attr=' . uri_escape($a->{attr});
1278                    $a->{chomp_resbody} = 1;
1279          }          }
1280    
1281          sub get_basic_credentials {          my $rv = $self->shuttle_url( $self->{url} . $path,
1282                  my($self, $realm, $uri) = @_;                  'application/x-www-form-urlencoded',
1283  #               return ($user, $password);                  $arg,
1284                    \$resbody,
1285                    $a->{croak_on_error},
1286            );
1287    
1288            return if ($rv != 200);
1289    
1290            if ($a->{etch}) {
1291                    $self->{kwords} = {};
1292                    return +{} unless ($resbody);
1293                    foreach my $l (split(/\n/, $resbody)) {
1294                            my ($k,$v) = split(/\t/, $l, 2);
1295                            $self->{kwords}->{$k} = $v if ($v);
1296                    }
1297                    return $self->{kwords};
1298            } elsif ($a->{chomp_resbody}) {
1299                    return unless (defined($resbody));
1300                    chomp($resbody);
1301                    return $resbody;
1302            } else {
1303                    return new Search::Estraier::Document($resbody);
1304          }          }
1305  }  }
1306    
1307    
1308    =head2 name
1309    
1310  =head2 new    my $node_name = $node->name;
1311    
1312  Create new connection to node master.  =cut
1313    
1314    my $master = new Search::Estraier::Master(  sub name {
1315          url => 'http://localhost:1978',          my $self = shift;
1316          user => 'admin',          $self->_set_info unless ($self->{inform}->{name});
1317          passwd => 'admin',          return $self->{inform}->{name};
1318    }
1319    
1320    
1321    =head2 label
1322    
1323      my $node_label = $node->label;
1324    
1325    =cut
1326    
1327    sub label {
1328            my $self = shift;
1329            $self->_set_info unless ($self->{inform}->{label});
1330            return $self->{inform}->{label};
1331    }
1332    
1333    
1334    =head2 doc_num
1335    
1336      my $documents_in_node = $node->doc_num;
1337    
1338    =cut
1339    
1340    sub doc_num {
1341            my $self = shift;
1342            $self->_set_info if ($self->{inform}->{dnum} < 0);
1343            return $self->{inform}->{dnum};
1344    }
1345    
1346    
1347    =head2 word_num
1348    
1349      my $words_in_node = $node->word_num;
1350    
1351    =cut
1352    
1353    sub word_num {
1354            my $self = shift;
1355            $self->_set_info if ($self->{inform}->{wnum} < 0);
1356            return $self->{inform}->{wnum};
1357    }
1358    
1359    
1360    =head2 size
1361    
1362      my $node_size = $node->size;
1363    
1364    =cut
1365    
1366    sub size {
1367            my $self = shift;
1368            $self->_set_info if ($self->{inform}->{size} < 0);
1369            return $self->{inform}->{size};
1370    }
1371    
1372    
1373    =head2 search
1374    
1375    Search documents which match condition
1376    
1377      my $nres = $node->search( $cond, $depth );
1378    
1379    C<$cond> is C<Search::Estraier::Condition> object, while <$depth> specifies
1380    depth for meta search.
1381    
1382    Function results C<Search::Estraier::NodeResult> object.
1383    
1384    =cut
1385    
1386    sub search {
1387            my $self = shift;
1388            my ($cond, $depth) = @_;
1389            return unless ($cond && defined($depth) && $self->{url});
1390            croak "cond mush be Search::Estraier::Condition, not '$cond->isa'" unless ($cond->isa('Search::Estraier::Condition'));
1391            croak "depth needs number, not '$depth'" unless ($depth =~ m/^\d+$/);
1392    
1393            my $resbody;
1394    
1395            my $rv = $self->shuttle_url( $self->{url} . '/search',
1396                    'application/x-www-form-urlencoded',
1397                    $self->cond_to_query( $cond, $depth ),
1398                    \$resbody,
1399            );
1400            return if ($rv != 200);
1401    
1402            my @records     = split /--------\[.*?\]--------(?::END)?\r?\n/, $resbody;
1403            my $hintsText   = splice @records, 0, 2; # starts with empty record
1404            my $hints               = { $hintsText =~ m/^(.*?)\t(.*?)$/gsm };
1405    
1406            # process records
1407            my $docs = [];
1408            foreach my $record (@records)
1409            {
1410                    # split into keys and snippets
1411                    my ($keys, $snippet) = $record =~ m/^(.*?)\n\n(.*?)$/s;
1412    
1413                    # create document hash
1414                    my $doc                         = { $keys =~ m/^(.*?)=(.*?)$/gsm };
1415                    $doc->{'@keywords'}     = $doc->{keywords};
1416                    ($doc->{keywords})      = $keys =~ m/^%VECTOR\t(.*?)$/gm;
1417                    $doc->{snippet}         = $snippet;
1418    
1419                    push @$docs, new Search::Estraier::ResultDocument(
1420                            attrs           => $doc,
1421                            uri             => $doc->{'@uri'},
1422                            snippet         => $snippet,
1423                            keywords        => $doc->{'keywords'},
1424                    );
1425            }
1426    
1427            return new Search::Estraier::NodeResult( docs => $docs, hints => $hints );
1428    }
1429    
1430    
1431    =head2 cond_to_query
1432    
1433    Return URI encoded string generated from Search::Estraier::Condition
1434    
1435      my $args = $node->cond_to_query( $cond, $depth );
1436    
1437    =cut
1438    
1439    sub cond_to_query {
1440            my $self = shift;
1441    
1442            my $cond = shift || return;
1443            croak "condition must be Search::Estraier::Condition, not '$cond->isa'" unless ($cond->isa('Search::Estraier::Condition'));
1444            my $depth = shift;
1445    
1446            my @args;
1447    
1448            if (my $phrase = $cond->phrase) {
1449                    push @args, 'phrase=' . uri_escape($phrase);
1450            }
1451    
1452            if (my @attrs = $cond->attrs) {
1453                    for my $i ( 0 .. $#attrs ) {
1454                            push @args,'attr' . ($i+1) . '=' . uri_escape( $attrs[$i] ) if ($attrs[$i]);
1455                    }
1456            }
1457    
1458            if (my $order = $cond->order) {
1459                    push @args, 'order=' . uri_escape($order);
1460            }
1461                    
1462            if (my $max = $cond->max) {
1463                    push @args, 'max=' . $max;
1464            } else {
1465                    push @args, 'max=' . (1 << 30);
1466            }
1467    
1468            if (my $options = $cond->options) {
1469                    push @args, 'options=' . $options;
1470            }
1471    
1472            push @args, 'depth=' . $depth if ($depth);
1473            push @args, 'wwidth=' . $self->{wwidth};
1474            push @args, 'hwidth=' . $self->{hwidth};
1475            push @args, 'awidth=' . $self->{awidth};
1476            push @args, 'skip=' . $self->{skip} if ($self->{skip});
1477    
1478            return join('&', @args);
1479    }
1480    
1481    
1482    =head2 shuttle_url
1483    
1484    This is method which uses C<LWP::UserAgent> to communicate with Hyper Estraier node
1485    master.
1486    
1487      my $rv = shuttle_url( $url, $content_type, $req_body, \$resbody );
1488    
1489    C<$resheads> and C<$resbody> booleans controll if response headers and/or response
1490    body will be saved within object.
1491    
1492    =cut
1493    
1494    use LWP::UserAgent;
1495    
1496    sub shuttle_url {
1497            my $self = shift;
1498    
1499            my ($url, $content_type, $reqbody, $resbody, $croak_on_error) = @_;
1500    
1501            $croak_on_error = $self->{croak_on_error} unless defined($croak_on_error);
1502    
1503            $self->{status} = -1;
1504    
1505            warn "## $url\n" if ($self->{debug});
1506    
1507            $url = new URI($url);
1508            if (
1509                            !$url || !$url->scheme || !$url->scheme eq 'http' ||
1510                            !$url->host || !$url->port || $url->port < 1
1511                    ) {
1512                    carp "can't parse $url\n";
1513                    return -1;
1514            }
1515    
1516            my $ua = LWP::UserAgent->new;
1517            $ua->agent( "Search-Estraier/$Search::Estraier::VERSION" );
1518    
1519            my $req;
1520            if ($reqbody) {
1521                    $req = HTTP::Request->new(POST => $url);
1522            } else {
1523                    $req = HTTP::Request->new(GET => $url);
1524            }
1525    
1526            $req->headers->header( 'Host' => $url->host . ":" . $url->port );
1527            $req->headers->header( 'Connection', 'close' );
1528            $req->headers->header( 'Authorization', 'Basic ' . $self->{auth} ) if ($self->{auth});
1529            $req->content_type( $content_type );
1530    
1531            warn $req->headers->as_string,"\n" if ($self->{debug});
1532    
1533            if ($reqbody) {
1534                    warn "$reqbody\n" if ($self->{debug});
1535                    $req->content( $reqbody );
1536            }
1537    
1538            my $res = $ua->request($req) || croak "can't make request to $url: $!";
1539    
1540            warn "## response status: ",$res->status_line,"\n" if ($self->{debug});
1541    
1542            ($self->{status}, $self->{status_message}) = split(/\s+/, $res->status_line, 2);
1543    
1544            if (! $res->is_success) {
1545                    if ($croak_on_error) {
1546                            croak("can't get $url: ",$res->status_line);
1547                    } else {
1548                            return -1;
1549                    }
1550            }
1551    
1552            $$resbody .= $res->content;
1553    
1554            warn "## response body:\n$$resbody\n" if ($resbody && $self->{debug});
1555    
1556            return $self->{status};
1557    }
1558    
1559    
1560    =head2 set_snippet_width
1561    
1562    Set width of snippets in results
1563    
1564      $node->set_snippet_width( $wwidth, $hwidth, $awidth );
1565    
1566    C<$wwidth> specifies whole width of snippet. It's C<480> by default. If it's C<0> snippet
1567    is not sent with results. If it is negative, whole document text is sent instead of snippet.
1568    
1569    C<$hwidth> specified width of strings from beginning of string. Default
1570    value is C<96>. Negative or zero value keep previous value.
1571    
1572    C<$awidth> specifies width of strings around each highlighted word. It's C<96> by default.
1573    If negative of zero value is provided previous value is kept unchanged.
1574    
1575    =cut
1576    
1577    sub set_snippet_width {
1578            my $self = shift;
1579    
1580            my ($wwidth, $hwidth, $awidth) = @_;
1581            $self->{wwidth} = $wwidth;
1582            $self->{hwidth} = $hwidth if ($hwidth >= 0);
1583            $self->{awidth} = $awidth if ($awidth >= 0);
1584    }
1585    
1586    
1587    =head2 set_user
1588    
1589    Manage users of node
1590    
1591      $node->set_user( 'name', $mode );
1592    
1593    C<$mode> can be one of:
1594    
1595    =over 4
1596    
1597    =item 0
1598    
1599    delete account
1600    
1601    =item 1
1602    
1603    set administrative right for user
1604    
1605    =item 2
1606    
1607    set user account as guest
1608    
1609    =back
1610    
1611    Return true on success, otherwise false.
1612    
1613    =cut
1614    
1615    sub set_user {
1616            my $self = shift;
1617            my ($name, $mode) = @_;
1618    
1619            return unless ($self->{url});
1620            croak "mode must be number, not '$mode'" unless ($mode =~ m/^\d+$/);
1621    
1622            $self->shuttle_url( $self->{url} . '/_set_user',
1623                    'text/plain',
1624                    'name=' . uri_escape($name) . '&mode=' . $mode,
1625                    undef
1626            ) == 200;
1627    }
1628    
1629    
1630    =head2 set_link
1631    
1632    Manage node links
1633    
1634      $node->set_link('http://localhost:1978/node/another', 'another node label', $credit);
1635    
1636    If C<$credit> is negative, link is removed.
1637    
1638    =cut
1639    
1640    sub set_link {
1641            my $self = shift;
1642            my ($url, $label, $credit) = @_;
1643    
1644            return unless ($self->{url});
1645            croak "mode credit be number, not '$credit'" unless ($credit =~ m/^\d+$/);
1646    
1647            my $reqbody = 'url=' . uri_escape($url) . '&label=' . uri_escape($label);
1648            $reqbody .= '&credit=' . $credit if ($credit > 0);
1649    
1650            if ($self->shuttle_url( $self->{url} . '/_set_link',
1651                    'application/x-www-form-urlencoded',
1652                    $reqbody,
1653                    undef
1654            ) == 200) {
1655                    # refresh node info after adding link
1656                    $self->_set_info;
1657                    return 1;
1658            }
1659    }
1660    
1661    =head2 admins
1662    
1663     my @admins = @{ $node->admins };
1664    
1665    Return array of users with admin rights on node
1666    
1667    =cut
1668    
1669    sub admins {
1670            my $self = shift;
1671            $self->_set_info unless ($self->{inform}->{name});
1672            return $self->{inform}->{admins};
1673    }
1674    
1675    =head2 guests
1676    
1677     my @guests = @{ $node->guests };
1678    
1679    Return array of users with guest rights on node
1680    
1681    =cut
1682    
1683    sub guests {
1684            my $self = shift;
1685            $self->_set_info unless ($self->{inform}->{name});
1686            return $self->{inform}->{guests};
1687    }
1688    
1689    =head2 links
1690    
1691     my $links = @{ $node->links };
1692    
1693    Return array of links for this node
1694    
1695    =cut
1696    
1697    sub links {
1698            my $self = shift;
1699            $self->_set_info unless ($self->{inform}->{name});
1700            return $self->{inform}->{links};
1701    }
1702    
1703    =head2 master
1704    
1705    Set actions on Hyper Estraier node master (C<estmaster> process)
1706    
1707      $node->master(
1708            action => 'sync'
1709    );    );
1710    
1711    All available actions are documented in
1712    L<http://hyperestraier.sourceforge.net/nguide-en.html#protocol>
1713    
1714  =cut  =cut
1715    
1716  sub new {  my $estmaster_rest = {
1717          my $class = shift;          shutdown => {
1718          my $self = {@_};                  status => 202,
1719          bless($self, $class);          },
1720            sync => {
1721                    status => 202,
1722            },
1723            backup => {
1724                    status => 202,
1725            },
1726            userlist => {
1727                    status => 200,
1728                    returns => [ qw/name passwd flags fname misc/ ],
1729            },
1730            useradd => {
1731                    required => [ qw/name passwd flags/ ],
1732                    optional => [ qw/fname misc/ ],
1733                    status => 200,
1734            },
1735            userdel => {
1736                    required => [ qw/name/ ],
1737                    status => 200,
1738            },
1739            nodelist => {
1740                    status => 200,
1741                    returns => [ qw/name label doc_num word_num size/ ],
1742            },
1743            nodeadd => {
1744                    required => [ qw/name/ ],
1745                    optional => [ qw/label/ ],
1746                    status => 200,
1747            },
1748            nodedel => {
1749                    required => [ qw/name/ ],
1750                    status => 200,
1751            },
1752            nodeclr => {
1753                    required => [ qw/name/ ],
1754                    status => 200,
1755            },
1756            nodertt => {
1757                    status => 200,  
1758            },
1759    };
1760    
1761    sub master {
1762            my $self = shift;
1763    
1764            my $args = {@_};
1765    
1766            # have action?
1767            my $action = $args->{action} || croak "need action, available: ",
1768                    join(", ",keys %{ $estmaster_rest });
1769    
1770            # check if action is valid
1771            my $rest = $estmaster_rest->{$action};
1772            croak "action '$action' is not supported, available actions: ",
1773                    join(", ",keys %{ $estmaster_rest }) unless ($rest);
1774    
1775            croak "BUG: action '$action' needs return status" unless ($rest->{status});
1776    
1777            my @args;
1778    
1779            if ($rest->{required} || $rest->{optional}) {
1780    
1781                    map {
1782                            croak "need parametar '$_' for action '$action'" unless ($args->{$_});
1783                            push @args, $_ . '=' . uri_escape( $args->{$_} );
1784                    } ( @{ $rest->{required} } );
1785    
1786                    map {
1787                            push @args, $_ . '=' . uri_escape( $args->{$_} ) if ($args->{$_});
1788                    } ( @{ $rest->{optional} } );
1789    
         foreach my $p (qw/url user passwd/) {  
                 croak "need $p" unless ($self->{$p});  
1790          }          }
1791    
1792          $self ? return $self : return undef;          my $uri = new URI( $self->{url} );
1793    
1794            my $resbody;
1795    
1796            my $status = $self->shuttle_url(
1797                    'http://' . $uri->host_port . '/master?action=' . $action ,
1798                    'application/x-www-form-urlencoded',
1799                    join('&', @args),
1800                    \$resbody,
1801                    1,
1802            ) or confess "shuttle_url failed";
1803    
1804            if ($status == $rest->{status}) {
1805                    if ($rest->{returns} && wantarray) {
1806    
1807                            my @results;
1808                            my $fields = $#{$rest->{returns}};
1809    
1810                            foreach my $line ( split(/[\r\n]/,$resbody) ) {
1811                                    my @e = split(/\t/, $line, $fields + 1);
1812                                    my $row;
1813                                    foreach my $i ( 0 .. $fields) {
1814                                            $row->{ $rest->{returns}->[$i] } = $e[ $i ];
1815                                    }
1816                                    push @results, $row;
1817                            }
1818    
1819                            return @results;
1820    
1821                    } elsif ($resbody) {
1822                            chomp $resbody;
1823                            return $resbody;
1824                    } else {
1825                            return 0E0;
1826                    }
1827            }
1828    
1829            carp "expected status $rest->{status}, but got $status";
1830            return undef;
1831  }  }
1832    
1833    =head1 PRIVATE METHODS
1834    
1835    You could call those directly, but you don't have to. I hope.
1836    
1837    =head2 _set_info
1838    
1839    Set information for node
1840    
1841      $node->_set_info;
1842    
1843    =cut
1844    
1845    sub _set_info {
1846            my $self = shift;
1847    
1848            $self->{status} = -1;
1849            return unless ($self->{url});
1850    
1851            my $resbody;
1852            my $rv = $self->shuttle_url( $self->{url} . '/inform',
1853                    'text/plain',
1854                    undef,
1855                    \$resbody,
1856            );
1857    
1858            return if ($rv != 200 || !$resbody);
1859    
1860            my @lines = split(/[\r\n]/,$resbody);
1861    
1862            $self->{inform} = {};
1863    
1864            ( $self->{inform}->{name}, $self->{inform}->{label}, $self->{inform}->{dnum},
1865                    $self->{inform}->{wnum}, $self->{inform}->{size} ) = split(/\t/, shift @lines, 5);
1866    
1867            return $resbody unless (@lines);
1868    
1869            shift @lines;
1870    
1871            while(my $admin = shift @lines) {
1872                    push @{$self->{inform}->{admins}}, $admin;
1873            }
1874    
1875            while(my $guest = shift @lines) {
1876                    push @{$self->{inform}->{guests}}, $guest;
1877            }
1878    
1879            while(my $link = shift @lines) {
1880                    push @{$self->{inform}->{links}}, $link;
1881            }
1882    
1883            return $resbody;
1884    
1885    }
1886    
1887  ###  ###
1888    
# Line 803  Hyper Estraier Ruby interface on which t Line 1900  Hyper Estraier Ruby interface on which t
1900    
1901  Dobrica Pavlinusic, E<lt>dpavlin@rot13.orgE<gt>  Dobrica Pavlinusic, E<lt>dpavlin@rot13.orgE<gt>
1902    
1903    Robert Klep E<lt>robert@klep.nameE<gt> contributed refactored search code
1904    
1905  =head1 COPYRIGHT AND LICENSE  =head1 COPYRIGHT AND LICENSE
1906    

Legend:
Removed from v.30  
changed lines
  Added in v.136

  ViewVC Help
Powered by ViewVC 1.1.26