graphql 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graph_ql/{types/input_value.rb → argument.rb} +4 -1
- data/lib/graph_ql/{scalars/boolean_type.rb → boolean_type.rb} +0 -0
- data/lib/graph_ql/definition_helpers.rb +10 -0
- data/lib/graph_ql/definition_helpers/argument_definer.rb +5 -2
- data/lib/graph_ql/definition_helpers/definable.rb +1 -1
- data/lib/graph_ql/definition_helpers/field_definer.rb +4 -1
- data/lib/graph_ql/definition_helpers/non_null_with_bang.rb +3 -1
- data/lib/graph_ql/definition_helpers/string_named_hash.rb +7 -1
- data/lib/graph_ql/definition_helpers/type_definer.rb +13 -1
- data/lib/graph_ql/directive.rb +14 -6
- data/lib/graph_ql/{directives → directive}/include_directive.rb +1 -1
- data/lib/graph_ql/{directives → directive}/skip_directive.rb +1 -1
- data/lib/graph_ql/enum_type.rb +66 -0
- data/lib/graph_ql/field.rb +49 -13
- data/lib/graph_ql/{scalars/float_type.rb → float_type.rb} +0 -0
- data/lib/graph_ql/{scalars/id_type.rb → id_type.rb} +0 -0
- data/lib/graph_ql/input_object_type.rb +33 -0
- data/lib/graph_ql/{scalars/int_type.rb → int_type.rb} +0 -0
- data/lib/graph_ql/interface_type.rb +33 -0
- data/lib/graph_ql/introspection/field_type.rb +1 -0
- data/lib/graph_ql/introspection/fields_field.rb +1 -0
- data/lib/graph_ql/introspection/input_value_type.rb +1 -1
- data/lib/graph_ql/introspection/introspection_query.rb +77 -0
- data/lib/graph_ql/introspection/schema_field.rb +13 -0
- data/lib/graph_ql/introspection/type_by_name_field.rb +14 -0
- data/lib/graph_ql/introspection/type_kind_enum.rb +1 -1
- data/lib/graph_ql/{types/list_type.rb → list_type.rb} +3 -0
- data/lib/graph_ql/{parser/nodes.rb → nodes.rb} +8 -1
- data/lib/graph_ql/{types/non_null_type.rb → non_null_type.rb} +3 -0
- data/lib/graph_ql/object_type.rb +92 -0
- data/lib/graph_ql/parser.rb +107 -6
- data/lib/graph_ql/query.rb +8 -0
- data/lib/graph_ql/query/arguments.rb +13 -4
- data/lib/graph_ql/{directives → query}/directive_chain.rb +2 -2
- data/lib/graph_ql/query/field_resolution_strategy.rb +2 -2
- data/lib/graph_ql/query/operation_resolver.rb +3 -3
- data/lib/graph_ql/query/selection_resolver.rb +1 -1
- data/lib/graph_ql/scalar_type.rb +9 -0
- data/lib/graph_ql/schema.rb +26 -17
- data/lib/graph_ql/schema/type_reducer.rb +7 -0
- data/lib/graph_ql/static_validation/arguments_validator.rb +1 -0
- data/lib/graph_ql/static_validation/message.rb +4 -1
- data/lib/graph_ql/static_validation/rules/fields_are_defined_on_type.rb +1 -0
- data/lib/graph_ql/static_validation/rules/fields_have_appropriate_selections.rb +1 -0
- data/lib/graph_ql/static_validation/type_stack.rb +24 -4
- data/lib/graph_ql/static_validation/validator.rb +27 -0
- data/lib/graph_ql/{scalars/string_type.rb → string_type.rb} +0 -0
- data/lib/graph_ql/transform.rb +87 -0
- data/lib/graph_ql/type_kinds.rb +9 -0
- data/lib/graph_ql/{types/union.rb → union_type.rb} +8 -4
- data/lib/graph_ql/version.rb +1 -1
- data/lib/graph_ql/{parser/visitor.rb → visitor.rb} +29 -4
- data/lib/graphql.rb +28 -11
- data/readme.md +11 -1
- data/spec/graph_ql/{types/enum_spec.rb → enum_type_spec.rb} +1 -1
- data/spec/graph_ql/{fields/field_spec.rb → field_spec.rb} +0 -0
- data/spec/graph_ql/{scalars/id_type_spec.rb → id_type_spec.rb} +0 -0
- data/spec/graph_ql/{types/input_object_type_spec.rb → input_object_type_spec.rb} +0 -0
- data/spec/graph_ql/{types/interface_spec.rb → interface_type_spec.rb} +1 -1
- data/spec/graph_ql/introspection/introspection_query_spec.rb +10 -0
- data/spec/graph_ql/introspection/schema_type_spec.rb +0 -4
- data/spec/graph_ql/introspection/type_type_spec.rb +1 -5
- data/spec/graph_ql/{types/object_type_spec.rb → object_type_spec.rb} +0 -0
- data/spec/graph_ql/{parser/parser_spec.rb → parser_spec.rb} +0 -0
- data/spec/graph_ql/query/operation_resolver_spec.rb +1 -1
- data/spec/graph_ql/query_spec.rb +6 -2
- data/spec/graph_ql/schema/type_validator_spec.rb +1 -1
- data/spec/graph_ql/{parser/transform_spec.rb → transform_spec.rb} +0 -0
- data/spec/graph_ql/{types/union_spec.rb → union_type_spec.rb} +2 -2
- data/spec/graph_ql/{parser/visitor_spec.rb → visitor_spec.rb} +0 -0
- data/spec/support/{dummy_app.rb → dairy_app.rb} +8 -8
- data/spec/support/{dummy_data.rb → dairy_data.rb} +0 -0
- data/spec/support/star_wars_data.rb +71 -0
- data/spec/support/star_wars_schema.rb +87 -0
- metadata +59 -50
- data/lib/graph_ql/definition_helpers/forwardable.rb +0 -10
- data/lib/graph_ql/parser/parser.rb +0 -108
- data/lib/graph_ql/parser/transform.rb +0 -87
- data/lib/graph_ql/scalars/scalar_type.rb +0 -5
- data/lib/graph_ql/types/enum.rb +0 -32
- data/lib/graph_ql/types/input_object_type.rb +0 -14
- data/lib/graph_ql/types/interface.rb +0 -14
- data/lib/graph_ql/types/object_type.rb +0 -66
@@ -13,6 +13,13 @@ class GraphQL::Schema::TypeReducer
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
# Reduce all of `types` and return the combined result
|
17
|
+
def self.find_all(types)
|
18
|
+
types.reduce({}) do |memo, type|
|
19
|
+
self.new(type, memo).result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
16
23
|
private
|
17
24
|
|
18
25
|
def find_types(type, type_hash)
|
@@ -5,6 +5,7 @@ class GraphQL::StaticValidation::ArgumentsValidator
|
|
5
5
|
def validate(context)
|
6
6
|
visitor = context.visitor
|
7
7
|
visitor[GraphQL::Nodes::Field] << -> (node, parent) {
|
8
|
+
return if context.skip_field?(node.name)
|
8
9
|
field_defn = context.field_definition
|
9
10
|
validate_node(node, field_defn, context)
|
10
11
|
}
|
@@ -8,12 +8,15 @@ class GraphQL::StaticValidation::Message
|
|
8
8
|
GraphQL::StaticValidation::Message.new(message, line: node.line, col: node.col)
|
9
9
|
end
|
10
10
|
end
|
11
|
-
attr_reader :message, :line, :
|
11
|
+
attr_reader :message, :line, :col
|
12
|
+
|
12
13
|
def initialize(message, line: nil, col: nil)
|
13
14
|
@message = message
|
14
15
|
@line = line
|
15
16
|
@col = col
|
16
17
|
end
|
18
|
+
|
19
|
+
# A hash representation of this Message
|
17
20
|
def to_h
|
18
21
|
{
|
19
22
|
"message" => message,
|
@@ -6,6 +6,7 @@ class GraphQL::StaticValidation::FieldsAreDefinedOnType
|
|
6
6
|
def validate(context)
|
7
7
|
visitor = context.visitor
|
8
8
|
visitor[GraphQL::Nodes::Field] << -> (node, parent) {
|
9
|
+
return if context.skip_field?(node.name)
|
9
10
|
parent_type = context.object_types[-2]
|
10
11
|
parent_type = parent_type.kind.unwrap(parent_type)
|
11
12
|
validate_field(context.errors, node, parent_type, parent)
|
@@ -5,6 +5,7 @@ class GraphQL::StaticValidation::FieldsHaveAppropriateSelections
|
|
5
5
|
|
6
6
|
def validate(context)
|
7
7
|
context.visitor[GraphQL::Nodes::Field] << -> (node, parent) {
|
8
|
+
return if context.skip_field?(node.name)
|
8
9
|
field_defn = context.field_definition
|
9
10
|
validate_field_selections(node, field_defn, context.errors)
|
10
11
|
}
|
@@ -7,7 +7,25 @@ class GraphQL::StaticValidation::TypeStack
|
|
7
7
|
GraphQL::Nodes::FragmentDefinition,
|
8
8
|
]
|
9
9
|
|
10
|
-
|
10
|
+
# @return [GraphQL::Schema] the schema whose types are present in this document
|
11
|
+
attr_reader :schema
|
12
|
+
|
13
|
+
# When it enters an object (starting with query or mutation root), it's pushed on this stack.
|
14
|
+
# When it exits, it's popped off.
|
15
|
+
# @return [Array<GraphQL::ObjectType, GraphQL::Union, GraphQL::Interface>]
|
16
|
+
attr_reader :object_types
|
17
|
+
|
18
|
+
# When it enters a field, it's pushed on this stack (useful for nested fields, args).
|
19
|
+
# When it exits, it's popped off.
|
20
|
+
# @return [Array<GraphQL::Field>] fields which have been entered
|
21
|
+
attr_reader :field_definitions
|
22
|
+
|
23
|
+
# Directives are pushed on, then popped off while traversing the tree
|
24
|
+
# @return [Array<GraphQL::Node::Directive>] directives which have been entered
|
25
|
+
attr_reader :directive_definitions
|
26
|
+
|
27
|
+
# @param schema [GraphQL::Schema] the schema whose types to use when climbing this document
|
28
|
+
# @param visitor [GraphQL::Visitor] a visitor to follow & watch the types
|
11
29
|
def initialize(schema, visitor)
|
12
30
|
@schema = schema
|
13
31
|
@object_types = []
|
@@ -61,7 +79,7 @@ class GraphQL::StaticValidation::TypeStack
|
|
61
79
|
parent_type = stack.object_types.last
|
62
80
|
parent_type = parent_type.kind.unwrap(parent_type)
|
63
81
|
if parent_type.kind.fields?
|
64
|
-
field_class = parent_type
|
82
|
+
field_class = stack.schema.get_field(parent_type, node.name)
|
65
83
|
stack.field_definitions.push(field_class)
|
66
84
|
if !field_class.nil?
|
67
85
|
next_object_type = field_class.type
|
@@ -92,8 +110,10 @@ class GraphQL::StaticValidation::TypeStack
|
|
92
110
|
end
|
93
111
|
end
|
94
112
|
|
113
|
+
# A no-op strategy (don't handle this node)
|
95
114
|
class NullStrategy
|
96
|
-
def
|
97
|
-
def
|
115
|
+
def self.new; self; end
|
116
|
+
def self.push(stack, node); end
|
117
|
+
def self.pop(stack, node); end
|
98
118
|
end
|
99
119
|
end
|
@@ -1,12 +1,23 @@
|
|
1
1
|
# Initialized with a {GraphQL::Schema}, then it can validate {GraphQL::Nodes::Documents}s based on that schema.
|
2
2
|
#
|
3
3
|
# By default, it's used by {GraphQL::Query}
|
4
|
+
#
|
5
|
+
# @example Validate a query
|
6
|
+
# validator = GraphQL::StaticValidation::Validator.new(schema: MySchema)
|
7
|
+
# document = GraphQL.parse(query_string)
|
8
|
+
# errors = validator.validate(document)
|
9
|
+
#
|
4
10
|
class GraphQL::StaticValidation::Validator
|
11
|
+
# @param schema [GraphQL::Schema]
|
12
|
+
# @param rule [Array<#validate(context)>] a list of rules to use when validating
|
5
13
|
def initialize(schema:, rules: GraphQL::StaticValidation::ALL_RULES)
|
6
14
|
@schema = schema
|
7
15
|
@rules = rules
|
8
16
|
end
|
9
17
|
|
18
|
+
# Validate `document` against the schema. Returns an array of message hashes.
|
19
|
+
# @param document [GraphQL::Nodes::Document]
|
20
|
+
# @return [Array<Hash>]
|
10
21
|
def validate(document)
|
11
22
|
context = Context.new(@schema, document)
|
12
23
|
@rules.each do |rules|
|
@@ -16,6 +27,16 @@ class GraphQL::StaticValidation::Validator
|
|
16
27
|
context.errors.map(&:to_h)
|
17
28
|
end
|
18
29
|
|
30
|
+
# The validation context gets passed to each validator.
|
31
|
+
#
|
32
|
+
# It exposes a {GraphQL::Visitor} where validators may add hooks. ({Visitor#visit} is called in {Validator#validate})
|
33
|
+
#
|
34
|
+
# It provides access to the schema & fragments which validators may read from.
|
35
|
+
#
|
36
|
+
# It holds a list of errors which each validator may add to.
|
37
|
+
#
|
38
|
+
# It also provides limited access to the {TypeStack} instance,
|
39
|
+
# which tracks state as you climb in and out of different fields.
|
19
40
|
class Context
|
20
41
|
attr_reader :schema, :document, :errors, :visitor, :fragments
|
21
42
|
def initialize(schema, document)
|
@@ -40,5 +61,11 @@ class GraphQL::StaticValidation::Validator
|
|
40
61
|
def directive_definition
|
41
62
|
@type_stack.directive_definitions.last
|
42
63
|
end
|
64
|
+
|
65
|
+
# Don't try to validate dynamic fields
|
66
|
+
# since they aren't defined by the type system
|
67
|
+
def skip_field?(field_name)
|
68
|
+
GraphQL::Schema::DYNAMIC_FIELDS.include?(field_name)
|
69
|
+
end
|
43
70
|
end
|
44
71
|
end
|
File without changes
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module GraphQL
|
2
|
+
# {Transform} is a [parslet](http://kschiess.github.io/parslet/) transform for for turning the AST into objects in {GraphQL::Nodes} objects.
|
3
|
+
class Transform < Parslet::Transform
|
4
|
+
def self.optional_sequence(name)
|
5
|
+
rule(name => simple(:val)) { [] }
|
6
|
+
rule(name => sequence(:val)) { val }
|
7
|
+
end
|
8
|
+
|
9
|
+
# Document
|
10
|
+
rule(document_parts: sequence(:p)) { Nodes::Document.new(parts: p, line: p.first.line, col: p.first.col)}
|
11
|
+
|
12
|
+
# Fragment Definition
|
13
|
+
rule(
|
14
|
+
fragment_keyword: simple(:kw),
|
15
|
+
fragment_name: simple(:name),
|
16
|
+
type_condition: simple(:type),
|
17
|
+
directives: sequence(:directives),
|
18
|
+
selections: sequence(:selections)
|
19
|
+
) { Nodes::FragmentDefinition.new(name: name.to_s, type: type.to_s, directives: directives, selections: selections, position_source: kw)}
|
20
|
+
|
21
|
+
rule(
|
22
|
+
fragment_spread_keyword: simple(:kw),
|
23
|
+
fragment_spread_name: simple(:n),
|
24
|
+
directives: sequence(:d)
|
25
|
+
) { Nodes::FragmentSpread.new(name: n.to_s, directives: d, position_source: kw)}
|
26
|
+
|
27
|
+
rule(
|
28
|
+
fragment_spread_keyword: simple(:kw),
|
29
|
+
inline_fragment_type: simple(:n),
|
30
|
+
directives: sequence(:d),
|
31
|
+
selections: sequence(:s),
|
32
|
+
) { Nodes::InlineFragment.new(type: n.to_s, directives: d, selections: s, position_source: kw)}
|
33
|
+
|
34
|
+
# Operation Definition
|
35
|
+
rule(
|
36
|
+
operation_type: simple(:ot),
|
37
|
+
name: simple(:n),
|
38
|
+
variables: sequence(:v),
|
39
|
+
directives: sequence(:d),
|
40
|
+
selections: sequence(:s),
|
41
|
+
) { Nodes::OperationDefinition.new(operation_type: ot.to_s, name: n.to_s, variables: v, directives: d, selections: s, position_source: ot) }
|
42
|
+
optional_sequence(:optional_variables)
|
43
|
+
rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: simple(:v)) { Nodes::Variable.new(name: n.name, type: t, default_value: v, line: n.line, col: n.col)}
|
44
|
+
rule(variable_name: simple(:n), variable_type: simple(:t), variable_optional_default_value: sequence(:v)) { Nodes::Variable.new(name: n.name, type: t, default_value: v, line: n.line, col: n.col)}
|
45
|
+
rule(variable_default_value: simple(:v) ) { v }
|
46
|
+
rule(variable_default_value: sequence(:v) ) { v }
|
47
|
+
# Query short-hand
|
48
|
+
rule(unnamed_selections: sequence(:s)) { Nodes::OperationDefinition.new(selections: s, operation_type: "query", name: nil, variables: [], directives: [], line: s.first.line, col: s.first.col)}
|
49
|
+
|
50
|
+
# Field
|
51
|
+
rule(
|
52
|
+
alias: simple(:a),
|
53
|
+
field_name: simple(:name),
|
54
|
+
field_arguments: sequence(:args),
|
55
|
+
directives: sequence(:dir),
|
56
|
+
selections: sequence(:sel)
|
57
|
+
) { Nodes::Field.new(alias: a && a.to_s, name: name.to_s, arguments: args, directives: dir, selections: sel, position_source: [a, name].find { |part| !part.nil? }) }
|
58
|
+
|
59
|
+
rule(alias_name: simple(:a)) { a }
|
60
|
+
optional_sequence(:optional_field_arguments)
|
61
|
+
rule(field_argument_name: simple(:n), field_argument_value: simple(:v)) { Nodes::Argument.new(name: n.to_s, value: v, position_source: n)}
|
62
|
+
optional_sequence(:optional_selections)
|
63
|
+
optional_sequence(:optional_directives)
|
64
|
+
|
65
|
+
# Directive
|
66
|
+
rule(directive_name: simple(:name), directive_arguments: sequence(:args)) { Nodes::Directive.new(name: name.to_s, arguments: args, position_source: name ) }
|
67
|
+
rule(directive_argument_name: simple(:n), directive_argument_value: simple(:v)) { Nodes::Argument.new(name: n.to_s, value: v, position_source: n)}
|
68
|
+
optional_sequence(:optional_directive_arguments)
|
69
|
+
|
70
|
+
# Type Defs
|
71
|
+
rule(type_name: simple(:n)) { Nodes::TypeName.new(name: n.to_s, position_source: n) }
|
72
|
+
rule(list_type: simple(:t)) { Nodes::ListType.new(of_type: t, line: t.line, col: t.col)}
|
73
|
+
rule(non_null_type: simple(:t)) { Nodes::NonNullType.new(of_type: t, line: t.line, col: t.col)}
|
74
|
+
|
75
|
+
# Values
|
76
|
+
rule(array: sequence(:v)) { v }
|
77
|
+
rule(boolean: simple(:v)) { v == "true" ? true : false }
|
78
|
+
rule(input_object: sequence(:v)) { Nodes::InputObject.new(pairs: v, line: v.first.line, col: v.first.col) }
|
79
|
+
rule(input_object_name: simple(:n), input_object_value: simple(:v)) { Nodes::Argument.new(name: n.to_s, value: v, position_source: n)}
|
80
|
+
rule(int: simple(:v)) { v.to_i }
|
81
|
+
rule(float: simple(:v)) { v.to_f }
|
82
|
+
rule(string: simple(:v)) { v.to_s }
|
83
|
+
rule(variable: simple(:v)) { Nodes::VariableIdentifier.new(name: v.to_s, position_source: v) }
|
84
|
+
rule(enum: simple(:v)) { Nodes::Enum.new(name: v.to_s, position_source: v)}
|
85
|
+
end
|
86
|
+
TRANSFORM = GraphQL::Transform.new
|
87
|
+
end
|
data/lib/graph_ql/type_kinds.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
# Type kinds are the basic categories which a type may belong to (`Object`, `Scalar`, `Union`...)
|
1
2
|
module GraphQL::TypeKinds
|
3
|
+
# These objects are singletons, eg `GraphQL::TypeKinds::UNION`, `GraphQL::TypeKinds::SCALAR`.
|
2
4
|
class TypeKind
|
3
5
|
attr_reader :name
|
4
6
|
def initialize(name, resolves: false, fields: false, wraps: false, input: false)
|
@@ -10,13 +12,19 @@ module GraphQL::TypeKinds
|
|
10
12
|
@composite = fields? || resolves?
|
11
13
|
end
|
12
14
|
|
15
|
+
# Does this TypeKind have multiple possible implementors?
|
13
16
|
def resolves?; @resolves; end
|
17
|
+
# Does this TypeKind have queryable fields?
|
14
18
|
def fields?; @fields; end
|
19
|
+
# Does this TypeKind modify another type?
|
15
20
|
def wraps?; @wraps; end
|
21
|
+
# Is this TypeKind a valid query input?
|
16
22
|
def input?; @input; end
|
17
23
|
def to_s; @name; end
|
24
|
+
# Is this TypeKind composed of many values?
|
18
25
|
def composite?; @composite; end
|
19
26
|
|
27
|
+
# Get the implementing type for `value` from `type` (no-op for TypeKinds which don't `resolves?`)
|
20
28
|
def resolve(type, value)
|
21
29
|
if resolves?
|
22
30
|
type.resolve_type(value)
|
@@ -25,6 +33,7 @@ module GraphQL::TypeKinds
|
|
25
33
|
end
|
26
34
|
end
|
27
35
|
|
36
|
+
# Get the modified type for `type` (no-op for TypeKinds which don't `wraps?`)
|
28
37
|
def unwrap(type)
|
29
38
|
if wraps?
|
30
39
|
wrapped_type = type.of_type
|
@@ -1,5 +1,10 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# A collection of {ObjectType}s
|
2
|
+
#
|
3
|
+
# @example a union of types
|
4
|
+
# PetUnion = GraphQL::UnionType.new("Pet", "House pets", [DogType, CatType])
|
5
|
+
#
|
6
|
+
class GraphQL::UnionType
|
7
|
+
include GraphQL::DefinitionHelpers::NonNullWithBang
|
3
8
|
attr_reader :name, :description, :possible_types
|
4
9
|
def initialize(name, desc, types)
|
5
10
|
@name = name
|
@@ -11,8 +16,7 @@ class GraphQL::Union
|
|
11
16
|
GraphQL::TypeKinds::UNION
|
12
17
|
end
|
13
18
|
|
14
|
-
#
|
15
|
-
# Reimplement if needed
|
19
|
+
# @see {InterfaceType#resolve_type}
|
16
20
|
def resolve_type(object)
|
17
21
|
type_name = object.class.name
|
18
22
|
possible_types.find {|t| t.name == type_name}
|
data/lib/graph_ql/version.rb
CHANGED
@@ -1,28 +1,44 @@
|
|
1
1
|
# Depth-first traversal through the tree, calling hooks at each stop.
|
2
2
|
#
|
3
|
-
# @example
|
3
|
+
# @example Create a visitor, add hooks, then search a document
|
4
4
|
# total_field_count = 0
|
5
5
|
# visitor = GraphQL::Visitor.new
|
6
|
+
# # Whenever you find a field, increment the field count:
|
6
7
|
# visitor[GraphQL::Nodes::Field] << -> (node) { total_field_count += 1 }
|
8
|
+
# # When we finish, print the field count:
|
7
9
|
# visitor[GraphQL::Nodes::Document].leave << -> (node) { p total_field_count }
|
8
10
|
# visitor.visit(document)
|
9
11
|
# # => 6
|
10
12
|
#
|
11
13
|
class GraphQL::Visitor
|
14
|
+
# If any hook returns this value, the {Visitor} stops visiting this
|
15
|
+
# node right away
|
12
16
|
SKIP = :_skip
|
13
17
|
|
14
|
-
|
18
|
+
# @return [Array<Proc>] Hooks to call when entering _any_ node
|
19
|
+
attr_reader :enter
|
20
|
+
# @return [Array<Proc>] Hooks to call when leaving _any_ node
|
21
|
+
attr_reader :leave
|
22
|
+
|
15
23
|
def initialize
|
16
24
|
@visitors = {}
|
17
25
|
@enter = []
|
18
26
|
@leave = []
|
19
27
|
end
|
20
28
|
|
29
|
+
# Get a {NodeVisitor} for `node_class`
|
30
|
+
# @param node_class [Class] The node class that you want to listen to
|
31
|
+
# @return [NodeVisitor]
|
32
|
+
#
|
33
|
+
# @example Run a hook whenever you enter a new Field
|
34
|
+
# visitor[GraphQL::Nodes::Field] << -> (node, parent) { p "Here's a field" }
|
21
35
|
def [](node_class)
|
22
36
|
@visitors[node_class] ||= NodeVisitor.new
|
23
37
|
end
|
24
38
|
|
25
|
-
#
|
39
|
+
# Visit `root` and all children, applying hooks as you go
|
40
|
+
# @param root [GraphQL::Nodes::AbstractNode] some node to start parsing on
|
41
|
+
# @return [void]
|
26
42
|
def visit(root, parent=nil)
|
27
43
|
begin_visit(root, parent) &&
|
28
44
|
root.children.reduce(true) { |memo, child| memo && visit(child, root) }
|
@@ -49,13 +65,22 @@ class GraphQL::Visitor
|
|
49
65
|
hooks.reduce(true) { |memo, proc| memo && (proc.call(node, parent) != SKIP) }
|
50
66
|
end
|
51
67
|
|
68
|
+
# Collect `enter` and `leave` hooks for classes in {GraphQL::Nodes}
|
69
|
+
#
|
70
|
+
# Access {NodeVisitor}s via {GraphQL::Visitor#[]}
|
52
71
|
class NodeVisitor
|
53
|
-
|
72
|
+
# @return [Array<Proc>] Hooks to call when entering a node of this type
|
73
|
+
attr_reader :enter
|
74
|
+
# @return [Array<Proc>] Hooks to call when leaving a node of this type
|
75
|
+
attr_reader :leave
|
76
|
+
|
54
77
|
def initialize
|
55
78
|
@enter = []
|
56
79
|
@leave = []
|
57
80
|
end
|
58
81
|
|
82
|
+
# Shorthand to add a hook to the {#enter} array
|
83
|
+
# @param hook [Proc] A hook to add
|
59
84
|
def <<(hook)
|
60
85
|
enter << hook
|
61
86
|
end
|
data/lib/graphql.rb
CHANGED
@@ -3,6 +3,10 @@ require "parslet"
|
|
3
3
|
require "singleton"
|
4
4
|
|
5
5
|
module GraphQL
|
6
|
+
# Turn a query string into an AST
|
7
|
+
# @param string [String] a GraphQL query string
|
8
|
+
# @param as [Symbol] If you want to use this to parse some _piece_ of a document, pass the rule name (from {GraphQL::Parser::Parser})
|
9
|
+
# @return [GraphQL::Nodes::Document]
|
6
10
|
def self.parse(string, as: nil)
|
7
11
|
parser = as ? GraphQL::PARSER.send(as) : GraphQL::PARSER
|
8
12
|
tree = parser.parse(string)
|
@@ -12,26 +16,33 @@ module GraphQL
|
|
12
16
|
raise [line, col, string].join(", ")
|
13
17
|
end
|
14
18
|
|
19
|
+
# Types & Fields that support GraphQL introspection queries
|
15
20
|
module Introspection; end
|
16
21
|
end
|
17
22
|
|
18
|
-
def require_dir(dir)
|
19
|
-
Dir.glob(File.expand_path("../graph_ql/#{dir}/*.rb", __FILE__)).each do |file|
|
20
|
-
require file
|
21
|
-
end
|
22
|
-
end
|
23
23
|
# Order matters for these:
|
24
24
|
|
25
|
-
|
26
|
-
require 'graph_ql/
|
27
|
-
|
25
|
+
require 'graph_ql/definition_helpers'
|
26
|
+
require 'graph_ql/object_type'
|
27
|
+
|
28
|
+
require 'graph_ql/enum_type'
|
29
|
+
require 'graph_ql/input_object_type'
|
30
|
+
require 'graph_ql/interface_type'
|
31
|
+
require 'graph_ql/list_type'
|
32
|
+
require 'graph_ql/non_null_type'
|
33
|
+
require 'graph_ql/union_type'
|
28
34
|
|
35
|
+
require 'graph_ql/argument'
|
29
36
|
require 'graph_ql/field'
|
30
37
|
require 'graph_ql/type_kinds'
|
31
38
|
require 'graph_ql/introspection/typename_field'
|
32
39
|
|
33
|
-
require 'graph_ql/
|
34
|
-
|
40
|
+
require 'graph_ql/scalar_type'
|
41
|
+
require 'graph_ql/boolean_type'
|
42
|
+
require 'graph_ql/float_type'
|
43
|
+
require 'graph_ql/id_type'
|
44
|
+
require 'graph_ql/int_type'
|
45
|
+
require 'graph_ql/string_type'
|
35
46
|
|
36
47
|
require 'graph_ql/introspection/input_value_type'
|
37
48
|
require 'graph_ql/introspection/enum_value_type'
|
@@ -45,13 +56,19 @@ require 'graph_ql/introspection/enum_values_field'
|
|
45
56
|
require 'graph_ql/introspection/interfaces_field'
|
46
57
|
|
47
58
|
require 'graph_ql/introspection/type_type'
|
59
|
+
require 'graph_ql/introspection/arguments_field'
|
48
60
|
require 'graph_ql/introspection/field_type'
|
49
61
|
|
50
|
-
require 'graph_ql/introspection/arguments_field'
|
51
62
|
require 'graph_ql/introspection/directive_type'
|
52
63
|
require 'graph_ql/introspection/schema_type'
|
64
|
+
require 'graph_ql/introspection/schema_field'
|
65
|
+
require 'graph_ql/introspection/type_by_name_field'
|
66
|
+
require 'graph_ql/introspection/introspection_query'
|
53
67
|
|
68
|
+
require 'graph_ql/nodes'
|
54
69
|
require 'graph_ql/parser'
|
70
|
+
require 'graph_ql/transform'
|
71
|
+
require 'graph_ql/visitor'
|
55
72
|
require 'graph_ql/directive'
|
56
73
|
require 'graph_ql/schema'
|
57
74
|
|