graphql 0.18.4 → 0.18.5

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +1 -2
  3. data/lib/graphql/argument.rb +2 -2
  4. data/lib/graphql/base_type.rb +17 -0
  5. data/lib/graphql/define.rb +1 -1
  6. data/lib/graphql/directive.rb +6 -0
  7. data/lib/graphql/enum_type.rb +48 -4
  8. data/lib/graphql/field.rb +81 -19
  9. data/lib/graphql/field/resolve.rb +1 -1
  10. data/lib/graphql/input_object_type.rb +17 -3
  11. data/lib/graphql/interface_type.rb +12 -2
  12. data/lib/graphql/list_type.rb +23 -4
  13. data/lib/graphql/non_null_type.rb +27 -2
  14. data/lib/graphql/query.rb +0 -1
  15. data/lib/graphql/query/arguments.rb +1 -1
  16. data/lib/graphql/query/serial_execution/value_resolution.rb +3 -1
  17. data/lib/graphql/relay/global_node_identification.rb +29 -16
  18. data/lib/graphql/scalar_type.rb +24 -1
  19. data/lib/graphql/schema.rb +109 -19
  20. data/lib/graphql/schema/printer.rb +3 -3
  21. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  22. data/lib/graphql/union_type.rb +19 -6
  23. data/lib/graphql/version.rb +1 -1
  24. data/readme.md +5 -6
  25. data/spec/graphql/analysis/query_complexity_spec.rb +1 -1
  26. data/spec/graphql/argument_spec.rb +1 -1
  27. data/spec/graphql/field_spec.rb +1 -1
  28. data/spec/graphql/interface_type_spec.rb +0 -19
  29. data/spec/graphql/query/context_spec.rb +1 -1
  30. data/spec/graphql/query/executor_spec.rb +1 -1
  31. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +8 -4
  32. data/spec/graphql/relay/array_connection_spec.rb +17 -17
  33. data/spec/graphql/relay/connection_type_spec.rb +1 -1
  34. data/spec/graphql/relay/global_node_identification_spec.rb +3 -21
  35. data/spec/graphql/relay/mutation_spec.rb +2 -2
  36. data/spec/graphql/relay/page_info_spec.rb +9 -9
  37. data/spec/graphql/relay/relation_connection_spec.rb +31 -31
  38. data/spec/graphql/schema/printer_spec.rb +1 -1
  39. data/spec/graphql/schema/reduce_types_spec.rb +1 -1
  40. data/spec/graphql/schema/timeout_middleware_spec.rb +1 -1
  41. data/spec/graphql/schema_spec.rb +18 -0
  42. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +6 -6
  43. data/spec/graphql/union_type_spec.rb +0 -4
  44. data/spec/spec_helper.rb +1 -1
  45. data/spec/support/dairy_app.rb +13 -8
  46. data/spec/support/star_wars_schema.rb +18 -16
  47. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45a2831bb2cf4c54f83f856f5e614a64b4e2a06e
4
- data.tar.gz: ca550cdf4e4e9772f0facd364295754bf2bc1440
3
+ metadata.gz: 9d69978a50350e058883fa1f9fdfc5c6e5dcc80b
4
+ data.tar.gz: 97cc2fdfc3778f6b75cf34a2b9d5dcf6354a1a28
5
5
  SHA512:
6
- metadata.gz: 3b52b0fd7948401ad258096f00611d63d7576d83981bd7efbda85a5d34fb7e3938ec661f357d92ab7d8bcf158a3e56ccc2be60a8402249746a9f14cda3edc601
7
- data.tar.gz: 06cab8b10aa6f6d1fd03049e24cdca3f97c014801d16fbc127d6adb6f81b55bad57797c35cfeacf4e0958834cd5e74e9e8925ca304b9e70439028ae31e1f9ba7
6
+ metadata.gz: c6177a5c17291fc6f95f0f2d8e14f3831ebc9af7f46addd15bc3283a63310151c8fc7fbfc1acc332a181ab808060afb399fd87ce83db6e0088c093adabd8b35b
7
+ data.tar.gz: b0464b86095e2aafa933d4dc5b6e5491ef2354df6025943967a78d36d63d7f7c2111e6bfaefe3b11abd5d8937c1ba14e160b5fe9d255cc7b5501d56b0f8e92a1
data/lib/graphql.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require "json"
2
2
  require "set"
