File Coverage

blib/lib/RDF/Helper/Properties.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


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