File Coverage

blib/lib/GraphQL/Type/List.pm
Criterion Covered Total %
statement 144 215 66.9
branch 46 150 30.6
condition 3 3 100.0
subroutine 25 25 100.0
pod 5 5 100.0
total 223 398 56.0


line stmt bran cond sub pod time code
1             package GraphQL::Type::List;
2              
3 18     18   374 use 5.014;
  18         71  
4 18     18   93 use strict;
  18         38  
  18         348  
5 18     18   93 use warnings;
  18         35  
  18         396  
6 18     18   90 use Moo;
  18         38  
  18         107  
7 18     18   6301 use Types::Standard -all;
  18         61  
  18         156  
8 18     18   840301 use GraphQL::Type::Library -all;
  18         51  
  18         155  
9 18     18   252590 use GraphQL::MaybeTypeCheck;
  18         45  
  18         152  
10 18     18   983 use GraphQL::Debug qw(_debug);
  18         42  
  18         1939  
11             extends qw(GraphQL::Type);
12             with qw(GraphQL::Role::Nullable);
13              
14             # A-ha
15             my @TAKE_ON_ME = qw(
16             GraphQL::Role::Input
17             GraphQL::Role::Output
18             );
19              
20             our $VERSION = '0.02';
21 18     18   121 use constant DEBUG => $ENV{GRAPHQL_DEBUG};
  18         38  
  18         4049  
22              
23             =head1 NAME
24              
25             GraphQL::Type::List - GraphQL type that is a list of another type
26              
27             =head1 SYNOPSIS
28              
29             use GraphQL::Type::List;
30             my $type = GraphQL::Type::List->new(of => $other_type);
31              
32             =head1 DESCRIPTION
33              
34             Type that is a wrapper for the type it is a list of. If the wrapped type
35             has any of these roles, it will assume them: L<GraphQL::Role::Input>,
36             L<GraphQL::Role::Output>. It is always L<GraphQL::Role::Nullable>.
37              
38             =head1 ATTRIBUTES
39              
40             =head2 of
41              
42             GraphQL type object of which this is a list.
43              
44             =cut
45              
46             has of => (
47             is => 'ro',
48             isa => InstanceOf['GraphQL::Type'],
49             required => 1,
50             handles => [ qw(name) ],
51             );
52              
53             =head1 METHODS
54              
55             =head2 BUILD
56              
57             L<Moo> method that applies the relevant roles.
58              
59             =cut
60              
61             sub BUILD {
62 9     124 1 660 my ($self, $args) = @_;
63 9         219 my $of = $self->of;
64 180         476 Role::Tiny->apply_roles_to_object($self, grep $of->DOES($_), @TAKE_ON_ME);
65             }
66              
67             =head2 to_string
68              
69             Part of serialisation.
70              
71             =cut
72              
73             has to_string => (is => 'lazy', isa => Str, init_arg => undef, builder => sub {
74 180     9   429 my ($self) = @_;
75 180         302 '[' . $self->of->to_string . ']';
76             });
77              
78             =head2 is_valid
79              
80             True if given Perl array-ref is a valid value for this type.
81              
82             =cut
83              
84 3 50   3 1 334 method is_valid(Any $item) :ReturnType(Bool) {
  3 50       10  
  3 50       6  
  3         8  
  3         11  
  3         28  
85 3 50       13 return 1 if !defined $item;
86 3         8 my $of = $self->of;
87 3 50       5 return if grep !$of->is_valid($_), @{ $self->uplift($item) };
  3         10  
88 3         35 1;
89 18     18   3853 }
  18         47  
  18         160  
90              
91             =head2 uplift
92              
93             Turn given Perl entity into valid value for this type if possible.
94             Mainly to promote single value into a list if type dictates.
95              
96             =cut
97              
98             # This is a crime against God. graphql-js does it however.
99 38 50   38 1 112 method uplift(Any $item) :ReturnType(Any) {
  38 50       98  
  38 50       99  
  38         91  
  38         95  
  38         271  
100 38 100 100     259 return $item if ref($item) eq 'ARRAY' or !defined $item;
101 7         33 [ $item ];
102 18     18   9284 }
  18         43  
  18         131  
