File Coverage

blib/lib/RDF/Helper/Properties.pm
Criterion Covered Total %
statement 40 68 58.8
branch 8 24 33.3
condition 3 6 50.0
subroutine 12 14 85.7
pod n/a
total 63 112 56.2


line stmt bran cond sub pod time code
1             package RDF::Helper::Properties;
2              
3             # Uhm, well, this should probably also be a role at some point...
4              
5 2     2   110852 use RDF::Trine qw(iri variable statement);
  2         2479790  
  2         169  
6 2     2   19 use Types::Standard qw(InstanceOf ArrayRef HashRef Object);
  2         5  
  2         24  
7 2     2   1675 use Scalar::Util qw(blessed);
  2         8  
  2         95  
8 2     2   10 use Carp qw(confess);
  2         4  
  2         75  
9 2     2   9 use Moo;
  2         4  
  2         16  
10 2     2   5176 use namespace::autoclean -also => [qw/cached/];
  2         11820  
  2         11  
11              
12             our $VERSION = '0.24';
13              
14             has model => (
15             is => 'ro',
16             isa => InstanceOf['RDF::Trine::Model'],
17             required => 1,
18             );
19              
20             has [qw/ page_properties title_properties /] => (
21             is => 'lazy',
22             isa => ArrayRef[InstanceOf['RDF::Trine::Node::Resource']],
23             predicate => 1,
24             clearer => 1,
25             );
26              
27             use constant {
28 2         10 _build_page_properties => [
29             iri( 'http://xmlns.com/foaf/0.1/homepage' ),
30             iri( 'http://xmlns.com/foaf/0.1/page' ),
31             ],
32             _build_title_properties => [
33             iri( 'http://xmlns.com/foaf/0.1/name' ),
34             iri( 'http://purl.org/dc/terms/title' ),
35             iri( 'http://purl.org/dc/elements/1.1/title' ),
36             iri( 'http://www.w3.org/2004/02/skos/core#prefLabel' ),
37             iri( 'http://www.geonames.org/ontology#officialName' ),
38             iri( 'http://www.geonames.org/ontology#name' ),
39             iri( 'http://purl.org/vocabularies/getty/vp/labelPreferred' ),
40             iri( 'http://opengraphprotocol.org/schema/title' ),
41             iri( 'http://www.w3.org/2000/01/rdf-schema#label' ),
42             # doap ?
43             ],
44 2     2   327 };
  2         5  
