graphql 0.2.0 → 0.3.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graph_ql/{types → definition_helpers}/argument_definer.rb +0 -0
  3. data/lib/graph_ql/definition_helpers/definable.rb +18 -0
  4. data/lib/graph_ql/{types → definition_helpers}/field_definer.rb +0 -0
  5. data/lib/graph_ql/definition_helpers/forwardable.rb +10 -0
  6. data/lib/graph_ql/definition_helpers/non_null_with_bang.rb +9 -0
  7. data/lib/graph_ql/definition_helpers/string_named_hash.rb +12 -0
  8. data/lib/graph_ql/{types → definition_helpers}/type_definer.rb +1 -0
  9. data/lib/graph_ql/directive.rb +9 -5
  10. data/lib/graph_ql/directives/directive_chain.rb +1 -1
  11. data/lib/graph_ql/field.rb +1 -5
  12. data/lib/graph_ql/parser/transform.rb +1 -3
  13. data/lib/graph_ql/query.rb +14 -3
  14. data/lib/graph_ql/query/arguments.rb +6 -5
  15. data/lib/graph_ql/query/field_resolution_strategy.rb +3 -3
  16. data/lib/graph_ql/{types → scalars}/boolean_type.rb +0 -0
  17. data/lib/graph_ql/{types → scalars}/float_type.rb +1 -1
  18. data/lib/graph_ql/scalars/id_type.rb +6 -0
  19. data/lib/graph_ql/{types → scalars}/int_type.rb +0 -0
  20. data/lib/graph_ql/{types → scalars}/scalar_type.rb +0 -4
  21. data/lib/graph_ql/{types → scalars}/string_type.rb +0 -0
  22. data/lib/graph_ql/static_validation.rb +7 -10
  23. data/lib/graph_ql/static_validation/all_rules.rb +23 -0
  24. data/lib/graph_ql/static_validation/literal_validator.rb +0 -3
  25. data/lib/graph_ql/static_validation/{argument_literals_are_compatible.rb → rules/argument_literals_are_compatible.rb} +0 -0
  26. data/lib/graph_ql/static_validation/{arguments_are_defined.rb → rules/arguments_are_defined.rb} +5 -0
  27. data/lib/graph_ql/static_validation/{directives_are_defined.rb → rules/directives_are_defined.rb} +0 -0
  28. data/lib/graph_ql/static_validation/{fields_are_defined_on_type.rb → rules/fields_are_defined_on_type.rb} +0 -0
  29. data/lib/graph_ql/static_validation/{fields_have_appropriate_selections.rb → rules/fields_have_appropriate_selections.rb} +0 -0
  30. data/lib/graph_ql/static_validation/{fields_will_merge.rb → rules/fields_will_merge.rb} +13 -5
  31. data/lib/graph_ql/static_validation/rules/fragment_spreads_are_possible.rb +50 -0
  32. data/lib/graph_ql/static_validation/{fragment_types_exist.rb → rules/fragment_types_exist.rb} +0 -0
  33. data/lib/graph_ql/static_validation/rules/fragments_are_finite.rb +23 -0
  34. data/lib/graph_ql/static_validation/rules/fragments_are_on_composite_types.rb +27 -0
  35. data/lib/graph_ql/static_validation/{fragments_are_used.rb → rules/fragments_are_used.rb} +0 -0
  36. data/lib/graph_ql/static_validation/{required_arguments_are_present.rb → rules/required_arguments_are_present.rb} +0 -0
  37. data/lib/graph_ql/static_validation/rules/variable_default_values_are_correctly_typed.rb +24 -0
  38. data/lib/graph_ql/static_validation/rules/variable_usages_are_allowed.rb +47 -0
  39. data/lib/graph_ql/static_validation/rules/variables_are_input_types.rb +27 -0
  40. data/lib/graph_ql/static_validation/rules/variables_are_used_and_defined.rb +31 -0
  41. data/lib/graph_ql/static_validation/type_stack.rb +13 -1
  42. data/lib/graph_ql/static_validation/validator.rb +14 -18
  43. data/lib/graph_ql/type_kinds.rb +8 -4
  44. data/lib/graph_ql/{enum.rb → types/enum.rb} +7 -6
  45. data/lib/graph_ql/types/input_object_type.rb +3 -10
  46. data/lib/graph_ql/{interface.rb → types/interface.rb} +0 -4
  47. data/lib/graph_ql/types/list_type.rb +0 -4
  48. data/lib/graph_ql/types/non_null_type.rb +0 -4
  49. data/lib/graph_ql/types/object_type.rb +28 -13
  50. data/lib/graph_ql/{union.rb → types/union.rb} +0 -4
  51. data/lib/graph_ql/version.rb +1 -1
  52. data/lib/graphql.rb +9 -40
  53. data/readme.md +26 -20
  54. data/spec/graph_ql/directive_spec.rb +4 -4
  55. data/spec/graph_ql/introspection/directive_type_spec.rb +2 -2
  56. data/spec/graph_ql/introspection/schema_type_spec.rb +3 -2
  57. data/spec/graph_ql/introspection/type_type_spec.rb +7 -7
  58. data/spec/graph_ql/parser/parser_spec.rb +6 -2
  59. data/spec/graph_ql/query_spec.rb +26 -8
  60. data/spec/graph_ql/scalars/id_type_spec.rb +24 -0
  61. data/spec/graph_ql/schema/type_reducer_spec.rb +1 -0
  62. data/spec/graph_ql/schema/type_validator_spec.rb +1 -1
  63. data/spec/graph_ql/static_validation/{argument_literals_are_compatible_spec.rb → rules/argument_literals_are_compatible_spec.rb} +1 -1
  64. data/spec/graph_ql/static_validation/{arguments_are_defined_spec.rb → rules/arguments_are_defined_spec.rb} +1 -1
  65. data/spec/graph_ql/static_validation/{directives_are_defined_spec.rb → rules/directives_are_defined_spec.rb} +1 -1
  66. data/spec/graph_ql/static_validation/{fields_are_defined_on_type_spec.rb → rules/fields_are_defined_on_type_spec.rb} +1 -1
  67. data/spec/graph_ql/static_validation/{fields_have_appropriate_selections_spec.rb → rules/fields_have_appropriate_selections_spec.rb} +1 -1
  68. data/spec/graph_ql/static_validation/{fields_will_merge_spec.rb → rules/fields_will_merge_spec.rb} +1 -1
  69. data/spec/graph_ql/static_validation/rules/fragment_spreads_are_possible_spec.rb +46 -0
  70. data/spec/graph_ql/static_validation/{fragment_types_exist_spec.rb → rules/fragment_types_exist_spec.rb} +1 -1
  71. data/spec/graph_ql/static_validation/rules/fragments_are_finite_spec.rb +43 -0
  72. data/spec/graph_ql/static_validation/rules/fragments_are_on_composite_types_spec.rb +48 -0
  73. data/spec/graph_ql/static_validation/{fragments_are_used_spec.rb → rules/fragments_are_used_spec.rb} +1 -1
  74. data/spec/graph_ql/static_validation/{required_arguments_are_present_spec.rb → rules/required_arguments_are_present_spec.rb} +1 -1
  75. data/spec/graph_ql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +43 -0
  76. data/spec/graph_ql/static_validation/rules/variable_usages_are_allowed_spec.rb +45 -0
  77. data/spec/graph_ql/static_validation/rules/variables_are_input_types_spec.rb +36 -0
  78. data/spec/graph_ql/static_validation/rules/variables_are_used_and_defined_spec.rb +44 -0
  79. data/spec/graph_ql/static_validation/type_stack_spec.rb +2 -2
  80. data/spec/graph_ql/static_validation/validator_spec.rb +44 -5
  81. data/spec/graph_ql/types/enum_spec.rb +10 -0
  82. data/spec/graph_ql/{interface_spec.rb → types/interface_spec.rb} +1 -1
  83. data/spec/graph_ql/types/object_type_spec.rb +13 -0
  84. data/spec/graph_ql/{union_spec.rb → types/union_spec.rb} +0 -0
  85. data/spec/support/dummy_app.rb +7 -6
  86. data/spec/support/dummy_data.rb +3 -3
  87. metadata +74 -46
  88. data/lib/graph_ql/types/non_null_with_bang.rb +0 -5
  89. data/spec/graph_ql/enum_spec.rb +0 -5
