graphql 0.11.1 → 0.12.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +5 -2
  3. data/lib/graphql/argument.rb +8 -3
  4. data/lib/graphql/base_type.rb +5 -3
  5. data/lib/graphql/boolean_type.rb +6 -1
  6. data/lib/graphql/define.rb +8 -0
  7. data/lib/graphql/define/assign_argument.rb +19 -0
  8. data/lib/graphql/define/assign_enum_value.rb +16 -0
  9. data/lib/graphql/define/assign_object_field.rb +19 -0
  10. data/lib/graphql/define/assignment_dictionary.rb +26 -0
  11. data/lib/graphql/define/defined_object_proxy.rb +32 -0
  12. data/lib/graphql/define/instance_definable.rb +79 -0
  13. data/lib/graphql/{definition_helpers → define}/non_null_with_bang.rb +1 -1
  14. data/lib/graphql/{definition_helpers → define}/type_definer.rb +1 -1
  15. data/lib/graphql/directive.rb +7 -2
  16. data/lib/graphql/enum_type.rb +12 -6
  17. data/lib/graphql/execution_error.rb +1 -1
  18. data/lib/graphql/field.rb +26 -23
  19. data/lib/graphql/float_type.rb +2 -3
  20. data/lib/graphql/id_type.rb +9 -1
  21. data/lib/graphql/input_object_type.rb +11 -8
  22. data/lib/graphql/int_type.rb +2 -1
  23. data/lib/graphql/interface_type.rb +7 -2
  24. data/lib/graphql/introspection/input_value_type.rb +10 -1
  25. data/lib/graphql/invalid_null_error.rb +1 -1
  26. data/lib/graphql/object_type.rb +27 -25
  27. data/lib/graphql/query.rb +5 -8
  28. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -10
  29. data/lib/graphql/scalar_type.rb +4 -3
  30. data/lib/graphql/schema.rb +1 -1
  31. data/lib/graphql/static_validation/rules/document_does_not_exceed_max_depth.rb +1 -1
  32. data/lib/graphql/static_validation/validation_context.rb +5 -4
  33. data/lib/graphql/static_validation/validator.rb +3 -3
  34. data/lib/graphql/string_type.rb +2 -1
  35. data/lib/graphql/union_type.rb +1 -1
  36. data/lib/graphql/version.rb +1 -1
  37. data/readme.md +4 -10
  38. data/spec/graphql/boolean_type_spec.rb +20 -0
  39. data/spec/graphql/define/instance_definable_spec.rb +55 -0
  40. data/spec/graphql/field_spec.rb +14 -2
  41. data/spec/graphql/float_type_spec.rb +15 -0
  42. data/spec/graphql/id_type_spec.rb +9 -0
  43. data/spec/graphql/int_type_spec.rb +15 -0
  44. data/spec/graphql/introspection/input_value_type_spec.rb +36 -0
  45. data/spec/graphql/introspection/type_type_spec.rb +0 -23
  46. data/spec/graphql/query_spec.rb +16 -0
  47. data/spec/graphql/static_validation/complexity_validator_spec.rb +1 -1
  48. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +4 -3
  49. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +4 -3
  50. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -3
  51. data/spec/graphql/static_validation/rules/document_does_not_exceed_max_depth_spec.rb +9 -1
  52. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +3 -2
  53. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -3
  54. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +4 -3
  55. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +4 -3
  56. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +4 -3
  57. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -3
  58. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +4 -3
  59. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +4 -3
  60. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +4 -3
  61. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +5 -4
  62. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +4 -3
  63. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +4 -3
  64. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +4 -3
  65. data/spec/graphql/static_validation/type_stack_spec.rb +3 -2
  66. data/spec/graphql/static_validation/validator_spec.rb +3 -25
  67. data/spec/graphql/string_type_spec.rb +15 -0
  68. data/spec/support/dairy_app.rb +2 -0
  69. metadata +25 -9
  70. data/lib/graphql/definition_helpers.rb +0 -4
  71. data/lib/graphql/definition_helpers/defined_by_config.rb +0 -123
  72. data/lib/graphql/definition_helpers/string_named_hash.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dd3e3f13402965d47c58fd6a3bc5f4252a208417
4
- data.tar.gz: 2b0d0396fdf6fbed1b234cefb21dfb50578b2b49
3
+ metadata.gz: b779fe5af77f89213fae236cb0e7441bdfe4b0c1
4
+ data.tar.gz: 457dc5cb4468a2c57f758162dc112db07e0801df
5
5
  SHA512:
