File Coverage

blib/lib/Attean/API/Query.pm
Criterion Covered Total %
statement 345 383 90.0
branch 81 96 84.3
condition 30 31 96.7
subroutine 56 60 93.3
pod 0 28 0.0
total 512 598 85.6


line stmt bran cond sub pod time code
1 50     50   659 use v5.14;
  50         165  
2 50     50   240 use warnings;
  50         85  
  50         2228  
3              
4             =head1 NAME
5              
6             Attean::API::Query - Utility package defining query-related roles
7              
8             =head1 VERSION
9              
10             This document describes Attean::API::Query version 0.033
11              
12             =head1 SYNOPSIS
13              
14             use v5.14;
15             use Attean;
16              
17             =head1 DESCRIPTION
18              
19             This is a utility package for defining query-related roles:
20              
21             =over 4
22              
23             =item * L<Attean::API::DirectedAcyclicGraph>
24              
25             =cut
26              
27             use Scalar::Util qw(refaddr);
28 50     50   237 use Types::Standard qw(ArrayRef ConsumerOf);
  50         85  
  50         1757  
29 50     50   230  
  50         82  
  50         250  
30             use Moo::Role;
31 50     50   23284  
  50         107  
  50         336  
32             # =item C<< children >>
33             #
34             # An ARRAY reference of L<Attean::API::DirectedAcyclicGraph> objects.
35             #
36             # =back
37             #
38             # =cut
39              
40             has 'children' => (
41             is => 'ro',
42             isa => ArrayRef[ConsumerOf['Attean::API::DirectedAcyclicGraph']],
43             default => sub { [] },
44             );
45            
46             # =item C<< is_leaf >>
47             #
48             # Returns true if the referent has zero C<< children >>, false otherwise.
49             #
50             # =cut
51              
52             my $self = shift;
53             return not(scalar(@{ $self->children }));
54 3     3 0 1023 }
55 3         4
  3         17  
56             # =item C<< walk( prefix => \&pre_cb, postfix => \&pre_cb ) >>
57             #
58             # Walks the graph rooted at the referent, calling C<< &pre_cb >> (if supplied)
59             # before descending, and C<< &post_cb >> (if supplied) after descending. The
60             # callback functions are passed the current graph walk node as the single
61             # argument.
62             #
63             # =cut
64              
65             my $self = shift;
66             my %args = @_;
67             my $level = $args{ level } // 0;
68 150     150 0 226 my $parent = $args{ parent };
69 150         350 if (my $cb = $args{ prefix }) {
70 150   100     408 $cb->( $self, $level, $parent );
71 150         196 }
72 150 50       257 foreach my $c (@{ $self->children }) {
73 150         241 $c->walk( %args, level => (1+$level), parent => $self );
74             }
75 150         927 if (my $cb = $args{ postfix }) {
  150         341  
76 99         329 $cb->( $self, $level, $parent );
77             }
78 150 100       386 }
79 2         4
80             # =item C<< has_only_subtree_types( @classes ) >>
81             #
82             # Returns true if the invocant and all of its sub-trees are instances of only
83             # the listed classes, false otherwise.
84             #
85             # =cut
86              
87             my $self = shift;
88             my @types = @_;
89             my %types = map { $_ => 1 } @types;
90             return 0 unless (exists $types{ ref($self) });
91 2     2 0 8
92 2         14 my %classes;
93 2         4 $self->walk( prefix => sub {
  2         6  
94 2 100       9 my $plan = shift;
95             $classes{ref($plan)}++;
96 1         2 });
97             foreach my $type (@types) {
98 1     1   2 delete $classes{$type};
99 1         2 }
100 1         8 my @keys = keys %classes;
101 1         3 return (scalar(@keys) == 0) ? 1 : 0;
102 1         3 }
103              
104 1         3 # =item C<< cover( prefix => \&pre_cb, postfix => \&pre_cb ) >>
105 1 50       5 #
106             # Similar to C<< walk >>, walks the graph rooted at the referent, calling
107             # C<< &pre_cb >> (if supplied) before descending, and C<< &post_cb >> (if
108             # supplied) after descending. However, unlike C<< walk >>, each node in the graph
109             # is visited only once.
110             #
111             # =cut
112              
113             my $self = shift;
114             return $self->_cover({}, @_);
115             }
116            
117             my $self = shift;
118 1     1 0 2 my $seen = shift;
119 1         4 my %cb = @_;
120             return if ($seen->{refaddr($self)}++);
121             if (my $cb = $cb{ prefix }) {
122             $cb->( $self );
123 3     3   3 }
124 3         4 foreach my $c (@{ $self->children }) {
125 3         6 $c->_cover( $seen, %cb );
126 3 100       11 }
127 2 50       4 if (my $cb = $cb{ postfix }) {
128 2         3 $cb->( $self );
129             }
130 2         5 }
  2         6  
