File Coverage

blib/lib/Attean/API/Binding.pm
Criterion Covered Total %
statement 292 398 73.3
branch 44 118 37.2
condition 6 15 40.0
subroutine 63 71 88.7
pod 21 42 50.0
total 426 644 66.1


line stmt bran cond sub pod time code
1 50     50   531 use v5.14;
  50         149  
2 50     50   260 use warnings;
  50         122  
  50         1709  
3              
4             =head1 NAME
5              
6             Attean::API::Binding - Name to term bindings
7              
8             =head1 VERSION
9              
10             This document describes Attean::API::Binding version 0.032
11              
12             =head1 DESCRIPTION
13              
14             The Attean::API::Binding role defines a common API for all objects that map
15             names to L<Attean::API::Term> objects. This includes triples, quads, and
16             SPARQL results (variable bindings).
17              
18             =head1 REQUIRED METHODS
19              
20             Classes consuming this role must provide the following methods:
21              
22             =over 4
23              
24             =item C<< value( $name ) >>
25              
26             Returns the L<Attean::API::Term> object mapped to the variable named C<< $name >>,
27             or C<< undef >> if no such term is mapped.
28              
29             =item C<< variables >>
30              
31             Returns a list of the variable names mapped to L<Attean::API::Term> objects in
32             this mapping.
33              
34             =item C<< apply_map( $mapper ) >>
35              
36             Returns a new mapping object (of the same class as the referent) with term
37             objects rewritten using the supplied L<Attean::Mapper> object C<< $mapper >>.
38              
39             =back
40              
41             =head1 METHODS
42              
43             This role provides default implementations of the following methods:
44              
45             =over 4
46              
47             =item C<< mapping >>
48              
49             Returns a HASH mapping variable names to L<Attean::API::Term> objects.
50              
51             =item C<< values >>
52              
53             Returns a list of L<Attean::API::Term> objects corresponding to the variable
54             names returned by the referent's C<< variables >> method.
55              
56             =item C<< tuples_string >>
57              
58             Returns a string serialization of the L<Attean::API::Term> objects in the order
59             they are returned by the referent's C<< values >> method.
60              
61             =item C<< as_string >>
62              
63             Returns a string serialization of the variable bindings.
64              
65             =item C<< has_blanks >>
66              
67             Returns true if any variable is bound to an L<Attean::API::Blank> term, false
68             otherwise.
69              
70             =cut
71              
72 50     50   282 use Type::Tiny::Role;
  50         120  
  50         2024  
73              
74             use Scalar::Util qw(blessed);
75 50     50   291 use List::MoreUtils qw(zip);
  50         160  
  50         2686  
76 50     50   387  
  50         130  
  50         552  
77             use Moo::Role;
78 50     50   31374
  50         103  
  50         299  
79             requires 'value';
80             requires 'variables';
81             requires 'apply_map';
82            
83             my $self = shift;
84             my @k = $self->variables;
85 61     61 1 2253 my @v = $self->values;
86 61         106 return zip @k, @v;
87 61         98 }
88 61         389
89             my $self = shift;
90             return map { $self->value($_) } $self->variables;
91             }
92 2818     2818 1 25489
93 2818         5268 my $self = shift;
  9724         15392  
94             my @strs = map { $_->ntriples_string } $self->values;
95             return join(' ', @strs) . ' .';
96             }
97 403     403 1 3346 shift->tuples_string();
98 403         854 }
  1444         30821  
