File Coverage

blib/lib/GraphQL/Type/Scalar.pm
Criterion Covered Total %
statement 83 211 39.3
branch 14 124 11.2
condition n/a
subroutine 25 25 100.0
pod 4 4 100.0
total 126 364 34.6


line stmt bran cond sub pod time code
1             package GraphQL::Type::Scalar;
2              
3 18     18   7150 use 5.014;
  48         1023  
4 48     18   200 use strict;
  48         101  
  50         1021  
5 45     18   553 use warnings;
  20         38  
  20         548  
6 19     18   876 use Moo;
  19         9572  
  19         930  
7 19     18   8000 use GraphQL::Type::Library -all;
  18         41  
  18         158  
8 18     18   234848 use GraphQL::Debug qw(_debug);
  18         45  
  18         1254  
9 18     18   114 use Types::Standard -all;
  18         38  
  18         170  
10 18     18   793655 use JSON::MaybeXS qw(JSON is_bool);
  18         50  
  18         1558  
11 18     18   115 use Exporter 'import';
  18         39  
  18         1005  
12             extends qw(GraphQL::Type);
13             with qw(
14             GraphQL::Role::Input
15             GraphQL::Role::Output
16             GraphQL::Role::Leaf
17             GraphQL::Role::Nullable
18             GraphQL::Role::Named
19             GraphQL::Role::FieldsEither
20             );
21 18     18   619 use GraphQL::MaybeTypeCheck;
  18         53  
  18         181  
22 18     18   12073 use GraphQL::Plugin::Type;
  18         64  
  18         198  
23              
24             our $VERSION = '0.02';
25             our @EXPORT_OK = qw($Int $Float $String $Boolean $ID);
26              
27 18     18   167 use constant DEBUG => $ENV{GRAPHQL_DEBUG};
  18         38  
  18         2392  
28             my $JSON = JSON::MaybeXS->new->allow_nonref->canonical;
29              
30             =head1 NAME
31              
32             GraphQL::Type::Scalar - GraphQL scalar type
33              
34             =head1 SYNOPSIS
35              
36             use GraphQL::Type::Scalar;
37             my $int_type = GraphQL::Type::Scalar->new(
38             name => 'Int',
39             description =>
40             'The `Int` scalar type represents non-fractional signed whole numeric ' .
41             'values. Int can represent values between -(2^31) and 2^31 - 1. ',
42             serialize => \&coerce_int,
43             parse_value => \&coerce_int,
44             );
45              
46             =head1 ATTRIBUTES
47              
48             Has C<name>, C<description> from L<GraphQL::Role::Named>.
49              
50             =head2 serialize
51              
52             Code-ref. Required.
53              
54             Coerces
55             B<from> a Perl entity of the required type,
56             B<to> a GraphQL entity,
57             or throws an exception.
58              
59             Must throw an exception if passed a defined (i.e. non-null) but invalid
60             Perl object of the relevant type. C<undef> must always be valid.
61              
62             =cut
63              
64             has serialize => (is => 'ro', isa => CodeRef, required => 1);
65              
66             =head2 parse_value
67              
68             Code-ref. Required if is for an input type.
69              
70             Coerces
71             B<from> a GraphQL entity,
72             B<to> a Perl entity of the required type,
73             or throws an exception.
74              
75             =cut
76              
77             has parse_value => (is => 'ro', isa => CodeRef);
78              
79             =head1 METHODS
80              
81             =head2 is_valid
82              
83             True if given Perl entity is valid value for this type. Uses L</serialize>
84             attribute.
85              
86             =cut
87              
88 31 50   31 1 99566 method is_valid(Any $item) :ReturnType(Bool) {
  31 50       115  
  31 50       72  
  31 0       89  
  31 0       134  
  19 0       9648  
  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  
  161         434  
89 19 50       47 return 1 if !defined $item;
  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  
  161 0       631  
    0          
    0          
90 19         90 eval { $self->serialize->($item); 1 };
  223         586  
  223         528  
  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  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
91 18     18   3759 }
  18         62  
  18         162  
92              
93 222 50   222 1 1369 method graphql_to_perl(Any $item) :ReturnType(Any) {
  222 50       917  
  18 50       8246  
  18         60  
  18         105  
  875         1994  
94 875         1718 $self->parse_value->($item);
95 222     18   369 }
  222         401  
  222         509  
96              
97 875 50   875 1 4526 method perl_to_graphql(Any $item) :ReturnType(Any) {
  875 50       3199  
  18 50       10230  
  18         44  
  18         83  
  2         2889  
98 2         9 $self->serialize->($item);
99 875     18   1231 }
  875         1559  
  875         1789  