131 2         7  
132             my $self = shift;
133 2 50       6 my @types = @_;
134 0         0 my @p;
135             $self->walk( prefix => sub {
136             my $a = shift;
137             foreach my $t (@types) {
138             push(@p, $a) if ($a->isa($t) or $a->does($t));
139 11     11 0 1631 }
140 11         25 });
141 11         17 return @p;
142             }
143 43     43   45 }
144 43         49  
145 43 100 66     191 use AtteanX::SPARQL::Constants;
146             use AtteanX::SPARQL::Token;
147 11         73 use Encode qw(decode_utf8);
148 11         54 use Attean::API::Iterator;
149             use Attean::API::Serializer;
150             use AtteanX::Serializer::SPARQL;
151              
152             use Moo::Role;
153 50     50   36438  
  50         98  
  50         5923  
154 50     50   292 requires 'sparql_tokens';
  50         87  
  50         916  
155 50     50   22358
  50         473317  
  50         3284  
156 50     50   18719 my $self = shift;
  50         145  
  50         1666  
157 50     50   21504 my $s = AtteanX::Serializer::SPARQL->new();
  50         181  
  50         2663  
158 50     50   21681 my $i = $self->sparql_tokens;
  50         130  
  50         1412  
159             my $bytes = $s->serialize_iter_to_bytes($i);
160 50     50   377 return decode_utf8($bytes);
  50         90  
  50         191  
161             }
162            
163             my $self = shift;
164             if ($self->does('Attean::API::SPARQLQuerySerializable')) {
165 54     54 0 14422 my $l = AtteanX::SPARQL::Token->lbrace;
166 54         1193 my $r = AtteanX::SPARQL::Token->rbrace;
167 54         7814 my @tokens;
168 54         1882 push(@tokens, $l);
169 54         614 push(@tokens, $self->sparql_tokens->elements);
170             push(@tokens, $r);
171             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
172             } else {
173 29     29 0 47 return $self->sparql_tokens;
174 29 50       72 }
175 0         0 }
176 0         0
177 0         0 my $self = shift;
178 0         0 my $dataset = shift;
179 0         0 my @default = @{ $dataset->{ default } || [] };
180 0         0 my @named = @{ $dataset->{ named } || [] };
181 0         0 my $has_dataset = (scalar(@default) + scalar(@named));
182             my @tokens;
183 29         549 if ($has_dataset) {
184             my $from = AtteanX::SPARQL::Token->keyword('FROM');
185             my $named = AtteanX::SPARQL::Token->keyword('NAMED');
186             foreach my $i (sort { $a->as_string cmp $b->as_string } @default) {
187             push(@tokens, $from);
188 33     33 0 49 push(@tokens, $i->sparql_tokens->elements);
189 33         42 }
190 33 100       39 foreach my $i (sort { $a->as_string cmp $b->as_string } @named) {
  33         116  
191 33 100       49 push(@tokens, $from);
  33         93  
192 33         56 push(@tokens, $named);
193 33         37 push(@tokens, $i->sparql_tokens->elements);
194 33 100       58 }
195 4         10 }
196 4         139 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
197 4         132 }
  0         0  
