File Coverage

blib/lib/SeeAlso/Response.pm
Criterion Covered Total %
statement 153 191 80.1
branch 50 74 67.5
condition 23 45 51.1
subroutine 27 31 87.1
pod 18 19 94.7
total 271 360 75.2


line stmt bran cond sub pod time code
1 7     7   24873 use strict;
  7         17  
  7         412  
2 7     7   39 use warnings;
  7         12  
  7         388  
3             package SeeAlso::Response;
4             {
5             $SeeAlso::Response::VERSION = '0.71';
6             }
7             #ABSTRACT: SeeAlso Simple Response
8              
9 7     7   5433 use JSON::XS qw(encode_json);
  7         45704  
  7         610  
10 7     7   6048 use Data::Validate::URI qw(is_uri);
  7         405305  
  7         513  
11 7     7   8437 use Text::CSV;
  7         97197  
  7         58  
12 7     7   3013 use SeeAlso::Identifier;
  7         18  
  7         183  
13 7     7   46 use Carp;
  7         30  
  7         1105  
14              
15             use overload (
16 1     1   5 '""' => sub { $_[0]->as_string },
17 2 50   2   23 'bool' => sub { $_[0]->size or $_[0]->query }
18 7     7   42 );
  7         33  
  7         71  
19              
20              
21             sub new {
22 42     42 1 1162 my $this = shift;
23              
24 42   66     192 my $class = ref($this) || $this;
25 42         223 my $self = bless {
26             'labels' => [],
27             'descriptions' => [],
28             'uris' => []
29             }, $class;
30              
31 42         140 $self->set( @_ );
32              
33 41         123 return $self;
34             }
35              
36              
37             sub set {
38 45     45 1 91 my ($self, $query, $labels, $descriptions, $uris) = @_;
39              
40 45         115 $self->query( $query );
41              
42 45 100       117 if (defined $labels) {
43 7 50 33     98 croak ("four parameters expected in SeeAlso::Response->new")
      33        
      33        
      33        
44             unless ref($labels) eq "ARRAY"
45             and defined $descriptions and ref($descriptions) eq "ARRAY"
46             and defined $uris and ref($uris) eq "ARRAY";
47 7         9 my $l = @{$labels};
  7         15  
48 7         158 croak ("length of arguments to SeeAlso::Response->new differ")
49 7 100 66     11 unless @{$descriptions} == $l and @{$uris} == $l;
  6         24  
50              
51 6         16 $self->{labels} = [];
52 6         14 $self->{descriptions} = [];
53 6         11 $self->{uris} = [];
54              
55 6         12 for (my $i=0; $i < @{$labels}; $i++) {
  12         68  
56 6         21 $self->add($$labels[$i], $$descriptions[$i], $$uris[$i]);
57             }
58             }
59              
60 44         71 return $self;
61             }
62              
63              
64             sub add {
65 28     28 1 1945 my ($self, $label, $description, $uri) = @_;
66              
67 28 50       79 $label = defined $label ? "$label" : "";
68 28 100       69 $description = defined $description ? "$description" : "";
69 28 100       64 $uri = defined $uri ? "$uri" : "";
70 28 100       80 if ( $uri ne "" ) {
71 10 100       217 croak("irregular response URI")
72             unless $uri =~ /^[a-z][a-z0-9.+\-]*:/i;
73             }
74              
75 27 100 100     107 return $self unless $label ne "" or $description ne "" or $uri ne "";
      66        
76              
77 26         34 push @{ $self->{labels} }, $label;
  26         70  
78 26         36 push @{ $self->{descriptions} }, $description;
  26         64  
79 26         33 push @{ $self->{uris} }, $uri;
  26         56  
80              
81 26         63 return $self;
82             }
83              
84              
85             sub size {
86 62     62 1 213 my $self = shift;
87 62         64 return scalar @{$self->{labels}};
  62         281  
88             }
89              
90              
91             sub get {
92 19     19 1 2081 my ($self, $index) = @_;
93 19 100 66     123 return unless defined $index and $index >= 0 and $index < $self->size();
      100        
94              
95 15         33 my $label = $self->{labels}->[$index];
96 15         30 my $description = $self->{descriptions}->[$index];
97 15         37 my $uri = $self->{uris}->[$index];
98              
99 15         58 return ($label, $description, $uri);
100             }
101              
102              
103             sub query {
104 72     72 1 139 my $self = shift;
105 72 100       174 if ( scalar @_ ) {
106 46         69 my $query = shift;
107 46 100       329 $query = SeeAlso::Identifier->new( $query )
108             unless UNIVERSAL::isa( $query, 'SeeAlso::Identifier' );
109 46         258 $self->{query} = $query;
110             }
111 72         218 return $self->{query};
112             }
113              
114              
115             *identifier = *query;
116              
117              
118             sub labels {
119 2     2 1 798 my $self = shift;
120 2         3 return @{ $self->{labels} };
  2         12  
121             }
122              
123              
124             sub descriptions {
125 2     2 1 1400 my $self = shift;
126 2         3 return ( @{ $self->{descriptions} } );
  2         11  
127             }
128              
129              
130             sub uris {
131 2     2 0 1399 my $self = shift;
132 2         4 return @{ $self->{uris} };
  2         11  
133             }
134              
135              
136             sub toJSON {
137 28     28 1 92 my ($self, $callback, $json) = @_;
138              
139 28         124 my $response = [
140             $self->{query}->as_string,
141             $self->{labels},
142             $self->{descriptions},
143             $self->{uris}
144             ];
145              
146 28         86 return _JSON( $response, $callback, $json );
147             }
148              
149              
150             sub as_string {
151 2     2 1 8 return $_[0]->toJSON;
152             }
153              
154              
155             sub fromJSON {
156 3     3 1 1047 my ($self, $jsonstring) = @_;
157 3         42 my $json = JSON::XS->new->decode($jsonstring);
158              
159 2         11 croak("SeeAlso response format must be array of size 4")
160 2 50 33     15 unless ref($json) eq "ARRAY" and @{$json} == 4;
161              
162 2 100       7 if (ref($self)) { # call as method
163 1         2 $self->set( @{$json} );
  1         4  
164 1         5 return $self;
165             } else { # call as constructor
166 1         3 return SeeAlso::Response->new( @{$json} );
  1         6  
167             }
168             }
169              
170              
171             sub toCSV {
172 2     2 1 6 my ($self, $headers) = @_;
173 2         26 my $csv = Text::CSV->new( { binary => 1, always_quote => 1 } );
174 2         215 my @lines;
175 2         7 for(my $i=0; $i<$self->size(); $i++) {
176 2         5 my $status = $csv->combine ( $self->get($i) ); # TODO: handle error status
177 2         279 push @lines, $csv->string();
178             }
179 2         32 return join ("\n", @lines);
180             }
181              
182              
183             sub toBEACON {
184 0     0 1 0 my ($self, $beacon) = @_;
185 0         0 my @lines;
186 0         0 my $query = $self->query;
187 0         0 $query =~ s/[|\n]//g;
188              
189             #$this->meta('TARGET')
190              
191 0         0 for(my $i=0; $i<$self->size(); $i++) {
192             ## no critic
193 0         0 my ($label, $description, $url) = map { s/[|\n]//g; $_; } $self->get($i);
  0         0  
  0         0  
194             ## use critic
195 0         0 my @line = ($query);
196              
197             # TODO: remove url, if #TARGET is given
198              
199 0 0       0 if ( is_uri( $url ) ) { # may skip label/description
200 0 0 0     0 push @line, $label unless $label eq "" and $description eq "";
201 0 0       0 push @line, $description unless $description eq "";
202 0         0 push @line, $url;
203             } else { # no uri
204             #if ($url eq "") {
205             # TODO: add only if no empty
206 0         0 push @line, $label;
207             #} else {
208             # TODO
209             # push @line, $label, $description, $url;
210 0 0 0     0 push @line, $description unless $description eq "" and $url eq "";
211 0 0       0 push @line, $url unless $url eq "";
212             }
213             #if ($label != "")
214 0         0 push @lines, join('|',@line);
215             }
216 0         0 return join ("\n", @lines);
217             }
218            
219              
220             sub toRDF {
221 6     6 1 9 my ($self) = @_;
222 6         14 my $subject = $self->query;
223 6 100       22 return { } unless is_uri( $subject->as_string );
224 5         2073 my $values = { };
225              
226 5         18 for(my $i=0; $i<$self->size(); $i++) {
227 11         26 my ($label, $predicate, $object) = $self->get($i);
228 11 100       333 next unless is_uri($predicate); # TODO: use rdfs:label as default?
229              
230 10 100       953 if ($object) {
231 8 50       238 next unless is_uri($object);
232 8         731 $object = { "value" => $object, 'type' => 'uri' };
233             } else {
234 2         10 $object = { "value" => $label, 'type' => 'literal' };
235             }
236              
237 10 100       31 if ($values->{$predicate}) {
238 3         4 push @{ $values->{$predicate} }, $object;
  3         16  
239             } else {
240 7         37 $values->{$predicate} = [ $object ];
241             }
242             }
243              
244             return {
245 5         24 $subject => $values
246             };
247             }
248              
249              
250             sub toRDFJSON {
251 0     0 1 0 my ($self, $callback, $json) = @_;
252 0         0 return _JSON( $self->toRDF, $callback, $json );
253             }
254              
255              
256             sub toRDFXML {
257 0     0 1 0 my ($self) = @_;
258 0         0 my ($subject, $values) = %{$self->toRDF};
  0         0  
259              
260 0         0 my @xml = ('');
261             # TODO: $subject => $values
262 0         0 push @xml, '';
263 0         0 push @xml, '';
264 0         0 foreach my $predicate (%{$values}) {
  0         0  
265             # TODO
266             #
267             # $literal
268             }
269 0         0 push @xml, '';
270 0         0 push @xml, '';
271              
272 0         0 return join("\n", @xml) . "\n";
273             }
274              
275              
276             sub toN3 {
277 6     6 1 19 my ($self) = @_;
278 6 50       14 return "" unless $self->size();
279 6         18 my $rdf = $self->toRDF();
280 6         99 my ($subject, $values) = %$rdf;
281 6 100 66     47 return "" unless $subject && %$values;
282 5         8 my @lines;
283              
284 5         15 foreach my $predicate (keys %$values) {
285 7         10 my @objects = @{$values->{$predicate}};
  7         20  
286 7 50       25 if ($predicate eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') {
    50          
287 0         0 $predicate = 'a';
288             } elsif ($predicate eq 'http://www.w3.org/2002/07/owl#sameAs') {
289 0         0 $predicate = '=';
290             } else {
291 7         16 $predicate = "<$predicate>";
292             }
293 10         14 @objects = map {
294 7         11 my $object = $_;
295 10 100       23 if ($object->{type} eq 'uri') {
296 8         38 '<' . $object->{value} . '>';
297             } else {
298 2         7 _escape( $object->{value} );
299             }
300             } @objects;
301 7 100       16 if (@objects > 1) {
302 3         19 push @lines, (" $predicate\n " . join(" ,\n ", @objects) );
303             } else {
304 4         20 push @lines, " $predicate " . $objects[0];
305             }
306             }
307              
308 5         14 my $n3 = "<$subject>";
309 5 100       13 if (@lines > 1) {
310 2         24 return "$n3\n " . join(" ;\n ",@lines) . " .";
311             } else {
312 3         55 return $n3 . $lines[0] . " .";
313             }
314             }
315              
316              
317             sub toRedirect {
318 0     0 1 0 my ($self, $default) = @_;
319 0         0 my ($a,$b,$url) = $self->get(0);
320 0 0       0 $url = $default unless $url;
321 0 0       0 return unless $url;
322              
323 0         0 return <
324             Status: 302 Found
325             Location: $url
326             URI: <$url>
327             Content-type: text/html
328              
329            
330             HTTP
331             }
332              
333              
334             my %ESCAPED = (
335             "\t" => 't',
336             "\n" => 'n',
337             "\r" => 'r',
338             "\"" => '"',
339             "\\" => '\\',
340             );
341            
342              
343             sub _escape {
344 2     2   5 local $_ = $_[0];
345 2         19 s/([\t\n\r\"\\])/\\$ESCAPED{$1}/sg;
346 2         12 return '"' . $_ . '"';
347             }
348              
349              
350             sub _JSON {
351 28     28   55 my ($object, $callback, $JSON) = @_;
352              
353 28 50 66     106 croak ("Invalid callback name")
354             if ( $callback and !($callback =~ /^[a-z][a-z0-9._\[\]]*$/i));
355              
356             # TODO: change this behaviour (no UTF-8) ?
357 28 50       200 $JSON = JSON::XS->new->utf8(0) unless $JSON;
358              
359 28         184 my $jsonstring = $JSON->encode($object);
360              
361 28 100       290 return $callback ? "$callback($jsonstring);" : $jsonstring;
362             }
363              
364             1;
365              
366             __END__