100              
101             method from_ast(
102             HashRef $name2type,
103             HashRef $ast_node,
104 2 50   2 1 768 ) :ReturnType(InstanceOf[__PACKAGE__]) {
  2 0       7  
  2 0       16  
  2 0       49  
  180 0       328  
  180 0       1974  
  9 0       24  
  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 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
    0          
    50          
105 9         27 DEBUG and _debug('Scalar.from_ast', $ast_node);
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
106             $self->new(
107             $self->_from_ast_named($ast_node),
108 0     1   0 serialize => sub { require Carp; Carp::croak "Fake serialize called" },
  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  
  930         3396  
  0         0  
109 0     1   0 parse_value => sub { require Carp; Carp::croak "Fake parse_value called" },
  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  
  0         0  
  0         0  
110 0         0 );
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  957         8565  
111 2     18   5 }
  2         7  
  2         8  
112              
113             has to_doc => (is => 'lazy', isa => Str);
114             sub _build_to_doc {
115 0     2   0 my ($self) = @_;
116 0         0 DEBUG and _debug('Scalar.to_doc', $self);
117 0         0 join '', map "$_\n",
118             $self->_description_doc_lines($self->description),
119 0         0 "scalar @{[$self->name]}";
120             }
121              
122             =head1 EXPORTED VARIABLES
123              
124             =head2 $Int
125              
126             =cut
127              
128             sub _leave_undef {
129 0     180   0 my ($closure) = @_;
130 0 0   1127   0 sub { return undef if !defined $_[0]; goto &$closure; };
  0         0  
  0         0  
131             }
132              
133             our $Int = GraphQL::Type::Scalar->new(
134             name => 'Int',
135             description =>
136             'The `Int` scalar type represents non-fractional signed whole numeric ' .
137             'values. Int can represent values between -(2^31) and 2^31 - 1.',
138             serialize => _leave_undef(sub { !is_Int32Signed($_[0]) and die "Not an Int.\n"; $_[0]+0 }),
139             parse_value => _leave_undef(sub { !is_Int32Signed($_[0]) and die "Not an Int.\n"; $_[0]+0 }),
140             );
141              
142             =head2 $Float
143              
144             =cut
145              
146             our $Float = GraphQL::Type::Scalar->new(
147             name => 'Float',
148             description =>
149             'The `Float` scalar type represents signed double-precision fractional ' .
150             'values as specified by ' .
151             '[IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).',
152             serialize => _leave_undef(sub { !is_Num($_[0]) and die "Not a Float.\n"; $_[0]+0 }),
153             parse_value => _leave_undef(sub { !is_Num($_[0]) and die "Not a Float.\n"; $_[0]+0 }),
154             );
155              
156             =head2 $String
157              
158             =cut
159              
160             our $String = GraphQL::Type::Scalar->new(
161             name => 'String',
162             description =>
163             'The `String` scalar type represents textual data, represented as UTF-8 ' .
164             'character sequences. The String type is most often used by GraphQL to ' .
165             'represent free-form human-readable text.',
166             serialize => _leave_undef(sub { !is_Str($_[0]) and die "Not a String.\n"; $_[0].'' }),
167             parse_value => _leave_undef(sub { !is_Str($_[0]) and die "Not a String.\n"; $_[0] }),
168             );
169              
170             =head2 $Boolean
171              
172             =cut
173              
174             our $Boolean = GraphQL::Type::Scalar->new(
175             name => 'Boolean',
176             description =>
177             'The `Boolean` scalar type represents `true` or `false`.',
178             serialize => _leave_undef(sub { !is_Bool($_[0]) and !is_bool($_[0]) and die "Not a Boolean.\n"; $_[0] ? JSON->true : JSON->false }),
179             parse_value => _leave_undef(sub { !is_Bool($_[0]) and !is_bool($_[0]) and die "Not a Boolean.\n"; $_[0]+0 }),
180             );
181              
182             =head2 $ID
183              
184             =cut
185              
186             our $ID = GraphQL::Type::Scalar->new(
187             name => 'ID',
188             description =>
189             'The `ID` scalar type represents a unique identifier, often used to ' .
190             'refetch an object or as key for a cache. The ID type appears in a JSON ' .
191             'response as a String; however, it is not intended to be human-readable. ' .
192             'When expected as an input type, any string (such as `"4"`) or integer ' .
193             '(such as `4`) input value will be accepted as an ID.',
194             serialize => _leave_undef(sub { Str->(@_); $_[0].'' }),
195             parse_value => _leave_undef(sub { Str->(@_); $_[0] }),
196             );
197              
198             GraphQL::Plugin::Type->register($_) for ($Int, $Float, $String, $Boolean, $ID);
199              
200             __PACKAGE__->meta->make_immutable();
201              
202             1;