198 4         8
199 4         15 my $self = shift;
200             my %args = @_;
201 4         8 my $dataset = $args{dataset} || {};
  0         0  
202 4         7
203 4         5 my $as = AtteanX::SPARQL::Token->keyword('AS');
204 4         10 my $lparen = AtteanX::SPARQL::Token->lparen;
205             my $rparen = AtteanX::SPARQL::Token->rparen;
206            
207 33         475 my $algebra = $self;
208            
209             my %modifiers;
210             my $form = 'SELECT';
211 33     33 0 45 if ($algebra->isa('Attean::Algebra::Ask')) {
212 33         46 $form = 'ASK';
213 33   100     117 ($algebra) = @{ $algebra->children };
214             } elsif ($algebra->isa('Attean::Algebra::Describe')) {
215 33         132 $form = 'DESCRIBE';
216 33         1399 $modifiers{describe} = $algebra->terms;
217 33         1183 ($algebra) = @{ $algebra->children };
218             } elsif ($algebra->isa('Attean::Algebra::Construct')) {
219 33         1090 $form = 'CONSTRUCT';
220             $modifiers{construct} = $algebra->triples;
221 33         40 ($algebra) = @{ $algebra->children };
222 33         43 }
223 33 100       361
    100          
    100          
224 3         5 unless ($form eq 'CONSTRUCT' or $form eq 'DESCRIBE') {
225 3         4 while ($algebra->isa('Attean::Algebra::Extend') or $algebra->isa('Attean::Algebra::Group') or $algebra->isa('Attean::Algebra::OrderBy') or $algebra->isa('Attean::Algebra::Distinct') or $algebra->isa('Attean::Algebra::Reduced') or $algebra->isa('Attean::Algebra::Slice') or $algebra->isa('Attean::Algebra::Project')) {
  3         10  
226             # TODO: Handle HAVING
227 3         5 # TODO: Error if Slice appears before distinct/reduced
228 3         11 if ($algebra->isa('Attean::Algebra::Distinct')) {
229 3         4 $modifiers{ distinct } = 1;
  3         9  
230             } elsif ($algebra->isa('Attean::Algebra::Reduced')) {
231 3         6 $modifiers{ reduced } = 1;
232 3         10 } elsif ($algebra->isa('Attean::Algebra::Slice')) {
233 3         4 if ($algebra->limit >= 0) {
  3         9  
234             $modifiers{ limit } = $algebra->limit;
235             }
236 33 100 100     171 if ($algebra->offset > 0) {
237 27   100     502 $modifiers{ offset } = $algebra->offset;
      100        
      100        
      100        
      100        
      100        
238             }
239             } elsif ($algebra->isa('Attean::Algebra::OrderBy')) {
240 30 100       227 $modifiers{order} = $algebra->comparators;
    100          
    100          
    100          
    100          
    100          
    50          
241 4         7 } elsif ($algebra->isa('Attean::Algebra::Extend')) {
242             my $v = $algebra->variable;
243 2         4 my $name = $v->value;
244             my $expr = $algebra->expression;
245 6 50       15 my @tokens;
246 6         12 push(@tokens, $lparen);
247             push(@tokens, $expr->sparql_tokens->elements);
248 6 100       12 push(@tokens, $as);
249 4         7 push(@tokens, $v->sparql_tokens->elements);
250             push(@tokens, $rparen);
251             $modifiers{project_expression_tokens}{$name} = \@tokens;
252 1         4 } elsif ($algebra->isa('Attean::Algebra::Project')) {
253             my $vars = $algebra->variables;
254 4         6 my ($child) = @{ $algebra->children };
255 4         11 my @vars = sort(map { $_->value } @$vars);
256 4         6 my @subvars = sort($child->in_scope_variables);
257 4         6 if (scalar(@vars) == scalar(@subvars) and join('.', @vars) eq join('.', @subvars)) {
258 4         5 # this is a SELECT * query
259 4         11 } else {
260 4         33 foreach my $v (@$vars) {
261 4         10 my $name = $v->value;
262 4         9 unless ($modifiers{project_variables}{$name}++) {
263 4         12 push(@{ $modifiers{project_variables_order} }, $name);
264             }
265 12         37 }
266 12         27 }
  12         42  
267 12         25 } elsif ($algebra->isa('Attean::Algebra::Group')) {
  22         72  
268 12         47 my $aggs = $algebra->aggregates;
269 12 100 100     639 my $groups = $algebra->groupby;
270             foreach my $agg (@$aggs) {
271             my $v = $agg->variable;
272 7         176 my $name = $v->value;
273 13         24 my @tokens;
274 13 50       29 push(@tokens, $lparen);
275 13         13 push(@tokens, $agg->sparql_tokens->elements);
  13         33  
276             push(@tokens, $as);
277             push(@tokens, $v->sparql_tokens->elements);
278             push(@tokens, $rparen);
279             unless ($modifiers{project_variables}{$name}++) {
280 1         3 push(@{ $modifiers{project_variables_order} }, $name);
281 1         3 }
282 1         2 $modifiers{project_expression_tokens}{$name} = \@tokens;
283 1         3 }
284 1         3 foreach my $group (@$groups) {
285 1         2 push(@{ $modifiers{groups} }, $group->sparql_tokens->elements);
286 1         1 }
287 1         4 } else {
288 1         4 die "Unexpected pattern type encountered in query_tokens: " . ref($algebra);
289 1         2 }
290 1         3 ($algebra) = @{ $algebra->children };
291 1 50       4 }
292 1         1 }
  1         3  
