File Coverage

blib/lib/SeeAlso/Source.pm
Criterion Covered Total %
statement 78 135 57.7
branch 44 88 50.0
condition 7 20 35.0
subroutine 14 17 82.3
pod 10 10 100.0
total 153 270 56.6


line stmt bran cond sub pod time code
1 6     6   41103 use strict;
  6         14  
  6         289  
2 6     6   32 use warnings;
  6         15  
  6         383  
3             package SeeAlso::Source;
4             {
5             $SeeAlso::Source::VERSION = '0.71';
6             }
7             #ABSTRACT: Provides OpenSearch Suggestions reponses
8              
9 6     6   34 use Carp qw(croak);
  6         12  
  6         372  
10 6     6   1385 use SeeAlso::Response;
  6         16  
  6         186  
11 6     6   3266 use SeeAlso::Server;
  6         17  
  6         302  
12              
13 6     6   45 use base 'Exporter';
  6         11  
  6         14833  
14             our @EXPORT_OK = qw(expand_from_config serve);
15              
16              
17             sub new {
18 18     18 1 2729 my $class = shift;
19 18         26 my ($callback, $cache);
20              
21 18 100 66     106 $callback = shift
22             if ref($_[0]) eq 'CODE' or UNIVERSAL::isa($_[0],'SeeAlso::Source');
23 18 50       91 $cache = shift if UNIVERSAL::isa($_[0], 'Cache');
24 18 100       53 shift if not defined $_[0];
25              
26 18         37 my (%params) = @_;
27 18         51 expand_from_config( \%params, 'Source' );
28              
29 18         50 my $self = bless { }, $class;
30              
31 18 100       44 $callback = $params{callback} unless defined $callback;
32 18 50       41 $cache = $params{cache} unless defined $cache;
33              
34 18 100       61 $self->callback( $callback ) if $callback;
35 18 50       42 $self->cache( $cache ) if $cache;
36 18 100       52 $self->description( %params ) if %params;
37              
38 18         70 return $self;
39             }
40              
41              
42             sub callback {
43 38     38 1 57 my $self = shift;
44              
45 38 100       86 if ( scalar @_ ) {
46 14         23 my $callback = $_[0];
47              
48 14 50 66     101 croak('callback parameter must be a code reference or SeeAlso::Source')
      66        
49             if defined $callback and ref( $callback ) ne 'CODE'
50             and not UNIVERSAL::isa( $callback, 'SeeAlso::Source' );
51              
52 14         50 $self->{callback} = $callback;
53             }
54              
55 38 50       96 return unless defined $self->{callback};
56 38 100       174 return $self->{callback} if ref($self->{callback}) eq 'CODE';
57 2     1   12 return sub { $self->{callback}->query( $_[0] ) };
  1         7  
58             }
59              
60              
61             sub cache {
62 0     0 1 0 my $self = shift;
63              
64 0 0       0 if ( scalar @_ ) {
65 0 0 0     0 croak 'Cache must be a Cache object'
      0        
66             unless not defined $_[0]
67             or UNIVERSAL::isa( $_[0], 'Cache' )
68             or UNIVERSAL::isa( $_[0], 'SeeAlso::Source' );
69 0         0 $self->{cache} = $_[0];
70             }
71              
72 0         0 return $self->{cache};
73             }
74              
75              
76             sub query {
77 24     24 1 58 my ($self, $identifier, %params) = @_;
78              
79 24 100       108 $identifier = SeeAlso::Identifier->new( $identifier )
80             unless UNIVERSAL::isa( $identifier, 'SeeAlso::Identifier' );
81              
82 24         75 my $key = $identifier->hash;
83              
84 24 50 33     89 if ( $self->{cache} and not $params{force} ) {
85 0 0       0 if ( UNIVERSAL::isa( $self->{cache}, 'Cache' ) ) {
86 0         0 my $response = $self->{cache}->thaw( $key );
87 0 0       0 return $response if defined $response;
88             } else {
89 0         0 my $response = $self->{cache}->query( $identifier );
90 0 0       0 return $response if $response->size;
91             }
92             }
93              
94 24         58 my $response = $self->query_callback( $identifier );
95              
96 22 50       129 $response = SeeAlso::Response->new( $identifier )
97             unless UNIVERSAL::isa( $response, 'SeeAlso::Response' );
98              
99 22 50       61 if ( $self->{cache} ) {
100 0 0       0 if ( UNIVERSAL::isa( $self->{cache}, 'Cache' ) ) {
101 0         0 $self->{cache}->freeze( $key, $response );
102             } else {
103 0         0 $self->{cache}->update( $response );
104             }
105             }
106              
107 22         129 return $response;
108             }
109              
110              
111             sub query_callback {
112 24     24 1 30 my ($self, $identifier) = @_;
113 24 50       78 return $self->{callback} ?
114             $self->callback->( $identifier ) :
115             SeeAlso::Response->new( $identifier );
116             }
117              
118              
119             sub description {
120 40     40 1 84 my $self = shift;
121 40         62 my $key = $_[0];
122              
123 40 100       118 if (scalar @_ > 1) {
    100          
124 14         36 my %param = @_;
125 14         29 foreach my $key (keys %param) {
126 19 50       42 my $value = defined $param{$key} ? $param{$key} : '';
127 19 50       39 if ($key =~ /^Examples?$/) {
128 0 0       0 $value = [ $value ] unless ref($value) eq "ARRAY";
129             # TODO: check examples (must be an array of a hash)
130 0         0 $key = "Examples";
131             } else {
132 19         42 $value =~ s/\s+/ /g; # to string
133             }
134 19 100       47 if ($self->{description}) {
135 9         41 $self->{description}{$key} = $value;
136             } else {
137 10         31 my %description = ($key => $value);
138 10         43 $self->{description} = \%description;
139             }
140             }
141             } elsif ( $self->{description} ) {
142 20 100       101 return $self->{description}{$key} if defined $key;
143 1         5 return $self->{description};
144             } else { # this is needed if no description was defined
145 6 100       21 return if defined $key;
146 5         5 my %hash;
147 5         21 return \%hash;
148             }
149             }
150              
151              
152             sub about {
153 2     2 1 7 my $self = shift;
154              
155 2         5 my $name = $self->description("ShortName");
156 2         11 my $description = $self->description("Description");
157 2         6 my $url = $self->description("BaseURL");
158              
159 2 100       6 $name = "" unless defined $name;
160 2 100       5 $description = "" unless defined $description;
161 2 100       5 $url = "" unless defined $url;
162              
163 2         6 return ($name, $description, $url);
164             }
165              
166              
167             sub serve {
168 0     0 1 0 my ($source, $query, $config);
169 0 0       0 if ( UNIVERSAL::isa( $_[0], 'SeeAlso::Source' ) ) {
170 0         0 ($source, $config) = @_;
171             } else {
172 0 0       0 $query = shift if ref($_[0]) eq 'CODE';
173 0         0 $config = shift;
174 0         0 $source = SeeAlso::Source->new( $query, config => $config );
175             }
176              
177 0         0 my $server = SeeAlso::Server->new( config => $config );
178              
179 0         0 binmode \*STDOUT, ":encoding(UTF-8)";
180 0         0 print $server->query( $source );
181 0         0 exit;
182             }
183              
184              
185             sub load_config {
186 0     0 1 0 my $file = shift;
187 0         0 open(my $fh, "<", $file);
188 0         0 my $config = eval { JSON->new->relaxed->utf8->decode(join('',<$fh>)); };
  0         0  
189 0         0 close $fh;
190 0   0     0 return $config || { };
191             }
192              
193              
194             sub expand_from_config {
195 29     29 1 49 my ($config, $section) = @_;
196 29 50       109 return unless defined $config->{config};
197              
198 0           my $cfg = $config->{config};
199 0 0         if ( ref($cfg) eq 'HASH' ) {
200 0           $cfg = $cfg->{$section};
201             } else {
202 0           $cfg = { };
203 0           my $file = $config->{config};
204 0 0         if ( $file =~ /\.ini$/ ) {
    0          
    0          
205 0           eval {
206 0           require Config::IniFiles;
207 0           my $ini = Config::IniFiles->new( -file => $config->{config}, -allowcontinue => 1 );
208 0           foreach my $hash ( $ini->Parameters($section) ) {
209 0           $cfg->{$hash} = $ini->val($section,$hash);
210             }
211             };
212             } elsif ( $file =~ /\.y[a]?ml$/ ) {
213 0           eval {
214 0           require YAML::Any;
215 0           my $config = YAML::Any::LoadFile( $file );
216 0           $cfg = $config->{$section};
217             };
218             } elsif ( $file =~ /\.json$/ ) {
219 0           eval {
220 0           open(my $fh, "<", $file);
221 0           my $config = JSON->new->relaxed->utf8->decode(join('',<$fh>));
222 0           close $fh;
223 0           $cfg = $config->{$section};
224             };
225             } else {
226 0           croak "Unknown configuration file type $file";
227             }
228 0 0         croak "Failed to read configuration file $file: $@" if $@;
229             }
230 0 0         return unless ref($cfg) eq 'HASH';
231 0           foreach my $hash ( keys %{ $cfg } ) {
  0            
232 0 0         $config->{$hash} = $cfg->{$hash} unless defined $config->{$hash};
233             }
234             }
235              
236             1;
237              
238             __END__