103              
104 27 50   27 1 87 method graphql_to_perl(Any $item) :ReturnType(Maybe[ArrayRef]) {
  27 50       86  
  27 50       58  
  27         66  
  27         79  
  27         201  
105 27 100       128 return $item if !defined $item;
106 18         72 $item = $self->uplift($item);
107 18         89 my $of = $self->of;
108 18         42 my $i = 0;
109 18         32 my @errors;
110             my @values = map {
111 18         62 my $value = eval { $of->graphql_to_perl($_) };
  28         44  
  28         95  
112 28 100       195 push @errors, qq{In element #$i: $@} if $@;
113 28         51 $i++;
114 28         87 $value;
115             } @$item;
116 18 100       66 die @errors if @errors;
117 16         63 \@values;
118 18     18   9814 }
  18         42  
  18         76  
119              
120 2 50   2 1 9 method perl_to_graphql(Any $item) :ReturnType(Maybe[ArrayRef]) {
  2 50       6  
  2 50       4  
  2         3  
  2         20  
  2         29  
121 2 50       6 return $item if !defined $item;
122 2         7 $item = $self->uplift($item);
123 2         8 my $of = $self->of;
124 2         4 my $i = 0;
125 2         3 my @errors;
126             my @values = map {
127 2         4 my $value = eval { $of->perl_to_graphql($_) };
  2         4  
  2         6  
128 2 50       5 push @errors, qq{In element #$i: $@} if $@;
129 2         3 $i++;
130 2         7 $value;
131             } @$item;
132 2 50       5 die @errors if @errors;
133 2         11 \@values;
134 18     18   10110 }
  18         50  
  18         82  
135              
136             method _complete_value(
137             HashRef $context,
138             ArrayRef[HashRef] $nodes,
139             HashRef $info,
140             ArrayRef $path,
141             ArrayRef $result,
142 180 50   180   466 ) {
  180 50       389  
  180 50       1363  
  180 50       2383  
  180 50       1296  
  180 100       1257  
  180 0       1087  
  180         490  
  180         269  
  180         268  
143             # TODO promise stuff
144 180         715 my $item_type = $self->of;
145 176         302 my $index = 0;
146 176         657 my @errors;
147 0         0 my @completed = map GraphQL::Execution::_complete_value_catching_error(
148             $context,
149             $item_type,
150             $nodes,
151             $info,
152             [ @$path, $index++ ],
153             $_,
154             ), @$result;
155 0         0 DEBUG and _debug("List._complete_value(done)", \@completed);
156 0 0       0 (grep is_Promise($_), @completed)
157             ? _promise_for_list($context, \@completed)
158             : _merge_list(\@completed);
159             }
160              
161             fun _merge_list(
162             ArrayRef[ExecutionPartialResult] $list,
163 172 50   172   1372 ) :ReturnType(ExecutionPartialResult) {
  172 50       407  
  172 0       356  
  172 0       505  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  172         1106  
164 0         0 DEBUG and _debug("List._merge_list", $list);
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  172         3379  
165 0 50       0 my @errors = map @{ $_->{errors} || [] }, @$list;
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 100       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  172         241  
  172         418  
166 18         10936 my @data = map $_->{data}, @$list;
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  344         1139  
167 18         41 DEBUG and _debug("List._merge_list(after)", \@data, \@errors);
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  172         519  
168 18 50       97 +{ data => \@data, @errors ? (errors => \@errors) : () };
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  172 0       231  
    0          
    100          
169 18     18   34318 }
  18         54  
  18         93  
170              
171             fun _promise_for_list(
172             HashRef $context,
173             ArrayRef $list,
174 12 50   12   48 ) :ReturnType(Promise) {
  12 50       116  
  12 50       87  
  12 50       30  
  12         42  
  12         59  
175 8         1586 DEBUG and _debug('_promise_for_list', $list);
176             die "Given a promise in list but no PromiseCode given\n"
177 8 50       50 if !$context->{promise_code};
178             return $context->{promise_code}{all}->(@$list)->then(sub {
179 124     8   398 DEBUG and _debug('_promise_for_list(all)', @_);
180 124         579 _merge_list([ map $_->[0], @_ ]);
181 124         59050 });
182 12     18   268 }
  12         38  
  12         30  
183              
184             =head2 name
185              
186             The C<name> of the type this object is a list of.
187              
188             =cut
189              
190             __PACKAGE__->meta->make_immutable();
191              
192             1;