@@ -0,0 +1,23 @@
1
+ class GraphQL::StaticValidation::FragmentsAreFinite
2
+ include GraphQL::StaticValidation::Message::MessageHelper
3
+
4
+ def validate(context)
5
+ context.visitor[GraphQL::Nodes::FragmentDefinition] << -> (node, parent) {
6
+ if has_nested_spread(node, [], context)
7
+ context.errors << message("Fragment #{node.name} contains an infinite loop", node)
8
+ end
9
+ }
10
+ end
11
+
12
+ private
13
+
14
+ def has_nested_spread(fragment_def, parent_fragment_names, context)
15
+ nested_spreads = fragment_def.selections
16
+ .select {|f| f.is_a?(GraphQL::Nodes::FragmentSpread)}
17
+
18
+ nested_spreads.any? do |spread|
19
+ nested_def = context.fragments[spread.name]
20
+ parent_fragment_names.include?(spread.name) || has_nested_spread(nested_def, parent_fragment_names + [fragment_def.name], context)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ class GraphQL::StaticValidation::FragmentsAreOnCompositeTypes
2
+ include GraphQL::StaticValidation::Message::MessageHelper
3
+
4
+ HAS_TYPE_CONDITION = [
5
+ GraphQL::Nodes::FragmentDefinition,
6
+ GraphQL::Nodes::InlineFragment,
7
+ ]
8
+
9
+ def validate(context)
10
+ HAS_TYPE_CONDITION.each do |node_class|
11
+ context.visitor[node_class] << -> (node, parent) {
12
+ validate_type_is_composite(node, context)
13
+ }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def validate_type_is_composite(node, context)
20
+ type_name = node.type
21
+ type_def = context.schema.types[type_name]
22
+ if type_def.nil? || !type_def.kind.composite?
23
+ context.errors << message("Invalid fragment on type #{type_name} (must be Union, Interface or Object)", node)
24
+ GraphQL::Visitor::SKIP
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ class GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped
2
+ include GraphQL::StaticValidation::Message::MessageHelper
3
+
4
+ def validate(context)
5
+ literal_validator = GraphQL::StaticValidation::LiteralValidator.new
6
+ context.visitor[GraphQL::Nodes::Variable] << -> (node, parent) {
7
+ if !node.default_value.nil?
8
+ validate_default_value(node, literal_validator, context)
9
+ end
10
+ }
11
+ end
12
+
13
+ def validate_default_value(node, literal_validator, context)
14
+ value = node.default_value
15
+ if node.type.is_a?(GraphQL::Nodes::NonNullType)
16
+ context.errors << message("Non-null variable $#{node.name} can't have a default value", node)
17
+ else
18
+ type = context.schema.types[node.type.name]
19
+ if !literal_validator.validate(value, type)
20
+ context.errors << message("Default value for $#{node.name} doesn't match type #{node.type.name}", node)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ class GraphQL::StaticValidation::VariableUsagesAreAllowed
2
+ include GraphQL::StaticValidation::Message::MessageHelper
3
+
4
+ def validate(context)
5
+ # holds { name => ast_node } pairs
6
+ declared_variables = {}
7
+
8
+ context.visitor[GraphQL::Nodes::OperationDefinition] << -> (node, parent) {
9
+ declared_variables = node.variables.each_with_object({}) { |var, memo| memo[var.name] = var }
10
+ }
11
+
12
+ context.visitor[GraphQL::Nodes::Argument] << -> (node, parent) {
13
+ return if !node.value.is_a?(GraphQL::Nodes::VariableIdentifier)
14
+ if parent.is_a?(GraphQL::Nodes::Field)
15
+ arguments = context.field_definition.arguments
16
+ elsif parent.is_a?(GraphQL::Nodes::Directive)
17
+ arguments = context.directive_definition.arguments
18
+ end
19
+ var_defn_ast = declared_variables[node.value.name]
20
+ validate_usage(arguments, node, var_defn_ast, context)
21
+ }
22
+ end
23
+
24
+ private
25
+
26
+ def validate_usage(arguments, arg_node, ast_var, context)
27
+ var_type = to_query_type(ast_var.type, context.schema.types)
28
+ if !ast_var.default_value.nil?
29
+ var_type = GraphQL::NonNullType.new(of_type: var_type)
30
+ end
31
+
32
+ arg_defn = arguments[arg_node.name]
33
+ if var_type != arg_defn.type
34
+ context.errors << message("Type mismatch on variable $#{ast_var.name} and argument #{arg_node.name} (#{var_type.to_s} / #{arg_defn.type.to_s})", arg_node)
35
+ end
36
+ end
37
+
38
+ def to_query_type(ast_type, types)
39
+ if ast_type.is_a?(GraphQL::Nodes::NonNullType)
40
+ GraphQL::NonNullType.new(of_type: to_query_type(ast_type.of_type, types))
41
+ elsif ast_type.is_a?(GraphQL::Nodes::ListType)
42
+ GraphQL::ListType.new(of_type: to_query_type(ast_type.of_type, types))
43
+ else
44
+ types[ast_type.name]
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ class GraphQL::StaticValidation::VariablesAreInputTypes
2
+ include GraphQL::StaticValidation::Message::MessageHelper
3
+
4
+ def validate(context)
5
+ context.visitor[GraphQL::Nodes::Variable] << -> (node, parent) {
6
+ validate_is_input_type(node, context)
7
+ }
8
+ end
9
+
10
+ private
11
+
12
+ def validate_is_input_type(node, context)
13
+ type_name = get_type_name(node.type)
14
+ type = context.schema.types[type_name]
15
+ if !type.kind.input?
16
+ context.errors << message("#{type.name} isn't a valid input type (on $#{node.name})", node)
17
+ end
18
+ end
19
+
20
+ def get_type_name(ast_type)
21
+ if ast_type.respond_to?(:of_type)
22
+ get_type_name(ast_type.of_type)
23
+ else
24
+ ast_type.name
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ class GraphQL::StaticValidation::VariablesAreUsedAndDefined
2
+ include GraphQL::StaticValidation::Message::MessageHelper
3
+
4
+ def validate(context)
5
+ # holds { name => used? } pairs
6
+ declared_variables = {}
7
+
8
+ context.visitor[GraphQL::Nodes::OperationDefinition] << -> (node, parent) {
9
+ declared_variables = node.variables.each_with_object({}) { |var, memo| memo[var.name] = false }
10
+ }
11
+
12
+ context.visitor[GraphQL::Nodes::VariableIdentifier] << -> (node, parent) {
13
+ if declared_variables.key?(node.name)
14
+ declared_variables[node.name] = true
15
+ else
16
+ context.errors << message("Variable $#{node.name} is used but not declared", node)
17
+ GraphQL::Visitor::SKIP
18
+ end
19
+ }
20
+
21
+ context.visitor[GraphQL::Nodes::OperationDefinition].leave << -> (node, parent) {
22
+ unused_variables = declared_variables
23
+ .select { |name, used| !used }
24
+ .keys
25
+
26
+ unused_variables.each do |var_name|
27
+ context.errors << message("Variable $#{var_name} is declared but not used", node)
28
+ end
29
+ }
30
+ end
31
+ end
@@ -7,11 +7,12 @@ class GraphQL::StaticValidation::TypeStack
7
7
  GraphQL::Nodes::FragmentDefinition,
