graphql 0.11.1 → 0.12.0

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