graphql 0.0.4 → 0.1.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.
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