293            
294 1         3 my @tokens;
295              
296 1         2 my $where = AtteanX::SPARQL::Token->keyword('WHERE');
297 1         2 my $lbrace = AtteanX::SPARQL::Token->lbrace;
  1         3  
298             my $rbrace = AtteanX::SPARQL::Token->rbrace;
299              
300 0         0  
301             if ($form eq 'SELECT') {
302 30         42 push(@tokens, AtteanX::SPARQL::Token->keyword('SELECT'));
  30         393  
303             if ($modifiers{distinct}) {
304             push(@tokens, AtteanX::SPARQL::Token->keyword('DISTINCT'));
305             } elsif ($modifiers{reduced}) {
306 33         44 push(@tokens, AtteanX::SPARQL::Token->keyword('REDUCED'));
307             }
308 33         79
309 33         1248 if (my $p = $modifiers{project_variables_order}) {
310 33         1128 foreach my $name (@$p) {
311             if (my $etokens = $modifiers{project_expression_tokens}{$name}) {
312             push(@tokens, @$etokens);
313 33 100       1098 } else {
    100          
    100          
    50          
314 24         56 my $v = Attean::Variable->new( value => $name );
315 24 100       834 push(@tokens, $v->sparql_tokens->elements);
    100          
316 4         9 }
317             }
318 2         5 } else {
319             push(@tokens, AtteanX::SPARQL::Token->star);
320             }
321 24 100       241
322 8         13 push(@tokens, $self->dataset_tokens($dataset)->elements);
323 14 100       31
324 5         13 push(@tokens, $where);
325             if ($algebra->isa('Attean::Algebra::Join')) {
326 9         122 # don't emit extraneous braces at the top-level
327 9         336 push(@tokens, $algebra->sparql_tokens->elements);
328             } else {
329             push(@tokens, $lbrace);
330             push(@tokens, $algebra->sparql_tokens->elements);
331 16         43 push(@tokens, $rbrace);
332             }
333             if (my $groups = $modifiers{groups}) {
334 24         566 push(@tokens, AtteanX::SPARQL::Token->keyword('GROUP'));
335             push(@tokens, AtteanX::SPARQL::Token->keyword('BY'));
336 24         64 push(@tokens, @$groups);
337 24 100       114 }
338             if (my $expr = $modifiers{having}) {
339 1         4 push(@tokens, AtteanX::SPARQL::Token->keyword('HAVING'));
340             push(@tokens, $expr->sparql_tokens->elements);
341 23         45 }
342 23         72 if (my $comps = $modifiers{order}) {
343 23         57 push(@tokens, AtteanX::SPARQL::Token->keyword('ORDER'));
344             push(@tokens, AtteanX::SPARQL::Token->keyword('BY'));
345 24 100       54 foreach my $c (@$comps) {
346 1         4 push(@tokens, $c->sparql_tokens->elements);
347 1         46 }
348 1         32 }
349             if (exists $modifiers{limit}) {
350 24 50       53 push(@tokens, AtteanX::SPARQL::Token->keyword('LIMIT'));
351 0         0 push(@tokens, AtteanX::SPARQL::Token->integer($modifiers{limit}));
352 0         0 }
353             if (exists $modifiers{offset}) {
354 24 100       50 push(@tokens, AtteanX::SPARQL::Token->keyword('OFFSET'));
355 1         4 push(@tokens, AtteanX::SPARQL::Token->integer($modifiers{offset}));
356 1         40 }
357 1         35 } elsif ($form eq 'DESCRIBE') {
358 1         3 push(@tokens, AtteanX::SPARQL::Token->keyword('DESCRIBE'));
359             foreach my $t (@{ $modifiers{describe} }) {
360             push(@tokens, $t->sparql_tokens->elements);
361 24 100       49 }
362 6         17 push(@tokens, $self->dataset_tokens($dataset)->elements);
363 6         253 push(@tokens, $where);
364             push(@tokens, $lbrace);
365 24 100       265 push(@tokens, $algebra->sparql_tokens->elements);
366 4         10 push(@tokens, $rbrace);
367 4         162 } elsif ($form eq 'CONSTRUCT') {
368             push(@tokens, AtteanX::SPARQL::Token->keyword('CONSTRUCT'));
369             push(@tokens, $lbrace);
370 3         9 foreach my $t (@{ $modifiers{construct} }) {
371 3         99 push(@tokens, $t->sparql_tokens->elements);
  3         9  
372 5         17 push(@tokens, AtteanX::SPARQL::Token->dot);
373             }
374 3         12 push(@tokens, $rbrace);
375 3         7 push(@tokens, $self->dataset_tokens($dataset)->elements);
376 3         6 push(@tokens, $where);
377 3         22 push(@tokens, $lbrace);
378 3         8 push(@tokens, $algebra->sparql_tokens->elements);
379             push(@tokens, $rbrace);
380 3         9 } elsif ($form eq 'ASK') {
381 3         100 push(@tokens, AtteanX::SPARQL::Token->keyword('ASK'));
382 3         5 push(@tokens, $self->dataset_tokens($dataset)->elements);
  3         6  
383 3         11 push(@tokens, $lbrace);
384 3         14 push(@tokens, $algebra->sparql_tokens->elements);
385             push(@tokens, $rbrace);
386 3         113 } else {
387 3         9 die "Unexpected query for '$form' in query_tokens";
388 3         8 }
389 3         5 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
390 3         9 }
391 3         8 }
392              
393 3         8 use Moo::Role;
394 3         105 use namespace::clean;
395 3         9 with 'Attean::API::SPARQLSerializable';
396 3         10  
397 3         9 my $self = shift;
398             return $self->query_tokens;
399 0         0 }
400             }
401 33         563  
402             =item * L<Attean::API::Algebra>
403              
404             =cut
405              
406 50     50   92059 use Moo::Role;
  50         96  
  50         236  