3
3
  require "singleton"
4
- require "forwardable"
5
4
 
6
5
  module GraphQL
7
6
  class Error < StandardError
@@ -18,7 +17,7 @@ module GraphQL
18
17
  end
19
18
 
20
19
  # Turn a query string into an AST
21
- # @param [String] a GraphQL query string
20
+ # @param query_string [String] a GraphQL query string
22
21
  # @return [GraphQL::Language::Nodes::Document]
23
22
  def self.parse(query_string)
24
23
  parse_with_racc(query_string)
@@ -9,9 +9,9 @@ module GraphQL
9
9
  # argument :favoriteFood, types.String, "Favorite thing to eat", default_value: "pizza"
10
10
  # end
11
11
  #
12
- # @example defining an input field for an {InputObjectType}
12
+ # @example defining an argument for an {InputObjectType}
13
13
  # GraphQL::InputObjectType.define do
14
- # input_field :newName, !types.String
14
+ # argument :newName, !types.String
15
15
  # end
16
16
  #
17
17
  class Argument
@@ -49,6 +49,17 @@ module GraphQL
49
49
  end
50
50
 
51
51
  module HasPossibleTypes
52
+
53
+ # If a (deprecated) `resolve_type` function was provided, call that and warn.
54
+ # Otherwise call `schema.resolve_type` (the proper behavior)
55
+ def legacy_resolve_type(object, ctx)
56
+ if @resolve_type_proc
57
+ resolve_type(object, ctx)
58
+ else
59
+ ctx.schema.resolve_type(object, ctx)
60
+ end
61
+ end
62
+
52
63
  # Return the implementing type for `object`.
53
64
  # The default implementation assumes that there's a type with the same name as `object.class.name`.
54
65
  # Maybe you'll need to override this in your own interfaces!
@@ -58,6 +69,7 @@ module GraphQL
58
69
  # @return [GraphQL::ObjectType] the type which should expose `object`
59
70
  def resolve_type(object, ctx)
60
71
  ensure_defined
72
+ warn_resolve_type_deprecated
61
73
  instance_exec(object, ctx, &(@resolve_type_proc || DEFAULT_RESOLVE_TYPE))
62
74
  end
63
75
 
@@ -69,8 +81,13 @@ module GraphQL
69
81
  }
70
82
 
71
83
  def resolve_type=(new_proc)
84
+ warn_resolve_type_deprecated
72
85
  @resolve_type_proc = new_proc || DEFAULT_RESOLVE_TYPE
73
86
  end
87
+
88
+ def warn_resolve_type_deprecated
89
+ warn("#{self.name}.resolve_type is deprecated; define Schema.resolve_type instead")
90
+ end
74
91
  end
75
92
 
76
93
  # Print the human-readable name of this type using the query-string naming pattern
@@ -19,7 +19,7 @@ module GraphQL
19
19
  # # After definition, read the key from metadata
20
20
  # PostType.metadata[:resolves_to_class_names] # => [...]
21
21
  #
22
- # @param [Object] the key to assign in metadata
22
+ # @param key [Object] the key to assign in metadata
23
23
  # @return [#call(defn, value)] an assignment for `.accepts_definitions` which writes `key` to `#metadata`
24
24
  def self.assign_metadata_key(key)
25
25
  GraphQL::Define::InstanceDefinable::AssignMetadataKey.new(key)
@@ -1,4 +1,10 @@
1
1
  module GraphQL
