| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 50 |  |  | 50 |  | 686 | use v5.14; | 
|  | 50 |  |  |  |  | 152 |  | 
| 2 | 50 |  |  | 50 |  | 253 | use warnings; | 
|  | 50 |  |  |  |  | 106 |  | 
|  | 50 |  |  |  |  | 2299 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | =head1 NAME | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | Attean::API::Model - RDF Model | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | =head1 VERSION | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | This document describes Attean::API::Model version 0.033 | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | The Attean::API::Model role defines a common API for all RDF models to conform | 
| 15 |  |  |  |  |  |  | to. It is provides a consistent interface for probing, counting, and retrieving | 
| 16 |  |  |  |  |  |  | L<Attean::API::Quad|Attean::API::Binding>s matching a pattern, as well as | 
| 17 |  |  |  |  |  |  | related functionality such as enumerating the graph names, and extracting | 
| 18 |  |  |  |  |  |  | structured data from the models' quads. | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | =head1 REQUIRED METHODS | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | The following methods are required by the L<Attean::API::Model> role: | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | =over 4 | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | =item C<< get_quads( $subject, $predicate, $object, $graph ) >> | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> for quads in the model that match the | 
| 29 |  |  |  |  |  |  | supplied C<< $subject >>, C<< $predicate >>, C<< $object >>, and C<< $graph >>. | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | Any of these terms may be: | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | * An L<Attean::API::Term> object, in which case matching is equality-based | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | * A L<Attean::API::Variable> object or C<< undef >>, in which case that term | 
| 36 |  |  |  |  |  |  | will be considered as a wildcard for the purposes of matching | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | * An ARRAY reference of L<Attean::API::Term> objects, in which case the | 
| 39 |  |  |  |  |  |  | matching will be equality-based on the disjunction of the supplied terms | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | The returned iterator conforms to both L<Attean::API::Iterator> and | 
| 42 |  |  |  |  |  |  | L<Attean::API::QuadIterator>. | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | =item C<< count_quads( $subject, $predicate, $object, $graph ) >> | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | Returns the number of quads in the model matching the supplied pattern | 
| 47 |  |  |  |  |  |  | (using the same matching semantics as C<< get_quads >>). | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | =item C<< count_quads_estimate( $subject, $predicate, $object, $graph ) >> | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | Returns an estimate of the number of quads in the model matching the supplied | 
| 52 |  |  |  |  |  |  | pattern (using the same matching semantics as C<< get_quads >>). This estimate | 
| 53 |  |  |  |  |  |  | is guaranteed to non-zero if the count returned from an equivalent call to | 
| 54 |  |  |  |  |  |  | `count_quads` would return a non-zero result. | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =item C<< get_graphs >> | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of distinct L<Attean::API::Term> objects | 
| 59 |  |  |  |  |  |  | that are used in the graph position of quads in the model. | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | =back | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | =head1 METHODS | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | The L<Attean::API::Model> role provides default implementations of the | 
| 66 |  |  |  |  |  |  | following methods: | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | =over 4 | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | =item C<< get_bindings( $subject, $predicate, $object, $graph ) >> | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of L<Attean::API::Result> objects | 
| 73 |  |  |  |  |  |  | corresponding to quads in the model matching the supplied pattern. For each | 
| 74 |  |  |  |  |  |  | L<Attean::API::Variable> in the pattern list, a mapping will be present in the | 
| 75 |  |  |  |  |  |  | corresponding result object. For example, | 
| 76 |  |  |  |  |  |  | C<< $model->get_bindings( variable('s') ) >> will return an iterator of results | 
| 77 |  |  |  |  |  |  | containing just a mapping from C<< 's' >> to subjects of all quads in the | 
| 78 |  |  |  |  |  |  | model. | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | =item C<< get_list( $graph, $head ) >> | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of L<Attean::API::Term> objects that are | 
| 83 |  |  |  |  |  |  | members of the rdf:List with the specified C<< $head >> (and matching | 
| 84 |  |  |  |  |  |  | restricted to only the specified C<< $graph >>). | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | To check if a certain term is a list, the C<holds> method may be used, for example: | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | $model->holds($head, iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#first'), undef, $graph)) | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | will return true if a given term C<$head> is a list. | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | =item C<< get_sequence( $graph, $head ) >> | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of L<Attean::API::Term> objects that are | 
| 95 |  |  |  |  |  |  | members of the rdf:Sequence with the specified C<< $head >> (and matching | 
| 96 |  |  |  |  |  |  | restricted to only the specified C<< $graph >>). | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | =item C<< subjects( $predicate, $object, $graph ) >> | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of L<Attean::API::Term> objects of all | 
| 101 |  |  |  |  |  |  | subjects of quads matching the supplied pattern (using the same matching | 
| 102 |  |  |  |  |  |  | semantics as C<< get_quads >>). | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | The objects returned will not necessarily be unique. It will instead be | 
| 105 |  |  |  |  |  |  | equivalent to calling C<< get_quads >> and accessing C<< $quad->subject >> | 
| 106 |  |  |  |  |  |  | for each C<< $quad >> value returned by the iterator. For an iterator of unique | 
| 107 |  |  |  |  |  |  | subjects, use C<< $model->subjects->uniq >>. | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | =item C<< predicates( $subject, $object, $graph ) >> | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of L<Attean::API::Term> objects of all | 
| 112 |  |  |  |  |  |  | predicates of quads matching the supplied pattern (using the same matching | 
| 113 |  |  |  |  |  |  | semantics as C<< get_quads >> with an C<< undef >> predicate). | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | The objects returned will not necessarily be unique | 
| 116 |  |  |  |  |  |  | (see the note for C<< subjects >> above). | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | =item C<< objects( $subject, $predicate, $graph ) >> | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of L<Attean::API::Term> objects of all | 
| 121 |  |  |  |  |  |  | objects of quads matching the supplied pattern (using the same matching | 
| 122 |  |  |  |  |  |  | semantics as C<< get_quads >> with an C<< undef >> object). | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | The objects returned will not necessarily be unique | 
| 125 |  |  |  |  |  |  | (see the note for C<< subjects >> above). | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | =item C<< graphs( $subject, $predicate, $object ) >> | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of L<Attean::API::Term> objects of all | 
| 130 |  |  |  |  |  |  | graphs of quads matching the supplied pattern (using the same matching | 
| 131 |  |  |  |  |  |  | semantics as C<< get_quads >> with an C<< undef >> graph). | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | The objects returned will not necessarily be unique | 
| 134 |  |  |  |  |  |  | (see the note for C<< subjects >> above). | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | =item C<< graph_nodes( $graph ) >> | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of L<Attean::API::Term> objects of unique | 
| 139 |  |  |  |  |  |  | subjects and objects present in the specified C<< $graph >>. | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | =item C<< holds($s, $p, $o, $g) >> | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | =item C<< holds($triple_pattern) >> | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | =item C<< holds($quad_pattern) >> | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | Returns true if the triple/quad pattern matches any data in the model, false | 
| 148 |  |  |  |  |  |  | otherwise. | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | =item C<< algebra_holds($algebra, $graph) >> | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | =item C<< algebra_holds($algebra, \@graphs) >> | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | Returns true if the algebra, evaluated with the supplied default graph(s) | 
| 155 |  |  |  |  |  |  | matches any data in the model, false otherwise. This is equivalent to the | 
| 156 |  |  |  |  |  |  | result of an ASK query over the supplied algebra. | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | =item C<< evaluate($algebra, [ $default_graph | \@default_graphs ]) >> | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | Returns an L<Attean::API::Iterator> of L<Attean::Result> objects which result | 
| 161 |  |  |  |  |  |  | from evaluating the given query algebra (e.g. one obtained from parsing a query | 
| 162 |  |  |  |  |  |  | with L<AtteanX::Parser::SPARQL>) with the supplied default graph(s) against data | 
| 163 |  |  |  |  |  |  | in the model. | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | =cut | 
| 166 |  |  |  |  |  |  |  | 
| 167 | 50 |  |  | 50 |  | 19239 | use Attean::API::Binding; | 
|  | 50 |  |  |  |  | 158 |  | 
|  | 50 |  |  |  |  | 2340 |  | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | use Sub::Install; | 
| 170 | 50 |  |  | 50 |  | 383 | use Sub::Util qw(set_subname); | 
|  | 50 |  |  |  |  | 88 |  | 
|  | 50 |  |  |  |  | 483 |  | 
| 171 | 50 |  |  | 50 |  | 2070 | use URI::Namespace; | 
|  | 50 |  |  |  |  | 94 |  | 
|  | 50 |  |  |  |  | 2334 |  | 
| 172 | 50 |  |  | 50 |  | 261 | use Scalar::Util qw(blessed); | 
|  | 50 |  |  |  |  | 120 |  | 
|  | 50 |  |  |  |  | 1280 |  | 
| 173 | 50 |  |  | 50 |  | 236 | use List::MoreUtils qw(uniq); | 
|  | 50 |  |  |  |  | 97 |  | 
|  | 50 |  |  |  |  | 1829 |  | 
| 174 | 50 |  |  | 50 |  | 296 | use Math::Cartesian::Product; | 
|  | 50 |  |  |  |  | 98 |  | 
|  | 50 |  |  |  |  | 299 |  | 
| 175 | 50 |  |  | 50 |  | 69511 | use Data::Dumper; | 
|  | 50 |  |  |  |  | 32976 |  | 
|  | 50 |  |  |  |  | 2256 |  | 
| 176 | 50 |  |  | 50 |  | 4029 |  | 
|  | 50 |  |  |  |  | 116 |  | 
|  | 50 |  |  |  |  | 1995 |  | 
| 177 |  |  |  |  |  |  | use Moo::Role; | 
| 178 | 50 |  |  | 50 |  | 273 |  | 
|  | 50 |  |  |  |  | 90 |  | 
|  | 50 |  |  |  |  | 328 |  | 
| 179 |  |  |  |  |  |  | # get_quads($s, $p, $o, $g) | 
| 180 |  |  |  |  |  |  | # or: | 
| 181 |  |  |  |  |  |  | # get_quads([$s1, $s2, ...], \@p, \@o, \@g) | 
| 182 |  |  |  |  |  |  | requires 'get_quads'; | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | my $self	= shift; | 
| 185 |  |  |  |  |  |  | my @nodes	= @_; | 
| 186 | 50 |  |  | 50 | 1 | 236 | my @pos		= Attean::API::Quad->variables; | 
| 187 | 50 |  |  |  |  | 119 | # 		my %vars; | 
| 188 | 50 |  |  |  |  | 631 | my %bound; | 
| 189 |  |  |  |  |  |  | my %projected_vars; | 
| 190 | 50 |  |  |  |  | 102 | foreach my $i (0 .. $#nodes) { | 
| 191 |  |  |  |  |  |  | my $n	= $nodes[$i]; | 
| 192 | 50 |  |  |  |  | 163 | $bound{ $pos[ $i ] }	= $n; | 
| 193 | 196 |  |  |  |  | 1758 | if (blessed($n) and $n->does('Attean::API::Binding')) { | 
| 194 | 196 |  |  |  |  | 325 | foreach my $v ($n->referenced_variables) { | 
| 195 | 196 | 50 | 66 |  |  | 713 | $projected_vars{ $v }++; | 
|  |  | 100 | 100 |  |  |  |  | 
| 196 | 0 |  |  |  |  | 0 | } | 
| 197 | 0 |  |  |  |  | 0 | } elsif (blessed($n) and $n->isa('Attean::Variable')) { | 
| 198 |  |  |  |  |  |  | my $name	= $n->value; | 
| 199 |  |  |  |  |  |  | # 				$vars{ $pos[ $i ] }	= $name; | 
| 200 | 84 |  |  |  |  | 1857 | $projected_vars{ $name }++; | 
| 201 |  |  |  |  |  |  | } | 
| 202 | 84 |  |  |  |  | 264 | } | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | my @patterns; | 
| 205 |  |  |  |  |  |  | cartesian { | 
| 206 | 50 |  |  |  |  | 797 | my %bound; | 
| 207 |  |  |  |  |  |  | foreach my $i (0 .. $#_) { | 
| 208 | 50 |  |  | 50 |  | 3316 | my $n	= $_[$i]; | 
| 209 | 50 |  |  |  |  | 136 | $bound{ $pos[ $i ] }	= $n; | 
| 210 | 196 |  |  |  |  | 250 | } | 
| 211 | 196 |  |  |  |  | 335 | push(@patterns, Attean::QuadPattern->new( %bound )); | 
| 212 |  |  |  |  |  |  | } map { ref($_) eq 'ARRAY' ? $_ : [$_] } @nodes; | 
| 213 | 50 |  |  |  |  | 1134 |  | 
| 214 | 50 | 100 |  |  |  | 341 | my $quads		= $self->get_quads(@nodes); | 
|  | 196 |  |  |  |  | 610 |  | 
| 215 |  |  |  |  |  |  | unless (blessed($quads)) { | 
| 216 | 50 |  |  |  |  | 3050 | return Attean::ListIterator->new(values => [], item_type => 'Attean::API::Result', variables => []); | 
| 217 | 50 | 50 |  |  |  | 212 | } | 
| 218 | 0 |  |  |  |  | 0 | return $quads->map(sub { | 
| 219 |  |  |  |  |  |  | my $q			= shift; | 
| 220 |  |  |  |  |  |  | # 			warn 'model got quad: ' . $q->as_string . "\n"; | 
| 221 | 85 |  |  | 85 |  | 144 | foreach my $pattern (@patterns) { | 
| 222 |  |  |  |  |  |  | # 				warn 'model using pattern: ' . $pattern->as_string . "\n"; | 
| 223 | 85 |  |  |  |  | 151 | if (my $b = $pattern->unify($q)) { | 
| 224 |  |  |  |  |  |  | # 					warn 'unified binding: ' . $b->as_string; | 
| 225 | 85 | 50 |  |  |  | 196 | my $g	= $pattern->ground($b); | 
| 226 |  |  |  |  |  |  | # 					warn "get_bindings unification: " . $b->as_string; | 
| 227 | 85 |  |  |  |  | 2069 | # 					warn "get_bindings ground: " . $g->as_string; | 
| 228 |  |  |  |  |  |  | # 					warn 'project vars: ' . Dumper([keys %projected_vars]); | 
| 229 |  |  |  |  |  |  | my $p	= $b->project(keys %projected_vars); | 
| 230 |  |  |  |  |  |  | # 					warn "get_bindings result: " . $p->as_string; | 
| 231 | 85 |  |  |  |  | 2131 | return $p; | 
| 232 |  |  |  |  |  |  | } | 
| 233 | 85 |  |  |  |  | 2134 | } | 
| 234 |  |  |  |  |  |  | return; | 
| 235 |  |  |  |  |  |  | }, 'Attean::API::Result', variables => [keys %projected_vars]); | 
| 236 | 0 |  |  |  |  | 0 | } | 
| 237 | 50 |  |  |  |  | 454 |  | 
| 238 |  |  |  |  |  |  | requires 'count_quads'; | 
| 239 |  |  |  |  |  |  | requires 'count_quads_estimate'; | 
| 240 |  |  |  |  |  |  | requires 'get_graphs'; | 
| 241 |  |  |  |  |  |  | requires 'holds'; | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | my $self	= shift; | 
| 244 |  |  |  |  |  |  | die "get_list called without a graph name" unless (scalar(@_)); | 
| 245 |  |  |  |  |  |  | my $graph	= shift; | 
| 246 | 2 |  |  | 2 | 1 | 1657 | die "get_list called without a list head" unless (scalar(@_)); | 
| 247 | 2 | 50 |  |  |  | 8 | my $head	= shift; | 
| 248 | 2 |  |  |  |  | 4 | my $rdf_first	= Attean::IRI->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#first'); | 
| 249 | 2 | 100 |  |  |  | 14 | my $rdf_rest	= Attean::IRI->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#rest'); | 
| 250 | 1 |  |  |  |  | 4 | my $rdf_nil		= Attean::IRI->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'); | 
| 251 | 1 |  |  |  |  | 26 | my @elements; | 
| 252 | 1 |  |  |  |  | 292 | my %seen; | 
| 253 | 1 |  |  |  |  | 186 | while (blessed($head) and not($head->does('Attean::API::IRI') and $head->value eq $rdf_nil->value)) { | 
| 254 | 1 |  |  |  |  | 171 | if ($seen{ $head->as_string }++) { | 
| 255 |  |  |  |  |  |  | die "Loop found during rdf:List traversal"; | 
| 256 | 1 |  | 66 |  |  | 12 | } | 
|  |  |  | 66 |  |  |  |  | 
| 257 | 3 | 50 |  |  |  | 97 | my @n		= $self->objects( $head, $rdf_first )->elements; | 
| 258 | 0 |  |  |  |  | 0 | if (scalar(@n) != 1) { | 
| 259 |  |  |  |  |  |  | die "Invalid structure found during rdf:List traversal"; | 
| 260 | 3 |  |  |  |  | 39 | } | 
| 261 | 3 | 50 |  |  |  | 20 | push(@elements, @n); | 
| 262 | 0 |  |  |  |  | 0 | ($head)	= $self->objects( $head, $rdf_rest )->elements; | 
| 263 |  |  |  |  |  |  | } | 
| 264 | 3 |  |  |  |  | 610 | return Attean::ListIterator->new(values => \@elements, item_type => 'Attean::API::Term' ); | 
| 265 | 3 |  |  |  |  | 16 | } | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 1 |  |  |  |  | 49 | my $self	= shift; | 
| 268 |  |  |  |  |  |  | my $graph	= shift; | 
| 269 |  |  |  |  |  |  | my $head	= shift; | 
| 270 |  |  |  |  |  |  | my $rdf		= 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; | 
| 271 | 2 |  |  | 2 | 1 | 365 | my @elements; | 
| 272 | 2 |  |  |  |  | 4 | my $i		= 1; | 
| 273 | 2 |  |  |  |  | 3 | while (1) { | 
| 274 | 2 |  |  |  |  | 4 | my $term	= Attean::IRI->new("${rdf}_$i"); | 
| 275 | 2 |  |  |  |  | 3 | my @elem	= $self->objects( $head, $term, $graph )->elements; | 
| 276 | 2 |  |  |  |  | 3 | last unless (scalar(@elem)); | 
| 277 | 2 |  |  |  |  | 3 | if (scalar(@elem) > 1) { | 
| 278 | 6 |  |  |  |  | 115 | my $count	= scalar(@elem); | 
| 279 | 6 |  |  |  |  | 1143 | die "Invalid structure found during rdf:Seq access: $count elements found for element $i"; | 
| 280 | 6 | 100 |  |  |  | 33 | } | 
| 281 | 5 | 100 |  |  |  | 12 | my $elem	= $elem[0]; | 
| 282 | 1 |  |  |  |  | 2 | last unless (blessed($elem)); | 
| 283 | 1 |  |  |  |  | 17 | push(@elements, $elem); | 
| 284 |  |  |  |  |  |  | $i++; | 
| 285 | 4 |  |  |  |  | 5 | } | 
| 286 | 4 | 50 |  |  |  | 13 | return Attean::ListIterator->new(values => \@elements, item_type => 'Attean::API::Term' ); | 
| 287 | 4 |  |  |  |  | 8 | } | 
| 288 | 4 |  |  |  |  | 11 |  | 
| 289 |  |  |  |  |  |  | { | 
| 290 | 1 |  |  |  |  | 18 | # auto-generate methods subjects, predicates, objects, and graphs | 
| 291 |  |  |  |  |  |  | my @pos		= Attean::API::Quad->variables; | 
| 292 |  |  |  |  |  |  | my %pos		= map { $pos[$_] => $_ } (0 .. $#pos); | 
| 293 |  |  |  |  |  |  | for my $method (@pos) { | 
| 294 |  |  |  |  |  |  | my $code	= sub { | 
| 295 |  |  |  |  |  |  | my $self	= shift; | 
| 296 |  |  |  |  |  |  | my @nodes	= @_; | 
| 297 |  |  |  |  |  |  | $#nodes		= 3; | 
| 298 |  |  |  |  |  |  | splice(@nodes, $pos{$method}, 0, undef); | 
| 299 | 20 |  |  | 20 |  | 447 | $#nodes		= 3; | 
|  |  |  |  | 20 |  |  |  | 
|  |  |  |  | 20 |  |  |  | 
|  |  |  |  | 20 |  |  |  | 
| 300 | 20 |  |  |  |  | 45 | my $iter	= $self->get_quads(@nodes); | 
| 301 | 20 |  |  |  |  | 63 | my $nodes	= $iter->map( | 
| 302 | 20 |  |  |  |  | 73 | sub { $_->$method() }, | 
| 303 | 20 |  |  |  |  | 39 | 'Attean::API::Term', | 
| 304 | 20 |  |  |  |  | 96 | ); | 
| 305 |  |  |  |  |  |  | return $nodes; | 
| 306 | 33 |  |  | 33 |  | 115 | }; | 
| 307 | 20 |  |  |  |  | 162 | Sub::Install::install_sub({ | 
| 308 |  |  |  |  |  |  | code	=> set_subname("${method}s", $code), | 
| 309 | 20 |  |  |  |  | 738 | as		=> "${method}s" | 
| 310 |  |  |  |  |  |  | }); | 
| 311 |  |  |  |  |  |  | } | 
| 312 |  |  |  |  |  |  | } | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | my $self	= shift; | 
| 315 |  |  |  |  |  |  | my $graph	= shift; | 
| 316 |  |  |  |  |  |  | my $s		= $self->subjects(undef, undef, $graph); | 
| 317 |  |  |  |  |  |  | my $o		= $self->objects(undef, undef, $graph); | 
| 318 |  |  |  |  |  |  | my $union	= Attean::IteratorSequence->new( iterators => [$s, $o], item_type => 'Attean::API::Term' ); | 
| 319 | 2 |  |  | 2 | 1 | 7 | my %seen; | 
| 320 | 2 |  |  |  |  | 4 | return $union->grep(sub {not($seen{shift->as_string}++)}); | 
| 321 | 2 |  |  |  |  | 10 | } | 
| 322 | 2 |  |  |  |  | 13 |  | 
| 323 | 2 |  |  |  |  | 45 | my $self 	= shift; | 
| 324 | 2 |  |  |  |  | 64 | my $algebra	= shift || die "No algebra available in evaluate call"; | 
| 325 | 2 |  |  | 12 |  | 17 | my $default_graphs	= shift || die "No default graphs available in evaluate call"; | 
|  | 12 |  |  |  |  | 57 |  | 
| 326 |  |  |  |  |  |  | $default_graphs	= [$default_graphs] if (blessed($default_graphs)); | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | unless (blessed($algebra) and $algebra->does('Attean::API::Algebra')) { | 
| 329 | 2 |  |  | 2 | 1 | 30 | die "Unexpected argument to evaluate: " . Dumper($algebra); | 
| 330 | 2 |  | 50 |  |  | 7 | } | 
| 331 | 2 |  | 50 |  |  | 10 |  | 
| 332 | 2 | 50 |  |  |  | 45 | my $planner	= Attean::IDPQueryPlanner->new(); | 
| 333 |  |  |  |  |  |  | my $plan	= $planner->plan_for_algebra($algebra, $self, $default_graphs); | 
| 334 | 2 | 50 | 33 |  |  | 31 | my $iter	= $plan->evaluate($self); | 
| 335 | 0 |  |  |  |  | 0 | return $iter; | 
| 336 |  |  |  |  |  |  | } | 
| 337 |  |  |  |  |  |  |  | 
| 338 | 2 |  |  |  |  | 88 | my $self 	= shift; | 
| 339 | 2 |  |  |  |  | 3577 | my $algebra	= shift || die "No algebra available in algebra_holds call"; | 
| 340 | 2 |  |  |  |  | 24 | my $default_graphs	= shift || die "No default graphs available in algebra_holds call"; | 
| 341 | 2 |  |  |  |  | 109 | $default_graphs	= [$default_graphs] if (blessed($default_graphs)); | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | unless (blessed($algebra) and $algebra->does('Attean::API::Algebra')) { | 
| 344 |  |  |  |  |  |  | die "Unexpected argument to algebra_holds: " . Dumper($algebra); | 
| 345 | 6 |  |  | 6 | 1 | 11 | } | 
| 346 | 6 |  | 50 |  |  | 19 |  | 
| 347 | 6 |  | 50 |  |  | 14 | unless ($algebra->isa('Attean::Algebra::Ask')) { | 
| 348 | 6 | 50 |  |  |  | 24 | $algebra	= Attean::Algebra::Ask->new(children => [$algebra]); | 
| 349 |  |  |  |  |  |  | } | 
| 350 | 6 | 50 | 33 |  |  | 38 | my $planner	= Attean::IDPQueryPlanner->new(); | 
| 351 | 0 |  |  |  |  | 0 | my $plan	= $planner->plan_for_algebra($algebra, $self, $default_graphs); | 
| 352 |  |  |  |  |  |  | my $iter	= $plan->evaluate($self); | 
| 353 |  |  |  |  |  |  | my $r		= $iter->next; | 
| 354 | 6 | 50 |  |  |  | 129 | my $ebv		= eval { $r->ebv }; | 
| 355 | 6 |  |  |  |  | 78 | return 0 if ($@); | 
| 356 |  |  |  |  |  |  | return $ebv; | 
| 357 | 6 |  |  |  |  | 81 | } | 
| 358 | 6 |  |  |  |  | 5857 |  | 
| 359 | 6 |  |  |  |  | 29 | my $self 	= shift; | 
| 360 | 6 |  |  |  |  | 237 | return 0 unless scalar(@_); | 
| 361 | 6 |  |  |  |  | 19 | if (not defined($_[0]) or (blessed($_[0]) and $_[0]->does('Attean::API::TermOrVariable'))) { | 
|  | 6 |  |  |  |  | 27 |  | 
| 362 | 6 | 50 |  |  |  | 15 | # firt argument is undef or a term/variable, so we assume this is a call with up to 3 term/variable/undef args | 
| 363 | 6 |  |  |  |  | 101 | return ($self->count_quads_estimate(@_) > 0); | 
| 364 |  |  |  |  |  |  | } elsif (blessed($_[0]) and $_[0]->does('Attean::API::TripleOrQuadPattern')) { | 
| 365 |  |  |  |  |  |  | my $t	= shift; | 
| 366 |  |  |  |  |  |  | return ($self->count_quads_estimate($t->values) > 0); | 
| 367 | 16 |  |  | 16 | 1 | 1667 | } else { | 
| 368 | 16 | 50 |  |  |  | 43 | die "Unexpected argument to holds: " . Dumper($_[0]); | 
| 369 | 16 | 100 | 66 |  |  | 121 | } | 
|  |  | 50 | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 370 |  |  |  |  |  |  | } | 
| 371 | 13 |  |  |  |  | 392 | } | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 3 |  |  |  |  | 128 |  | 
| 374 | 3 |  |  |  |  | 20 | use Attean::RDF; | 
| 375 |  |  |  |  |  |  | use LWP::UserAgent; | 
| 376 | 0 |  |  |  |  | 0 | use Encode qw(encode); | 
| 377 |  |  |  |  |  |  | use Scalar::Util qw(blessed); | 
| 378 |  |  |  |  |  |  | use Role::Tiny (); | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | use Moo::Role; | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | requires 'add_quad'; | 
| 383 | 50 |  |  | 50 |  | 86915 | requires 'remove_quad'; | 
|  | 50 |  |  |  |  | 148 |  | 
|  | 50 |  |  |  |  | 394 |  | 
| 384 | 50 |  |  | 50 |  | 67581 | requires 'create_graph'; | 
|  | 50 |  |  |  |  | 1608468 |  | 
|  | 50 |  |  |  |  | 1943 |  | 
| 385 | 50 |  |  | 50 |  | 652 | requires 'drop_graph'; | 
|  | 50 |  |  |  |  | 5230 |  | 
|  | 50 |  |  |  |  | 3025 |  | 
| 386 | 50 |  |  | 50 |  | 308 | requires 'clear_graph'; | 
|  | 50 |  |  |  |  | 88 |  | 
|  | 50 |  |  |  |  | 1590 |  | 
| 387 | 50 |  |  | 50 |  | 407 | requires 'add_iter'; | 
|  | 50 |  |  |  |  | 84 |  | 
|  | 50 |  |  |  |  | 677 |  | 
| 388 |  |  |  |  |  |  |  | 
| 389 | 50 |  |  | 50 |  | 219 | with 'Attean::API::Model'; | 
|  | 50 |  |  |  |  | 89 |  | 
|  | 50 |  |  |  |  | 396 |  | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | my $self	= shift; | 
| 392 |  |  |  |  |  |  | my $graph	= shift; | 
| 393 |  |  |  |  |  |  | my @urls	= @_; | 
| 394 |  |  |  |  |  |  | my $ua		= LWP::UserAgent->new(); | 
| 395 |  |  |  |  |  |  | my $accept	= Attean->acceptable_parsers( handles => 'Attean::API::Triple' ); | 
| 396 |  |  |  |  |  |  | $ua->default_headers->push_header( 'Accept' => $accept ); | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | foreach my $u (@urls) { | 
| 399 |  |  |  |  |  |  | my $url		= blessed($u) ? $u->value : $u; | 
| 400 |  |  |  |  |  |  | my $resp	= $ua->get($url); | 
| 401 | 0 |  |  | 0 | 0 | 0 | if ($resp->is_success) { | 
| 402 | 0 |  |  |  |  | 0 | my $ct		= $resp->header('Content-Type'); | 
| 403 | 0 |  |  |  |  | 0 | my $pclass = Attean->get_parser( media_type => $ct, filename => $url ) // Attean->get_parser('ntriples'); | 
| 404 | 0 |  |  |  |  | 0 | if ($pclass) { | 
| 405 | 0 |  |  |  |  | 0 | my $p		= $pclass->new(base => iri($url)); | 
| 406 | 0 |  |  |  |  | 0 | my $str		= $resp->decoded_content; | 
| 407 |  |  |  |  |  |  | my $bytes	= encode('UTF-8', $str, Encode::FB_CROAK); | 
| 408 | 0 |  |  |  |  | 0 | my $iter	= $p->parse_iter_from_bytes( $bytes ); | 
| 409 | 0 | 0 |  |  |  | 0 | $self->add_iter($iter->as_quads($graph)); | 
| 410 | 0 |  |  |  |  | 0 | } else { | 
| 411 | 0 | 0 |  |  |  | 0 | die "No parser found for content type $ct: $url"; | 
| 412 | 0 |  |  |  |  | 0 | } | 
| 413 | 0 |  | 0 |  |  | 0 | } else { | 
| 414 | 0 | 0 |  |  |  | 0 | die $resp->status_line; | 
| 415 | 0 |  |  |  |  | 0 | } | 
| 416 | 0 |  |  |  |  | 0 | } | 
| 417 | 0 |  |  |  |  | 0 | } | 
| 418 | 0 |  |  |  |  | 0 |  | 
| 419 | 0 |  |  |  |  | 0 | # $model->load_triples( 'turtle', iri('http://example.org/graph1') => "@prefix foaf: ...", iri('http://example.org/graph2') => "@prefix foaf: ..." ); | 
| 420 |  |  |  |  |  |  | my $self	= shift; | 
| 421 | 0 |  |  |  |  | 0 | my $format	= shift; | 
| 422 |  |  |  |  |  |  | my $class	= Attean->get_parser($format) || die "Failed to load parser for '$format'"; | 
| 423 |  |  |  |  |  |  | my $parser	= $class->new() || die "Failed to construct parser for '$format'"; | 
| 424 | 0 |  |  |  |  | 0 | while (scalar(@_)) { | 
| 425 |  |  |  |  |  |  | my ($graph, $string)	= splice(@_, 0, 2); | 
| 426 |  |  |  |  |  |  | my $iter	= $parser->parse_iter_from_bytes(encode('UTF-8', $string, Encode::FB_CROAK)); | 
| 427 |  |  |  |  |  |  | my $quads	= $iter->as_quads($graph); | 
| 428 |  |  |  |  |  |  | $self->add_iter($quads); | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  | } | 
| 431 | 6 |  |  | 6 | 1 | 3075 |  | 
| 432 | 6 |  |  |  |  | 13 | my $self	= shift; | 
| 433 | 6 |  | 50 |  |  | 51 | my $format	= shift; | 
| 434 | 6 |  | 50 |  |  | 139 | my $class	= Attean->get_parser($format) || die "Failed to load parser for '$format'"; | 
| 435 | 6 |  |  |  |  | 294 | my $parser	= $class->new() || die "Failed to construct parser for '$format'"; | 
| 436 | 6 |  |  |  |  | 29 | while (scalar(@_)) { | 
| 437 | 6 |  |  |  |  | 78 | my ($graph, $fh)	= splice(@_, 0, 2); | 
| 438 | 6 |  |  |  |  | 3634 | my $iter	= $parser->parse_iter_from_io($fh); | 
| 439 | 6 |  |  |  |  | 359 | my $quads	= $iter->as_quads($graph); | 
| 440 |  |  |  |  |  |  | $self->add_iter($quads); | 
| 441 |  |  |  |  |  |  | } | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 | 1 |  |  | 1 | 1 | 88 | my $self	= shift; | 
| 445 | 1 |  |  |  |  | 4 | my $iter	= shift; | 
| 446 | 1 |  | 50 |  |  | 9 | my $type	= $iter->item_type; | 
| 447 | 1 |  | 50 |  |  | 55 | die "Iterator type $type isn't quads" unless (Role::Tiny::does_role($type, 'Attean::API::Quad')); | 
| 448 | 1 |  |  |  |  | 66 | while (my $q = $iter->next) { | 
| 449 | 1 |  |  |  |  | 5 | $self->add_quad($q); | 
| 450 | 1 |  |  |  |  | 6 | } | 
| 451 | 1 |  |  |  |  | 158 | } | 
| 452 | 1 |  |  |  |  | 59 |  | 
| 453 |  |  |  |  |  |  | my $self	= shift; | 
| 454 |  |  |  |  |  |  | die "add_list called without a graph name" unless (scalar(@_)); | 
| 455 |  |  |  |  |  |  | my $graph	= shift; | 
| 456 |  |  |  |  |  |  | my @elements	= @_; | 
| 457 | 2 |  |  | 2 | 1 | 50 | my $rdf_first	= Attean::IRI->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#first'); | 
| 458 | 2 |  |  |  |  | 4 | my $rdf_rest	= Attean::IRI->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#rest'); | 
| 459 | 2 |  |  |  |  | 11 | my $rdf_nil		= Attean::IRI->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'); | 
| 460 | 2 | 50 |  |  |  | 7 | if (scalar(@elements) == 0) { | 
| 461 | 2 |  |  |  |  | 37 | return $rdf_nil; | 
| 462 | 10 |  |  |  |  | 37 | } else { | 
| 463 |  |  |  |  |  |  | my $head		= Attean::Blank->new(); | 
| 464 |  |  |  |  |  |  | my $node		= shift(@elements); | 
| 465 |  |  |  |  |  |  | my $rest		= $self->add_list($graph, @elements); | 
| 466 |  |  |  |  |  |  | $self->add_quad( Attean::Quad->new($head, $rdf_first, $node, $graph) ); | 
| 467 | 5 |  |  | 5 | 1 | 220 | $self->add_quad( Attean::Quad->new($head, $rdf_rest, $rest, $graph) ); | 
| 468 | 5 | 100 |  |  |  | 26 | return $head; | 
| 469 | 4 |  |  |  |  | 5 | } | 
| 470 | 4 |  |  |  |  | 11 | } | 
| 471 | 4 |  |  |  |  | 65 | } | 
| 472 | 4 |  |  |  |  | 765 |  | 
| 473 | 4 |  |  |  |  | 732 |  | 
| 474 | 4 | 100 |  |  |  | 774 | use Moo::Role; | 
| 475 | 1 |  |  |  |  | 6 |  | 
| 476 |  |  |  |  |  |  | requires 'etag_value_for_quads'; | 
| 477 | 3 |  |  |  |  | 56 | } | 
| 478 | 3 |  |  |  |  | 169 |  | 
| 479 | 3 |  |  |  |  | 16 |  | 
| 480 | 3 |  |  |  |  | 54 | use Moo::Role; | 
| 481 | 3 |  |  |  |  | 45 |  | 
| 482 | 3 |  |  |  |  | 23 | requires 'mtime_for_quads'; | 
| 483 |  |  |  |  |  |  | } | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | use Moo::Role; | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | with 'Attean::API::MutableModel'; | 
| 489 | 50 |  |  | 50 |  | 52231 |  | 
|  | 50 |  |  |  |  | 110 |  | 
|  | 50 |  |  |  |  | 409 |  | 
| 490 |  |  |  |  |  |  | requires 'begin_bulk_updates'; | 
| 491 |  |  |  |  |  |  | requires 'end_bulk_updates'; | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | around [qw(load_triples load_triples_from_io add_iter add_list)] => sub { | 
| 494 |  |  |  |  |  |  | my $orig	= shift; | 
| 495 |  |  |  |  |  |  | my $self	= shift; | 
| 496 | 50 |  |  | 50 |  | 16781 | $self->begin_bulk_updates(); | 
|  | 50 |  |  |  |  | 96 |  | 
|  | 50 |  |  |  |  | 190 |  | 
| 497 |  |  |  |  |  |  | $self->$orig(@_); | 
| 498 |  |  |  |  |  |  | $self->end_bulk_updates(); | 
| 499 |  |  |  |  |  |  | }; | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | # End bulk updates the moment a read operation is performed... | 
| 502 |  |  |  |  |  |  | before [qw(get_quads get_bindings count_quads count_quads_estimate get_graphs subject predicate object graph)] => sub { | 
| 503 | 50 |  |  | 50 |  | 17525 | my $self	= shift; | 
|  | 50 |  |  |  |  | 211 |  | 
|  | 50 |  |  |  |  | 215 |  | 
| 504 |  |  |  |  |  |  | $self->end_bulk_updates(); | 
| 505 |  |  |  |  |  |  | }; | 
| 506 |  |  |  |  |  |  | } | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | use Moo::Role; | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | with 'Attean::API::Model'; | 
| 511 |  |  |  |  |  |  | } | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | 1; | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | =back | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | =head1 BUGS | 
| 521 |  |  |  |  |  |  |  | 
| 522 |  |  |  |  |  |  | Please report any bugs or feature requests to through the GitHub web interface | 
| 523 |  |  |  |  |  |  | at L<https://github.com/kasei/attean/issues>. | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 526 | 50 |  |  | 50 |  | 20769 |  | 
|  | 50 |  |  |  |  | 101 |  | 
|  | 50 |  |  |  |  | 200 |  | 
| 527 |  |  |  |  |  |  |  | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | =head1 AUTHOR | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | Gregory Todd Williams  C<< <gwilliams@cpan.org> >> | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | Copyright (c) 2014--2022 Gregory Todd Williams. | 
| 536 |  |  |  |  |  |  | This program is free software; you can redistribute it and/or modify it under | 
| 537 |  |  |  |  |  |  | the same terms as Perl itself. | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | =cut |