graphql 0.12.1 → 0.13.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 +4 -4
- data/lib/graphql.rb +31 -41
- data/lib/graphql/argument.rb +23 -21
- data/lib/graphql/base_type.rb +5 -8
- data/lib/graphql/define/assign_argument.rb +5 -2
- data/lib/graphql/define/type_definer.rb +2 -1
- data/lib/graphql/directive.rb +34 -36
- data/lib/graphql/directive/include_directive.rb +3 -7
- data/lib/graphql/directive/skip_directive.rb +3 -7
- data/lib/graphql/enum_type.rb +78 -76
- data/lib/graphql/execution_error.rb +1 -3
- data/lib/graphql/field.rb +99 -95
- data/lib/graphql/input_object_type.rb +49 -47
- data/lib/graphql/interface_type.rb +31 -34
- data/lib/graphql/introspection.rb +19 -18
- data/lib/graphql/introspection/directive_location_enum.rb +8 -0
- data/lib/graphql/introspection/directive_type.rb +1 -3
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/fields_field.rb +1 -1
- data/lib/graphql/introspection/introspection_query.rb +1 -3
- data/lib/graphql/introspection/possible_types_field.rb +7 -1
- data/lib/graphql/introspection/schema_field.rb +13 -9
- data/lib/graphql/introspection/type_by_name_field.rb +13 -17
- data/lib/graphql/introspection/typename_field.rb +12 -8
- data/lib/graphql/language.rb +5 -9
- data/lib/graphql/language/lexer.rb +668 -0
- data/lib/graphql/language/lexer.rl +149 -0
- data/lib/graphql/language/parser.rb +842 -116
- data/lib/graphql/language/parser.y +264 -0
- data/lib/graphql/language/token.rb +21 -0
- data/lib/graphql/list_type.rb +33 -31
- data/lib/graphql/non_null_type.rb +33 -31
- data/lib/graphql/object_type.rb +52 -55
- data/lib/graphql/query.rb +83 -80
- data/lib/graphql/query/context.rb +5 -1
- data/lib/graphql/query/directive_resolution.rb +16 -0
- data/lib/graphql/query/executor.rb +3 -3
- data/lib/graphql/query/input_validation_result.rb +17 -15
- data/lib/graphql/query/serial_execution.rb +5 -5
- data/lib/graphql/query/serial_execution/execution_context.rb +4 -3
- data/lib/graphql/query/serial_execution/selection_resolution.rb +19 -21
- data/lib/graphql/query/serial_execution/value_resolution.rb +1 -1
- data/lib/graphql/query/type_resolver.rb +22 -18
- data/lib/graphql/query/variable_validation_error.rb +14 -12
- data/lib/graphql/schema.rb +87 -77
- data/lib/graphql/schema/each_item_validator.rb +16 -12
- data/lib/graphql/schema/field_validator.rb +14 -10
- data/lib/graphql/schema/implementation_validator.rb +26 -22
- data/lib/graphql/schema/middleware_chain.rb +2 -1
- data/lib/graphql/schema/possible_types.rb +34 -0
- data/lib/graphql/schema/printer.rb +122 -120
- data/lib/graphql/schema/type_expression.rb +1 -0
- data/lib/graphql/schema/type_map.rb +3 -10
- data/lib/graphql/schema/type_reducer.rb +65 -81
- data/lib/graphql/schema/type_validator.rb +45 -41
- data/lib/graphql/static_validation.rb +7 -9
- data/lib/graphql/static_validation/all_rules.rb +29 -24
- data/lib/graphql/static_validation/arguments_validator.rb +39 -35
- data/lib/graphql/static_validation/literal_validator.rb +44 -40
- data/lib/graphql/static_validation/message.rb +30 -26
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +15 -11
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +14 -10
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +16 -12
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +59 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +25 -21
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +28 -24
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +84 -80
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +49 -43
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +22 -17
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +19 -15
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +25 -20
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +36 -23
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +29 -25
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +21 -17
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +79 -70
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +24 -20
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +122 -119
- data/lib/graphql/static_validation/type_stack.rb +138 -129
- data/lib/graphql/static_validation/validator.rb +29 -25
- data/lib/graphql/type_kinds.rb +42 -40
- data/lib/graphql/union_type.rb +22 -16
- data/lib/graphql/version.rb +1 -1
- data/readme.md +12 -27
- data/spec/graphql/base_type_spec.rb +3 -3
- data/spec/graphql/directive_spec.rb +10 -18
- data/spec/graphql/enum_type_spec.rb +7 -7
- data/spec/graphql/execution_error_spec.rb +1 -1
- data/spec/graphql/field_spec.rb +14 -13
- data/spec/graphql/id_type_spec.rb +6 -6
- data/spec/graphql/input_object_type_spec.rb +39 -39
- data/spec/graphql/interface_type_spec.rb +16 -32
- data/spec/graphql/introspection/directive_type_spec.rb +5 -9
- data/spec/graphql/introspection/input_value_type_spec.rb +10 -4
- data/spec/graphql/introspection/introspection_query_spec.rb +2 -2
- data/spec/graphql/introspection/schema_type_spec.rb +2 -2
- data/spec/graphql/introspection/type_type_spec.rb +34 -6
- data/spec/graphql/language/parser_spec.rb +299 -105
- data/spec/graphql/language/visitor_spec.rb +4 -4
- data/spec/graphql/list_type_spec.rb +11 -11
- data/spec/graphql/object_type_spec.rb +10 -10
- data/spec/graphql/query/arguments_spec.rb +7 -7
- data/spec/graphql/query/context_spec.rb +11 -3
- data/spec/graphql/query/executor_spec.rb +26 -19
- data/spec/graphql/query/serial_execution/execution_context_spec.rb +6 -6
- data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -2
- data/spec/graphql/query/type_resolver_spec.rb +3 -3
- data/spec/graphql/query_spec.rb +6 -38
- data/spec/graphql/scalar_type_spec.rb +28 -19
- data/spec/graphql/schema/field_validator_spec.rb +1 -1
- data/spec/graphql/schema/middleware_chain_spec.rb +12 -1
- data/spec/graphql/schema/printer_spec.rb +12 -4
- data/spec/graphql/schema/rescue_middleware_spec.rb +1 -1
- data/spec/graphql/schema/type_expression_spec.rb +2 -2
- data/spec/graphql/schema/type_reducer_spec.rb +21 -36
- data/spec/graphql/schema/type_validator_spec.rb +9 -9
- data/spec/graphql/schema_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +5 -5
- data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +39 -0
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +5 -5
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +3 -3
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +5 -5
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +3 -1
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +4 -4
- data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +3 -3
- data/spec/graphql/static_validation/type_stack_spec.rb +3 -2
- data/spec/graphql/static_validation/validator_spec.rb +26 -6
- data/spec/graphql/union_type_spec.rb +5 -4
- data/spec/spec_helper.rb +2 -5
- data/spec/support/dairy_app.rb +30 -9
- data/spec/support/dairy_data.rb +1 -1
- data/spec/support/star_wars_data.rb +26 -26
- data/spec/support/star_wars_schema.rb +1 -1
- metadata +40 -21
- data/lib/graphql/language/transform.rb +0 -113
- data/lib/graphql/query/directive_chain.rb +0 -44
- data/lib/graphql/repl.rb +0 -27
- data/spec/graphql/language/transform_spec.rb +0 -156
@@ -1,27 +1,31 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module GraphQL
|
2
|
+
module StaticValidation
|
3
|
+
class VariablesAreInputTypes
|
4
|
+
include GraphQL::StaticValidation::Message::MessageHelper
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def validate(context)
|
7
|
+
context.visitor[GraphQL::Language::Nodes::VariableDefinition] << -> (node, parent) {
|
8
|
+
validate_is_input_type(node, context)
|
9
|
+
}
|
10
|
+
end
|
9
11
|
|
10
|
-
|
12
|
+
private
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
def validate_is_input_type(node, context)
|
15
|
+
type_name = get_type_name(node.type)
|
16
|
+
type = context.schema.types[type_name]
|
17
|
+
if !type.kind.input?
|
18
|
+
context.errors << message("#{type.name} isn't a valid input type (on $#{node.name})", node)
|
19
|
+
end
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
def get_type_name(ast_type)
|
23
|
+
if ast_type.respond_to?(:of_type)
|
24
|
+
get_type_name(ast_type.of_type)
|
25
|
+
else
|
26
|
+
ast_type.name
|
27
|
+
end
|
28
|
+
end
|
25
29
|
end
|
26
30
|
end
|
27
31
|
end
|
@@ -1,130 +1,133 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
1
|
+
module GraphQL
|
2
|
+
module StaticValidation
|
3
|
+
# The problem is
|
4
|
+
# - Variable usage must be determined at the OperationDefinition level
|
5
|
+
# - You can't tell how fragments use variables until you visit FragmentDefinitions (which may be at the end of the document)
|
6
|
+
#
|
7
|
+
# So, this validator includes some crazy logic to follow fragment spreads recursively, while avoiding infinite loops.
|
8
|
+
#
|
9
|
+
# `graphql-js` solves this problem by:
|
10
|
+
# - re-visiting the AST for each validator
|
11
|
+
# - allowing validators to say `followSpreads: true`
|
12
|
+
#
|
13
|
+
class VariablesAreUsedAndDefined
|
14
|
+
include GraphQL::StaticValidation::Message::MessageHelper
|
15
|
+
|
16
|
+
class VariableUsage
|
17
|
+
attr_accessor :ast_node, :used_by, :declared_by
|
18
|
+
def used?
|
19
|
+
!!@used_by
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
def declared?
|
23
|
+
!!@declared_by
|
24
|
+
end
|
25
|
+
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
def variable_hash
|
28
|
+
Hash.new {|h, k| h[k] = VariableUsage.new }
|
29
|
+
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
31
|
+
def validate(context)
|
32
|
+
variable_usages_for_context = Hash.new {|hash, key| hash[key] = variable_hash }
|
33
|
+
spreads_for_context = Hash.new {|hash, key| hash[key] = [] }
|
34
|
+
variable_context_stack = []
|
35
|
+
|
36
|
+
# OperationDefinitions and FragmentDefinitions
|
37
|
+
# both push themselves onto the context stack (and pop themselves off)
|
38
|
+
push_variable_context_stack = -> (node, parent) {
|
39
|
+
# initialize the hash of vars for this context:
|
40
|
+
variable_usages_for_context[node]
|
41
|
+
variable_context_stack.push(node)
|
42
|
+
}
|
43
|
+
|
44
|
+
pop_variable_context_stack = -> (node, parent) {
|
45
|
+
variable_context_stack.pop
|
46
|
+
}
|
47
|
+
|
48
|
+
|
49
|
+
context.visitor[GraphQL::Language::Nodes::OperationDefinition] << push_variable_context_stack
|
50
|
+
context.visitor[GraphQL::Language::Nodes::OperationDefinition] << -> (node, parent) {
|
51
|
+
# mark variables as defined:
|
52
|
+
var_hash = variable_usages_for_context[node]
|
53
|
+
node.variables.each { |var| var_hash[var.name].declared_by = node }
|
54
|
+
}
|
55
|
+
context.visitor[GraphQL::Language::Nodes::OperationDefinition].leave << pop_variable_context_stack
|
56
|
+
|
57
|
+
context.visitor[GraphQL::Language::Nodes::FragmentDefinition] << push_variable_context_stack
|
58
|
+
context.visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << pop_variable_context_stack
|
59
|
+
|
60
|
+
# For FragmentSpreads:
|
61
|
+
# - find the context on the stack
|
62
|
+
# - mark the context as containing this spread
|
63
|
+
context.visitor[GraphQL::Language::Nodes::FragmentSpread] << -> (node, parent) {
|
64
|
+
variable_context = variable_context_stack.last
|
65
|
+
spreads_for_context[variable_context] << node.name
|
66
|
+
}
|
67
|
+
|
68
|
+
# For VariableIdentifiers:
|
69
|
+
# - mark the variable as used
|
70
|
+
# - assign its AST node
|
71
|
+
context.visitor[GraphQL::Language::Nodes::VariableIdentifier] << -> (node, parent) {
|
72
|
+
usage_context = variable_context_stack.last
|
73
|
+
declared_variables = variable_usages_for_context[usage_context]
|
74
|
+
usage = declared_variables[node.name]
|
75
|
+
usage.used_by = usage_context
|
76
|
+
usage.ast_node = node
|
77
|
+
}
|
78
|
+
|
79
|
+
|
80
|
+
context.visitor[GraphQL::Language::Nodes::Document].leave << -> (node, parent) {
|
81
|
+
fragment_definitions = variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::FragmentDefinition) }
|
82
|
+
operation_definitions = variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::OperationDefinition) }
|
83
|
+
|
84
|
+
operation_definitions.each do |node, node_variables|
|
85
|
+
follow_spreads(node, node_variables, spreads_for_context, fragment_definitions, [])
|
86
|
+
create_errors(node_variables, context)
|
87
|
+
end
|
88
|
+
}
|
85
89
|
end
|
86
|
-
}
|
87
|
-
end
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
91
|
+
private
|
92
|
+
|
93
|
+
# Follow spreads in `node`, looking them up from `spreads_for_context` and finding their match in `fragment_definitions`.
|
94
|
+
# Use those fragments to update {VariableUsage}s in `parent_variables`.
|
95
|
+
# Avoid infinite loops by skipping anything in `visited_fragments`.
|
96
|
+
def follow_spreads(node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments)
|
97
|
+
spreads = spreads_for_context[node] - visited_fragments
|
98
|
+
spreads.each do |spread_name|
|
99
|
+
def_node, variables = fragment_definitions.find { |def_node, vars| def_node.name == spread_name }
|
100
|
+
next if !def_node
|
101
|
+
visited_fragments << spread_name
|
102
|
+
variables.each do |name, child_usage|
|
103
|
+
parent_usage = parent_variables[name]
|
104
|
+
if child_usage.used?
|
105
|
+
parent_usage.ast_node = child_usage.ast_node
|
106
|
+
parent_usage.used_by = child_usage.used_by
|
107
|
+
end
|
108
|
+
end
|
109
|
+
follow_spreads(def_node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments)
|
105
110
|
end
|
106
111
|
end
|
107
|
-
follow_spreads(def_node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments)
|
108
|
-
end
|
109
|
-
end
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
113
|
+
# Determine all the error messages,
|
114
|
+
# Then push messages into the validation context
|
115
|
+
def create_errors(node_variables, context)
|
116
|
+
errors = []
|
117
|
+
# Declared but not used:
|
118
|
+
errors += node_variables
|
119
|
+
.select { |name, usage| usage.declared? && !usage.used? }
|
120
|
+
.map { |var_name, usage| ["Variable $#{var_name} is declared by #{usage.declared_by.name} but not used", usage.declared_by] }
|
121
|
+
|
122
|
+
# Used but not declared:
|
123
|
+
errors += node_variables
|
124
|
+
.select { |name, usage| usage.used? && !usage.declared? }
|
125
|
+
.map { |var_name, usage| ["Variable $#{var_name} is used by #{usage.used_by.name} but not declared", usage.ast_node] }
|
126
|
+
|
127
|
+
errors.each do |error_args|
|
128
|
+
context.errors << message(*error_args)
|
129
|
+
end
|
130
|
+
end
|
127
131
|
end
|
128
132
|
end
|
129
|
-
|
130
133
|
end
|
@@ -1,150 +1,159 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
1
|
+
module GraphQL
|
2
|
+
module StaticValidation
|
3
|
+
# - Ride along with `GraphQL::Language::Visitor`
|
4
|
+
# - Track type info, expose it to validators
|
5
|
+
class TypeStack
|
6
|
+
# These are jumping-off points for infering types down the tree
|
7
|
+
TYPE_INFERRENCE_ROOTS = [
|
8
|
+
GraphQL::Language::Nodes::OperationDefinition,
|
9
|
+
GraphQL::Language::Nodes::FragmentDefinition,
|
10
|
+
]
|
11
|
+
|
12
|
+
# @return [GraphQL::Schema] the schema whose types are present in this document
|
13
|
+
attr_reader :schema
|
14
|
+
|
15
|
+
# When it enters an object (starting with query or mutation root), it's pushed on this stack.
|
16
|
+
# When it exits, it's popped off.
|
17
|
+
# @return [Array<GraphQL::ObjectType, GraphQL::Union, GraphQL::Interface>]
|
18
|
+
attr_reader :object_types
|
19
|
+
|
20
|
+
# When it enters a field, it's pushed on this stack (useful for nested fields, args).
|
21
|
+
# When it exits, it's popped off.
|
22
|
+
# @return [Array<GraphQL::Field>] fields which have been entered
|
23
|
+
attr_reader :field_definitions
|
24
|
+
|
25
|
+
# Directives are pushed on, then popped off while traversing the tree
|
26
|
+
# @return [Array<GraphQL::Node::Directive>] directives which have been entered
|
27
|
+
attr_reader :directive_definitions
|
28
|
+
|
29
|
+
# @return [Array<GraphQL::Node::Argument>] arguments which have been entered
|
30
|
+
attr_reader :argument_definitions
|
31
|
+
|
32
|
+
# @param schema [GraphQL::Schema] the schema whose types to use when climbing this document
|
33
|
+
# @param visitor [GraphQL::Language::Visitor] a visitor to follow & watch the types
|
34
|
+
def initialize(schema, visitor)
|
35
|
+
@schema = schema
|
36
|
+
@object_types = []
|
37
|
+
@field_definitions = []
|
38
|
+
@directive_definitions = []
|
39
|
+
@argument_definitions = []
|
40
|
+
visitor.enter << -> (node, parent) { PUSH_STRATEGIES[node.class].push(self, node) }
|
41
|
+
visitor.leave << -> (node, parent) { PUSH_STRATEGIES[node.class].pop(self, node) }
|
42
|
+
end
|
41
43
|
|
42
|
-
|
44
|
+
private
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
+
# Look up strategies by name and use singleton instance to push and pop
|
47
|
+
PUSH_STRATEGIES = Hash.new { |hash, key| hash[key] = get_strategy_for_node_class(key) }
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
def self.get_strategy_for_node_class(node_class)
|
50
|
+
node_class_name = node_class.name.split("::").last
|
51
|
+
strategy_key = "#{node_class_name}Strategy"
|
52
|
+
const_defined?(strategy_key) ? const_get(strategy_key).new : NullStrategy.new
|
53
|
+
end
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
class FragmentWithTypeStrategy
|
56
|
+
def push(stack, node)
|
57
|
+
object_type = if node.type
|
58
|
+
stack.schema.types.fetch(node.type, nil)
|
59
|
+
else
|
60
|
+
stack.object_types.last
|
61
|
+
end
|
62
|
+
if !object_type.nil?
|
63
|
+
object_type = object_type.unwrap
|
64
|
+
end
|
65
|
+
stack.object_types.push(object_type)
|
66
|
+
end
|
67
|
+
|
68
|
+
def pop(stack, node)
|
69
|
+
stack.object_types.pop
|
70
|
+
end
|
58
71
|
end
|
59
|
-
stack.object_types.push(object_type)
|
60
|
-
end
|
61
72
|
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
end
|
73
|
+
class FragmentDefinitionStrategy < FragmentWithTypeStrategy; end
|
74
|
+
class InlineFragmentStrategy < FragmentWithTypeStrategy; end
|
66
75
|
|
67
|
-
|
68
|
-
|
76
|
+
class OperationDefinitionStrategy
|
77
|
+
def push(stack, node)
|
78
|
+
# eg, QueryType, MutationType
|
79
|
+
object_type = stack.schema.public_send(node.operation_type)
|
80
|
+
stack.object_types.push(object_type)
|
81
|
+
end
|
69
82
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
stack.object_types.push(object_type)
|
75
|
-
end
|
76
|
-
def pop(stack, node)
|
77
|
-
stack.object_types.pop
|
78
|
-
end
|
79
|
-
end
|
83
|
+
def pop(stack, node)
|
84
|
+
stack.object_types.pop
|
85
|
+
end
|
86
|
+
end
|
80
87
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
88
|
+
class FieldStrategy
|
89
|
+
def push(stack, node)
|
90
|
+
parent_type = stack.object_types.last
|
91
|
+
parent_type = parent_type.unwrap
|
92
|
+
if parent_type.kind.fields?
|
93
|
+
field_class = stack.schema.get_field(parent_type, node.name)
|
94
|
+
stack.field_definitions.push(field_class)
|
95
|
+
if !field_class.nil?
|
96
|
+
next_object_type = field_class.type
|
97
|
+
stack.object_types.push(next_object_type)
|
98
|
+
else
|
99
|
+
stack.object_types.push(nil)
|
100
|
+
end
|
101
|
+
else
|
102
|
+
stack.field_definitions.push(nil)
|
103
|
+
stack.object_types.push(parent_type)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def pop(stack, node)
|
108
|
+
stack.field_definitions.pop
|
109
|
+
stack.object_types.pop
|
93
110
|
end
|
94
|
-
else
|
95
|
-
stack.field_definitions.push(nil)
|
96
|
-
stack.object_types.push(parent_type)
|
97
111
|
end
|
98
|
-
end
|
99
112
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
113
|
+
class DirectiveStrategy
|
114
|
+
def push(stack, node)
|
115
|
+
directive_defn = stack.schema.directives[node.name]
|
116
|
+
stack.directive_definitions.push(directive_defn)
|
117
|
+
end
|
105
118
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
end
|
119
|
+
def pop(stack, node)
|
120
|
+
stack.directive_definitions.pop
|
121
|
+
end
|
122
|
+
end
|
111
123
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
124
|
+
class ArgumentStrategy
|
125
|
+
# Push `argument_defn` onto the stack.
|
126
|
+
# It's possible that `argument_defn` will be nil.
|
127
|
+
# Push it anyways so `pop` has something to pop.
|
128
|
+
def push(stack, node)
|
129
|
+
if stack.argument_definitions.last
|
130
|
+
arg_type = stack.argument_definitions.last.type.unwrap
|
131
|
+
if arg_type.kind.input_object?
|
132
|
+
argument_defn = arg_type.input_fields[node.name]
|
133
|
+
else
|
134
|
+
argument_defn = nil
|
135
|
+
end
|
136
|
+
elsif stack.directive_definitions.last
|
137
|
+
argument_defn = stack.directive_definitions.last.arguments[node.name]
|
138
|
+
elsif stack.field_definitions.last
|
139
|
+
argument_defn = stack.field_definitions.last.arguments[node.name]
|
140
|
+
else
|
141
|
+
argument_defn = nil
|
142
|
+
end
|
143
|
+
stack.argument_definitions.push(argument_defn)
|
144
|
+
end
|
116
145
|
|
117
|
-
|
118
|
-
|
119
|
-
# It's possible that `argument_defn` will be nil.
|
120
|
-
# Push it anyways so `pop` has something to pop.
|
121
|
-
def push(stack, node)
|
122
|
-
if stack.argument_definitions.last
|
123
|
-
arg_type = stack.argument_definitions.last.type.unwrap
|
124
|
-
if arg_type.kind.input_object?
|
125
|
-
argument_defn = arg_type.input_fields[node.name]
|
126
|
-
else
|
127
|
-
argument_defn = nil
|
146
|
+
def pop(stack, node)
|
147
|
+
stack.argument_definitions.pop
|
128
148
|
end
|
129
|
-
elsif stack.directive_definitions.last
|
130
|
-
argument_defn = stack.directive_definitions.last.arguments[node.name]
|
131
|
-
elsif stack.field_definitions.last
|
132
|
-
argument_defn = stack.field_definitions.last.arguments[node.name]
|
133
|
-
else
|
134
|
-
argument_defn = nil
|
135
149
|
end
|
136
|
-
stack.argument_definitions.push(argument_defn)
|
137
|
-
end
|
138
150
|
|
139
|
-
|
140
|
-
|
151
|
+
# A no-op strategy (don't handle this node)
|
152
|
+
class NullStrategy
|
153
|
+
def self.new; self; end
|
154
|
+
def self.push(stack, node); end
|
155
|
+
def self.pop(stack, node); end
|
156
|
+
end
|
141
157
|
end
|
142
158
|
end
|
143
|
-
|
144
|
-
# A no-op strategy (don't handle this node)
|
145
|
-
class NullStrategy
|
146
|
-
def self.new; self; end
|
147
|
-
def self.push(stack, node); end
|
148
|
-
def self.pop(stack, node); end
|
149
|
-
end
|
150
159
|
end
|