6
- metadata.gz: 221f5b736dbdd2c43cb45b1000b5d843b2a4101d63733a6200b79cc87889c3c0619830bf26e8fba1fa0e980875cb4061246642b2c8cdf45e2f9a6c7bf916d52a
7
- data.tar.gz: f67031ea6cb082c2b0906108fb8e772cb4f5464470c0bf0ca449d52b8eb1a21e6e2a8b6040633c99129fc82c7cda8850b30007ea7a137ffb12977b6127e66c37
6
+ metadata.gz: 1910cea7688e8ddd090ee5e7d64300a6ae39524e2ad5677a0552072ba4704a20213cbd25b9889900bcb2b610b6ff5aedfd9c2757614715aaf44ca4535a091ccd
7
+ data.tar.gz: cfcbe1c03d9fd8b9ed510af1d64d24dacaf147b337d383eb7de6c596ed200f6a8c2b3c87deef8dfab3cdb7b421811d034a4516c3c6db0e3af089b94758d8cc16
@@ -5,7 +5,10 @@ require "singleton"
5
5
  require "forwardable"
6
6
 
7
7
  module GraphQL
8
- class ParseError < StandardError
8
+ class Error < StandardError
9
+ end
10
+
11
+ class ParseError < Error
9
12
  attr_reader :line, :col, :query
10
13
  def initialize(message, line, col, query)
11
14
  super(message)
@@ -37,7 +40,7 @@ end
37
40
 
38
41
  # Order matters for these:
39
42
 
40
- require 'graphql/definition_helpers'
43
+ require 'graphql/define'
41
44
  require 'graphql/base_type'
42
45
  require 'graphql/object_type'
43
46
 
@@ -1,5 +1,7 @@
1
1
  # Used for defined arguments ({Field}, {InputObjectType})
2
2
  #
3
+ # {#name} must be a String.
4
+ #
3
5
  # @example defining an argument for a field
4
6
  # GraphQL::Field.define do
5
7
  # # ...
@@ -12,7 +14,10 @@
12
14
  # end
13
15
  #
14
16
  class GraphQL::Argument
15
- include GraphQL::DefinitionHelpers::DefinedByConfig
16
- defined_by_config :name, :type, :description, :default_value
17
- attr_accessor :name, :type, :description, :default_value
17
+ include GraphQL::Define::InstanceDefinable
18
+ accepts_definitions :name, :type, :description, :default_value
19
+ attr_accessor :type, :description, :default_value
20
+
21
+ # @return [String] The name of this argument on its {GraphQL::Field} or {GraphQL::InputObjectType}
22
+ attr_accessor :name
18
23
  end
@@ -1,8 +1,10 @@
1
1
  module GraphQL
2
2
  # The parent for all type classes.
3
3
  class BaseType
4
- include GraphQL::DefinitionHelpers::NonNullWithBang
5
- include GraphQL::DefinitionHelpers::DefinedByConfig
4
+ include GraphQL::Define::NonNullWithBang
5
+ include GraphQL::Define::InstanceDefinable
6
+ attr_accessor :name, :description
7
+ accepts_definitions :name, :description
6
8
 
7
9
  # @param other [GraphQL::BaseType] compare to this object
8
10
  # @return [Boolean] are these types equivalent? (incl. non-null, list)
@@ -50,7 +52,7 @@ module GraphQL
50
52
  # @param object [Object] the object which needs a type to expose it
51
53
  # @return [GraphQL::ObjectType] the type which should expose `object`
52
54
  def resolve_type(object)
53
- instance_exec(object, &@resolve_type_proc)
55
+ instance_exec(object, &(@resolve_type_proc || DEFAULT_RESOLVE_TYPE))
54
56
  end
55
57
 
56
58
  # The default implementation of {#resolve_type} gets `object.class.name`
@@ -1,4 +1,9 @@
1
1
  GraphQL::BOOLEAN_TYPE = GraphQL::ScalarType.define do
2
+ # Everything else is nil
3
+ ALLOWED_INPUTS = [true, false]
4
+
2
5
  name "Boolean"
3
- coerce -> (value) { !!value }
6
+
7
+ coerce_input -> (value) { ALLOWED_INPUTS.include?(value) ? value : nil }
8
+ coerce_result -> (value) { !!value }
4
9
  end