407 50     50   16398 use Types::Standard qw(ArrayRef ConsumerOf);
  50         112  
  50         285  
408            
409             with 'Attean::API::SPARQLSerializable';
410             has 'hints' => (is => 'rw', isa => ArrayRef[ArrayRef[ConsumerOf['Attean::API::Term']]], default => sub { [] });
411 26     26 0 82
412 26         59 requires 'as_sparql';
413             requires 'in_scope_variables'; # variables that will be in-scope after this operation is evaluated
414            
415             my $self = shift;
416             return unless (scalar(@{ $self->children }) == 1);
417             return $self->children->[0];
418             }
419            
420             my $self = shift;
421 50     50   27005 return "$self";
  50         107  
  50         172  
422 50     50   15788 }
  50         132  
  50         329  
423            
424             my $self = shift;
425             my $string = '';
426             $self->walk( prefix => sub {
427             my $a = shift;
428             my $level = shift;
429             my $parent = shift;
430             my $indent = ' ' x $level;
431 2     2 0 5 $string .= "-$indent " . $a->algebra_as_string($level) . "\n";
432 2 100       2 });
  2         11  
433 1         5 return $string;
434             }
435            
436             my $self = shift;
437 0     0 0 0 my %blanks;
438 0         0 $self->walk( prefix => sub {
439             my $a = shift;
440             if ($a->isa('Attean::Algebra::BGP')) {
441             my @triples = @{ $a->triples };
442 20     20 0 2025 my @nodes = grep { $_->does('Attean::API::Blank') } map { $_->values } @triples;
443 20         35 foreach my $b (@nodes) {
444             $blanks{ $b->value } = $b;
445 74     74   81 }
446 74         71 } elsif ($a->isa('Attean::Algebra::Path')) {
447 74         70 my @nodes = grep { $_->does('Attean::API::Blank') } ($a->subject, $a->object);
448 74         112 foreach my $b (@nodes) {
449 74         240 $blanks{ $b->value } = $b;
450 20         140 }
451 20         140 }
452             });
453             return values %blanks;
454             }
455 4     4 0 5
456 4         4 if ($ENV{ATTEAN_TYPECHECK}) {
457             around 'BUILD' => sub {
458 4     4   6 my $orig = shift;
459 4 50       38 my $self = shift;
    50          
460 0         0 $self->$orig(@_);
  0         0  
461 0         0 my $name = ref($self);
  0         0  
  0         0  
462 0         0 $name =~ s/^.*://;
463 0         0 if ($self->can('arity')) {
464             my $arity = $self->arity;
465             my $children = $self->children;
466 0         0 my $size = scalar(@$children);
  0         0  
467 0         0 unless ($size == $arity) {
468 0         0 Carp::confess "${name} algebra construction with bad number of children (expected $arity, but got $size)";
469             }
470             }
471 4         23 }
472 4         27 }
473             }
474              
475       502 0   =item * L<Attean::API::QueryTree>
476              
477             =cut
478              
479             use Moo::Role;
480             with 'Attean::API::DirectedAcyclicGraph';
481             }
482              
483             =item * L<Attean::API::NullaryQueryTree>
484              
485             =cut
486              
487             use Moo::Role;
488             with 'Attean::API::QueryTree';
489             }
490              
491             =item * L<Attean::API::UnaryQueryTree>
492              
493             =cut
494              
495             use Moo::Role;
496             with 'Attean::API::QueryTree';
497            
498             my $self = shift;
499             return $self->children->[0];
500 50     50   59203 }
  50         106  
  50         259  