8
8
  ]
9
9
 
10
- attr_reader :schema, :object_types, :field_definitions
10
+ attr_reader :schema, :object_types, :field_definitions, :directive_definitions
11
11
  def initialize(schema, visitor)
12
12
  @schema = schema
13
13
  @object_types = []
14
14
  @field_definitions = []
15
+ @directive_definitions = []
15
16
  visitor.enter << -> (node, parent) { PUSH_STRATEGIES[node.class].push(self, node) }
16
17
  visitor.leave << -> (node, parent) { PUSH_STRATEGIES[node.class].pop(self, node) }
17
18
  end
@@ -80,6 +81,17 @@ class GraphQL::StaticValidation::TypeStack
80
81
  end
81
82
  end
82
83
 
84
+ class DirectiveStrategy
85
+ def push(stack, node)
86
+ directive_defn = stack.schema.directives[node.name]
87
+ stack.directive_definitions.push(directive_defn)
88
+ end
89
+
90
+ def pop(stack, node)
91
+ stack.directive_definitions.pop
92
+ end
93
+ end
94
+
83
95
  class NullStrategy
84
96
  def push(stack, node); end
85
97
  def pop(stack, node); end
@@ -1,25 +1,16 @@
1
+ # Initialized with a {GraphQL::Schema}, then it can validate {GraphQL::Nodes::Documents}s based on that schema.
2
+ #
3
+ # By default, it's used by {GraphQL::Query}
1
4
  class GraphQL::StaticValidation::Validator