45              
46             has cache => (
47             is => 'rw',
48             isa => HashRef|Object,
49             lazy => 1,
50             builder => 1,
51             predicate => 1,
52             clearer => 1,
53             );
54              
55             sub _build_cache
56             {
57             +{
58 1     1   43 title => {
59             '<http://www.w3.org/2000/01/rdf-schema#label>' => 'label',
60             '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>' => 'type',
61             },
62             pred => {
63             '<http://www.w3.org/2000/01/rdf-schema#label>' => 'label',
64             '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>' => 'type',
65             '<http://purl.org/dc/elements/1.1/type>' => 'Type',
66             },
67             };
68             }
69              
70             sub _cache_set
71             {
72 3     3   128 my ($self, $type, $uri, $value) = @_;
73 3         91 my $cache = $self->cache;
74 3 50       31 if (blessed $cache)
75 0         0 { $cache->set("$type$uri" => $value) }
76             else
77 3         16 { $cache->{$type}{$uri} = $value }
78 3         62 return $value;
79             }
80              
81             sub _cache_get
82             {
83 4     4   8 my ($self, $type, $uri) = @_;
84 4         80 my $cache = $self->cache;
85 4 50       67 if (blessed $cache)
86 0         0 { return $cache->get("$type$uri") }
87             else
88 4         43 { return $cache->{$type}{$uri} }
89             }
90              
91             # Helper function, cleaned away by namespace::autoclean
92             sub cached
93             {
94             my ($method_name, $coderef) = @_;
95 2     2   866 no strict 'refs';
  2         5  
  2         1829  
96             *{$method_name} = sub
97             {
98 4     4   37100 my ($self, $node) = @_;
99 4   66     11 my $return = $self->_cache_get($method_name, $node)
100             || $self->_cache_set($method_name, $node, $self->$coderef($node));
101            
102 4 50 33     358 if (blessed $return and $return->isa('RDF::Trine::Node'))
103             {
104 4 100       31 if ($return->is_literal)
    50          
105             {
106             return wantarray
107 2 100       16 ? ($return->literal_value, $return->literal_value_language, $return->literal_datatype)
108             : $return->literal_value;
109             }
110             elsif ($return->is_resource)
111             {
112 2         34 return $return->uri_value;
113             }
114             else
115             {
116 0           return $return->as_string;
117             }
118             }
119            
120 0           return $return;
121             }
122             }
123              
124             cached page => sub
125             {
126             my ($self, $node) = @_;
127            
128             confess "Node argument needs to be a RDF::Trine::Node::Resource."
129             unless $node && $node->isa('RDF::Trine::Node::Resource');
130            
131             my @props = @{ $self->page_properties };
132            
133             my ($object) =
134             grep { blessed $_ and $_->is_resource }
135             scalar $self->model->objects_for_predicate_list($node, @props);
136             ($object) =
137             grep { blessed $_ and $_->is_resource }
138             $self->model->objects_for_predicate_list($node, @props)
139             unless $object;
140            
141             return $object if $object;
142            
143             # Return the common link to ourselves
144             return iri($node->uri_value . '/page');
145             };
146              
147             cached title => sub
148             {
149             my ($self, $node) = @_;
150            
151             my @props = @{ $self->title_properties };
152            
153             my ($object) =
154             grep { blessed $_ and $_->is_literal }
155             scalar $self->model->objects_for_predicate_list($node, @props);
156             ($object) =
157             grep { blessed $_ and $_->is_literal }
158             $self->model->objects_for_predicate_list($node, @props)
159             unless $object;
160            
161             return $object if $object;
162            
163             # and finally fall back on just returning the node
164             return $node;
165             };
166              
167             cached description => sub
168             {
169             my ($self, $node) = @_;
170             my $model = $self->model;
171            
172             my $iter = $model->get_statements( $node );
173             my @label = @{ $self->title_properties };
174            
175             my @desc;
176             while (my $st = $iter->next)
177             {
178             my $p = $st->predicate;
179             my $ps;
180            
181             if ($ps = $self->_cache_get(pred => $p))
182             { 1 }
183            
184             elsif (my $pname = $model->objects_for_predicate_list($p, @label))
185             { $ps = $self->html_node_value( $pname ) }
186            
187             elsif ($p->is_resource and $p->uri_value =~ m<^http://www.w3.org/1999/02/22-rdf-syntax-ns#_(\d+)$>)
188             { $ps = '#' . $1 }
189            
190             else
191             {
192             # try to turn the predicate into a qname and use the local part as the printable name
193             my $name;
194             eval {
195             (undef, $name) = $p->qname;
196             };
197             $ps = _escape( $name || $p->uri_value );
198             }
199            
200             $self->_cache_set(pred => $p, $ps);
201             my $obj = $st->object;
202             my $os = $self->html_node_value( $obj, $p );
203            
204             push(@desc, [$ps, $os]);
205             }
206            
207             return \@desc;
208             };
209              
210             sub html_node_value
211             {
212 0     0     my $self = shift;
213 0           my $n = shift;
214 0           my $rdfapred = shift;
215 0           my $qname = '';
216 0           my $xmlns = '';
217            
218 0 0         if ($rdfapred)
219             {
220 0           eval {
221 0           my ($ns, $ln) = $rdfapred->qname;
222 0           $xmlns = qq[xmlns:ns="${ns}"];
223 0           $qname = qq[ns:$ln];
224             };
225             }
226            
227 0 0         return '' unless blessed $n;
228            
229 0 0         if ($n->is_literal)
    0          
230             {
231 0           my $l = _escape( $n->literal_value );
232            
233 0 0         return $qname
234             ? qq[<span $xmlns property="${qname}">$l</span>]
235             : $l;
236             }
237            
238             elsif ($n->is_resource)
239             {
240 0           my $uri = _escape( $n->uri_value );
241 0           my $title = _escape( $self->title($n) );
242            
243 0 0         return $qname
244             ? qq[<a $xmlns rel="${qname}" href="${uri}">$title</a>]
245             : qq[<a href="${uri}">$title</a>];
246             }
247            
248             else
249             {
250 0           return $n->as_string;
251             }
252             }
253              
254             sub _escape
255             {
256 0     0     my $l = shift;
257 0           for ($l)
258             {
259 0           s/&/&amp;/g;
260 0           s/</&lt;/g;
261 0           s/"/&quot;/g;
262             }
263 0           return $l;
264             }
265              
266             __PACKAGE__->meta->make_immutable || 1;
267             __END__
268              
269             =head1 NAME
270              
271             RDF::Helper::Properties - Module that provides shortcuts to retrieve certain information
272              
273             =head1 VERSION
274              
275             Version 0.22
276              
277             =head1 SYNOPSIS
278              
279             my $helper = RDF::Helper::Properties->new($model);
280             print $helper->title($node);
281              
282             =head1 DESCRIPTION
283              
284             =head2 Constructor
285              
286             =over
287              
288             =item C<< new(model => $model, %attributes) >>
289              
290             Moose-style constructor.
291              
292             =back
293              
294             =head2 Attributes
295              
296             =over
297              
298             =item C<< model >>
299              
300             The RDF::Trine::Model which data will be extracted from. The only attribute
301             which the constructor requires.
302              
303             =item C<< page_properties >>
304              
305             An arrayref of RDF::Trine::Node::Resource objects, each of which are
306             taken to mean "something a bit like foaf:homepage". There is a sensible
307             default.
308              
309             =item C<< title_properties >>
310              
311             An arrayref of RDF::Trine::Node::Resource objects, each of which are
312             taken to mean "something a bit like foaf:name". There is a sensible
313             default.
314              
315             =item C<< cache >>
316              
317             A hashref for caching data into, or a blessed object which supports C<get>
318             and C<set> methods compatible with L<CHI> and L<Cache::Cache>. If you do not
319             supply a cache, then a hashref will be used by default.
320              
321             =back
322              
323             =head2 Methods
324              
325             =over
326              
327             =item C<< page($node) >>
328              
329             A suitable page to redirect to, based on foaf:page or foaf:homepage.
330              
331             =item C<< title($node) >>
332              
333             A suitable title for the document will be returned, based on document contents.
334              
335             Called in list context, returns a ($value, $lang, $datatype) tuple.
336              
337             =item C<< description($node) >>
338              
339             A suitable description for the document will be returned, based on document contents
340              
341             =item C<< html_node_value($node) >>
342              
343             Formats the nodes for HTML output.
344              
345             =back
346              
347             =begin private
348              
349             =item C<< cached($subname, $coderef) >>
350              
351             Install a cached version of a sub.
352              
353             =end private
354              
355             =head1 AUTHOR
356              
357             Most of the code was written by Gregory Todd Williams C<<
358             <gwilliams@cpan.org> >> for L<RDF::LinkedData::Apache>, but refactored
359             into this class for use by other modules by Kjetil Kjernsmo, C<<
360             <kjetilk at cpan.org> >>, then refactored again by Toby Inkster,
361             C<< <tobyink at cpan.org> >>.
362              
363             =head1 COPYRIGHT & LICENSE
364              
365             Copyright 2010 Gregory Todd Williams and ABC Startsiden AS.
366              
367             Copyright 2012 Toby Inkster.
368              
369             This program is free software; you can redistribute it and/or modify it
370             under the same terms as Perl itself.
371