99 403         8185
100             my $self = shift;
101             foreach my $term ($self->values) {
102 266     266 1 14273 return 1 if ($term->does('Attean::API::Blank'));
103             if ($term->does('Attean::API::Binding')) {
104             return 1 if ($term->has_blanks);
105             }
106 56     56 1 91 }
107 56         107 return 0;
108 94 100       753 }
109 55 50       980
110 0 0       0 =item C<< sameTerms( $other ) >>
111              
112             =cut
113 17         257  
114             my $self = shift;
115             my $other = shift;
116             return 0 unless ($other->does('Attean::API::Binding'));
117             my @variables = sort $self->variables;
118             my @other_vars = sort $other->variables;
119             return 0 unless (scalar(@variables) == scalar(@other_vars));
120             foreach my $i (0 .. $#variables) {
121 0     0 1 0 return 0 unless $variables[$i] eq $other_vars[$i];
122 0         0 }
123 0 0       0
124 0         0 foreach my $v (@variables) {
125 0         0 my $value = $self->value($v);
126 0 0       0 my $other_value = $other->value($v);
127 0         0 if ($value->does('Attean::API::Binding')) {
128 0 0       0 return 0 unless $value->sameTerms($other_value);
129             } else {
130             return 0 unless ($value->equals($other_value));
131 0         0 }
132 0         0 }
133 0         0 return 1;
134 0 0       0 }
135 0 0       0
136             =item C<< equals( $other ) >>
137 0 0       0  
138             =cut
139              
140 0         0 my $self = shift;
141             my $other = shift;
142             return 0 unless ($other->does('Attean::API::Binding'));
143             my @variables = sort $self->variables;
144             my @other_vars = sort $other->variables;
145             unless (scalar(@variables) == scalar(@other_vars)) {
146             return 0;
147             }
148 0     0 1 0 foreach my $i (0 .. $#variables) {
149 0         0 unless ($variables[$i] eq $other_vars[$i]) {
150 0 0       0 return 0;
151 0         0 }
152 0         0 }
153 0 0       0
154 0         0 foreach my $v (@variables) {
155             my $value = $self->value($v);
156 0         0 my $other_value = $other->value($v);
157 0 0       0 if ($value->does('Attean::API::Binding')) {
158 0         0 unless ($value->equals($other_value)) {
159             return 0;
160             }
161             } else {
162 0         0 unless (0 == $value->compare($other_value)) {
163 0         0 return 0;
164 0         0 }
165 0 0       0 }
166 0 0       0 }
167 0         0 return 1;
168             }
169            
170 0 0       0 =item C<< blanks >>
171 0         0  
172             Returns all the values in this mapping (recursively, if any values are embedded
173             bindings) that are blank nodes.
174              
175 0         0 =cut
176              
177             my $self = shift;
178             my %nodes;
179             foreach my $term ($self->values) {
180             if ($term->does('Attean::API::Blank')) {
181             $nodes{ $term->value } = $term;
182             }
183             if ($term->does('Attean::API::Binding')) {
184             foreach my $b ($term->blanks) {
185             $nodes{ $b->value } = $b;
186 30     30 1 41 }
187 30         39 }
188 30         45 }
189 110 100       1274 return CORE::values %nodes;
190 40         494 }
191            
192 110 50       1125 =item C<< referenced_variables >>
193 0         0  
194 0         0 Returns a list of the names of any variable values that are referenced in this
195             binding (recursively, if any values are embedded bindings).
196              
197             =cut
198 30         492  
199             my $self = shift;
200             my %vars;
201             foreach my $v ($self->values) {
202             if ($v->does('Attean::API::Variable')) {
203             $vars{$v->value}++;
204             } elsif ($v->does('Attean::API::Binding')) {
205             foreach my $name ($v->referenced_variables) {
206             $vars{$name}++;
207             }
208             }
209 104     104 1 149 }
210 104         151 return keys %vars;
211 104         238 }
212 312 100       3843
    100          
213 138         2029 =item C<< is_ground >>
214              
215 13         436 Returns tue is all the bound values consume L<Attean::API::Term>, false otherwise.
216 13         29  
217             =cut
218              
219             my $self = shift;
220 104         2191 my @non_terms = grep { not($_->does('Attean::API::Term')) } $self->values;
221             my @bad = grep { not($_->does('Attean::API::Binding') and $_->is_ground) } @non_terms;
222             return (scalar(@bad) == 0);
223             }
224            
225             =item C<< values_consuming_role( $role ) >>
226              
227             Returns the list of bound values that consume C<< $role >>.
228              
229             =cut
230 164     164 1 277  
231 164         435 my $self = shift;
  632         6598  
232 164   33     1955 my $role = shift;
  17         192  
233 164         3155 return grep { $_->does($role) } $self->values;
234             }
235            
236             =item C<< tree_attributes >>
237              
238             Returns the variables which are bound in this object.
239              
240             =cut
241              
242             my $self = shift;
243 294     294 1 421 return $self->variables;
244 294         396 }
245 294         518
  882         9924  
