graphql 0.3.0 → 0.4.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/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
|
|