@@ -0,0 +1,8 @@
1
+ require "graphql/define/assign_argument"
2
+ require "graphql/define/assign_enum_value"
3
+ require "graphql/define/assign_object_field"
4
+ require "graphql/define/assignment_dictionary"
5
+ require "graphql/define/defined_object_proxy"
6
+ require "graphql/define/instance_definable"
7
+ require "graphql/define/non_null_with_bang"
8
+ require "graphql/define/type_definer"
@@ -0,0 +1,19 @@
1
+ module GraphQL
2
+ module Define
3
+ # Turn argument configs into a {GraphQL::Argument}.
4
+ module AssignArgument
5
+ def self.call(target, name, type, description = nil, default_value: nil, &block)
6
+ argument = if block_given?
7
+ GraphQL::Argument.define(&block)
8
+ else
9
+ GraphQL::Argument.new
10
+ end
11
+ argument.name = name.to_s
12
+ argument.type = type
13
+ argument.description = description
14
+ argument.default_value = default_value
15
+ target.arguments[name.to_s] = argument
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ module GraphQL
2
+ module Define
3
+ # Turn enum value configs into a {GraphQL::EnumType::EnumValue} and register it with the {GraphQL::EnumType}
4
+ module AssignEnumValue
5
+ def self.call(enum_type, name, desc = nil, deprecation_reason: nil, value: name)
6
+ enum_value = GraphQL::EnumType::EnumValue.new(
7
+ name: name,
8
+ description: desc,
9
+ deprecation_reason: deprecation_reason,
10
+ value: value
11
+ )
12
+ enum_type.add_value(enum_value)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ module GraphQL
2
+ module Define
3
+ # Turn field configs into a {GraphQL::Field} and attach it to a {GraphQL::ObjectType} or {GraphQL::InterfaceType}
4
+ module AssignObjectField
5
+ def self.call(fields_type, name, type = nil, desc = nil, field: nil, property: nil, &block)
6
+ if block_given?
7
+ field = GraphQL::Field.define(&block)
8
+ else
9
+ field ||= GraphQL::Field.new
10
+ end
11
+ type && field.type = type
12
+ desc && field.description = desc
13
+ property && field.property = property
14
+ field.name ||= name.to_s
15
+ fields_type.fields[name.to_s] = field
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ module GraphQL
2
+ module Define
3
+ # Create a hash of definitions out of provided arguments.
4
+ #
5
+ # @example Create definitions (some default, some custom)
6
+ # hash = AssignmentDictionary.create(:name, :description, field: (value, field_name) -> { value.create_field(field) })
7
+ #
8
+ module AssignmentDictionary
9
+ # Turn `keys` into a hash suitable for {GraphQL::Define::InstanceDefinable}
10
+ # @param Any number of symbols for default assignment, followed by an (optional) hash of custom assignment procs.
11
+ # @return [Hash] keys are attributes which may be defined. values are procs which assign values to the target object.
12
+ def self.create(*keys)
13
+ initial = if keys.last.is_a?(Hash)
14
+ keys.pop
15
+ else
16
+ {}
17
+ end
18
+ keys.inject(initial) do |memo, key|
19
+ assign_key = "#{key}="
20
+ memo[key] = -> (target, value) { target.public_send(assign_key, value) }
21
+ memo
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ module GraphQL
2
+ module Define
3
+ class DefinedObjectProxy
4
+ def initialize(target, dictionary)
5
+ @target = target
6
+ @dictionary = dictionary
7
+ end
8
+
9
+ def types
10
+ GraphQL::Define::TypeDefiner.instance
11
+ end
12
+
13
+ def method_missing(name, *args, &block)
14
+ definition = @dictionary[name]
15
+ if definition
16
+ definition.call(@target, *args, &block)
17
+ else
18
+ p "Failed to find config #{name} in #{inspect}"
19
+ super
20
+ end
21
+ end
22
+
23
+ def to_s
24
+ inspect
25
+ end
26
+
27
+ def inspect
28
+ "<DefinedObjectProxy #{@target} (#{@dictionary.keys})>"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,79 @@
1
+ module GraphQL
2
+ module Define
3
+ # This module provides the `.define { ... }` API for {GraphQL::BaseType}, {GraphQL::Field}, {GraphQL::Argument} and {GraphQL::Directive}.
4
+ #
5
+ # The goals are:
6
+ # - Minimal overhead in consuming classes
7
+ # - Independence between consuming classes
8
+ # - Extendable by third-party libraries without monkey-patching or other nastiness
9
+ #
10
+ # @example Make a class definable
11
+ # class Car
12
+ # attr_accessor :make, :model, :all_wheel_drive
13
+ #
14
+ # accepts_definitions(
15
+ # # These attrs will be defined with plain setters, `{attr}=`
16
+ # :make, :model,
17
+ # # This attr has a custom definition which applies the config to the target
18
+ # doors: -> (car, doors_count) { doors_count.times { car.doors << Door.new } }
19
+ # )
20
+ # end
21
+ #
22
+ # # Create an instance with `.define`:
23
+ # subaru_baja = Car.define do
24
+ # make "Subaru"
25
+ # model "Baja"
26
+ # doors 4
27
+ # end
28
+ #
29
+ # # The custom proc was applied:
30
+ # subaru_baja.doors #=> [<Door>, <Door>, <Door>, <Door>]
31
+ #
32
+ # @example Extending the definition of a class
33
+ # # Add some definitions:
34
+ # Car.accepts_definitions(:all_wheel_drive)
35
+ #
36
+ # # Use it in a definition
37
+ # subaru_baja = Car.define do
38
+ # # ...
39
+ # all_wheel_drive true
40
+ # end
41
+ #
42
+ module InstanceDefinable
43
+ def self.included(base)
44
+ base.extend(ClassMethods)
45
+ end
46
+
47
+ module ClassMethods
48
+ # Define an instance of this class using its {.definitions}.
49
+ def define(&block)
50
+ instance = self.new
51
+ proxy = DefinedObjectProxy.new(instance, dictionary)
52
+ block && proxy.instance_eval(&block)
53
+ instance
54
+ end
55
+
56
+ # Attach definitions to this class.
57
+ # Each symbol in `accepts` will be assigned with `{key}=`.
58
+ # The last entry in accepts may be a hash of name-proc pairs for custom definitions.
59
+ def accepts_definitions(*accepts)
60
+ @own_dictionary = own_dictionary.merge(AssignmentDictionary.create(*accepts))
61
+ end
62
+
63
+ # @return [Hash] combined definitions for self and ancestors
64
+ def dictionary
65
+ if superclass.respond_to?(:dictionary)
66
+ own_dictionary.merge(superclass.dictionary)
67
+ else
68
+ own_dictionary
69
+ end
70
+ end
71
+
72
+ # @return [Hash] definitions for this class only
73
+ def own_dictionary
74
+ @own_dictionary ||= {}
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
- module DefinitionHelpers
2
+ module Define
3
3
  # Wrap the object in NonNullType in response to `!`