246             =item C<< apply_bindings( $binding ) >>
247              
248             Construct a new binding by replacing variables with their bound values from
249             C<< $binding >>.
250              
251             =cut
252              
253             my $self = shift;
254             my $class = ref($self);
255 2     2 1 4 my $bind = shift;
256 2         6 my %data;
257             foreach my $k ($self->variables) {
258             my $v = $self->value($k);
259             if ($v->does('Attean::API::TriplePattern')) {
260             my $replace = $v->apply_bindings($bind);
261             $data{ $k } = $replace;
262             } elsif ($v->does('Attean::API::Variable')) {
263             my $name = $v->value;
264             my $replace = $bind->value($name);
265             if (defined($replace) and blessed($replace)) {
266             $data{ $k } = $replace;
267 0     0 1 0 } else {
268 0         0 $data{ $k } = $v;
269 0         0 }
270 0         0 } else {
271 0         0 $data{ $k } = $v;
272 0         0 }
273 0 0       0 }
    0          
274 0         0 return $class->new( bindings => \%data );
275 0         0 }
276             }
277 0         0  
278 0         0 use Encode qw(encode);
279 0 0 0     0 use List::MoreUtils qw(zip);
280 0         0 use Scalar::Util qw(blessed);
281             use Attean::RDF;
282 0         0 use Attean::API::Query;
283              
284             use Moo::Role;
285 0         0  
286             with 'Attean::API::SPARQLSerializable';
287            
288 0         0 around BUILDARGS => sub {
289             my $orig = shift;
290             my $class = shift;
291             my @args = @_;
292             if (scalar(@args) == 0 or not(defined($_[0])) or blessed($args[0])) {
293 50     50   69653 my @names = $class->variables;
  50         140  
  50         2678  
294 50     50   340 foreach my $i (0 .. $#names) {
  50         131  
  50         280  
295 50     50   27988 my $k = $names[$i];
  50         140  
  50         1943  
296 50     50   311 my $v = $args[$i];
  50         108  
  50         496  
297 50     50   39082 unless (defined($v)) {
  50         108  
  50         925  
298             $args[$i] = Attean::RDF::variable($k);
299 50     50   245 }
  50         95  
  50         221  
300             }
301             my %args;
302             @args{ $class->variables } = @args;
303             return $class->$orig(%args);
304             } elsif (scalar(@args) == 2) {
305             if (defined($args[0]) and $args[0] eq 'bindings') {
306             return $class->$orig(%{ $args[1] });
307             }
308             }
309            
310             if (scalar(@_) % 2) {
311             Carp::cluck;
312             }
313             my %args = @_;
314             foreach my $k ($class->variables) {
315             if (not(exists $args{$k}) or not($args{$k})) {
316             $args{$k} = Attean::RDF::variable($k);
317             }
318             }
319            
320             return $class->$orig(%args);
321             };
322              
323             my $self = shift;
324             my $class = ref($self);
325             my $mapper = shift;
326             my %values;
327             foreach my $pos ($self->variables) {
328             my $value = $self->value($pos);
329             if ($value->does('Attean::API::Binding')) {
330             $values{$pos} = $value->apply_map($mapper);
331             } else {
332             $values{$pos} = $mapper->map($value);
333             }
334             }
335             return $class->new( %values );
336             }
337            
338             my $self = shift;
339 36     36 0 63 my $class = ref($self);
340 36         61 my $bind = shift;
341 36         52 my %data;
342 36         51 foreach my $k ($self->variables) {
343 36         74 my $v = $self->value($k);
344 135         1057 if ($v->does('Attean::API::Variable')) {
345 135 50       333 my $name = $v->value;
346 0         0 my $replace = $bind->value($name);
347             if (defined($replace) and blessed($replace)) {
348 135         2476 $data{ $k } = $replace;
349             } else {
350             $data{ $k } = $v;
351 36         965 }
352             }
353             }
354             return Attean::Result->new( bindings => \%data );
355 2     2 0 4 }
356 2         3  
357 2         4 my $self = shift;
358 2         3 my $type = ref($self);
359 2         4 my $role = $self->does('Attean::API::TriplePattern') ? 'Attean::API::TriplePattern' : 'Attean::API::QuadPattern';
360 7         48 my $iter = Attean::ListIterator->new( values => [$self], item_type => $role );
361 7 100       20 my $triples = $iter->canonical_set();
362 3         50 my ($t) = @$triples;
363 3         7 return $t;
364 3 50 33     16 }
365 3         9  
366             my $self = shift;
367 0         0 my $result = shift;
368            
369             my %bindings;
370             my @vars = $self->variables();
371 2         65 foreach my $pos (@vars) {
372             my $pp = $self->$pos();
373             if ($pp->does('Attean::API::Variable')) {
374             $bindings{ $pos } = $result->value($pp->value);
375 14     14 0 95 } elsif ($pp->does('Attean::API::TriplePattern')) {
376 14         23 my $sub_ground = $pp->ground($result);
377 14 100       41 $bindings{ $pos } = $sub_ground;
378 14         473 } else {
379 14         2940 $bindings{ $pos } = $pp;
380 14         22 }
381 14         85 }
382            
383             return scalar(@vars) == 3
384             ? Attean::Triple->new( %bindings )
385 85     85 0 114 : Attean::Quad->new( %bindings );
386 85         104 }
387            
388 85         124 my $self = shift;
389 85         180 my $quad = shift;
390 85         149 my %binding;
391 340         569 foreach my $pos ($self->variables) {
392 340 100       590 my $pp = $self->$pos();
    50          
393 200         2414 my $qp = $quad->$pos();
394             if ($pp->does('Attean::API::Variable')) {
395 0         0 if (my $already = $binding{ $pp->value }) {
396 0         0 return unless $already->equals($qp);
397             }
398 140         4485 $binding{ $pp->value } = $qp;
399             } elsif ($pp->does('Attean::API::TriplePattern')) {
400             return unless ($qp->does('Attean::API::Triple'));
401             my $sub_binding = $pp->unify($qp);
402 85 50       1675 return unless $sub_binding;
403             my $bkeys = Set::Scalar->new(keys %binding);
404             my $sbkeys = Set::Scalar->new($sub_binding->variables);
405             my $i = $bkeys->intersection($sbkeys);
406             for my $key ($i->elements) {
407             # variable bound in multiple places with different values
408 316     316 0 364 return unless ($binding{$key}->equals($sub_binding->value($key)));
409 316         364 }
410 316         398 my $mapping = {$sub_binding->mapping};
411 316         610 @binding{ keys %$mapping } = values %$mapping;
412 1221         2237 } else {
413 1221         1881 # bound position doesn't match
414 1221 100       2094 use Data::Dumper;
    50          
415 849 50       9719 if ($pp->does('Attean::API::QuadPattern')) {
416 0 0       0 Carp::cluck 'XXX unify: ' . Dumper($self);
417             }
418 849         1642
419             return unless ($pp->equals($qp));
420 0 0       0 }
421 0         0 }
422 0 0       0
423 0         0 # warn 'final mapping: ' . Dumper(\%binding);
424 0         0 return Attean::Result->new( bindings => \%binding );
425 0         0 }
426 0         0  
427             =item C<< parse ( $string ) >>
428 0 0       0  
429             Returns a triple or quad pattern object using the variables and/or terms
430 0         0 parsed from C<< $string >> in SPARQL syntax.
431 0         0  
432             =cut
433              
434 50     50   60759 my $self = shift;
  50         112  
  50         16464  
