graphql-rb 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Rakefile +26 -0
- data/lib/graphql/configuration/configurable.rb +46 -0
- data/lib/graphql/configuration/configuration.rb +92 -0
- data/lib/graphql/configuration/slot.rb +124 -0
- data/lib/graphql/configuration.rb +3 -0
- data/lib/graphql/errors/error.rb +3 -0
- data/lib/graphql/errors.rb +1 -0
- data/lib/graphql/executor.rb +83 -0
- data/lib/graphql/introspection/meta_fields.rb +54 -0
- data/lib/graphql/introspection/query.rb +81 -0
- data/lib/graphql/introspection/schema.rb +158 -0
- data/lib/graphql/introspection.rb +3 -0
- data/lib/graphql/language/argument.rb +11 -0
- data/lib/graphql/language/directive.rb +5 -0
- data/lib/graphql/language/document.rb +23 -0
- data/lib/graphql/language/field.rb +55 -0
- data/lib/graphql/language/fragment_definition.rb +23 -0
- data/lib/graphql/language/fragment_spread.rb +5 -0
- data/lib/graphql/language/inline_fragment.rb +23 -0
- data/lib/graphql/language/list_type.rb +15 -0
- data/lib/graphql/language/name.rb +5 -0
- data/lib/graphql/language/named_type.rb +15 -0
- data/lib/graphql/language/non_null_type.rb +15 -0
- data/lib/graphql/language/operation_definition.rb +33 -0
- data/lib/graphql/language/parser.rb +331 -0
- data/lib/graphql/language/selection_set.rb +107 -0
- data/lib/graphql/language/transform.rb +101 -0
- data/lib/graphql/language/value.rb +24 -0
- data/lib/graphql/language/variable.rb +11 -0
- data/lib/graphql/language/variable_definition.rb +34 -0
- data/lib/graphql/language.rb +40 -0
- data/lib/graphql/type/argument.rb +16 -0
- data/lib/graphql/type/directive.rb +37 -0
- data/lib/graphql/type/directives.rb +25 -0
- data/lib/graphql/type/enum_type.rb +100 -0
- data/lib/graphql/type/field.rb +50 -0
- data/lib/graphql/type/input_object_type.rb +47 -0
- data/lib/graphql/type/interface_type.rb +64 -0
- data/lib/graphql/type/list.rb +23 -0
- data/lib/graphql/type/non_null.rb +25 -0
- data/lib/graphql/type/object_type.rb +57 -0
- data/lib/graphql/type/scalar_type.rb +137 -0
- data/lib/graphql/type/schema.rb +49 -0
- data/lib/graphql/type/union_type.rb +39 -0
- data/lib/graphql/type.rb +82 -0
- data/lib/graphql/validator.rb +43 -0
- data/lib/graphql/version.rb +3 -0
- data/lib/graphql.rb +21 -0
- data/spec/configuration/configuration_spec.rb +4 -0
- data/spec/data.rb +89 -0
- data/spec/introspection/full_spec.rb +12 -0
- data/spec/introspection/simple_spec.rb +153 -0
- data/spec/language/parser_spec.rb +73 -0
- data/spec/schema.rb +145 -0
- data/spec/spec_helper.rb +99 -0
- data/spec/type/enum_spec.rb +27 -0
- data/spec/type/input_object_spec.rb +21 -0
- data/spec/type/list_spec.rb +16 -0
- data/spec/type/non_null_spec.rb +22 -0
- data/spec/type/scalar_type_spec.rb +117 -0
- data/spec/type/schema_spec.rb +13 -0
- metadata +202 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
Document = Struct.new('Document', :definitions) do
|
4
|
+
|
5
|
+
def operations
|
6
|
+
@operations ||= definitions.select { |definition| definition.is_a?(OperationDefinition) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def operation(name)
|
10
|
+
operations.find { |operation| operation.name == name }
|
11
|
+
end
|
12
|
+
|
13
|
+
def fragments
|
14
|
+
@fragments ||= definitions.select { |definition| definition.is_a?(FragmentDefinition) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def fragment(name)
|
18
|
+
fragments.find { |fragment| fragment.name == name }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
Field = Struct.new('Field', :alias, :name, :arguments, :directives, :selection_set) do
|
4
|
+
|
5
|
+
# GraphQL Specification
|
6
|
+
# 6.3 Evaluate selection sets
|
7
|
+
# CollectFields
|
8
|
+
# responseKey implementation
|
9
|
+
#
|
10
|
+
def key
|
11
|
+
self.alias || self.name
|
12
|
+
end
|
13
|
+
|
14
|
+
# GraphQL Specification
|
15
|
+
# 6.4.1 Field entries
|
16
|
+
# ResolveFieldOnObject implementation
|
17
|
+
# objectType, object, firstField = self
|
18
|
+
# + context[document, schema, root]
|
19
|
+
#
|
20
|
+
# TODO: think of should or shouldn't we pass self as fourth parameter
|
21
|
+
#
|
22
|
+
def resolve(context, object_type, object)
|
23
|
+
arguments = [
|
24
|
+
object,
|
25
|
+
materialize_arguments(object_type, context[:variables]),
|
26
|
+
context
|
27
|
+
]
|
28
|
+
|
29
|
+
resolve = schema_field(object_type).resolve
|
30
|
+
arguments = arguments.slice(0, resolve.arity) if resolve.arity >= 0
|
31
|
+
|
32
|
+
resolve.call(*arguments)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def materialize_arguments(object_type, variables)
|
37
|
+
schema_field(object_type).args.reduce({}) do |memo, field_argument|
|
38
|
+
argument = arguments.find { |argument| argument.name == field_argument.name }
|
39
|
+
memo[argument.name.to_sym] = argument.materialize(field_argument.type, variables) unless argument.nil?
|
40
|
+
memo
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def schema_field(object_type)
|
45
|
+
case
|
46
|
+
when GraphQL::Introspection.meta_field?(name)
|
47
|
+
GraphQL::Introspection.meta_field(name)
|
48
|
+
else
|
49
|
+
object_type.field(name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
FragmentDefinition = Struct.new('FragmentDefinition', :name, :type_condition, :directives, :selection_set) do
|
4
|
+
|
5
|
+
# GraphQL Specification
|
6
|
+
# 6.3 Evaluating selection sets
|
7
|
+
# doesFragmentTypeApply implementation
|
8
|
+
# objectType, fragmentType = self.type_condition => Schema.type
|
9
|
+
# + context[schema, document]
|
10
|
+
#
|
11
|
+
def apply?(context, object_type)
|
12
|
+
type = context[:schema].type(type_condition.type)
|
13
|
+
|
14
|
+
return type == object_type if type.is_a?(GraphQLObjectType)
|
15
|
+
|
16
|
+
return type.possible_type?(object_type) if type.is_a?(GraphQLInterfaceType)
|
17
|
+
|
18
|
+
return type.possible_type?(object_type) if type.is_a?(GraphQLUnionType)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
InlineFragment = Struct.new('InlineFragment', :type_condition, :directives, :selection_set) do
|
4
|
+
|
5
|
+
# GraphQL Specification
|
6
|
+
# 6.3 Evaluating selection sets
|
7
|
+
# doesFragmentTypeApply implementation
|
8
|
+
# objectType, fragmentType = self.type_condition => Schema.type
|
9
|
+
# + context[schema, document]
|
10
|
+
#
|
11
|
+
def apply?(context, object_type)
|
12
|
+
type = context[:schema].type(type_condition.type)
|
13
|
+
|
14
|
+
return type == object_type if type.is_a?(GraphQLObjectType)
|
15
|
+
|
16
|
+
return type.possible_type?(object_type) if type.is_a?(GraphQLInterfaceType)
|
17
|
+
|
18
|
+
return type.possible_type?(object_type) if type.is_a?(GraphQLUnionType)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
ListType = Struct.new('ListType', :type) do
|
4
|
+
|
5
|
+
def named_type
|
6
|
+
type.respond_to?(:named_type) ? type.named_type : type
|
7
|
+
end
|
8
|
+
|
9
|
+
def materialize(schema)
|
10
|
+
GraphQLList.new(type.materialize(schema))
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
NonNullType = Struct.new('NonNullType', :type) do
|
4
|
+
|
5
|
+
def named_type
|
6
|
+
type.respond_to?(:named_type) ? type.named_type : type
|
7
|
+
end
|
8
|
+
|
9
|
+
def materialize(schema)
|
10
|
+
GraphQLNonNull.new(type.materialize(schema))
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
OperationDefinition = Struct.new('OperationDefinition', :type, :name, :variable_definitions, :directives, :selection_set) do
|
4
|
+
|
5
|
+
|
6
|
+
def evaluate(context)
|
7
|
+
context[:variables] = materialize_variables(context[:schema], context[:params])
|
8
|
+
type == 'mutation' ? execute_serially(context) : execute(context)
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def execute(context)
|
13
|
+
selection_set.evaluate(context, context[:schema].query_type, context[:root])
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def execute_serially(context)
|
18
|
+
raise "Not implemented. Yet."
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def materialize_variables(schema, params)
|
23
|
+
variable_definitions.reduce({}) do |memo, variable_definition|
|
24
|
+
name = variable_definition.variable.name.to_sym
|
25
|
+
memo[name] = variable_definition.materialize(schema, params[name])
|
26
|
+
memo
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,331 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Language
|
5
|
+
|
6
|
+
class Parser < Parslet::Parser
|
7
|
+
|
8
|
+
rule(:root) { document }
|
9
|
+
|
10
|
+
# Source Character
|
11
|
+
#
|
12
|
+
|
13
|
+
rule(:source_character) { any }
|
14
|
+
|
15
|
+
# Ignored Tokens
|
16
|
+
#
|
17
|
+
|
18
|
+
rule(:ignored) { white_space | line_terminator | comment | comma }
|
19
|
+
|
20
|
+
rule(:ignored?) { ignored.repeat(0) }
|
21
|
+
|
22
|
+
rule(:ignored!) { ignored.repeat(1) }
|
23
|
+
|
24
|
+
rule(:white_space) { match('[\u0009\u000b\u000c\u0020\u00a0]') }
|
25
|
+
|
26
|
+
rule(:line_terminator) { match('[\u000a\u000d\u2028\u2029]') }
|
27
|
+
|
28
|
+
rule(:comment) { str('#') >> comment_char.repeat }
|
29
|
+
|
30
|
+
rule(:comment_char) { line_terminator.absent? >> source_character }
|
31
|
+
|
32
|
+
rule(:comma) { str(',') }
|
33
|
+
|
34
|
+
# Lexical Tokens
|
35
|
+
#
|
36
|
+
|
37
|
+
rule(:token) { punctuator | name | int_value | float_value | string_value }
|
38
|
+
|
39
|
+
rule(:punctuator) { match('[!$():=@[]{|}]') | str('...') }
|
40
|
+
|
41
|
+
rule(:integer_part) do
|
42
|
+
negative_sign.maybe >> str('0') |
|
43
|
+
negative_sign.maybe >> non_zero_digit >> digit.repeat
|
44
|
+
end
|
45
|
+
|
46
|
+
rule(:negative_sign) { str('-') }
|
47
|
+
|
48
|
+
rule(:digit) { match('[0-9]') }
|
49
|
+
|
50
|
+
rule(:non_zero_digit) { str('0').absent? >> digit }
|
51
|
+
|
52
|
+
rule(:fractional_part) { str('.') >> digit.repeat(1) }
|
53
|
+
|
54
|
+
rule(:exponent_part) { exponent_indicator >> sign.maybe >> digit.repeat(1) }
|
55
|
+
|
56
|
+
rule(:exponent_indicator) { match('[eE]') }
|
57
|
+
|
58
|
+
rule(:sign) { match('-+') }
|
59
|
+
|
60
|
+
rule(:string_character) do
|
61
|
+
(str('"') | str('\\') | line_terminator).absent? >> source_character |
|
62
|
+
str('\\') >> escaped_unicode |
|
63
|
+
str('\\') >> escaped_character
|
64
|
+
end
|
65
|
+
|
66
|
+
rule(:escaped_unicode) { str('u') >> match('[a-fA-F0-9]').repeat(4) }
|
67
|
+
|
68
|
+
rule(:escaped_character) { match('["\\/bfnrt]') }
|
69
|
+
|
70
|
+
rule(:name) do
|
71
|
+
(match('[_a-zA-Z]') >> match('[_a-zA-Z0-9]').repeat).as(:name)
|
72
|
+
end
|
73
|
+
|
74
|
+
rule(:string_value) do
|
75
|
+
str('"') >> string_character.repeat.as(:string_value) >> str('"')
|
76
|
+
end
|
77
|
+
|
78
|
+
rule(:int_value) do
|
79
|
+
integer_part.as(:int_value)
|
80
|
+
end
|
81
|
+
|
82
|
+
rule(:float_value) do
|
83
|
+
(
|
84
|
+
integer_part >> fractional_part >> exponent_part |
|
85
|
+
integer_part >> fractional_part |
|
86
|
+
integer_part >> exponent_part
|
87
|
+
).as(:float_value)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Query Document
|
91
|
+
#
|
92
|
+
|
93
|
+
rule(:document) do
|
94
|
+
ignored? >> definition.repeat(1).as(:document) >> ignored?
|
95
|
+
end
|
96
|
+
|
97
|
+
rule(:definition) do
|
98
|
+
ignored? >> (operation_definition | fragment_definition) >> ignored?
|
99
|
+
end
|
100
|
+
|
101
|
+
rule(:operation_definition) do
|
102
|
+
ignored? >> (
|
103
|
+
selection_set.as(:selection_set) |
|
104
|
+
operation_type.as(:type) >>
|
105
|
+
name.as(:name) >>
|
106
|
+
variable_definitions.maybe.as(:variable_definitions) >>
|
107
|
+
directives.maybe.as(:directives) >>
|
108
|
+
selection_set.as(:selection_set)
|
109
|
+
).as(:operation_definition) >> ignored?
|
110
|
+
end
|
111
|
+
|
112
|
+
rule(:operation_type) do
|
113
|
+
ignored? >> (str('query') | str('mutation')).as(:name) >> ignored?
|
114
|
+
end
|
115
|
+
|
116
|
+
rule(:selection_set) do
|
117
|
+
ignored? >> (str('{') >> selection.repeat(1).as(:selections) >> str('}')).as(:selection_set) >> ignored?
|
118
|
+
end
|
119
|
+
|
120
|
+
rule(:selection) do
|
121
|
+
ignored? >> (field | fragment_spread | inline_fragment) >> ignored?
|
122
|
+
end
|
123
|
+
|
124
|
+
rule(:field) do
|
125
|
+
ignored? >> (
|
126
|
+
field_alias.maybe.as(:alias) >>
|
127
|
+
name.as(:name) >>
|
128
|
+
arguments.maybe.as(:arguments) >>
|
129
|
+
directives.maybe.as(:directives) >>
|
130
|
+
selection_set.maybe.as(:selection_set)
|
131
|
+
).as(:field) >> ignored?
|
132
|
+
end
|
133
|
+
|
134
|
+
rule(:field_alias) do
|
135
|
+
ignored? >> name >> str(':') >> ignored?
|
136
|
+
end
|
137
|
+
|
138
|
+
rule(:arguments) do
|
139
|
+
ignored? >> (
|
140
|
+
str('(') >> argument.repeat(1) >> str(')')
|
141
|
+
) >> ignored?
|
142
|
+
end
|
143
|
+
|
144
|
+
rule(:argument) do
|
145
|
+
ignored? >> (
|
146
|
+
name.as(:name) >>
|
147
|
+
str(':') >>
|
148
|
+
value.as(:value)
|
149
|
+
).as(:argument) >> ignored?
|
150
|
+
end
|
151
|
+
|
152
|
+
rule(:fragment_spread) do
|
153
|
+
ignored? >> (
|
154
|
+
str('...') >>
|
155
|
+
fragment_name.as(:name) >>
|
156
|
+
directives.maybe.as(:directives)
|
157
|
+
).as(:fragment_spread) >> ignored?
|
158
|
+
end
|
159
|
+
|
160
|
+
rule(:inline_fragment) do
|
161
|
+
ignored? >> (
|
162
|
+
str('...') >>
|
163
|
+
ignored? >>
|
164
|
+
str('on') >>
|
165
|
+
ignored? >>
|
166
|
+
type_condition.as(:type_condition) >>
|
167
|
+
directives.maybe.as(:directives) >>
|
168
|
+
selection_set.as(:selection_set)
|
169
|
+
).as(:inline_fragment) >> ignored?
|
170
|
+
end
|
171
|
+
|
172
|
+
rule(:fragment_definition) do
|
173
|
+
ignored? >> (
|
174
|
+
str('fragment') >>
|
175
|
+
fragment_name.as(:name) >>
|
176
|
+
str('on') >>
|
177
|
+
type_condition.as(:type_condition) >>
|
178
|
+
directives.maybe.as(:directives) >>
|
179
|
+
selection_set.as(:selection_set)
|
180
|
+
).as(:fragment_definition) >> ignored?
|
181
|
+
end
|
182
|
+
|
183
|
+
rule(:fragment_name) do
|
184
|
+
ignored? >> (str('on') >> ignored).absent? >> name.as(:name) >> ignored?
|
185
|
+
end
|
186
|
+
|
187
|
+
rule(:type_condition) do
|
188
|
+
ignored? >> named_type >> ignored?
|
189
|
+
end
|
190
|
+
|
191
|
+
rule(:value) do
|
192
|
+
ignored? >> (
|
193
|
+
variable |
|
194
|
+
(
|
195
|
+
float_value |
|
196
|
+
int_value |
|
197
|
+
string_value |
|
198
|
+
boolean_value |
|
199
|
+
enum_value |
|
200
|
+
list_value |
|
201
|
+
object_value
|
202
|
+
).as(:value)
|
203
|
+
) >> ignored?
|
204
|
+
end
|
205
|
+
|
206
|
+
rule(:value_const) do
|
207
|
+
ignored? >> (
|
208
|
+
float_value |
|
209
|
+
int_value |
|
210
|
+
string_value |
|
211
|
+
boolean_value |
|
212
|
+
enum_value |
|
213
|
+
list_value_const |
|
214
|
+
object_value_const
|
215
|
+
).as(:value) >> ignored?
|
216
|
+
end
|
217
|
+
|
218
|
+
rule(:boolean_value) do
|
219
|
+
ignored? >> (
|
220
|
+
str('true') |
|
221
|
+
str('false')
|
222
|
+
).as(:boolean_value) >> ignored?
|
223
|
+
end
|
224
|
+
|
225
|
+
rule(:enum_value) do
|
226
|
+
ignored? >> (
|
227
|
+
(
|
228
|
+
str('true') |
|
229
|
+
str('false') |
|
230
|
+
str('null')
|
231
|
+
).absent? >> name
|
232
|
+
).as(:enum_value) >> ignored?
|
233
|
+
end
|
234
|
+
|
235
|
+
rule(:list_value) do
|
236
|
+
ignored? >> (
|
237
|
+
str('[') >>
|
238
|
+
value.repeat(1) >>
|
239
|
+
str(']')
|
240
|
+
).as(:list_value) >> ignored?
|
241
|
+
end
|
242
|
+
|
243
|
+
rule(:list_value_const) do
|
244
|
+
ignored? >> (
|
245
|
+
str('[') >>
|
246
|
+
value_const.repeat(1) >>
|
247
|
+
str(']')
|
248
|
+
).as(:list_value) >> ignored?
|
249
|
+
end
|
250
|
+
|
251
|
+
rule(:object_value) do
|
252
|
+
ignored? >> (
|
253
|
+
str('{') >>
|
254
|
+
object_field.repeat(1) >>
|
255
|
+
str('}')
|
256
|
+
).as(:object_value) >> ignored?
|
257
|
+
end
|
258
|
+
|
259
|
+
rule(:object_value_const) do
|
260
|
+
ignored? >> (
|
261
|
+
str('{') >>
|
262
|
+
object_field_const.repeat(1) >>
|
263
|
+
str('}')
|
264
|
+
).as(:object_value) >> ignored?
|
265
|
+
end
|
266
|
+
|
267
|
+
rule(:object_field) do
|
268
|
+
ignored? >> name.as(:name) >> str(':') >> value.as(:value) >> ignored?
|
269
|
+
end
|
270
|
+
|
271
|
+
rule(:object_field_const) do
|
272
|
+
ignored? >> name.as(:name) >> str(':') >> value_const.as(:value) >> ignored?
|
273
|
+
end
|
274
|
+
|
275
|
+
rule(:variable_definitions) do
|
276
|
+
ignored? >> str('(') >> variable_definition.repeat(1) >> str(')') >> ignored?
|
277
|
+
end
|
278
|
+
|
279
|
+
rule(:variable_definition) do
|
280
|
+
ignored? >> (
|
281
|
+
variable.as(:variable) >>
|
282
|
+
str(':') >>
|
283
|
+
type.as(:type) >>
|
284
|
+
default_value.maybe.as(:default_value)
|
285
|
+
).as(:variable_definition) >> ignored?
|
286
|
+
end
|
287
|
+
|
288
|
+
rule(:variable) do
|
289
|
+
ignored? >> (
|
290
|
+
str('$') >> name
|
291
|
+
).as(:variable) >> ignored?
|
292
|
+
end
|
293
|
+
|
294
|
+
rule(:default_value) do
|
295
|
+
ignored? >> str('=') >> value_const >> ignored?
|
296
|
+
end
|
297
|
+
|
298
|
+
rule(:type) do
|
299
|
+
ignored? >> (non_null_type | list_type | named_type) >> ignored?
|
300
|
+
end
|
301
|
+
|
302
|
+
rule(:named_type) do
|
303
|
+
ignored? >> name.as(:named_type) >> ignored?
|
304
|
+
end
|
305
|
+
|
306
|
+
rule(:list_type) do
|
307
|
+
ignored? >> (
|
308
|
+
str('[') >> type >> str(']')
|
309
|
+
).as(:list_type) >> ignored?
|
310
|
+
end
|
311
|
+
|
312
|
+
rule(:non_null_type) do
|
313
|
+
ignored? >> (
|
314
|
+
list_type >> str('!') | named_type >> str('!')
|
315
|
+
).as(:non_null_type) >> ignored?
|
316
|
+
end
|
317
|
+
|
318
|
+
rule(:directives) do
|
319
|
+
ignored? >> directive.repeat(1) >> ignored?
|
320
|
+
end
|
321
|
+
|
322
|
+
rule(:directive) do
|
323
|
+
ignored? >> (
|
324
|
+
str('@') >> name.as(:name) >> arguments.maybe.as(:arguments)
|
325
|
+
).as(:directive) >> ignored?
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
SelectionSet = Struct.new("SelectionSet", :selections) do
|
4
|
+
|
5
|
+
def empty?
|
6
|
+
selections.empty?
|
7
|
+
end
|
8
|
+
|
9
|
+
# GraphQL Specification
|
10
|
+
# 6.4.1 Field entries
|
11
|
+
# GetFieldEntry implementation
|
12
|
+
# objectType, object, - fields
|
13
|
+
# + context[schema, document]
|
14
|
+
#
|
15
|
+
# TODO: think of way to have error accessor at this point. Executor?
|
16
|
+
#
|
17
|
+
def evaluate(context, object_type, object)
|
18
|
+
grouped_fields = collect_fields(context, object_type)
|
19
|
+
|
20
|
+
#
|
21
|
+
# TODO: Merge equal fields
|
22
|
+
#
|
23
|
+
|
24
|
+
grouped_fields.reduce({}) do |memo, (key, fields)|
|
25
|
+
field = fields.first
|
26
|
+
field_definition = case
|
27
|
+
when GraphQL::Introspection.meta_field?(field.name)
|
28
|
+
GraphQL::Introspection.meta_field(field.name)
|
29
|
+
else
|
30
|
+
object_type.field(field.name)
|
31
|
+
end
|
32
|
+
|
33
|
+
unless field_definition.nil?
|
34
|
+
resolve_context = context.merge({ parent_type: object_type })
|
35
|
+
|
36
|
+
resolved_object = field.resolve(context, object_type, object)
|
37
|
+
selection_set = merge_selection_sets(fields)
|
38
|
+
memo[key.to_sym] = Executor::FutureCompleter.complete_value(context, field_definition.type, resolved_object, selection_set)
|
39
|
+
end
|
40
|
+
|
41
|
+
memo
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# GraphQL Specification
|
46
|
+
# 6.3 Evaluate selection sets
|
47
|
+
# CollectFields implementation
|
48
|
+
# objectType, selectionSet = self, visitedFragments = []
|
49
|
+
# + context[schema, document]
|
50
|
+
#
|
51
|
+
def collect_fields(context, object_type, visited_fragments = [])
|
52
|
+
memo = {}
|
53
|
+
|
54
|
+
selections.each do |selection|
|
55
|
+
|
56
|
+
case selection
|
57
|
+
|
58
|
+
when Field
|
59
|
+
# TODO: Directives
|
60
|
+
(memo[selection.key] ||= []) << selection
|
61
|
+
|
62
|
+
when FragmentSpread
|
63
|
+
next if visited_fragments.include?(selection.name)
|
64
|
+
|
65
|
+
visited_fragments << selection.name
|
66
|
+
|
67
|
+
fragment = context[:document].fragment(selection.name)
|
68
|
+
|
69
|
+
next if fragment.nil?
|
70
|
+
|
71
|
+
next unless fragment.apply?(context, object_type)
|
72
|
+
|
73
|
+
fragment.selection_set.collect_fields(context, object_type).each do |key, fields|
|
74
|
+
memo[key] = (memo[key] ||= []).concat(fields)
|
75
|
+
end
|
76
|
+
|
77
|
+
when InlineFragment
|
78
|
+
next unless selection.apply?(context, object_type)
|
79
|
+
|
80
|
+
selection.selection_set.collect_fields(context, object_type).each do |key, fields|
|
81
|
+
memo[key] = (memo[key] ||= []).concat(fields)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
memo
|
89
|
+
end
|
90
|
+
|
91
|
+
# GraphQL Specification
|
92
|
+
# 6.4.1 Field entries
|
93
|
+
# MergeSelectionSets implementations
|
94
|
+
# fields
|
95
|
+
#
|
96
|
+
def merge_selection_sets(fields)
|
97
|
+
selections = fields.reduce([]) do |memo, field|
|
98
|
+
memo.concat field.selection_set.selections unless field.selection_set.nil? || field.selection_set.empty?
|
99
|
+
memo
|
100
|
+
end
|
101
|
+
|
102
|
+
SelectionSet.new(selections)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|