2
- VALIDATORS = [
3
- GraphQL::StaticValidation::DirectivesAreDefined,
4
- GraphQL::StaticValidation::ArgumentsAreDefined,
5
- GraphQL::StaticValidation::RequiredArgumentsArePresent,
6
- GraphQL::StaticValidation::ArgumentLiteralsAreCompatible,
7
- GraphQL::StaticValidation::FragmentTypesExist,
8
- GraphQL::StaticValidation::FragmentsAreUsed,
9
- GraphQL::StaticValidation::FieldsAreDefinedOnType,
10
- GraphQL::StaticValidation::FieldsWillMerge,
11
- GraphQL::StaticValidation::FieldsHaveAppropriateSelections,
12
- ]
13
-
14
- def initialize(schema:, validators: VALIDATORS)
5
+ def initialize(schema:, rules: GraphQL::StaticValidation::ALL_RULES)
15
6
  @schema = schema
16
- @validators = validators
7
+ @rules = rules
17
8
  end
18
9
 
19
10
  def validate(document)
20
11
  context = Context.new(@schema, document)
21
- @validators.each do |validator|
22
- validator.new.validate(context)
12
+ @rules.each do |rules|
13
+ rules.new.validate(context)
23
14
  end
24
15
  context.visitor.visit(document)
