graphqlite 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +46 -0
- data/LICENSE +21 -0
- data/README.md +339 -0
- data/lib/graphqlite/errors.rb +7 -0
- data/lib/graphqlite/executor.rb +380 -0
- data/lib/graphqlite/introspection.rb +222 -0
- data/lib/graphqlite/lexer.rb +266 -0
- data/lib/graphqlite/parser.rb +354 -0
- data/lib/graphqlite/schema.rb +238 -0
- data/lib/graphqlite/types.rb +336 -0
- data/lib/graphqlite/validator.rb +183 -0
- data/lib/graphqlite/version.rb +3 -0
- data/lib/graphqlite.rb +18 -0
- metadata +85 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
module GraphQLite
|
|
2
|
+
# Schema is the main entry point for defining GraphQL schemas
|
|
3
|
+
class Schema
|
|
4
|
+
attr_reader :query_type, :mutation_type, :subscription_type, :types, :directives
|
|
5
|
+
|
|
6
|
+
def initialize(&block)
|
|
7
|
+
@types = {}
|
|
8
|
+
@directives = {}
|
|
9
|
+
@query_type = nil
|
|
10
|
+
@mutation_type = nil
|
|
11
|
+
@subscription_type = nil
|
|
12
|
+
@type_resolver = TypeResolver.new(self)
|
|
13
|
+
|
|
14
|
+
# Register built-in scalars
|
|
15
|
+
register_type(Types::INT)
|
|
16
|
+
register_type(Types::FLOAT)
|
|
17
|
+
register_type(Types::STRING)
|
|
18
|
+
register_type(Types::BOOLEAN)
|
|
19
|
+
register_type(Types::ID)
|
|
20
|
+
|
|
21
|
+
# Build schema using DSL
|
|
22
|
+
instance_eval(&block) if block_given?
|
|
23
|
+
|
|
24
|
+
# Add introspection types
|
|
25
|
+
Introspection.add_introspection_types(self)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def query(&block)
|
|
29
|
+
@query_type = define_root_type('Query', &block)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def mutation(&block)
|
|
33
|
+
@mutation_type = define_root_type('Mutation', &block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def subscription(&block)
|
|
37
|
+
@subscription_type = define_root_type('Subscription', &block)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def object(name, description: nil, interfaces: [], &block)
|
|
41
|
+
type = Types::ObjectType.new(name.to_s, description: description, interfaces: interfaces)
|
|
42
|
+
register_type(type)
|
|
43
|
+
|
|
44
|
+
builder = TypeBuilder.new(type, self)
|
|
45
|
+
builder.instance_eval(&block) if block_given?
|
|
46
|
+
|
|
47
|
+
type
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def interface(name, description: nil, resolve_type: nil, &block)
|
|
51
|
+
type = Types::InterfaceType.new(name.to_s, description: description, resolve_type: resolve_type)
|
|
52
|
+
register_type(type)
|
|
53
|
+
|
|
54
|
+
builder = TypeBuilder.new(type, self)
|
|
55
|
+
builder.instance_eval(&block) if block_given?
|
|
56
|
+
|
|
57
|
+
type
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def union(name, types: [], description: nil, resolve_type: nil)
|
|
61
|
+
type = Types::UnionType.new(name.to_s, types: types, description: description, resolve_type: resolve_type)
|
|
62
|
+
register_type(type)
|
|
63
|
+
type
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def enum(name, values: {}, description: nil)
|
|
67
|
+
type = Types::EnumType.new(name.to_s, values: values, description: description)
|
|
68
|
+
register_type(type)
|
|
69
|
+
type
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def input_object(name, description: nil, &block)
|
|
73
|
+
type = Types::InputObjectType.new(name.to_s, description: description)
|
|
74
|
+
register_type(type)
|
|
75
|
+
|
|
76
|
+
builder = TypeBuilder.new(type, self)
|
|
77
|
+
builder.instance_eval(&block) if block_given?
|
|
78
|
+
|
|
79
|
+
type
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def scalar(name, description: nil, serialize: nil, parse_value: nil, parse_literal: nil)
|
|
83
|
+
type = Types::ScalarType.new(
|
|
84
|
+
name.to_s,
|
|
85
|
+
description: description,
|
|
86
|
+
serialize: serialize,
|
|
87
|
+
parse_value: parse_value,
|
|
88
|
+
parse_literal: parse_literal
|
|
89
|
+
)
|
|
90
|
+
register_type(type)
|
|
91
|
+
type
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def execute(query_string, variables: {}, operation_name: nil, context: {})
|
|
95
|
+
# Parse the query
|
|
96
|
+
document = Parser.new(query_string).parse
|
|
97
|
+
|
|
98
|
+
# Validate the query
|
|
99
|
+
validator = Validator.new(self)
|
|
100
|
+
errors = validator.validate(document)
|
|
101
|
+
return { 'errors' => errors.map { |e| { 'message' => e.message } } } unless errors.empty?
|
|
102
|
+
|
|
103
|
+
# Execute the query
|
|
104
|
+
executor = Executor.new(self)
|
|
105
|
+
executor.execute(document, variables: variables, operation_name: operation_name, context: context)
|
|
106
|
+
rescue ParseError, ValidationError, ExecutionError => e
|
|
107
|
+
{ 'errors' => [{ 'message' => e.message }] }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def get_type(name)
|
|
111
|
+
return nil if name.nil?
|
|
112
|
+
@types[name.to_s]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def register_type(type)
|
|
116
|
+
@types[type.name] = type
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def define_root_type(name, &block)
|
|
122
|
+
type = Types::ObjectType.new(name)
|
|
123
|
+
register_type(type)
|
|
124
|
+
|
|
125
|
+
builder = TypeBuilder.new(type, self)
|
|
126
|
+
builder.instance_eval(&block) if block_given?
|
|
127
|
+
|
|
128
|
+
type
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Helper class for building types using DSL
|
|
132
|
+
class TypeBuilder
|
|
133
|
+
def initialize(type, schema)
|
|
134
|
+
@type = type
|
|
135
|
+
@schema = schema
|
|
136
|
+
@resolver = TypeResolver.new(schema)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def field(name, type = nil, description: nil, null: true, deprecation_reason: nil, &block)
|
|
140
|
+
# Resolve type reference
|
|
141
|
+
resolved_type = @resolver.resolve(type)
|
|
142
|
+
resolved_type = Types::NonNullType.new(resolved_type) unless null
|
|
143
|
+
|
|
144
|
+
if @type.is_a?(Types::ObjectType)
|
|
145
|
+
# Check if block expects a parameter (builder pattern) or not (resolver pattern)
|
|
146
|
+
if block && block.arity > 0
|
|
147
|
+
# Builder pattern: pass FieldBuilder to block
|
|
148
|
+
field_def = @type.field(name, resolved_type, description: description, deprecation_reason: deprecation_reason)
|
|
149
|
+
field_builder = FieldBuilder.new(field_def, @schema)
|
|
150
|
+
block.call(field_builder)
|
|
151
|
+
field_builder
|
|
152
|
+
else
|
|
153
|
+
# Resolver pattern: block is the resolver
|
|
154
|
+
field_def = @type.field(name, resolved_type, description: description, deprecation_reason: deprecation_reason, &block)
|
|
155
|
+
FieldBuilder.new(field_def, @schema)
|
|
156
|
+
end
|
|
157
|
+
elsif @type.is_a?(Types::InterfaceType)
|
|
158
|
+
@type.field(name, resolved_type, description: description, deprecation_reason: deprecation_reason)
|
|
159
|
+
elsif @type.is_a?(Types::InputObjectType)
|
|
160
|
+
default_value = null ? nil : block&.call
|
|
161
|
+
@type.field(name, resolved_type, description: description, default_value: default_value)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Helper class for building fields with arguments
|
|
167
|
+
class FieldBuilder
|
|
168
|
+
attr_reader :field
|
|
169
|
+
|
|
170
|
+
def initialize(field, schema)
|
|
171
|
+
@field = field
|
|
172
|
+
@schema = schema
|
|
173
|
+
@resolver = TypeResolver.new(schema)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def argument(name, type, description: nil, default_value: nil)
|
|
177
|
+
resolved_type = @resolver.resolve(type)
|
|
178
|
+
@field.argument(name, resolved_type, description: description, default_value: default_value)
|
|
179
|
+
self
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
alias arg argument
|
|
183
|
+
|
|
184
|
+
def resolve(&block)
|
|
185
|
+
@field.instance_variable_set(:@resolve, block)
|
|
186
|
+
self
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Lazy type reference for forward references
|
|
191
|
+
class TypeReference
|
|
192
|
+
attr_reader :name, :schema
|
|
193
|
+
|
|
194
|
+
def initialize(name, schema)
|
|
195
|
+
@name = name
|
|
196
|
+
@schema = schema
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def resolve
|
|
200
|
+
type = @schema.get_type(@name.to_s)
|
|
201
|
+
raise TypeError, "Unknown type: #{@name}" unless type
|
|
202
|
+
type
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Type resolver handles type references (symbols, strings, types)
|
|
207
|
+
class TypeResolver
|
|
208
|
+
def initialize(schema)
|
|
209
|
+
@schema = schema
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def resolve(type_ref)
|
|
213
|
+
case type_ref
|
|
214
|
+
when Types::BaseType, Types::ListType, Types::NonNullType
|
|
215
|
+
type_ref
|
|
216
|
+
when String, Symbol
|
|
217
|
+
# Look up type by name - if it doesn't exist yet, return a lazy reference
|
|
218
|
+
type = @schema.get_type(type_ref.to_s)
|
|
219
|
+
return type if type
|
|
220
|
+
# Return lazy reference for forward references (mainly for introspection)
|
|
221
|
+
TypeReference.new(type_ref, @schema)
|
|
222
|
+
when Array
|
|
223
|
+
# Array notation for lists: [String] -> ListType
|
|
224
|
+
raise TypeError, "Array type must have exactly one element" unless type_ref.length == 1
|
|
225
|
+
Types::ListType.new(resolve(type_ref[0]))
|
|
226
|
+
when Class
|
|
227
|
+
# Ruby class reference - auto-convert to type name
|
|
228
|
+
type_name = type_ref.name.split('::').last
|
|
229
|
+
type = @schema.get_type(type_name)
|
|
230
|
+
return type if type
|
|
231
|
+
TypeReference.new(type_name, @schema)
|
|
232
|
+
else
|
|
233
|
+
raise TypeError, "Invalid type reference: #{type_ref.inspect}"
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
module GraphQLite
|
|
2
|
+
module Types
|
|
3
|
+
# Base class for all types
|
|
4
|
+
class BaseType
|
|
5
|
+
attr_reader :name, :description
|
|
6
|
+
|
|
7
|
+
def initialize(name, description: nil)
|
|
8
|
+
@name = name
|
|
9
|
+
@description = description
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_s
|
|
13
|
+
name
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def non_null?
|
|
17
|
+
false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def list?
|
|
21
|
+
false
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Scalar types
|
|
26
|
+
class ScalarType < BaseType
|
|
27
|
+
attr_reader :serialize, :parse_value, :parse_literal
|
|
28
|
+
|
|
29
|
+
def initialize(name, description: nil, serialize: nil, parse_value: nil, parse_literal: nil)
|
|
30
|
+
super(name, description: description)
|
|
31
|
+
@serialize = serialize || ->(value) { value }
|
|
32
|
+
@parse_value = parse_value || ->(value) { value }
|
|
33
|
+
@parse_literal = parse_literal || ->(value) { value }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def kind
|
|
37
|
+
'SCALAR'
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Object types
|
|
42
|
+
class ObjectType < BaseType
|
|
43
|
+
attr_reader :fields, :interfaces
|
|
44
|
+
|
|
45
|
+
def initialize(name, description: nil, interfaces: [])
|
|
46
|
+
super(name, description: description)
|
|
47
|
+
@fields = {}
|
|
48
|
+
@interfaces = interfaces
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def field(name, type = nil, description: nil, deprecation_reason: nil, &block)
|
|
52
|
+
@fields[name.to_s] = Field.new(
|
|
53
|
+
name: name.to_s,
|
|
54
|
+
type: type,
|
|
55
|
+
description: description,
|
|
56
|
+
deprecation_reason: deprecation_reason,
|
|
57
|
+
resolve: block
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def kind
|
|
62
|
+
'OBJECT'
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Interface types
|
|
67
|
+
class InterfaceType < BaseType
|
|
68
|
+
attr_reader :fields, :resolve_type
|
|
69
|
+
|
|
70
|
+
def initialize(name, description: nil, resolve_type: nil)
|
|
71
|
+
super(name, description: description)
|
|
72
|
+
@fields = {}
|
|
73
|
+
@resolve_type = resolve_type
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def field(name, type, description: nil, deprecation_reason: nil)
|
|
77
|
+
@fields[name.to_s] = Field.new(
|
|
78
|
+
name: name.to_s,
|
|
79
|
+
type: type,
|
|
80
|
+
description: description,
|
|
81
|
+
deprecation_reason: deprecation_reason
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def kind
|
|
86
|
+
'INTERFACE'
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Union types
|
|
91
|
+
class UnionType < BaseType
|
|
92
|
+
attr_reader :types, :resolve_type
|
|
93
|
+
|
|
94
|
+
def initialize(name, types: [], description: nil, resolve_type: nil)
|
|
95
|
+
super(name, description: description)
|
|
96
|
+
@types = types
|
|
97
|
+
@resolve_type = resolve_type
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def kind
|
|
101
|
+
'UNION'
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Enum types
|
|
106
|
+
class EnumType < BaseType
|
|
107
|
+
attr_reader :values
|
|
108
|
+
|
|
109
|
+
def initialize(name, values: {}, description: nil)
|
|
110
|
+
super(name, description: description)
|
|
111
|
+
@values = values.transform_values do |value|
|
|
112
|
+
value.is_a?(Hash) ? EnumValue.new(**value) : EnumValue.new(value: value)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def kind
|
|
117
|
+
'ENUM'
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
EnumValue = Struct.new(:value, :description, :deprecation_reason, keyword_init: true)
|
|
122
|
+
|
|
123
|
+
# Input object types
|
|
124
|
+
class InputObjectType < BaseType
|
|
125
|
+
attr_reader :fields
|
|
126
|
+
|
|
127
|
+
def initialize(name, description: nil)
|
|
128
|
+
super(name, description: description)
|
|
129
|
+
@fields = {}
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def field(name, type, description: nil, default_value: nil)
|
|
133
|
+
@fields[name.to_s] = InputField.new(
|
|
134
|
+
name: name.to_s,
|
|
135
|
+
type: type,
|
|
136
|
+
description: description,
|
|
137
|
+
default_value: default_value
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def kind
|
|
142
|
+
'INPUT_OBJECT'
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# List type wrapper
|
|
147
|
+
class ListType
|
|
148
|
+
attr_reader :of_type
|
|
149
|
+
|
|
150
|
+
def initialize(of_type)
|
|
151
|
+
@of_type = of_type
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def to_s
|
|
155
|
+
"[#{@of_type}]"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def list?
|
|
159
|
+
true
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def non_null?
|
|
163
|
+
false
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def kind
|
|
167
|
+
'LIST'
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Resolve lazy type references
|
|
171
|
+
def resolve_types
|
|
172
|
+
@of_type = @of_type.resolve if @of_type.respond_to?(:resolve)
|
|
173
|
+
self
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Non-null type wrapper
|
|
178
|
+
class NonNullType
|
|
179
|
+
attr_reader :of_type
|
|
180
|
+
|
|
181
|
+
def initialize(of_type)
|
|
182
|
+
# Allow TypeReference as of_type, will be resolved later
|
|
183
|
+
raise TypeError, "Cannot wrap NonNullType in NonNullType" if of_type.is_a?(NonNullType)
|
|
184
|
+
@of_type = of_type
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def to_s
|
|
188
|
+
"#{@of_type}!"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def non_null?
|
|
192
|
+
true
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def list?
|
|
196
|
+
false
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def kind
|
|
200
|
+
'NON_NULL'
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Resolve lazy type references
|
|
204
|
+
def resolve_types
|
|
205
|
+
@of_type = @of_type.resolve if @of_type.respond_to?(:resolve)
|
|
206
|
+
self
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Field definition
|
|
211
|
+
class Field
|
|
212
|
+
attr_reader :name, :type, :description, :deprecation_reason, :arguments, :resolve
|
|
213
|
+
|
|
214
|
+
def initialize(name:, type:, description: nil, deprecation_reason: nil, resolve: nil)
|
|
215
|
+
@name = name
|
|
216
|
+
@type = type
|
|
217
|
+
@description = description
|
|
218
|
+
@deprecation_reason = deprecation_reason
|
|
219
|
+
@arguments = {}
|
|
220
|
+
@resolve = resolve
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def argument(name, type, description: nil, default_value: nil)
|
|
224
|
+
@arguments[name.to_s] = Argument.new(
|
|
225
|
+
name: name.to_s,
|
|
226
|
+
type: type,
|
|
227
|
+
description: description,
|
|
228
|
+
default_value: default_value
|
|
229
|
+
)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def deprecated?
|
|
233
|
+
!@deprecation_reason.nil?
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Argument definition
|
|
238
|
+
class Argument
|
|
239
|
+
attr_reader :name, :type, :description, :default_value
|
|
240
|
+
|
|
241
|
+
def initialize(name:, type:, description: nil, default_value: nil)
|
|
242
|
+
@name = name
|
|
243
|
+
@type = type
|
|
244
|
+
@description = description
|
|
245
|
+
@default_value = default_value
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Input field definition
|
|
250
|
+
class InputField
|
|
251
|
+
attr_reader :name, :type, :description, :default_value
|
|
252
|
+
|
|
253
|
+
def initialize(name:, type:, description: nil, default_value: nil)
|
|
254
|
+
@name = name
|
|
255
|
+
@type = type
|
|
256
|
+
@description = description
|
|
257
|
+
@default_value = default_value
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Built-in scalar types
|
|
262
|
+
INT = ScalarType.new(
|
|
263
|
+
'Int',
|
|
264
|
+
description: 'The `Int` scalar type represents non-fractional signed whole numeric values.',
|
|
265
|
+
serialize: ->(value) { value.to_i },
|
|
266
|
+
parse_value: ->(value) { value.to_i },
|
|
267
|
+
parse_literal: ->(value) { value.is_a?(Parser::IntValue) ? value.value.to_i : nil }
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
FLOAT = ScalarType.new(
|
|
271
|
+
'Float',
|
|
272
|
+
description: 'The `Float` scalar type represents signed double-precision fractional values.',
|
|
273
|
+
serialize: ->(value) { value.to_f },
|
|
274
|
+
parse_value: ->(value) { value.to_f },
|
|
275
|
+
parse_literal: ->(value) {
|
|
276
|
+
case value
|
|
277
|
+
when Parser::FloatValue
|
|
278
|
+
value.value.to_f
|
|
279
|
+
when Parser::IntValue
|
|
280
|
+
value.value.to_f
|
|
281
|
+
end
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
STRING = ScalarType.new(
|
|
286
|
+
'String',
|
|
287
|
+
description: 'The `String` scalar type represents textual data.',
|
|
288
|
+
serialize: ->(value) { value.to_s },
|
|
289
|
+
parse_value: ->(value) { value.to_s },
|
|
290
|
+
parse_literal: ->(value) { value.is_a?(Parser::StringValue) ? value.value : nil }
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
BOOLEAN = ScalarType.new(
|
|
294
|
+
'Boolean',
|
|
295
|
+
description: 'The `Boolean` scalar type represents `true` or `false`.',
|
|
296
|
+
serialize: ->(value) { !!value },
|
|
297
|
+
parse_value: ->(value) { !!value },
|
|
298
|
+
parse_literal: ->(value) { value.is_a?(Parser::BooleanValue) ? value.value : nil }
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
ID = ScalarType.new(
|
|
302
|
+
'ID',
|
|
303
|
+
description: 'The `ID` scalar type represents a unique identifier.',
|
|
304
|
+
serialize: ->(value) { value.to_s },
|
|
305
|
+
parse_value: ->(value) { value.to_s },
|
|
306
|
+
parse_literal: ->(value) {
|
|
307
|
+
case value
|
|
308
|
+
when Parser::StringValue, Parser::IntValue
|
|
309
|
+
value.value.to_s
|
|
310
|
+
end
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Helper methods to create type wrappers
|
|
315
|
+
def self.list(type)
|
|
316
|
+
ListType.new(type)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def self.non_null(type)
|
|
320
|
+
NonNullType.new(type)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Convenience method for non-null types
|
|
324
|
+
class BaseType
|
|
325
|
+
def !
|
|
326
|
+
NonNullType.new(self)
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
class ListType
|
|
331
|
+
def !
|
|
332
|
+
NonNullType.new(self)
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|