graphql 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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