4
4
  # @example required Int type
5
5
  # !GraphQL::INT_TYPE
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
- module DefinitionHelpers
2
+ module Define
3
3
  # Some conveniences for definining return & argument types.
4
4
  #
5
5
  # Passed into initialization blocks, eg {ObjectType#initialize}, {Field#initialize}
@@ -3,9 +3,10 @@
3
3
  # {Directive} & {DirectiveChain} implement `@skip` and `@include` with
4
4
  # minimal impact on query execution.
5
5
  class GraphQL::Directive
6
- include GraphQL::DefinitionHelpers::DefinedByConfig
6
+ include GraphQL::Define::InstanceDefinable
7
+ accepts_definitions :on, :name, :description, :resolve, argument: GraphQL::Define::AssignArgument
8
+
7
9
  attr_accessor :on, :arguments, :name, :description
8
- defined_by_config :on, :arguments, :name, :description, :resolve
9
10
 
10
11
  LOCATIONS = [
11
12
  ON_OPERATION = :on_operation?,
@@ -17,6 +18,10 @@ class GraphQL::Directive
17
18
  define_method(location) { self.on.include?(location) }
18
19
  end
19
20
 
21
+ def initialize
22
+ @arguments = {}
23
+ end
24
+
20
25
  def resolve(arguments, proc)
21
26
  @resolve_proc.call(arguments, proc)
22
27
  end
@@ -11,16 +11,22 @@
11
11
  # value("JAVASCRIPT", "Accidental lingua franca of the web")
12
12
  # end
13
13
  class GraphQL::EnumType < GraphQL::BaseType
14
- attr_accessor :name, :description, :values
15
- defined_by_config :name, :description, :values
14
+ accepts_definitions value: GraphQL::Define::AssignEnumValue
15
+
16
+ def initialize
17
+ @values_by_name = {}
18
+ @values_by_value = {}
19
+ end
16
20
 
17
21
  def values=(values)
18
22
  @values_by_name = {}
19
23
  @values_by_value = {}
20
- values.each do |enum_value|
21
- @values_by_name[enum_value.name] = enum_value
22
- @values_by_value[enum_value.value] = enum_value
23
- end
24
+ values.each { |enum_value| add_value(enum_value) }
25
+ end
26
+
27
+ def add_value(enum_value)
28
+ @values_by_name[enum_value.name] = enum_value
29
+ @values_by_value[enum_value.value] = enum_value
24
30
  end
25
31
 
26
32
  def values
@@ -2,7 +2,7 @@ module GraphQL
2
2
  # If a field's resolve function returns a {ExecutionError},
3
3
  # the error will be inserted into the response's `"errors"` key
4
4
  # and the field will resolve to `nil`.
5
- class ExecutionError < RuntimeError
5
+ class ExecutionError < GraphQL::Error
6
6
  # @return [GraphQL::Language::Nodes::Field] the field where the error occured
7
7
  attr_accessor :ast_node
8
8
 
@@ -1,6 +1,6 @@
1
1
  # {Field}s belong to {ObjectType}s and {InterfaceType}s.
2
2
  #
3
- # They're usually created with the `field` helper.
3
+ # They're usually created with the `field` helper. If you create it by hand, make sure {#name} is a String.
4
4
  #
5
5
  #
6
6
  # @example creating a field
@@ -38,28 +38,23 @@
38
38
  # end
39
39
  #
40
40
  class GraphQL::Field
41
- include GraphQL::DefinitionHelpers::DefinedByConfig
42
- attr_accessor :arguments, :deprecation_reason, :name, :description, :type, :property
41
+ include GraphQL::Define::InstanceDefinable
42
+ accepts_definitions :name, :description, :resolve, :type, :property, :deprecation_reason, argument: GraphQL::Define::AssignArgument
43
+
44
+ attr_accessor :deprecation_reason, :name, :description, :type, :property
43
45
  attr_reader :resolve_proc
44
- defined_by_config :arguments, :deprecation_reason, :name, :description, :type, :resolve, :property
46
+
47
+ # @return [String] The name of this field on its {GraphQL::ObjectType} (or {GraphQL::InterfaceType})
48
+ attr_reader :name
49
+
50
+ # @return [Hash<String, GraphQL::Argument>] Map String argument names to their {GraphQL::Argument} implementations
51
+ attr_accessor :arguments
45
52
 
46
53
  def initialize
47
54
  @arguments = {}
48
55
  @resolve_proc = build_default_resolver
49
56
  end
50
57
 
51
- def arguments(new_arguments=nil)
52
- if !new_arguments.nil?
53
- self.arguments=(new_arguments)
54
- end
55
- @arguments
56
- end
57
-
58
- # Define the arguments for this field using {StringNamedHash}
59
- def arguments=(new_arguments)
60
- @arguments = GraphQL::DefinitionHelpers::StringNamedHash.new(new_arguments).to_h
61
- end
62
-
63
58
  # Get a value for this field
64
59
  # @example resolving a field value
65
60
  # field.resolve(obj, args, ctx)
@@ -89,6 +84,18 @@ class GraphQL::Field
89
84
  end
90
85
  end
91
86
 
87
+ # You can only set a field's name _once_ -- this to prevent
88
+ # passing the same {Field} to multiple `.field` calls.
89
+ #
90
+ # This is important because {#name} may be used by {#resolve}.
91
+ def name=(new_name)
92
+ if @name.nil?
93
+ @name = new_name
94
+ else
95
+ raise("Can't rename an already-named field. (Tried to rename \"#{@name}\" to \"#{new_name}\".) If you're passing a field with the `field:` argument, make sure it's an unused instance of GraphQL::Field.")
96
+ end
97
+ end
98
+
92
99
  def to_s
93
100
  "<Field: #{name || "not-named"}>"
94
101
  end
@@ -96,13 +103,9 @@ class GraphQL::Field
96
103
  private
97
104
 
98
105
  def build_default_resolver
99
- # Note: lambda accesses the current Field via self
100
- -> (t, a, c) do
101
- if property = self.property
102
- t.public_send(property)
103
- else
104
- GraphQL::Query::DEFAULT_RESOLVE
105
- end
106
+ -> (obj, args, ctx) do
107
+ resolve_method = self.property || self.name
108
+ obj.public_send(resolve_method)
106
109
  end
107
110
  end
108
111
  end