2
+ # Directives are server-defined hooks for modifying execution.
3
+ #
4
+ # Two directives are included out-of-the-box:
5
+ # - `@skip(if: ...)` Skips the tagged field if the value of `if` is true
6
+ # - `@include(if: ...)` Includes the tagged field _only_ if `if` is true
7
+ #
2
8
  class Directive
3
9
  include GraphQL::Define::InstanceDefinable
4
10
  accepts_definitions :locations, :name, :description, :include_proc, argument: GraphQL::Define::AssignArgument
@@ -1,9 +1,15 @@
1
1
  module GraphQL
2
- # A finite set of possible values, represented in query strings with
3
- # SCREAMING_CASE_NAMES
2
+ # Represents a collection of related values.
3
+ # By convention, enum names are `SCREAMING_CASE_NAMES`,
4
+ # but other identifiers are supported too.
4
5
  #
5
- # @example An enum of programming languages
6
+ # You can use as return types _or_ as inputs.
7
+ #
8
+ # By default, enums are passed to `resolve` functions as
9
+ # the strings that identify them, but you can provide a
10
+ # custom Ruby value with the `value:` keyword.
6
11
  #
12
+ # @example An enum of programming languages
7
13
  # LanguageEnum = GraphQL::EnumType.define do
8
14
  # name "Languages"
9
15
  # description "Programming languages for Web projects"
@@ -11,6 +17,44 @@ module GraphQL
11
17
  # value("RUBY", "A very dynamic language aimed at programmer happiness")
12
18
  # value("JAVASCRIPT", "Accidental lingua franca of the web")
13
19
  # end
20
+ #
21
+ # @example Using an enum as a return type
22
+ # field :favoriteLanguage, LanguageEnum, "This person's favorite coding language"
23
+ # # ...
24
+ # # In a query:
25
+ # Schema.execute("{ coder(id: 1) { favoriteLanguage } }")
26
+ # # { "data" => { "coder" => { "favoriteLanguage" => "RUBY" } } }
27
+ #
28
+ # @example Defining an enum input
29
+ # field :coders, types[CoderType] do
30
+ # argument :knowing, types[LanguageType]
31
+ # resolve -> (obj, args, ctx) {
32
+ # Coder.where(language: args[:knowing])
33
+ # }
34
+ # end
35
+ #
36
+ # @example Using an enum as input
37
+ # {
38
+ # # find coders who know Python and Ruby
39
+ # coders(knowing: [PYTHON, RUBY]) {
40
+ # name
41
+ # hourlyRate
42
+ # }
43
+ # }
44
+ #
45
+ # @example Enum whose values are different in Ruby-land
46
+ # GraphQL::EnumType.define do
47
+ # # ...
48
+ # # use the `value:` keyword:
49
+ # value("RUBY", "Lisp? Smalltalk?", value: :rb)
50
+ # end
51
+ #
52
+ # # Now, resolve functions will receive `:rb` instead of `"RUBY"`
53
+ # field :favoriteLanguage, LanguageEnum
54
+ # resolve -> (obj, args, ctx) {
55
+ # args[:favoriteLanguage] # => :rb
56
+ # }
57
+ #
14
58
  class EnumType < GraphQL::BaseType
15
59
  accepts_definitions value: GraphQL::Define::AssignEnumValue
16
60
 
@@ -78,7 +122,7 @@ module GraphQL
78
122
 
79
123
  # A value within an {EnumType}
80
124
  #
81
- # Created with {EnumType#value}
125
+ # Created with the `value` helper
82
126
  class EnumValue
83
127
  attr_accessor :name, :description, :deprecation_reason, :value
84
128
  def initialize(name:, description:, deprecation_reason:, value:)
data/lib/graphql/field.rb CHANGED
@@ -5,40 +5,88 @@ module GraphQL
5
5
  #
6
6
  # They're usually created with the `field` helper. If you create it by hand, make sure {#name} is a String.
7
7
  #