501             }
502              
503             =item * L<Attean::API::BinaryQueryTree>
504              
505             =cut
506              
507             use Moo::Role;
508             with 'Attean::API::QueryTree';
509 50     50   16592 }
  50         108  
  50         183  
510 1     1 0 4  
511             =item * L<Attean::API::PropertyPath>
512              
513             =cut
514              
515             use Moo::Role;
516             with 'Attean::API::QueryTree';
517             requires 'as_string';
518             requires 'as_sparql';
519 50     50   16618 }
  50         117  
  50         227  
520 2     2 0 332  
521             =item * L<Attean::API::UnaryPropertyPath>
522              
523             =cut
524 36     36 0 11018  
525 36         267 use Types::Standard qw(ConsumerOf);
526              
527             use Moo::Role;
528              
529             # has 'path' => (is => 'ro', isa => ConsumerOf['Attean::API::PropertyPath'], required => 1);
530             my $self = shift;
531             my ($path) = @{ $self->children };
532             my $pstr = $path->as_string;
533             if ($path->does('Attean::API::UnaryPropertyPath')) {
534 50     50   18075 $pstr = "($pstr)";
  50         94  
  50         194  
535 0     0 0 0 }
536             my $str = sprintf("%s%s%s", $self->prefix_name, $pstr, $self->postfix_name);
537             return $str;
538             }
539             my $self = shift;
540             return "Property Path " . $self->prefix_name . $self->postfix_name;
541             }
542             with 'Attean::API::PropertyPath', 'Attean::API::UnaryQueryTree';
543             }
544 50     50   16775  
  50         106  
  50         188  