25
16
  context.errors.map(&:to_h)
@@ -30,10 +21,11 @@ class GraphQL::StaticValidation::Validator
30
21
  def initialize(schema, document)
31
22
  @schema = schema
32
23
  @document = document
33
- @fragments = {}
24
+ @fragments = document.parts.each_with_object({}) do |part, memo|
25
+ part.is_a?(GraphQL::Nodes::FragmentDefinition) && memo[part.name] = part
26
+ end
34
27
  @errors = []
35
28
  @visitor = GraphQL::Visitor.new
36
- @visitor[GraphQL::Nodes::FragmentDefinition] << -> (node, parent) { @fragments[node.name] = node }
37
29
  @type_stack = GraphQL::StaticValidation::TypeStack.new(schema, visitor)
38
30
  end
39
31
 
@@ -44,5 +36,9 @@ class GraphQL::StaticValidation::Validator
44
36
  def field_definition
45
37
  @type_stack.field_definitions.last
46
38
  end
39
+
40
+ def directive_definition
41
+ @type_stack.directive_definitions.last
42
+ end
47
43
  end
48
44
  end
@@ -1,17 +1,21 @@
1
1
  module GraphQL::TypeKinds
2
2
  class TypeKind
3
3
  attr_reader :name
4
- def initialize(name, resolves: false, fields: false, wraps: false)
4
+ def initialize(name, resolves: false, fields: false, wraps: false, input: false)
5
5
  @name = name
6
6
  @resolves = resolves
7
7
  @fields = fields
8
8
  @wraps = wraps
9
+ @input = input
10
+ @composite = fields? || resolves?
9
11
  end
10
12
 
11
13
  def resolves?; @resolves; end
12
14
  def fields?; @fields; end
13
15
  def wraps?; @wraps; end
16
+ def input?; @input; end
14
17
  def to_s; @name; end
18
+ def composite?; @composite; end
15
19
 
16
20
  def resolve(type, value)
17
21
  if resolves?
@@ -32,12 +36,12 @@ module GraphQL::TypeKinds
32
36
  end
33
37
 
34
38
  TYPE_KINDS = [
35
- SCALAR = TypeKind.new("SCALAR"),
39
+ SCALAR = TypeKind.new("SCALAR", input: true),
36
40
  OBJECT = TypeKind.new("OBJECT", fields: true),
37
41
  INTERFACE = TypeKind.new("INTERFACE", resolves: true, fields: true),
38
42
  UNION = TypeKind.new("UNION", resolves: true),
39
- ENUM = TypeKind.new("ENUM"),
40
- INPUT_OBJECT = TypeKind.new("INPUT_OBJECT"),
43
+ ENUM = TypeKind.new("ENUM", input: true),
44
+ INPUT_OBJECT = TypeKind.new("INPUT_OBJECT", input: true),
41
45
  LIST = TypeKind.new("LIST", wraps: true),
42
46
  NON_NULL = TypeKind.new("NON_NULL", wraps: true),
43
47
  ]
@@ -8,24 +8,25 @@ class GraphQL::Enum
8
8
  yield(self, GraphQL::TypeDefiner.instance, GraphQL::FieldDefiner.instance, GraphQL::ArgumentDefiner.instance)
9
9
  end
10
10
 
11
- def value(name, description=nil, deprecation_reason: nil)
12
- @values[name] = EnumValue.new(name: name, description: description, deprecation_reason: deprecation_reason)
11
+ def value(name, description=nil, deprecation_reason: nil, value: name)
12
+ @values[name] = EnumValue.new(name: name, description: description, deprecation_reason: deprecation_reason, value: value)
13
13
  end
14
14
 
15
15
  def kind