8
- # @example creating a field
8
+ # A field must have a return type, but if you want to defer the return type calculation until later,
9
+ # you can pass a proc for the return type. That proc will be called when the schema is defined.
10
+ #
11
+ # @example Lazy type resolution
12
+ # # If the field's type isn't defined yet, you can pass a proc
13
+ # field :city, -> { TypeForModelName.find("City") }
14
+ #
15
+ # For complex field definition, you can pass a block to the `field` helper, eg `field :name do ... end`.
16
+ # This block is equivalent to calling `GraphQL::Field.define { ... }`.
17
+ #
18
+ # @example Defining a field with a block
19
+ # field :city, CityType do
20
+ # # field definition continues inside the block
21
+ # end
22
+ #
23
+ # ## Resolve
24
+ #
25
+ # Fields have `resolve` functions to determine their values at query-time.
26
+ # The default implementation is to call a method on the object based on the field name.
27
+ #
28
+ # @example Create a field which calls a method with the same name.
9
29
  # GraphQL::ObjectType.define do
10
30
  # field :name, types.String, "The name of this thing "
11
31
  # end
12
32
  #
13
- # @example handling a circular reference
14
- # # If the field's type isn't defined yet, you have two options:
33
+ # You can specify a custom proc with the `resolve` helper.
15
34
  #
35
+ # There are some shortcuts for common `resolve` implementations:
36
+ # - Provide `property:` to call a method with a different name than the field name
37
+ # - Provide `hash_key:` to resolve the field by doing a key lookup, eg `obj[:my_hash_key]`
38
+ #
39
+ # @example Create a field that calls a different method on the object
16
40
  # GraphQL::ObjectType.define do
17
- # # If you pass a Proc, it will be evaluated at schema build-time
18
- # field :city, -> { CityType }
19
- # # If you pass a String, it will be looked up in the global namespace at schema build-time
20
- # field :country, "CountryType"
41
+ # # use the `property` keyword:
42
+ # field :firstName, types.String, property: :first_name
21
43
  # end
22
44
  #
23
- # @example creating a field that accesses a different property on the object
45
+ # @example Create a field looks up with `[hash_key]`
24
46
  # GraphQL::ObjectType.define do
25
- # # use the `property` option:
26
- # field :firstName, types.String, property: :first_name
47
+ # # use the `hash_key` keyword:
48
+ # field :firstName, types.String, hash_key: :first_name
27
49
  # end
28
50
  #
29
- # @example defining a field, then attaching it to a type
30
- # name_field = GraphQL::Field.define do
31
- # name("Name")
32
- # type(!types.String)
33
- # description("The name of this thing")
34
- # resolve -> (object, arguments, context) { object.name }
51
+ # ## Arguments
52
+ #
53
+ # Fields can take inputs; they're called arguments. You can define them with the `argument` helper.
54
+ #
55
+ # @example Create a field with an argument
56
+ # field :students, types[StudentType] do
57
+ # argument :grade, types.Int
58
+ # resolve -> (obj, args, ctx) {
59
+ # Student.where(grade: args[:grade])
60
+ # }
35
61
  # end
36
62
  #
37
- # NamedType = GraphQL::ObjectType.define do
38
- # # use the `field` option:
39
- # field :name, field: name_field
63
+ # They can have default values which will be provided to `resolve` if the query doesn't include a value.
64
+ #
65
+ # @example Argument with a default value
66
+ # field :events, types[EventType] do
67
+ # # by default, don't include past events
68
+ # argument :includePast, types.Boolean, default_value: false
69
+ # resolve -> (obj, args, ctx) {
70
+ # args[:includePast] # => false if no value was provided in the query
71
+ # # ...
72
+ # }
40
73
  # end
41
74
  #