435 372 50       11920 my $class = ref($self) || $self;
436 0         0 my $string = shift;
437             my $bytes = encode('UTF-8', $string, Encode::FB_CROAK);
438             my $parser = Attean->get_parser('SPARQL')->new(@_);
439 372 100       6083 my @values = $parser->parse_nodes($bytes);
440             my @keys = $self->variables;
441            
442             my $f = scalar(@values);
443             my $e = scalar(@keys);
444 301         5508 unless ($e == $f) {
445             die "${class}->parse found wrong number of nodes (found $f but expecting $e)";
446             }
447             return $self->new(zip @keys, @values);
448             }
449             }
450              
451             use List::MoreUtils qw(any);
452             use Carp;
453              
454             use Moo::Role;
455 4     4 0 21097  
456 4   33     25 with 'Attean::API::TripleOrQuadPattern';
457 4         10  
458 4         38 my $self = shift;
459 4         435 if (any { $_->does('Attean::API::Variable') } $self->values) {
460 4         287 croak 'Use a Pattern class to construct when using variables';
461 4         35 }
462             }
463 4         9 }
464 4         7  
465 4 50       13 use List::MoreUtils qw(zip);
466 0         0 use Scalar::Util qw(blessed);
467              
468 4         391 use Moo::Role;
469            
470              
471             my $self = shift;
472             my $key = shift;
473 50     50   423 return $self->$key() if ($key =~ /^(subject|predicate|object)$/);
  50         126  
  50         285  
