File Coverage

blib/lib/Locale/Memories.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Locale::Memories;
2              
3 1     1   42302 use strict;
  1         2  
  1         40  
4 1     1   1226 use utf8;
  1         14  
  1         7  
5 1     1   1823 use Data::Dumper;
  1         11066  
  1         83  
6 1     1   923 use String::Similarity;
  1         783  
  1         74  
7 1     1   1712 use Search::Xapian qw(:ops :db :enq_order);
  0            
  0            
8              
9             our $VERSION = '0.04';
10              
11             my $locale_prefix = '__LOCALE__';
12              
13             sub new {
14             my $class = shift;
15             my $arg_ref = shift;
16             bless {
17             index => undef,
18             locales => {},
19             index_path => $arg_ref->{index_path},
20             }, $class;
21             }
22              
23             sub load_index {
24             my ($self, $index_path) = @_;
25             return if $self->{index};
26             my $index = Search::Xapian::Database->new($index_path);
27             die "Index is not loaded" if !$index;
28             $self->{index} = $index;
29             }
30              
31             sub _build_index {
32             my ($self, $locale) = @_;
33             return if $self->{index};
34             my $database_class = 'Search::Xapian::WritableDatabase';
35             $self->{index}
36             = ($self->{index_path} ?
37             $database_class->new($self->{index_path}, DB_CREATE_OR_OVERWRITE)
38             : $database_class->new());
39             }
40              
41             sub _dequote {
42             my $str = shift;
43             $str =~ s{\A"(.*?)"\z}{$1}so;
44             $str =~ s{\\[trn]}{\n}gso;
45             return $str;
46             }
47              
48             sub _tokenize {
49             my ($self, $str) = @_;
50             my @terms = split /(?:\s|\n|\r)+/, $str;
51             for (@terms) {
52             next if /%\w/;
53             next if /\[_\d+]/;
54             s{\A\W+}{};
55             s{\W+\z}{};
56             }
57             @terms = map { lc } grep { $_ } @terms;
58             return @terms;
59             }
60              
61             sub _token_count_diff {
62             my ($self, $x, $y) = @_;
63             return abs($self->_tokenize($x) - $self->_tokenize($y));
64             }
65              
66             sub index_msg {
67             my ($self, $locale, $msg_id, $msg_str) = @_;
68             return if $msg_id eq '""';
69             return if $msg_str eq '""';
70              
71             $msg_id = _dequote($msg_id);
72              
73             if (!$self->{index}) {
74             $self->_build_index($locale);
75             }
76              
77             my $pos = 1;
78             my $doc = Search::Xapian::Document->new();
79             $doc->add_posting($locale_prefix.$locale, $pos++, 50);
80             for my $term ($self->_tokenize($msg_id)) {
81             $doc->add_posting($term, $pos++, 1);
82             }
83             local $Data::Dumper::Terse = 1;
84             local $Data::Dumper::Indent = 0;
85             $doc->set_data(Dumper [ $msg_id, $msg_str ]);
86             $self->{index}->add_document($doc);
87             }
88              
89             sub translate_msg {
90             my ($self, $locale, $msg_id) = @_;
91             return if !$self->{index};
92              
93             $msg_id = _dequote($msg_id);
94             return if !$msg_id;
95              
96             my @tokens = $self->_tokenize($msg_id);
97             return if !@tokens;
98              
99             my @translated_msgs;
100             for my $op (OP_PHRASE, OP_AND) {
101             my $query = Search::Xapian::Query->new($op, @tokens);
102             my $locale_query = Search::Xapian::Query->new(OP_OR,
103             $locale_prefix.$locale);
104             my $localized_query
105             = Search::Xapian::Query->new(OP_AND, $locale_query, $query);
106             my $enq = $self->{index}->enquire($localized_query);
107             my $matches = $enq->get_mset(0, 100);
108             next if !$matches->size();
109              
110             my $match = $matches->begin();
111             for (1 .. $matches->size()) {
112             my $doc = $match->get_document();
113             my $msg_ref = eval $doc->get_data();
114             if ($@) {
115             warn $@ if $@;
116             }
117             else {
118             push @translated_msgs, $msg_ref;
119             }
120             $match++;
121             }
122             last if @translated_msgs;
123             }
124             @translated_msgs
125             = (map { $_->[2] }
126             sort { $b->[0] <=> $a->[0] }
127             sort { $a->[1] <=> $b->[1] }
128             map { [ similarity(lc $msg_id, lc _dequote($_->[0])),
129             $self->_token_count_diff($msg_id, _dequote($_->[0])),
130             $_ ] }
131             @translated_msgs);
132             return wantarray ? @translated_msgs : $translated_msgs[0];
133             }
134              
135             1;
136             __END__