75
+ # Only certain types maybe used for inputs:
76
+ #
77
+ # - Scalars
78
+ # - Enums
79
+ # - Input Objects
80
+ # - Lists of those types
81
+ #
82
+ # Input types may also be non-null -- in that case, the query will fail
83
+ # if the input is not present.
84
+ #
85
+ # ## Complexity
86
+ #
87
+ # Fields can have _complexity_ values which describe the computation cost of resolving the field.
88
+ # You can provide the complexity as a constant with `complexity:` or as a proc, with the `complexity` helper.
89
+ #
42
90
  # @example Custom complexity values
43
91
  # # Complexity can be a number or a proc.
44
92
  #
@@ -57,12 +105,26 @@ module GraphQL
57
105
  # complexity -> (ctx, args, child_complexity) { child_complexity * args[:limit] }
58
106
  # end
59
107
  #
108
+ # @example Creating a field, then assigning it to a type
109
+ # name_field = GraphQL::Field.define do
110
+ # name("Name")
111
+ # type(!types.String)
112
+ # description("The name of this thing")
113
+ # resolve -> (object, arguments, context) { object.name }
114
+ # end
115
+ #
116
+ # NamedType = GraphQL::ObjectType.define do
117
+ # # The second argument may be a GraphQL::Field
118
+ # field :name, name_field
119
+ # end
120
+ #
60
121
  class Field
61
122
  include GraphQL::Define::InstanceDefinable
62
123
  accepts_definitions :name, :description, :resolve, :type, :property, :deprecation_reason, :complexity, :hash_key, argument: GraphQL::Define::AssignArgument
63
124
 
64
125
  lazy_defined_attr_accessor :deprecation_reason, :description, :property, :hash_key
65
126
 
127
+ # @return [<#call(obj, args,ctx)>] A proc-like object which can be called to return the field's value
66
128
  attr_reader :resolve_proc
67
129
 
68
130
  # @return [String] The name of this field on its {GraphQL::ObjectType} (or {GraphQL::InterfaceType})
@@ -4,7 +4,7 @@ module GraphQL
4
4
  module Resolve
5
5
  module_function
6
6
 
7
- # @param [GraphQL::Field] A field that needs a resolve proc
7
+ # @param field [GraphQL::Field] A field that needs a resolve proc
8
8
  # @return [Proc] A resolver for this field, based on its config
9
9
  def create_proc(field)
10
10
  if field.property
@@ -1,13 +1,27 @@
1
1
  module GraphQL
2
- # A complex input type for a field argument.
2
+ # {InputObjectType}s are key-value inputs for fields.
3
+ #
4
+ # Input objects have _arguments_ which are identical to {GraphQL::Field} arguments.
5
+ # They map names to types and support default values.
6
+ # Their input types can be any input types, including {InputObjectType}s.
3
7
  #
4
8
  # @example An input type with name and number
5
9
  # PlayerInput = GraphQL::InputObjectType.define do
6
10
  # name("Player")
7
- # input_field :name, !types.String
8
- # input_field :number, !types.Int
11
+ # argument :name, !types.String
12
+ # argument :number, !types.Int
9
13
  # end
10
14
  #
15
+ # In a `resolve` function, you can access the values by making nested lookups on `args`.
16
+ #
17
+ # @example Accessing input values in a resolve function
18
+ # resolve -> (obj, args, ctx) {
19
+ # args[:player][:name] # => "Tony Gwynn"
20
+ # args[:player][:number] # => 19
21
+ # args[:player].to_h # { "name" => "Tony Gwynn", "number" => 19 }
22
+ # # ...
23
+ # }
24
+ #
11
25
  class InputObjectType < GraphQL::BaseType