16
16
  GraphQL::TypeKinds::ENUM
17
17
  end
18
18
 
19
- def coerce(value)
20
- @values[value]
19
+ def coerce(value_name)
20
+ @values[value_name].value
21
21
  end
22
22
 
23
23
  class EnumValue
24
- attr_reader :name, :description, :deprecation_reason
25
- def initialize(name:, description:, deprecation_reason:)
24
+ attr_reader :name, :description, :deprecation_reason, :value
25
+ def initialize(name:, description:, deprecation_reason:, value:)
26
26
  @name = name
27
27
  @description = description
28
28
  @deprecation_reason = deprecation_reason
29
+ @value = value
29
30
  end
30
31
  end
31
32
  end
@@ -2,20 +2,13 @@ class GraphQL::InputObjectType < GraphQL::ObjectType
2
2
  attr_definable :input_fields
3
3
 
4
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}
5
+ if !new_fields.nil?
6
+ @new_fields = GraphQL::StringNamedHash.new(new_fields).to_h
11
7
  end
8
+ @new_fields
12
9
  end
13
10
 
14
11
  def kind
15
12
  GraphQL::TypeKinds::INPUT_OBJECT
16
13
  end
17
-
18
- def to_s
19
- "<GraphQL::InputObjectType #{name}>"
20
- end
21
14
  end
@@ -11,8 +11,4 @@ class GraphQL::Interface < GraphQL::ObjectType
11
11
  def resolve_type(object)
12
12
  @possible_types.find {|t| t.name == object.class.name }
13
13
  end
14
-
15
- def to_s
16
- "<GraphQL::Interface #{name}>"
17
- end
18
14
  end
@@ -7,8 +7,4 @@ class GraphQL::ListType < GraphQL::ObjectType
7
7
  def kind
8
8
  GraphQL::TypeKinds::LIST
9
9
  end
10
-
11
- def to_s
12
- "<GraphQL::ListType(#{of_type.name})>"
13
- end
14
10
  end
@@ -15,8 +15,4 @@ class GraphQL::NonNullType < GraphQL::ObjectType
15
15
  def kind
16
16
  GraphQL::TypeKinds::NON_NULL
17
17
  end
18
-
19
- def to_s
20
- "<GraphQL::NonNullType(#{of_type.name})>"
21
- end
22
18
  end
@@ -10,31 +10,27 @@ class GraphQL::ObjectType
10
10
  end
11
11
 
12
12
  def fields(new_fields=nil)
13
- if new_fields
13
+ if !new_fields.nil?
14
14
  self.fields = new_fields
15
- else
16
- @fields
17
15
  end
16
+ @fields
18
17
  end
19
18
 
20
19
  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 }
20
+ stringified_fields = GraphQL::StringNamedHash.new(new_fields).to_h
21
+ # TODO: should this field be exposed during introspection? https://github.com/graphql/graphql-js/issues/73
25
22
  stringified_fields["__typename"] = GraphQL::Introspection::TypenameField.create(self)
26
23
  @fields = stringified_fields
27
24
  end
28
25
 
29
26
  def interfaces(new_interfaces=nil)
30
- if new_interfaces.nil?
31
- @interfaces
32
- else
27
+ if !new_interfaces.nil?
33
28
  # if you define interfaces twice, you're gonna have a bad time :(
34
29
  # (because it gets registered with that interface, then overriden)
35
30
  @interfaces = new_interfaces
36
31
  new_interfaces.each {|i| i.possible_types << self }
37
32
  end
33
+ @interfaces
38
34
  end
39
35
 
40
36
  def kind
@@ -42,10 +38,29 @@ class GraphQL::ObjectType
42
38
  end
43
39
 
44
40
  def to_s
45
- "<GraphQL::ObjectType #{name}>"
41
+ Printer.instance.print(self)
42
+ end
43
+
44
+ alias :inspect :to_s
45
+
46
+ def ==(other)
47
+ if other.is_a?(GraphQL::ObjectType)
48
+ self.to_s == other.to_s
49
+ else
50
+ super
51
+ end
46
52
  end
47
53
 
48
- def inspect
49
- to_s
54
+ class Printer
55
+ include Singleton
56
+ def print(type)
57
+ if type.kind.non_null?
58
+ "#{print(type.of_type)}!"
59
+ elsif type.kind.list?
60
+ "[#{print(type.of_type)}]"
61
+ else
62
+ type.name
63
+ end
64
+ end
50
65
  end
51
66
  end