graphql 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graph_ql/directive.rb +36 -0
  3. data/lib/graph_ql/directives/directive_chain.rb +33 -0
  4. data/lib/graph_ql/directives/include_directive.rb +15 -0
  5. data/lib/graph_ql/directives/skip_directive.rb +15 -0
  6. data/lib/graph_ql/enum.rb +34 -0
  7. data/lib/graph_ql/fields/abstract_field.rb +37 -0
  8. data/lib/graph_ql/fields/access_field.rb +24 -0
  9. data/lib/graph_ql/fields/field.rb +34 -0
  10. data/lib/graph_ql/interface.rb +14 -0
  11. data/lib/graph_ql/introspection/arguments_field.rb +5 -0
  12. data/lib/graph_ql/introspection/directive_type.rb +12 -0
  13. data/lib/graph_ql/introspection/enum_value_type.rb +10 -0
  14. data/lib/graph_ql/introspection/enum_values_field.rb +15 -0
  15. data/lib/graph_ql/introspection/field_type.rb +11 -0
  16. data/lib/graph_ql/introspection/fields_field.rb +14 -0
  17. data/lib/graph_ql/introspection/input_fields_field.rb +12 -0
  18. data/lib/graph_ql/introspection/input_value_type.rb +10 -0
  19. data/lib/graph_ql/introspection/of_type_field.rb +12 -0
  20. data/lib/graph_ql/introspection/possible_types_field.rb +12 -0
  21. data/lib/graph_ql/introspection/schema_type.rb +32 -0
  22. data/lib/graph_ql/introspection/type_kind_enum.rb +7 -0
  23. data/lib/graph_ql/introspection/type_type.rb +22 -0
  24. data/lib/graph_ql/parser/nodes.rb +72 -0
  25. data/lib/graph_ql/parser/parser.rb +108 -0
  26. data/lib/graph_ql/parser/transform.rb +86 -0
  27. data/lib/graph_ql/parser/visitor.rb +47 -0
  28. data/lib/graph_ql/query.rb +50 -0
  29. data/lib/graph_ql/query/arguments.rb +25 -0
  30. data/lib/graph_ql/query/field_resolution_strategy.rb +83 -0
  31. data/lib/graph_ql/query/fragment_spread_resolution_strategy.rb +16 -0
  32. data/lib/graph_ql/query/inline_fragment_resolution_strategy.rb +14 -0
  33. data/lib/graph_ql/query/operation_resolver.rb +28 -0
  34. data/lib/graph_ql/query/selection_resolver.rb +20 -0
  35. data/lib/graph_ql/query/type_resolver.rb +19 -0
  36. data/lib/graph_ql/repl.rb +27 -0
  37. data/lib/graph_ql/schema.rb +30 -0
  38. data/lib/graph_ql/schema/type_reducer.rb +44 -0
  39. data/lib/graph_ql/type_kinds.rb +15 -0
  40. data/lib/graph_ql/types/abstract_type.rb +14 -0
  41. data/lib/graph_ql/types/boolean_type.rb +6 -0
  42. data/lib/graph_ql/types/float_type.rb +6 -0
  43. data/lib/graph_ql/types/input_object_type.rb +17 -0
  44. data/lib/graph_ql/types/input_value.rb +10 -0
  45. data/lib/graph_ql/types/int_type.rb +6 -0
  46. data/lib/graph_ql/types/list_type.rb +10 -0
  47. data/lib/graph_ql/types/non_null_type.rb +18 -0
  48. data/lib/graph_ql/types/non_null_with_bang.rb +5 -0
  49. data/lib/graph_ql/types/object_type.rb +62 -0
  50. data/lib/graph_ql/types/scalar_type.rb +5 -0
  51. data/lib/graph_ql/types/string_type.rb +6 -0
  52. data/lib/graph_ql/types/type_definer.rb +16 -0
  53. data/lib/graph_ql/union.rb +35 -0
  54. data/lib/graph_ql/validations/fields_are_defined_on_type.rb +44 -0
  55. data/lib/graph_ql/validations/fields_will_merge.rb +80 -0
  56. data/lib/graph_ql/validations/fragments_are_used.rb +24 -0
  57. data/lib/graph_ql/validator.rb +29 -0
  58. data/lib/graph_ql/version.rb +3 -0
  59. data/lib/graphql.rb +92 -99
  60. data/readme.md +17 -177
  61. data/spec/graph_ql/directive_spec.rb +81 -0
  62. data/spec/graph_ql/enum_spec.rb +5 -0
  63. data/spec/graph_ql/fields/field_spec.rb +10 -0
  64. data/spec/graph_ql/interface_spec.rb +13 -0
  65. data/spec/graph_ql/introspection/directive_type_spec.rb +40 -0
  66. data/spec/graph_ql/introspection/schema_type_spec.rb +39 -0
  67. data/spec/graph_ql/introspection/type_type_spec.rb +104 -0
  68. data/spec/graph_ql/parser/parser_spec.rb +120 -0
  69. data/spec/graph_ql/parser/transform_spec.rb +109 -0
  70. data/spec/graph_ql/parser/visitor_spec.rb +31 -0
  71. data/spec/graph_ql/query/operation_resolver_spec.rb +14 -0
  72. data/spec/graph_ql/query_spec.rb +82 -0
  73. data/spec/graph_ql/schema/type_reducer_spec.rb +24 -0
  74. data/spec/graph_ql/types/input_object_type_spec.rb +12 -0
  75. data/spec/graph_ql/types/object_type_spec.rb +35 -0
  76. data/spec/graph_ql/union_spec.rb +27 -0
  77. data/spec/graph_ql/validations/fields_are_defined_on_type_spec.rb +28 -0
  78. data/spec/graph_ql/validations/fields_will_merge_spec.rb +40 -0
  79. data/spec/graph_ql/validations/fragments_are_used_spec.rb +28 -0
  80. data/spec/graph_ql/validator_spec.rb +24 -0
  81. data/spec/spec_helper.rb +2 -2
  82. data/spec/support/dummy_app.rb +123 -63
  83. data/spec/support/dummy_data.rb +11 -0
  84. metadata +107 -59
  85. data/lib/graphql/call.rb +0 -8
  86. data/lib/graphql/connection.rb +0 -65
  87. data/lib/graphql/field.rb +0 -12
  88. data/lib/graphql/field_definer.rb +0 -25
  89. data/lib/graphql/introspection/call_type.rb +0 -13
  90. data/lib/graphql/introspection/connection.rb +0 -9
  91. data/lib/graphql/introspection/field_type.rb +0 -10
  92. data/lib/graphql/introspection/root_call_argument_node.rb +0 -5
  93. data/lib/graphql/introspection/root_call_type.rb +0 -20
  94. data/lib/graphql/introspection/schema_call.rb +0 -8
  95. data/lib/graphql/introspection/schema_type.rb +0 -17
  96. data/lib/graphql/introspection/type_call.rb +0 -8
  97. data/lib/graphql/introspection/type_type.rb +0 -18
  98. data/lib/graphql/node.rb +0 -244
  99. data/lib/graphql/parser/parser.rb +0 -39
  100. data/lib/graphql/parser/transform.rb +0 -22
  101. data/lib/graphql/query.rb +0 -109
  102. data/lib/graphql/root_call.rb +0 -202
  103. data/lib/graphql/root_call_argument.rb +0 -11
  104. data/lib/graphql/root_call_argument_definer.rb +0 -17
  105. data/lib/graphql/schema/all.rb +0 -46
  106. data/lib/graphql/schema/schema.rb +0 -87
  107. data/lib/graphql/schema/schema_validation.rb +0 -32
  108. data/lib/graphql/syntax/call.rb +0 -8
  109. data/lib/graphql/syntax/field.rb +0 -9
  110. data/lib/graphql/syntax/fragment.rb +0 -7
  111. data/lib/graphql/syntax/node.rb +0 -8
  112. data/lib/graphql/syntax/query.rb +0 -8
  113. data/lib/graphql/syntax/variable.rb +0 -7
  114. data/lib/graphql/types/boolean_type.rb +0 -3
  115. data/lib/graphql/types/number_type.rb +0 -3
  116. data/lib/graphql/types/object_type.rb +0 -6
  117. data/lib/graphql/types/string_type.rb +0 -3
  118. data/lib/graphql/version.rb +0 -3
  119. data/spec/graphql/node_spec.rb +0 -69
  120. data/spec/graphql/parser/parser_spec.rb +0 -168
  121. data/spec/graphql/parser/transform_spec.rb +0 -157
  122. data/spec/graphql/query_spec.rb +0 -274
  123. data/spec/graphql/root_call_spec.rb +0 -69
  124. data/spec/graphql/schema/schema_spec.rb +0 -93
  125. data/spec/graphql/schema/schema_validation_spec.rb +0 -48
  126. data/spec/support/nodes.rb +0 -175
