File Coverage

blib/lib/RDF/RDFa/Linter.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::RDFa::Linter;
2              
3 1     1   24468 use 5.008;
  1         3  
  1         33  
4 1     1   5 use strict;
  1         2  
  1         30  
5 1     1   549 use RDF::RDFa::Linter::Error;
  0            
  0            
6             use RDF::RDFa::Linter::Service::CreativeCommons;
7             use RDF::RDFa::Linter::Service::Facebook;
8             use RDF::RDFa::Linter::Service::Google;
9             use RDF::RDFa::Linter::Service::SchemaOrg;
10             use RDF::RDFa::Parser;
11             use RDF::Trine;
12             use RDF::Query;
13              
14             our $VERSION = '0.053';
15              
16             sub new
17             {
18             my ($class, $service, $thisuri, $parser) = @_;
19            
20             my $self = bless {
21             service => __PACKAGE__ . '::Service::' . $service,
22             uri => $thisuri,
23             parser => $parser,
24             }, $class;
25              
26             $parser->{'__linter'} = $self;
27             $parser->set_callbacks({
28             onprefix => \&cb_onprefix,
29             oncurie => \&cb_oncurie,
30             });
31             $self->{'graph'} = $parser->graph;
32             $self->{'lint'} = $self->{'service'}->new($parser->graph, $thisuri);
33              
34             return $self;
35             }
36              
37             sub info
38             {
39             my ($self) = @_;
40             return $self->{'lint'}->info;
41             }
42              
43             sub filtered_graph
44             {
45             my ($self) = @_;
46             return $self->{'lint'}->filtered_graph;
47             }
48              
49             sub find_errors
50             {
51             my ($self) = @_;
52             my @errs = @{ $self->{'parse_errors'} };
53             push @errs, $self->{'lint'}->find_errors;
54            
55             return @errs;
56             }
57              
58             sub cb_onprefix
59             {
60             my ($parser, $node, $prefix, $uri) = @_;
61             my $self = $parser->{'__linter'};
62            
63             my $preferred = $self->{'service'}->prefixes;
64            
65             if (defined $preferred->{$prefix}
66             and $preferred->{$prefix} ne $uri)
67             {
68             push @{ $self->{'parse_errors'} },
69             RDF::RDFa::Linter::Error->new(
70             'subject' => RDF::Trine::Node::Resource->new($self->{'uri'}),
71             'text' => "Prefix '$prefix' bound to <$uri>, instead of the usual <".$preferred->{$prefix}."> - this is allowed, but unusual.",
72             'level' => 1,
73             );
74             }
75             elsif (!defined $preferred->{$prefix})
76             {
77             while (my ($p,$f) = each %$preferred)
78             {
79             if ($f eq $uri)
80             {
81             push @{ $self->{'parse_errors'} },
82             RDF::RDFa::Linter::Error->new(
83             'subject' => RDF::Trine::Node::Resource->new($self->{'uri'}),
84             'text' => "Prefix '$prefix' bound to <$uri>, instead of the usual prefix '$p' - this is allowed, but unusual.",
85             'level' => 1,
86             );
87             }
88             }
89             }
90            
91             return 0;
92             }
93              
94             sub cb_oncurie
95             {
96             my ($parser, $node, $curie, $uri) = @_;
97             my $self = $parser->{'__linter'};
98              
99             return $uri unless $curie eq $uri || $uri eq '';
100              
101             my $preferred = $self->{'service'}->prefixes;
102            
103             if ($curie =~ m/^([^:]+):(.*)$/)
104             {
105             my ($pfx, $sfx) = ($1, $2);
106            
107             if (defined $preferred->{$pfx})
108             {
109             push @{ $self->{'parse_errors'} },
110             RDF::RDFa::Linter::Error->new(
111             'subject' => RDF::Trine::Node::Resource->new($self->{'uri'}),
112             'text' => "CURIE '$curie' used but '$pfx' is not bound - perhaps you forgot to specify xmlns:${pfx}=\"".$preferred->{$pfx}."\"",
113             'level' => 5,
114             );
115            
116             return $preferred->{$pfx} . $sfx;
117             }
118             elsif ($pfx !~ m'^(http|https|file|ftp|urn|tag|mailto|acct|data|
119             fax|tel|modem|gopher|info|news|sip|irc|javascript|sgn|ssh|xri|widget)$'ix)
120             {
121             push @{ $self->{'parse_errors'} },
122             RDF::RDFa::Linter::Error->new(
123             'subject' => RDF::Trine::Node::Resource->new($self->{'uri'}),
124             'text' => "CURIE '$curie' used but '$pfx' is not bound - perhaps you forgot to specify xmlns:${pfx}=\"SOMETHING\"",
125             'level' => 1,
126             );
127             }
128             }
129              
130             return $uri;
131             }
132              
133             sub __rdf_query
134             {
135             my ($sparql, $model) = @_;
136             my $result = RDF::Query->new($sparql)->execute($model);
137              
138             if ($result->is_boolean)
139             { return $result->get_boolean }
140             elsif ($result->is_bindings)
141             { return $result }
142              
143             $result->is_graph or die;
144              
145             my $return = RDF::Trine::Model->new;
146             $return->add_hashref( $result->as_hashref );
147             return $return;
148             }
149              
150             1;
151              
152             __END__
153              
154             =head1 NAME
155              
156             RDF::RDFa::Linter - find common mistakes in RDFa files
157              
158             =head1 SYNOPSIS
159              
160             my $parser = RDF::RDFa::Parser->new_from_url($input_url);
161             my $linter = RDF::RDFa::Linter->new('Facebook', $input_url, $parser);
162             my $model = $linter->filtered_graph;
163             my @errors = $linter->find_errors;
164              
165             =head1 DESCRIPTION
166              
167             In the above example, $model is an RDF::Trine::Model containing just the
168             statements from $input_url that the service (in this case, Facebook's
169             Open Graph) understands.
170              
171             @errors is a list of RDF::RDFa::Linter::Error objects. RDF::RDFa::Linter::Error
172             is a subclass of RDF::RDFa::Generator::HTML::Pretty::Note, which comes in
173             handy if you want to generate a report of the errors and filtered graph
174             together.
175              
176             TODO: proper documentation!!
177              
178             =head1 BUGS
179              
180             Please report any bugs to L<http://rt.cpan.org/>.
181              
182             =head1 SEE ALSO
183              
184             L<XML::LibXML>, L<RDF::RDFa::Parser>, L<RDF::RDFa::Generator>.
185              
186             L<http://www.perlrdf.org/>.
187              
188             L<http://check.rdfa.info/>.
189              
190             =head1 AUTHOR
191              
192             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
193              
194             =head1 COPYRIGHT AND LICENCE
195              
196             Copyright (C) 2010 by Toby Inkster
197              
198             This library is free software; you can redistribute it and/or modify
199             it under the same terms as Perl itself, either Perl version 5.8 or,
200             at your option, any later version of Perl 5 you may have available.
201              
202             =cut
203