474 50     50   45577 die "Unrecognized binding name '$key'";
  50         113  
  50         2336  
475             }
476 50     50   271
  50         113  
  50         232  
477             my $self = shift;
478             my $graph = shift;
479             my @keys = Attean::API::Quad->variables;
480             my @values = ($self->values, $graph);
481 672     672 0 23290 return Attean::QuadPattern->new(zip @keys, @values);
482 672 100   2340   3241 }
  2340         34763  
483 2         65
484             my $self = shift;
485             unless ($self->is_ground) {
486             die "Not a ground triple: " . $self->as_string;
487             }
488             my @terms = map { $_->does('Attean::API::TriplePattern') ? $_->as_triple : $_ } $self->values;
489 50     50   20423 return Attean::Triple->new(@terms);
  50         145  
  50         280  
490 50     50   27469 }
  50         110  
  50         1876  
491            
492 50     50   297 my $self = shift;
  50         121  
  50         238  
493             return $self->apply_statement(@_);
494 2739     2739 1 7516 }
495              
496             my $self = shift;
497 4595     4595 1 5331 my @tokens;
498 4595         5112 foreach my $t ($self->values) {
499 4595 100       22614 if ($t->does('Attean::API::TriplePattern')) {
500 1         11 push(@tokens, AtteanX::SPARQL::Token->ltlt);
501             push(@tokens, $t->sparql_tokens->elements);
502             push(@tokens, AtteanX::SPARQL::Token->gtgt);
503             } else {
504 26     26 1 92 push(@tokens, $t->sparql_tokens->elements);
505 26         30 }
506 26         75 }
507 26         62 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
508 26         532 }
509            
510             requires 'subject';
511             requires 'predicate';
512 0     0 0 0 requires 'object';
513 0 0       0  
514 0         0 with 'Attean::API::TripleOrQuadPattern', 'Attean::API::Binding', 'Attean::API::TermOrVariableOrTriplePattern';
515             }
516 0 0       0  
  0         0  
517 0         0 use Scalar::Util qw(blessed);
518             use Moo::Role;
519            
520             if ($ENV{ATTEAN_TYPECHECK}) {
521 1     1 0 38 my %map = (
522 1         4 subject => 'Attean::API::BlankOrIRIOrTriple',
523             predicate => 'Attean::API::IRI',
524             object => 'Attean::API::TermOrTriple'
525             );
526 102     102 0 166 foreach my $method (keys %map) {
527 102         164 my $role = $map{$method};
528 102         259 around $method => sub {
529 306 100       1025 my $orig = shift;
530 1         21 my $self = shift;
531 1         52 my $class = ref($self);
532 1         8 my $term = $self->$orig(@_);
533             my $type = Type::Tiny::Role->new( role => $role );
534 305         6748 my $err = $type->validate($term);
535             if ($err) {
536             die "${class}'s $method failed conformance check for role $role";
537 102         1590 }
538             return $term;
539             };
540             }
541             }
542            
543             my $self = shift;
544             my $graph = shift;
545             return Attean::Quad->new($self->values, $graph);
546             }
547              
548 50     50   37620 my $self = shift;
  50         132  
  50         2067  
549 50     50   297 my @values = $self->values;
  50         113  
  50         216  
550             return join(' ', '<<', (map { $_->ntriples_string } @values), '>>');
551             }
552              
553             my ($a, $b) = @_;
554             return 1 unless blessed($b);
555             if (not $b->does('Attean::API::Triple')) {
556             # this is a type-error for equality testing, but special handling is needed in calling code for ORDER BY in which Triples sort last (after literals)
557             die "TypeError: cannot compare an RDF-star triple and a non-triple";
558             }
559            
560             foreach my $pos ($a->variables) {
561             my $at = $a->$pos();
562             my $bt = $b->$pos();
563             my $c = $at->compare($bt);
564            
565             # If they are equal, continue. otherwise check if either term is an IRI.
566             # This is because term equality is defined for IRIs, but < and > isn't.
567             next unless ($c);
568            
569             unless ($Attean::API::Binding::ALLOW_IRI_COMPARISON) {
570             for ($at, $bt) {
571             if ($_->does('Attean::API::IRI')) {
572             # Carp::cluck "TypeError comparison of IRI " . $at->ntriples_string . " <=> " . $bt->ntriples_string . "\n";
573             # last;
574             die "TypeError comparison of IRI" if ($_->does('Attean::API::IRI')); # comparison of IRIs is only defined for `ORDER BY`, not for general expressions
575 127     127 1 402 }
576 127         173 }
577 127         338 }
578              
579             if ($c) {
580             return $c;
581 0     0 0 0 }
582 0         0 }
583 0         0
  0         0  