@@ -0,0 +1,16 @@
1
+ class GraphQL::Query::FragmentSpreadResolutionStrategy
2
+ attr_reader :result
3
+ def initialize(ast_fragment_spread, type, target, operation_resolver)
4
+ fragments = operation_resolver.query.fragments
5
+ fragment_def = fragments[ast_fragment_spread.name]
6
+ child_type = operation_resolver.query.schema.types[fragment_def.type]
7
+ resolved_type = GraphQL::Query::TypeResolver.new(target, child_type, type).type
8
+ if resolved_type.nil?
9
+ @result = {}
10
+ else
11
+ selections = fragment_def.selections
12
+ resolver = GraphQL::Query::SelectionResolver.new(target, resolved_type, selections, operation_resolver)
13
+ @result = resolver.result
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ class GraphQL::Query::InlineFragmentResolutionStrategy
2
+ attr_reader :result
3
+ def initialize(ast_inline_fragment, type, target, operation_resolver)
4
+ child_type = operation_resolver.query.schema.types[ast_inline_fragment.type]
5
+ resolved_type = GraphQL::Query::TypeResolver.new(target, child_type, type).type
6
+ if resolved_type.nil?
7
+ @result = {}
8
+ else
9
+ selections = ast_inline_fragment.selections
10
+ resolver = GraphQL::Query::SelectionResolver.new(target, resolved_type, selections, operation_resolver)
11
+ @result = resolver.result
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ class GraphQL::Query::OperationResolver
2
+ extend GraphQL::Forwardable
3
+ attr_reader :variables, :query
4
+
5
+ def initialize(operation_definition, query)
6
+ @operation_definition = operation_definition
7
+ @variables = query.params
8
+ @query = query
9
+ end
10
+
11
+ delegate :context, to: :query
12
+
13
+ def result
14
+ @result ||= execute(@operation_definition, query)
15
+ end
16
+
17
+ private
18
+
19
+ def execute(op_def, query)
20
+ root = if op_def.operation_type == "query"
21
+ query.schema.query
22
+ elsif op_def.operation_type == "mutation"
23
+ query.schema.mutation
24
+ end
25
+ resolver = GraphQL::Query::SelectionResolver.new(nil, root, op_def.selections, self)
26
+ resolver.result
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ class GraphQL::Query::SelectionResolver
2
+ attr_reader :result
3
+
4
+ RESOLUTION_STRATEGIES = {
5
+ GraphQL::Nodes::Field => GraphQL::Query::FieldResolutionStrategy,
6
+ GraphQL::Nodes::FragmentSpread => GraphQL::Query::FragmentSpreadResolutionStrategy,
7
+ GraphQL::Nodes::InlineFragment => GraphQL::Query::InlineFragmentResolutionStrategy,
8
+ }
9
+
10
+ def initialize(target, type, selections, operation_resolver)
11
+ @result = selections.reduce({}) do |memo, ast_field|
12
+ chain = GraphQL::DirectiveChain.new(ast_field, operation_resolver) {
13
+ strategy_class = RESOLUTION_STRATEGIES[ast_field.class]
14
+ strategy = strategy_class.new(ast_field, type, target, operation_resolver)
15
+ strategy.result
16
+ }
17
+ memo.merge(chain.result)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ # Given an object, a type name (from the query) and a type object,
2
+ # Return the type that should be used for `object`
3
+ # or Return `nil` if it's a mismatch
4
+ class GraphQL::Query::TypeResolver
5
+ attr_reader :type
6
+ def initialize(target, child_type, parent_type)
7
+ @type = if child_type.nil?
8
+ nil
9
+ elsif GraphQL::TypeKinds::UNION == parent_type.kind
10
+ parent_type.resolve_type(target)
11
+ elsif GraphQL::TypeKinds::INTERFACE == child_type.kind
12
+ child_type.resolve_type(target)
13
+ elsif child_type == parent_type
14
+ parent_type
15
+ else
16
+ nil
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ require 'pp'
2
+ class GraphQL::Repl
3
+ def initialize(schema)
4
+ @schema = schema
5
+ end
6
+
7
+ def run
8
+ puts "Starting a repl for schema (type 'quit' to exit)"
9
+ while line = gets do
10
+ if line == "quit\n"
11
+ exit
12
+ end
13
+ execute_query(line)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def execute_query(query_string)
20
+ begin
21
+ query = GraphQL::Query.new(@schema, query_string)
22
+ puts JSON.pretty_generate(query.execute)
23
+ rescue StandardError => err
24
+ puts "Couldn't parse: #{err}\n\n" # #{err.backtrace.join("\n")}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ class GraphQL::Schema
2
+ extend ActiveSupport::Autoload
3
+ autoload(:TypeReducer)
4
+ DIRECTIVES = [GraphQL::SkipDirective, GraphQL::IncludeDirective]
5
+
6
+ attr_reader :query, :mutation, :directives
7
+ def initialize(query:, mutation:)
8
+ # Add fields to this query root for introspection:
9
+ query.fields = query.fields.merge({
10
+ "__type" => GraphQL::Field.new do |f|
11
+ f.description("A type in the GraphQL system")
12
+ f.type(!GraphQL::TypeType)
13
+ f.resolve -> (o, a, c) { self.types[a["name"]] || raise("No type found in schema for '#{a["name"]}'") }
14
+ end,
15
+ "__schema" => GraphQL::Field.new do |f|
16
+ f.description("This GraphQL schema")
17
+ f.type(!GraphQL::SchemaType)
18
+ f.resolve -> (o, a, c) { self }
19
+ end
20
+ })
21
+
22
+ @query = query
23
+ @mutation = mutation
24
+ @directives = DIRECTIVES.reduce({}) { |m, d| m[d.name] = d; m }
25
+ end
26
+
27
+ def types
28
+ @types ||= TypeReducer.new(query, {}).result
29
+ end
30
+ end
@@ -0,0 +1,44 @@
1
+ class GraphQL::Schema::TypeReducer
2
+ FIELDS_TYPE_KINDS = [GraphQL::TypeKinds::OBJECT, GraphQL::TypeKinds::INTERFACE]
3
+ POSSIBLE_TYPES_TYPE_KINDS = [GraphQL::TypeKinds::INTERFACE, GraphQL::TypeKinds::UNION]
4
+ attr_reader :type, :result
5
+ def initialize(type, existing_type_hash)
6
+ if [GraphQL::TypeKinds::NON_NULL, GraphQL::TypeKinds::LIST].include?(type.kind)
7
+ @result = reduce_type(type.of_type, existing_type_hash)
8
+ elsif existing_type_hash.has_key?(type.name)
9
+ # been here, done that
10
+ @result = existing_type_hash
11
+ else
12
+ @result = find_types(type, existing_type_hash.dup)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def find_types(type, type_hash)
19
+ type_hash[type.name] = type
20
+ if FIELDS_TYPE_KINDS.include?(type.kind)
21
+ type.fields.each do |name, field|
22
+ type_hash.merge!(reduce_type(field.type, type_hash))
23
+ field.arguments.each do |name, argument|
24
+ type_hash.merge!(reduce_type(argument[:type], type_hash))
25
+ end
26
+ end
27
+ end
28
+ if type.kind == GraphQL::TypeKinds::OBJECT
29
+ type.interfaces.each do |interface|
30
+ type_hash.merge!(reduce_type(interface, type_hash))
31
+ end
32
+ end
33
+ if POSSIBLE_TYPES_TYPE_KINDS.include?(type.kind)
34
+ type.possible_types.each do |possible_type|
35
+ type_hash.merge!(reduce_type(possible_type, type_hash))
36
+ end
37
+ end
38
+ type_hash
39
+ end
40
+
41
+ def reduce_type(type, type_hash)
42
+ self.class.new(type, type_hash).result
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ module GraphQL::TypeKinds
2
+ KIND_NAMES = %i{
3
+ SCALAR
4
+ OBJECT
5
+ INTERFACE
6
+ UNION
7
+ ENUM
8
+ INPUT_OBJECT
9
+ LIST
10
+ NON_NULL
11
+ }
12
+ KIND_NAMES.each do |type_kind|
13
+ const_set(type_kind, type_kind)
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # Any object can be a type as long as it implements:
2
+ # - #fields: Hash of { String => Field } pairs
3
+ # - #kind: one of GraphQL::TypeKinds
4
+ # - #interfaces: Array of Interfaces
5
+ # - #name: String
6
+ # - #description: String
7
+ #
8
+ class GraphQL::AbstractType
9
+ def fields; raise NotImplementedError; end
10
+ def kind; raise NotImplementedError; end
11
+ def interfaces; raise NotImplementedError; end
12
+ def name; raise NotImplementedError; end
13
+ def description; raise NotImplementedError; end
14
+ end
@@ -0,0 +1,6 @@
1
+ GraphQL::BOOLEAN_TYPE = GraphQL::ScalarType.new do
2
+ name "Boolean"
3
+ def coerce(value)
4
+ !!value
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ GraphQL::FLOAT_TYPE = GraphQL::ScalarType.new do
2
+ name "Float"
3
+ def coerce(value)
4
+ value.to_f
5
+ end
6
+ end
@@ -0,0 +1,17 @@
1
+ class GraphQL::InputObjectType < GraphQL::ObjectType
2
+ attr_definable :input_fields
3
+
4
+ def input_fields(new_fields=nil)
5
+ if new_fields.nil?
6
+ @new_fields
7
+ else
8
+ @new_fields = new_fields
9
+ .reduce({}) {|memo, (k, v)| memo[k.to_s] = v; memo}
10
+ .each { |k, v| v.respond_to?("name=") && v.name = k}
11
+ end
12
+ end
13
+
14
+ def kind
15
+ GraphQL::TypeKinds::INPUT_OBJECT
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ class GraphQL::InputValue
2
+ attr_reader :type, :description, :default_value
3
+ attr_accessor :name
4
+ def initialize(type:, description:, default_value:, name: nil)
5
+ @type = type
6
+ @description = description,
7
+ @default_value = default_value
8
+ @name = name
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+ GraphQL::INT_TYPE = GraphQL::ScalarType.new do
2
+ name "Int"
3
+ def coerce(value)
4
+ value.to_i
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ class GraphQL::ListType < GraphQL::ObjectType
2
+ attr_reader :of_type
3
+ def initialize(of_type:)
4
+ @of_type = of_type
5
+ end
6
+
7
+ def kind
8
+ GraphQL::TypeKinds::LIST
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ class GraphQL::NonNullType < GraphQL::ObjectType
2
+ attr_reader :of_type
3
+ def initialize(of_type:)
4
+ @of_type = of_type
5
+ end
6
+
7
+ def name
8
+ "Non-Null"
9
+ end
10
+
11
+ def coerce(value)
12
+ of_type.coerce(value)
13
+ end
14
+
15
+ def kind
16
+ GraphQL::TypeKinds::NON_NULL
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ module GraphQL::NonNullWithBang
2
+ def !
3
+ GraphQL::NonNullType.new(of_type: self)
4
+ end
5
+ end
@@ -0,0 +1,62 @@
1
+ class GraphQL::ObjectType
2
+ extend GraphQL::Definable
3
+ attr_definable :name, :description, :interfaces, :fields
4
+ include GraphQL::NonNullWithBang
5
+
6
+ def initialize(&block)
7
+ self.fields = []
8
+ self.interfaces = []
9
+ instance_eval(&block)
10
+ end
11
+
12
+ def fields(new_fields=nil)
13
+ if new_fields
14
+ self.fields = new_fields
15
+ else
16
+ @fields
17
+ end
18
+ end
19
+
20
+ def fields=(new_fields)
21
+ stringified_fields = new_fields
22
+ .reduce({}) { |memo, (key, value)| memo[key.to_s] = value; memo }
23
+ # Set the name from its context on this type:
24
+ stringified_fields.each {|k, v| v.respond_to?("name=") && v.name = k }
25
+ stringified_fields["__typename"] = GraphQL::Field.new do |f|
26
+ f.name "__typename"
27
+ f.description "The name of this type"
28
+ f.type -> { !GraphQL::STRING_TYPE }
29
+ f.resolve -> (o, a, c) { self.name }
30
+ end
31
+ @fields = stringified_fields
32
+ end
33
+
34
+ def field(type:, args: {}, property: nil, desc: "", deprecation_reason: nil)
35
+ GraphQL::AccessField.new(type: type, arguments: args, property: property, description: desc, deprecation_reason: deprecation_reason)
36
+ end
37
+
38
+ def arg(type:, desc: "", default_value: nil)
39
+ GraphQL::InputValue.new(type: type, description: desc, default_value: default_value)
40
+ end
41
+
42
+ def type
43
+ @type ||= GraphQL::TypeDefiner.new
44
+ end
45
+
46
+ def interfaces(new_interfaces=nil)
47
+ if new_interfaces.nil?
48
+ @interfaces
49
+ else
50
+ @interfaces = new_interfaces
51
+ new_interfaces.each {|i| i.possible_types << self }
52
+ end
53
+ end
54
+
55
+ def kind
56
+ GraphQL::TypeKinds::OBJECT
57
+ end
58
+
59
+ def to_s
60
+ name
61
+ end
62
+ end
@@ -0,0 +1,5 @@
1
+ class GraphQL::ScalarType < GraphQL::ObjectType
2
+ def kind
3
+ GraphQL::TypeKinds::SCALAR
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ GraphQL::STRING_TYPE = GraphQL::ScalarType.new do
2
+ name "String"
3
+ def coerce(value)
4
+ value.to_s
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ class GraphQL::TypeDefiner
2
+ TYPES = {
3
+ Int: GraphQL::INT_TYPE,
4
+ String: GraphQL::STRING_TYPE,
5
+ Float: GraphQL::FLOAT_TYPE,
6
+ Boolean: GraphQL::BOOLEAN_TYPE,
7
+ }
8
+
9
+ TYPES.each do |method_name, type|
10
+ define_method(method_name) { type }
11
+ end
12
+
13
+ def [](type)
14
+ GraphQL::ListType.new(of_type: type)
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ class GraphQL::Union
2
+ include GraphQL::NonNullWithBang
3
+ attr_reader :name, :possible_types
4
+ def initialize(name, types)
5
+ if types.length < 2
6
+ raise ArgumentError, "Union #{name} must be defined with 2 or more types, not #{types.length}"
7
+ end
8
+
9
+ non_object_types = types.select {|t| t.kind != GraphQL::TypeKinds::OBJECT}
10
+ if non_object_types.any?
11
+ types_string = non_object_types.map{|t| "#{t.name} #{t.kind}"}.join(", ")
12
+ raise ArgumentError, "Unions can only consist of Object types, but #{name} has non-object types: #{types_string}"
13
+ end
14
+
15
+ @name = name
16
+ @possible_types = types
17
+ end
18
+
19
+ def kind; GraphQL::TypeKinds::UNION; end
20
+
21
+ def include?(type)
22
+ possible_types.include?(type)
23
+ end
24
+
25
+ # Find a type in this union for a given object.
26
+ # Reimplement if needed
27
+ def resolve_type(object)
28
+ type_name = object.class.name
29
+ possible_types.find {|t| t.name == type_name}
30
+ end
31
+
32
+ def to_s
33
+ "<GraphQL::Union #{name} [#{possible_types.map(&:name).join(", ")}]>"
34
+ end
35
+ end