545             =item * L<Attean::API::NaryPropertyPath>
546              
547             =cut
548              
549             use Types::Standard qw(ArrayRef ConsumerOf);
550              
551             use Moo::Role;
552              
553             # has 'children' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::PropertyPath']], required => 1);
554             requires 'separator';
555 50     50   18195 my $self = shift;
  50         98  
  50         285  
556             my @children = @{ $self->children };
557 50     50   21977 if (scalar(@children) == 1) {
  50         90  
  50         175  
558             return $children[0]->as_string;
559 0     0 0 0 } else {
560             return sprintf("(%s)", join($self->separator, map { $_->as_string } @children));
561 3     3 0 12 }
562 4     4 0 11 }
563             my $self = shift;
564 5     5 0 2951 return "Property Path " . $self->separator;
565 5         7 }
  5         11  
566 5         13 with 'Attean::API::PropertyPath';
567 5 100       55 }
568 1         12  
569             =item * L<Attean::API::UnionScopeVariables>
570 5         80  
571 5         16 =cut
572              
573             use Moo::Role;
574 2     2 0 3 my $self = shift;
575 2         7 my $set = Set::Scalar->new();
576             foreach my $c (@{ $self->children }) {
577             $set->insert( $c->in_scope_variables );
578             }
579             return $set->elements;
580             }
581             }
582              
583             =item * L<Attean::API::IntersectionScopeVariables>
584              
585 50     50   23857 =cut
  50         96  
  50         224  
586              
587 50     50   24669 use Moo::Role;
  50         92  
  50         190  
588             my $self = shift;
589             my @c = @{ $self->children };
590             return unless scalar(@c);
591             my $set = Set::Scalar->new(shift(@c)->in_scope_variables);
592 7     7 0 4661 foreach my $c (@c) {
593 7         10 my $rhs = Set::Scalar->new($c->in_scope_variables);
  7         16  
594 7 100       17 $set = $set->intersection($rhs);
595 2         7 }
596             return $set->elements;
597 5         16 }
  10         39  
598             }
599              
600             1;
601 2     2 0 3  
602 2         6 =back
603              
604             =head1 BUGS
605              
606             Please report any bugs or feature requests to through the GitHub web interface
607             at L<https://github.com/kasei/attean/issues>.
608              
609             =head1 SEE ALSO
610              
611              
612 50     50   22761  
  50         97  
  50         190  
613             =head1 AUTHOR
614 21     21 0 76  
615 21         119 Gregory Todd Williams C<< <gwilliams@cpan.org> >>
616 21         1203  
  21         59  
617 28         727 =head1 COPYRIGHT
618              
619 21         2201 Copyright (c) 2014--2022 Gregory Todd Williams.
620             This program is free software; you can redistribute it and/or modify it under
621             the same terms as Perl itself.
622              
623             =cut