584             return 0;
585             # return $a->ntriples_string cmp $b->ntriples_string;
586             }
587 0     0 0 0  
588 0 0       0 with 'Attean::API::TriplePattern', 'Attean::API::TripleOrQuad', 'Attean::API::Binding', 'Attean::API::TermOrVariableOrTriplePattern';
589 0 0       0 with 'Attean::API::BlankOrIRIOrTriple';
590             with 'Attean::API::TermOrTriple';
591 0         0 }
592              
593             use Scalar::Util qw(blessed);
594 0         0 use List::MoreUtils qw(zip);
595 0         0  
596 0         0 use Moo::Role;
597 0         0
598             my $self = shift;
599             my $key = shift;
600             return $self->$key() if ($key =~ /^(subject|predicate|object|graph)$/);
601 0 0       0 die "Unrecognized binding name '$key'";
602             }
603 0 0       0
604 0         0 my $self = shift;
605 0 0       0 unless ($self->is_ground) {
606             die "Not a ground quad: " . $self->as_string;
607             }
608 0 0       0 return Attean::Quad->new($self->values);
609             }
610            
611             my $self = shift;
612             return $self->apply_statement(@_);
613 0 0       0 }
614 0         0  
615             my $self = shift;
616             my @keys = Attean::API::Triple->variables;
617             my @values = $self->values;
618 0         0 @values = @values[0 .. scalar(@keys)-1];
619             return Attean::TriplePattern->new(zip @keys, @values);
620             }
621              
622             my $self = shift;
623             my @tokens;
624             push(@tokens, AtteanX::SPARQL::Token->keyword('GRAPH'));
625             push(@tokens, $self->graph->sparql_tokens->elements);
626             push(@tokens, AtteanX::SPARQL::Token->lbrace());
627             my @values = ($self->values)[0..2];
628 50     50   36162 foreach my $t (@values) {
  50         128  
  50         2316  
629 50     50   342 push(@tokens, $t->sparql_tokens->elements);
  50         142  
  50         258  
630             }
631 50     50   27809 push(@tokens, AtteanX::SPARQL::Token->rbrace());
  50         102  
  50         187  
632             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
633 3014     3014 1 7818 }
634            
635 5346     5346 1 7970 requires 'subject';
636 5346         5836 requires 'predicate';
637 5346 100       20533 requires 'object';
638 1         10 requires 'graph';
639              
640             with 'Attean::API::TripleOrQuadPattern', 'Attean::API::Binding';
641             }
642 0     0 0 0  
643 0 0       0 use Moo::Role;
644 0         0
645             if ($ENV{ATTEAN_TYPECHECK}) {
646 0         0 my $type = Type::Tiny::Role->new( role => 'Attean::API::BlankOrIRI' );
647             around 'graph' => sub {
648             my $orig = shift;
649             my $self = shift;
650 1     1 0 39 my $class = ref($self);
651 1         4 my $term = $self->$orig(@_);
652             my $err = $type->validate($term);
653             die "${class}'s graph failed conformance check for role Attean::API::BlankOrIRI: $term" if ($err);
654             return $term;
655 0     0 0 0 };
656 0         0 }
657 0         0  
658 0         0 my $self = shift;
659 0         0 my @values = $self->values;
660             return Attean::Triple->new(@values[0..2]);
661             }
662              
663 2     2 0 87 with 'Attean::API::QuadPattern';
664 2         3 with 'Attean::API::TripleOrQuad', 'Attean::API::TripleOrQuadPattern', 'Attean::API::Triple';
665 2         12 }
666 2         103  
667 2         11  
668 2         104 use Scalar::Util qw(refaddr);
669 2         6 use Types::Standard qw(HashRef);
670 6         23  
671             use Moo::Role;
672 2         11
673 2         113 has 'eval_stash' => (is => 'rw', isa => HashRef);
674              
675             my $self = shift;
676             if (not $self->eval_stash) {
677             $self->eval_stash({});
678             }
679             }
680            
681             my $self = shift;
682             my $class = ref($self);
683             my $rowb = shift;
684            
685 50     50   40837 my %keysa;
  50         118  
  50         228  