12
26
  accepts_definitions(
13
27
  input_field: GraphQL::Define::AssignArgument,
@@ -1,7 +1,12 @@
1
1
  module GraphQL
2
- # A collection of types which implement the same fields
2
+ # An Interface contains a collection of types which implement some of the same fields.
3
3
  #
4
- # @example An interface with three required fields
4
+ # Interfaces can have fields, defined with `field`, just like an object type.
5
+ #
6
+ # Objects which implement this field _inherit_ field definitions from the interface.
7
+ # An object type can override the inherited definition by redefining that field.
8
+ #
9
+ # @example An interface with three fields
5
10
  # DeviceInterface = GraphQL::InterfaceType.define do
6
11
  # name("Device")
7
12
  # description("Hardware devices for computing")
@@ -11,6 +16,11 @@ module GraphQL
11
16
  # field :release_year, types.Int
12
17
  # end
13
18
  #
19
+ # @example Implementing an interface with an object type
20
+ # Laptoptype = GraphQL::ObjectType.define do
21
+ # interfaces [DeviceInterface]
22
+ # end
23
+ #
14
24
  class InterfaceType < GraphQL::BaseType
15
25
  include GraphQL::BaseType::HasPossibleTypes
16
26
  accepts_definitions :resolve_type, field: GraphQL::Define::AssignObjectField
@@ -1,7 +1,28 @@
1
1
  module GraphQL
2
- # A list type wraps another type.
2
+ # A list type modifies another type.
3
+ #
4
+ # List types can be created with the type helper (`types[InnerType]`)
5
+ # or {BaseType#to_list_type} (`InnerType.to_list_type`)
6
+ #
7
+ # For return types, it says that the returned value will be a list of the modified.
8
+ #
9
+ # @example A field which returns a list of items
10
+ # field :items, types[ItemType]
11
+ # # or
12
+ # field :items, ItemType.to_list_type
13
+ #
14
+ # For input types, it says that the incoming value will be a list of the modified type.
15
+ #
16
+ # @example A field which accepts a list of strings
17
+ # field :newNames do
18
+ # # ...
19
+ # argument :values, types[types.String]
20
+ # # or
21
+ # argument :values, types.String.to_list_type
22
+ # end
23
+ #
24
+ # Given a list type, you can always get the underlying type with {#unwrap}.
3
25
  #
4
- # Get the underlying type with {#unwrap}
5
26
  class ListType < GraphQL::BaseType
6
27
  include GraphQL::BaseType::ModifiesAnotherType
7
28
  attr_reader :of_type, :name
@@ -31,12 +52,10 @@ module GraphQL
31
52
  result
32
53
  end
33
54
 
34
-
35
55
  def coerce_non_null_input(value)
36
56
  ensure_array(value).map{ |item| of_type.coerce_input(item) }
37
57
  end
38
58
 
39
-
40
59
  private
41
60
 
42
61
  def ensure_array(value)
@@ -1,7 +1,32 @@
1
1
  module GraphQL
2
- # A non-null type wraps another type.
2
+ # A non-null type modifies another type.
3
+ #
4
+ # Non-null types can be created with `!` (`InnerType!`)
5
+ # or {BaseType#to_non_null_type} (`InnerType.to_non_null_type`)
6
+ #
7
+ # For return types, it says that the returned value will _always_ be present.
8
+ #
9
+ # @example A field which _always_ returns an error
10
+ # field :items, !ItemType
11
+ # # or
12
+ # field :items, ItemType.to_non_null_type
13
+ #
14
+ # (If the application fails to return a value, {InvalidNullError} will be raised.)
15
+ #
16
+ # For input types, it says that the incoming value _must_ be provided by the query.
17
+ #
18
+ # @example A field which _requires_ a string input
19
+ # field :newNames do
20
+ # # ...
21
+ # argument :values, !types.String
22
+ # # or
23
+ # argument :values, types.String.to_non_null_type
24
+ # end
25
+ #
26
+ # (If a value isn't provided, {Query::VariableValidationError} will be raised).
27
+ #
28
+ # Given a non-null type, you can always get the underlying type with {#unwrap}.
3
29
  #
4
- # Get the underlying type with {#unwrap}
5
30
  class NonNullType < GraphQL::BaseType
6
31
  include GraphQL::BaseType::ModifiesAnotherType
7
32