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   8169 use 5.014;
  48         1012  
4 48     18   221 use strict;
  48         108  
  50         927  
5 45     18   586 use warnings;
  20         37  
  20         570  
6 19     18   893 use Moo;
  19         9391  
  19         1161  
7 19     18   8096 use GraphQL::Type::Library -all;
  18         41  
  18         135  
8 18     18   249440 use GraphQL::Debug qw(_debug);
  18         45  
  18         1090  
9 18     18   108 use Types::Standard -all;
  18         37  
  18         128  
10 18     18   843426 use JSON::MaybeXS qw(JSON is_bool);
  18         46  
  18         1335  
11 18     18   120 use Exporter 'import';
  18         37  
  18         886  
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   414 use GraphQL::MaybeTypeCheck;
  18         36  
  18         141  
22 18     18   10872 use GraphQL::Plugin::Type;
  18         54  
  18         153  
23              
24             our $VERSION = '0.02';
25             our @EXPORT_OK = qw($Int $Float $String $Boolean $ID);
26              
27 18     18   146 use constant DEBUG => $ENV{GRAPHQL_DEBUG};
  18         39  
  18         2461  
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 108257 method is_valid(Any $item) :ReturnType(Bool) {
  31 50       128  
  31 50       79  
  31 0       95  
  31 0       154  
  19 0       9642  
  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         455  
89 19 50       67 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       568  
    0          
    0          
90 19         108 eval { $self->serialize->($item); 1 };
  223         577  
  223         637  
  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   3869 }
  18         82  
  18         186  
92              
93 222 50   222 1 1499 method graphql_to_perl(Any $item) :ReturnType(Any) {
  222 50       916  
  18 50       8039  
  18         66  
  18         113  
  875         1783  
94 875         1725 $self->parse_value->($item);
95 222     18   377 }
  222         439  
  222         567  
96              
97 875 50   875 1 5175 method perl_to_graphql(Any $item) :ReturnType(Any) {
  875 50       2611  
  18 50       10567  
  18         43  
  18         77  
  2         3412  
98 2         8 $self->serialize->($item);
99 875     18   1413 }
  875         1495  
  875         1738  
100              
101             method from_ast(
102             HashRef $name2type,
103             HashRef $ast_node,
104 2 50   2 1 949 ) :ReturnType(InstanceOf[__PACKAGE__]) {
  2 0       4  
  2 0       17  
  2 0       55  
  180 0       363  
  180 0       2172  
  9 0       25  
  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         37 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  
  928         3167  
  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  
  955         9015  
111 2     18   5 }
  2         6  
  2         10  
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   1125   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;