686             my @keysa = $self->variables;
687             @keysa{ @keysa } = (1) x scalar(@keysa);
688             my @shared = grep { exists $keysa{ $_ } } ($rowb->variables);
689             return @shared;
690             }
691            
692             my $self = shift;
693             my $class = ref($self);
694             my $rowb = shift;
695             my @shared = $self->shared_domain($rowb);
696             foreach my $key (@shared) {
697             my $val_a = $self->value($key);
698             my $val_b = $rowb->value($key);
699             my $equal = (refaddr($val_a) == refaddr($val_b)) || $val_a->equals( $val_b );
700             unless ($equal) {
701 15     15 0 25 return;
702 15         30 }
703 15         287 }
704            
705             my $row = { (map { $_ => $self->value($_) } grep { defined($self->value($_)) } $self->variables), (map { $_ => $rowb->value($_) } grep { defined($rowb->value($_)) } $rowb->variables) };
706             my $joined = Attean::Result->new( bindings => $row );
707             return $joined;
708             }
709            
710             =item C<< project( @keys ) >>
711              
712 50     50   25257 Returns a new L<Attean::Result> binding which contains variable-value mappings
  50         111  
  50         2416  
713 50     50   382 from the invocant for every variable name in C<< @keys >>.
  50         117  
  50         440  
714              
715 50     50   27976 =cut
  50         110  
  50         202  
716              
717             my $self = shift;
718             my @vars = @_;
719             my %bindings;
720 586     586 0 127805 foreach my $v (@vars) {
721 586 50       8013 my $term = $self->value($v);
722 586         10057 $bindings{ $v } = $term if ($term);
723             }
724             return Attean::Result->new( bindings => \%bindings );
725             }
726              
727 35     35 0 50 my $self = shift;
728 35         51 my %vars = map { $_ => 1 } @_;
729 35         44 my %bindings;
730             foreach my $v ($self->variables) {
731 35         40 unless ($vars{$v}) {
732 35         86 my $term = $self->value($v);
733 35         83 $bindings{ $v } = $term;
734 35         73 }
  55         121  
735 35         88 }
736             return Attean::Result->new( bindings => \%bindings );
737             }
738            
739 33     33 1 227 my $self = shift;
740 33         54 my $class = ref($self);
741 33         51 my $mapper = shift;
742 33         66 my %values;
743 33         70 foreach my $var ($self->variables) {
744 9         33 my $value = $self->value($var);
745 9         23 if ($value->does('Attean::API::Binding')) {
746 9   100     53 $values{$var} = $value->apply_map($mapper);
747 9 100       78 } else {
748 3         19 my $term = $mapper->map($value);
749             if ($term) {
750             $values{$var} = $term;
751             }
752 30         63 }
  39         74  
  39         87  
  47         84  
  47         75  
753 30         568 }
754 30         659 return $class->new( bindings => \%values );
755             }
756              
757             with 'Attean::API::Binding', 'Attean::API::ResultOrTerm';
758             }
759              
760             1;
761              
762              
763             =back
764              
765 86     86 0 158 =head1 BUGS
766 86         168  
767 86         103 Please report any bugs or feature requests to through the GitHub web interface
768 86         119 at L<https://github.com/kasei/attean/issues>.
769 190         354  
770 190 50       443 =head1 SEE ALSO
771              
772 86         1910  
773              
774             =head1 AUTHOR
775              
776 61     61 0 73 Gregory Todd Williams C<< <gwilliams@cpan.org> >>
777 61         97  
  4         13  
778 61         76 =head1 COPYRIGHT
779 61         128  
780 161 100       294 Copyright (c) 2014--2022 Gregory Todd Williams.
781 158         248 This program is free software; you can redistribute it and/or modify it under
782 158         263 the same terms as Perl itself.
783              
784             =cut