graphql 1.4.5 → 1.5.3
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/enum_generator.rb +33 -0
- data/lib/generators/graphql/function_generator.rb +15 -0
- data/lib/generators/graphql/install_generator.rb +118 -0
- data/lib/generators/graphql/interface_generator.rb +27 -0
- data/lib/generators/graphql/loader_generator.rb +17 -0
- data/lib/generators/graphql/mutation_generator.rb +19 -0
- data/lib/generators/graphql/object_generator.rb +34 -0
- data/lib/generators/graphql/templates/enum.erb +4 -0
- data/lib/generators/graphql/templates/function.erb +17 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +32 -0
- data/lib/generators/graphql/templates/interface.erb +4 -0
- data/lib/generators/graphql/templates/loader.erb +15 -0
- data/lib/generators/graphql/templates/mutation.erb +12 -0
- data/lib/generators/graphql/templates/object.erb +5 -0
- data/lib/generators/graphql/templates/query_type.erb +15 -0
- data/lib/generators/graphql/templates/schema.erb +34 -0
- data/lib/generators/graphql/templates/union.erb +4 -0
- data/lib/generators/graphql/type_generator.rb +78 -0
- data/lib/generators/graphql/union_generator.rb +33 -0
- data/lib/graphql.rb +10 -0
- data/lib/graphql/analysis/analyze_query.rb +1 -1
- data/lib/graphql/analysis/query_complexity.rb +6 -50
- data/lib/graphql/analysis/query_depth.rb +1 -1
- data/lib/graphql/argument.rb +21 -0
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +3 -3
- data/lib/graphql/define.rb +1 -0
- data/lib/graphql/define/assign_argument.rb +3 -19
- data/lib/graphql/define/assign_mutation_function.rb +34 -0
- data/lib/graphql/define/assign_object_field.rb +26 -14
- data/lib/graphql/define/defined_object_proxy.rb +21 -0
- data/lib/graphql/define/instance_definable.rb +61 -11
- data/lib/graphql/directive.rb +6 -1
- data/lib/graphql/execution/directive_checks.rb +1 -0
- data/lib/graphql/execution/execute.rb +14 -9
- data/lib/graphql/execution/field_result.rb +1 -0
- data/lib/graphql/execution/lazy.rb +8 -17
- data/lib/graphql/execution/lazy/lazy_method_map.rb +2 -0
- data/lib/graphql/execution/lazy/resolve.rb +1 -0
- data/lib/graphql/execution/selection_result.rb +1 -0
- data/lib/graphql/execution/typecast.rb +39 -26
- data/lib/graphql/field.rb +15 -3
- data/lib/graphql/field/resolve.rb +3 -3
- data/lib/graphql/function.rb +134 -0
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +1 -1
- data/lib/graphql/internal_representation.rb +1 -1
- data/lib/graphql/internal_representation/node.rb +35 -107
- data/lib/graphql/internal_representation/rewrite.rb +189 -183
- data/lib/graphql/internal_representation/visit.rb +38 -0
- data/lib/graphql/introspection/input_value_type.rb +10 -1
- data/lib/graphql/introspection/schema_type.rb +1 -1
- data/lib/graphql/language/lexer.rb +6 -3
- data/lib/graphql/language/lexer.rl +6 -3
- data/lib/graphql/object_type.rb +53 -13
- data/lib/graphql/query.rb +30 -14
- data/lib/graphql/query/arguments.rb +2 -0
- data/lib/graphql/query/context.rb +2 -2
- data/lib/graphql/query/literal_input.rb +9 -0
- data/lib/graphql/query/serial_execution/field_resolution.rb +2 -2
- data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
- data/lib/graphql/relay.rb +1 -0
- data/lib/graphql/relay/array_connection.rb +1 -1
- data/lib/graphql/relay/base_connection.rb +34 -15
- data/lib/graphql/relay/connection_resolve.rb +7 -2
- data/lib/graphql/relay/mutation.rb +45 -4
- data/lib/graphql/relay/node.rb +18 -6
- data/lib/graphql/relay/range_add.rb +45 -0
- data/lib/graphql/relay/relation_connection.rb +17 -2
- data/lib/graphql/runtime_type_error.rb +1 -0
- data/lib/graphql/schema.rb +40 -5
- data/lib/graphql/schema/base_64_encoder.rb +1 -0
- data/lib/graphql/schema/build_from_definition.rb +56 -21
- data/lib/graphql/schema/default_parse_error.rb +10 -0
- data/lib/graphql/schema/loader.rb +8 -1
- data/lib/graphql/schema/null_mask.rb +1 -0
- data/lib/graphql/schema/validation.rb +35 -0
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/arguments_validator.rb +7 -4
- data/lib/graphql/static_validation/definition_dependencies.rb +183 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +28 -96
- data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +23 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -5
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -31
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +11 -41
- data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +2 -2
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -7
- data/lib/graphql/static_validation/validation_context.rb +22 -1
- data/lib/graphql/static_validation/validator.rb +4 -1
- data/lib/graphql/string_type.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +12 -3
- data/spec/generators/graphql/enum_generator_spec.rb +29 -0
- data/spec/generators/graphql/function_generator_spec.rb +33 -0
- data/spec/generators/graphql/install_generator_spec.rb +185 -0
- data/spec/generators/graphql/interface_generator_spec.rb +32 -0
- data/spec/generators/graphql/loader_generator_spec.rb +31 -0
- data/spec/generators/graphql/mutation_generator_spec.rb +28 -0
- data/spec/generators/graphql/object_generator_spec.rb +42 -0
- data/spec/generators/graphql/union_generator_spec.rb +50 -0
- data/spec/graphql/analysis/query_complexity_spec.rb +2 -1
- data/spec/graphql/define/instance_definable_spec.rb +38 -0
- data/spec/graphql/directive/skip_directive_spec.rb +1 -0
- data/spec/graphql/directive_spec.rb +18 -0
- data/spec/graphql/execution/typecast_spec.rb +41 -46
- data/spec/graphql/field_spec.rb +1 -1
- data/spec/graphql/function_spec.rb +128 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +166 -129
- data/spec/graphql/introspection/type_type_spec.rb +1 -1
- data/spec/graphql/language/lexer_spec.rb +6 -0
- data/spec/graphql/object_type_spec.rb +73 -2
- data/spec/graphql/query/arguments_spec.rb +28 -0
- data/spec/graphql/query/variables_spec.rb +7 -1
- data/spec/graphql/query_spec.rb +30 -0
- data/spec/graphql/relay/base_connection_spec.rb +26 -8
- data/spec/graphql/relay/connection_resolve_spec.rb +45 -0
- data/spec/graphql/relay/connection_type_spec.rb +21 -0
- data/spec/graphql/relay/node_spec.rb +30 -2
- data/spec/graphql/relay/range_add_spec.rb +113 -0
- data/spec/graphql/schema/build_from_definition_spec.rb +114 -0
- data/spec/graphql/schema/loader_spec.rb +1 -0
- data/spec/graphql/schema/printer_spec.rb +2 -2
- data/spec/graphql/schema/validation_spec.rb +80 -11
- data/spec/graphql/schema/warden_spec.rb +10 -10
- data/spec/graphql/schema_spec.rb +18 -1
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +16 -0
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +50 -3
- data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +27 -0
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +57 -0
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +14 -0
- data/spec/graphql/string_type_spec.rb +7 -0
- data/spec/spec_helper.rb +3 -3
- data/spec/support/base_generator_test.rb +7 -0
- data/spec/support/dummy/schema.rb +32 -30
- data/spec/support/star_wars/schema.rb +81 -23
- metadata +98 -20
- data/lib/graphql/internal_representation/selection.rb +0 -85
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'generators/graphql/type_generator'
|
3
|
+
|
4
|
+
module Graphql
|
5
|
+
module Generators
|
6
|
+
# Generate a union type by name
|
7
|
+
# with the specified member types.
|
8
|
+
#
|
9
|
+
# ```
|
10
|
+
# rails g graphql:union SearchResultType ImageType AudioType
|
11
|
+
# ```
|
12
|
+
class UnionGenerator < TypeGeneratorBase
|
13
|
+
desc "Create a GraphQL::UnionType with the given name and possible types"
|
14
|
+
source_root File.expand_path('../templates', __FILE__)
|
15
|
+
|
16
|
+
argument :possible_types,
|
17
|
+
type: :array,
|
18
|
+
default: [],
|
19
|
+
banner: "type type ...",
|
20
|
+
desc: "Possible types for this union (expressed as Ruby or GraphQL)"
|
21
|
+
|
22
|
+
def create_type_file
|
23
|
+
template "union.erb", "app/graphql/types/#{type_file_name}.rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def normalized_possible_types
|
29
|
+
possible_types.map { |t| self.class.normalize_type_expression(t, mode: :ruby) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/graphql.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "delegate"
|
2
3
|
require "json"
|
3
4
|
require "set"
|
4
5
|
require "singleton"
|
@@ -16,6 +17,14 @@ module GraphQL
|
|
16
17
|
@col = col
|
17
18
|
@query = query
|
18
19
|
end
|
20
|
+
|
21
|
+
def to_h
|
22
|
+
locations = line ? [{ "line" => line, "column" => col }] : []
|
23
|
+
{
|
24
|
+
"message" => message,
|
25
|
+
"locations" => locations,
|
26
|
+
}
|
27
|
+
end
|
19
28
|
end
|
20
29
|
|
21
30
|
# Turn a query string into an AST
|
@@ -85,3 +94,4 @@ require "graphql/static_validation"
|
|
85
94
|
require "graphql/version"
|
86
95
|
require "graphql/relay"
|
87
96
|
require "graphql/compatibility"
|
97
|
+
require "graphql/function"
|
@@ -19,13 +19,10 @@ module GraphQL
|
|
19
19
|
# State for the query complexity calcuation:
|
20
20
|
# - `query` is needed for variables, then passed to handler
|
21
21
|
# - `complexities_on_type` holds complexity scores for each type in an IRep node
|
22
|
-
# - `skip_depth` increments for each skipped node, then decrements on the way out.
|
23
|
-
# While it's greater than `0`, we're visiting a skipped part of the query.
|
24
22
|
def initial_value(query)
|
25
23
|
{
|
26
24
|
query: query,
|
27
25
|
complexities_on_type: [TypeComplexity.new],
|
28
|
-
skip_depth: 0,
|
29
26
|
}
|
30
27
|
end
|
31
28
|
|
@@ -33,22 +30,12 @@ module GraphQL
|
|
33
30
|
def call(memo, visit_type, irep_node)
|
34
31
|
if irep_node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
|
35
32
|
if visit_type == :enter
|
36
|
-
|
37
|
-
memo[:skip_depth] += 1
|
38
|
-
elsif memo[:skip_depth] == 0
|
39
|
-
memo[:complexities_on_type].push(TypeComplexity.new)
|
40
|
-
end
|
33
|
+
memo[:complexities_on_type].push(TypeComplexity.new)
|
41
34
|
else
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
else
|
47
|
-
type_complexities = memo[:complexities_on_type].pop
|
48
|
-
child_complexity = type_complexities.max_possible_complexity
|
49
|
-
own_complexity = get_complexity(irep_node, memo[:query], child_complexity)
|
50
|
-
memo[:complexities_on_type].last.merge(irep_node.owner_type, own_complexity)
|
51
|
-
end
|
35
|
+
type_complexities = memo[:complexities_on_type].pop
|
36
|
+
child_complexity = type_complexities.max_possible_complexity
|
37
|
+
own_complexity = get_complexity(irep_node, memo[:query], child_complexity)
|
38
|
+
memo[:complexities_on_type].last.merge(irep_node.owner_type, own_complexity)
|
52
39
|
end
|
53
40
|
end
|
54
41
|
memo
|
@@ -89,22 +76,7 @@ module GraphQL
|
|
89
76
|
|
90
77
|
# Return the max possible complexity for types in this selection
|
91
78
|
def max_possible_complexity
|
92
|
-
|
93
|
-
|
94
|
-
@types.each do |type_defn, own_complexity|
|
95
|
-
type_complexity = @types.reduce(0) do |memo, (other_type, other_complexity)|
|
96
|
-
if types_overlap?(type_defn, other_type)
|
97
|
-
memo + other_complexity
|
98
|
-
else
|
99
|
-
memo
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
if type_complexity > max_complexity
|
104
|
-
max_complexity = type_complexity
|
105
|
-
end
|
106
|
-
end
|
107
|
-
max_complexity
|
79
|
+
@types.each_value.max || 0
|
108
80
|
end
|
109
81
|
|
110
82
|
# Store the complexity for the branch on `type_defn`.
|
@@ -112,22 +84,6 @@ module GraphQL
|
|
112
84
|
def merge(type_defn, complexity)
|
113
85
|
@types[type_defn] += complexity
|
114
86
|
end
|
115
|
-
|
116
|
-
private
|
117
|
-
# True if:
|
118
|
-
# - type_1 is type_2
|
119
|
-
# - type_1 is a member of type_2's possible types
|
120
|
-
def types_overlap?(type_1, type_2)
|
121
|
-
if type_1 == type_2
|
122
|
-
true
|
123
|
-
elsif type_2.kind.union?
|
124
|
-
type_2.include?(type_1)
|
125
|
-
elsif type_1.kind.object? && type_2.kind.interface?
|
126
|
-
type_1.interfaces.include?(type_2)
|
127
|
-
else
|
128
|
-
false
|
129
|
-
end
|
130
|
-
end
|
131
87
|
end
|
132
88
|
end
|
133
89
|
end
|
@@ -25,7 +25,7 @@ module GraphQL
|
|
25
25
|
def call(memo, visit_type, irep_node)
|
26
26
|
if irep_node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
|
27
27
|
# Don't validate introspection fields or skipped nodes
|
28
|
-
not_validated_node = GraphQL::Schema::DYNAMIC_FIELDS.include?(irep_node.definition_name)
|
28
|
+
not_validated_node = GraphQL::Schema::DYNAMIC_FIELDS.include?(irep_node.definition_name)
|
29
29
|
if visit_type == :enter
|
30
30
|
if not_validated_node
|
31
31
|
memo[:skip_depth] += 1
|
data/lib/graphql/argument.rb
CHANGED
@@ -53,5 +53,26 @@ module GraphQL
|
|
53
53
|
def expose_as
|
54
54
|
@expose_as ||= (@as || @name).to_s
|
55
55
|
end
|
56
|
+
|
57
|
+
NO_DEFAULT_VALUE = Object.new
|
58
|
+
# @api private
|
59
|
+
def self.from_dsl(name, type = nil, description = nil, default_value: NO_DEFAULT_VALUE, as: nil, &block)
|
60
|
+
argument = if block_given?
|
61
|
+
GraphQL::Argument.define(&block)
|
62
|
+
else
|
63
|
+
GraphQL::Argument.new
|
64
|
+
end
|
65
|
+
|
66
|
+
argument.name = name.to_s
|
67
|
+
type && argument.type = type
|
68
|
+
description && argument.description = description
|
69
|
+
if default_value != NO_DEFAULT_VALUE
|
70
|
+
argument.default_value = default_value
|
71
|
+
end
|
72
|
+
argument.as = as
|
73
|
+
|
74
|
+
|
75
|
+
argument
|
76
|
+
end
|
56
77
|
end
|
57
78
|
end
|
@@ -10,7 +10,7 @@ module GraphQL
|
|
10
10
|
has_count_interface = GraphQL::InterfaceType.define do
|
11
11
|
name "HasCount"
|
12
12
|
field :count, types.Int
|
13
|
-
field :counter, ->{
|
13
|
+
field :counter, ->{ has_count_interface }
|
14
14
|
end
|
15
15
|
|
16
16
|
counter_type = GraphQL::ObjectType.define do
|
@@ -29,7 +29,7 @@ module GraphQL
|
|
29
29
|
|
30
30
|
has_counter_interface = GraphQL::InterfaceType.define do
|
31
31
|
name "HasCounter"
|
32
|
-
field :counter,
|
32
|
+
field :counter, has_count_interface
|
33
33
|
end
|
34
34
|
|
35
35
|
query_type = GraphQL::ObjectType.define do
|
@@ -41,7 +41,7 @@ module GraphQL
|
|
41
41
|
schema = GraphQL::Schema.define(
|
42
42
|
query: query_type,
|
43
43
|
resolve_type: ->(o, c) { o == :counter ? counter_type : nil },
|
44
|
-
orphan_types: [alt_counter_type],
|
44
|
+
orphan_types: [alt_counter_type, counter_type],
|
45
45
|
query_execution_strategy: execution_strategy,
|
46
46
|
)
|
47
47
|
schema.metadata[:count] = 0
|
data/lib/graphql/define.rb
CHANGED
@@ -3,6 +3,7 @@ require "graphql/define/assign_argument"
|
|
3
3
|
require "graphql/define/assign_connection"
|
4
4
|
require "graphql/define/assign_enum_value"
|
5
5
|
require "graphql/define/assign_global_id_field"
|
6
|
+
require "graphql/define/assign_mutation_function"
|
6
7
|
require "graphql/define/assign_object_field"
|
7
8
|
require "graphql/define/defined_object_proxy"
|
8
9
|
require "graphql/define/instance_definable"
|
@@ -3,25 +3,9 @@ module GraphQL
|
|
3
3
|
module Define
|
4
4
|
# Turn argument configs into a {GraphQL::Argument}.
|
5
5
|
module AssignArgument
|
6
|
-
def self.call(target,
|
7
|
-
argument =
|
8
|
-
|
9
|
-
else
|
10
|
-
GraphQL::Argument.new
|
11
|
-
end
|
12
|
-
|
13
|
-
unsupported_keys = rest.keys - [:default_value, :as]
|
14
|
-
if unsupported_keys.any?
|
15
|
-
raise ArgumentError.new("unknown keyword#{unsupported_keys.length > 1 ? 's' : ''}: #{unsupported_keys.join(', ')}")
|
16
|
-
end
|
17
|
-
|
18
|
-
argument.name = name.to_s
|
19
|
-
type && argument.type = type
|
20
|
-
description && argument.description = description
|
21
|
-
rest.key?(:default_value) && argument.default_value = rest[:default_value]
|
22
|
-
argument.as = rest[:as]
|
23
|
-
|
24
|
-
target.arguments[name.to_s] = argument
|
6
|
+
def self.call(target, *args, **kwargs, &block)
|
7
|
+
argument = GraphQL::Argument.from_dsl(*args, **kwargs, &block)
|
8
|
+
target.arguments[argument.name] = argument
|
25
9
|
end
|
26
10
|
end
|
27
11
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Define
|
4
|
+
module AssignMutationFunction
|
5
|
+
def self.call(target, function)
|
6
|
+
# TODO: get all this logic somewhere easier to test
|
7
|
+
|
8
|
+
if !function.type.is_a?(GraphQL::ObjectType)
|
9
|
+
raise "Mutation functions must return object types (not #{function.type.unwrap})"
|
10
|
+
end
|
11
|
+
|
12
|
+
target.return_type = function.type.redefine {
|
13
|
+
name(target.name + "Payload")
|
14
|
+
field :clientMutationId, types.String, "A unique identifier for the client performing the mutation.", property: :client_mutation_id
|
15
|
+
}
|
16
|
+
|
17
|
+
target.arguments = function.arguments
|
18
|
+
target.description = function.description
|
19
|
+
target.resolve = ->(o, a, c) {
|
20
|
+
res = function.call(o, a, c)
|
21
|
+
ResultProxy.new(res, a[:clientMutationId])
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
class ResultProxy < SimpleDelegator
|
26
|
+
attr_reader :client_mutation_id
|
27
|
+
def initialize(target, client_mutation_id)
|
28
|
+
@client_mutation_id = client_mutation_id
|
29
|
+
super(target)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -3,31 +3,43 @@ module GraphQL
|
|
3
3
|
module Define
|
4
4
|
# Turn field configs into a {GraphQL::Field} and attach it to a {GraphQL::ObjectType} or {GraphQL::InterfaceType}
|
5
5
|
module AssignObjectField
|
6
|
-
def self.call(owner_type, name, type_or_field = nil, desc = nil, field: nil, **kwargs, &block)
|
6
|
+
def self.call(owner_type, name, type_or_field = nil, desc = nil, function: nil, field: nil, relay_mutation_function: nil, **kwargs, &block)
|
7
7
|
name_s = name.to_s
|
8
8
|
|
9
|
-
# Move some
|
10
|
-
kwargs[:description] ||= desc
|
11
|
-
kwargs[:name] ||= name_s
|
9
|
+
# Move some positional args into keywords if they're present
|
10
|
+
desc && kwargs[:description] ||= desc
|
11
|
+
name && kwargs[:name] ||= name_s
|
12
12
|
|
13
13
|
if !type_or_field.nil? && !type_or_field.is_a?(GraphQL::Field)
|
14
|
+
# Maybe a string, proc or BaseType
|
14
15
|
kwargs[:type] = type_or_field
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
base_field = if type_or_field.is_a?(GraphQL::Field)
|
19
|
+
type_or_field.redefine(name: name_s)
|
20
|
+
elsif function
|
21
|
+
GraphQL::Field.define(
|
22
|
+
arguments: function.arguments,
|
23
|
+
complexity: function.complexity,
|
24
|
+
name: name_s,
|
25
|
+
type: function.type,
|
26
|
+
resolve: function,
|
27
|
+
description: function.description,
|
28
|
+
deprecation_reason: function.deprecation_reason,
|
29
|
+
)
|
25
30
|
elsif field.is_a?(GraphQL::Field)
|
26
|
-
field
|
31
|
+
field.redefine(name: name_s)
|
27
32
|
else
|
28
|
-
|
33
|
+
nil
|
29
34
|
end
|
30
35
|
|
36
|
+
field = if base_field
|
37
|
+
base_field.redefine(kwargs, &block)
|
38
|
+
else
|
39
|
+
GraphQL::Field.define(kwargs, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
31
43
|
# Attach the field to the type
|
32
44
|
owner_type.fields[name_s] = field
|
33
45
|
end
|
@@ -1,16 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
module Define
|
4
|
+
# This object delegates most methods to a dictionary of functions, {@dictionary}.
|
5
|
+
# {@target} is passed to the specified function, along with any arguments and block.
|
6
|
+
# This allows a method-based DSL without adding methods to the defined class.
|
4
7
|
class DefinedObjectProxy
|
8
|
+
# The object which will be defined by definition functions
|
9
|
+
attr_reader :target
|
10
|
+
|
5
11
|
def initialize(target)
|
6
12
|
@target = target
|
7
13
|
@dictionary = target.class.dictionary
|
8
14
|
end
|
9
15
|
|
16
|
+
# Provides shorthand access to GraphQL's built-in types
|
10
17
|
def types
|
11
18
|
GraphQL::Define::TypeDefiner.instance
|
12
19
|
end
|
13
20
|
|
21
|
+
# Allow `plugin` to perform complex initialization on the definition.
|
22
|
+
# Calls `plugin.use(defn, **kwargs)`.
|
23
|
+
# @param plugin [<#use(defn, **kwargs)>] A plugin object
|
24
|
+
# @param kwargs [Hash] Any options for the plugin
|
25
|
+
def use(plugin, **kwargs)
|
26
|
+
# https://bugs.ruby-lang.org/issues/10708
|
27
|
+
if kwargs == {}
|
28
|
+
plugin.use(self)
|
29
|
+
else
|
30
|
+
plugin.use(self, **kwargs)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Lookup a function from the dictionary and call it if it's found.
|
14
35
|
def method_missing(name, *args, &block)
|
15
36
|
definition = @dictionary[name]
|
16
37
|
if definition
|
@@ -21,19 +21,23 @@ module GraphQL
|
|
21
21
|
#
|
22
22
|
# @example Make a class definable
|
23
23
|
# class Car
|
24
|
-
#
|
24
|
+
# include GraphQL::Define::InstanceDefinable
|
25
|
+
# attr_accessor :make, :model, :doors
|
25
26
|
# accepts_definitions(
|
26
27
|
# # These attrs will be defined with plain setters, `{attr}=`
|
27
28
|
# :make, :model,
|
28
29
|
# # This attr has a custom definition which applies the config to the target
|
29
30
|
# doors: ->(car, doors_count) { doors_count.times { car.doors << Door.new } }
|
30
31
|
# )
|
32
|
+
# ensure_defined(:make, :model, :doors)
|
31
33
|
#
|
32
34
|
# def initialize
|
33
35
|
# @doors = []
|
34
36
|
# end
|
35
37
|
# end
|
36
38
|
#
|
39
|
+
# class Door; end;
|
40
|
+
#
|
37
41
|
# # Create an instance with `.define`:
|
38
42
|
# subaru_baja = Car.define do
|
39
43
|
# make "Subaru"
|
@@ -57,6 +61,27 @@ module GraphQL
|
|
57
61
|
# # Access it from metadata
|
58
62
|
# subaru_baja.metadata[:all_wheel_drive] # => true
|
59
63
|
#
|
64
|
+
# @example Extending the definition of a class via a plugin
|
65
|
+
# # A plugin is any object that responds to `.use(definition)`
|
66
|
+
# module SubaruCar
|
67
|
+
# extend self
|
68
|
+
#
|
69
|
+
# def use(defn)
|
70
|
+
# # `defn` has the same methods as within `.define { ... }` block
|
71
|
+
# defn.make "Subaru"
|
72
|
+
# defn.doors 4
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# # Use the plugin within a `.define { ... }` block
|
77
|
+
# subaru_baja = Car.define do
|
78
|
+
# use SubaruCar
|
79
|
+
# model 'Baja'
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# subaru_baja.make # => "Subaru"
|
83
|
+
# subaru_baja.doors # => [<Door>, <Door>, <Door>, <Door>]
|
84
|
+
#
|
60
85
|
# @example Making a copy with an extended definition
|
61
86
|
# # Create an instance with `.define`:
|
62
87
|
# subaru_baja = Car.define do
|
@@ -91,6 +116,18 @@ module GraphQL
|
|
91
116
|
def define(**kwargs, &block)
|
92
117
|
# make sure the previous definition_proc was executed:
|
93
118
|
ensure_defined
|
119
|
+
|
120
|
+
method_names = self.class.ensure_defined_method_names
|
121
|
+
@pending_methods = method_names.map { |n| self.class.instance_method(n) }
|
122
|
+
self.singleton_class.class_eval do
|
123
|
+
method_names.each do |method_name|
|
124
|
+
define_method(method_name) { |*args, &block|
|
125
|
+
ensure_defined
|
126
|
+
self.send(method_name, *args, &block)
|
127
|
+
}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
94
131
|
@pending_definition = Definition.new(kwargs, block)
|
95
132
|
nil
|
96
133
|
end
|
@@ -119,8 +156,18 @@ module GraphQL
|
|
119
156
|
# @return [void]
|
120
157
|
def ensure_defined
|
121
158
|
if @pending_definition
|
159
|
+
|
122
160
|
defn = @pending_definition
|
123
161
|
@pending_definition = nil
|
162
|
+
|
163
|
+
pending_methods = @pending_methods
|
164
|
+
self.singleton_class.class_eval {
|
165
|
+
pending_methods.each do |method|
|
166
|
+
define_method(method.name, method)
|
167
|
+
end
|
168
|
+
}
|
169
|
+
@pending_methods = nil
|
170
|
+
|
124
171
|
defn_proxy = DefinedObjectProxy.new(self)
|
125
172
|
# Apply definition from `define(...)` kwargs
|
126
173
|
defn.define_keywords.each do |keyword, value|
|
@@ -130,6 +177,8 @@ module GraphQL
|
|
130
177
|
if defn.define_proc
|
131
178
|
defn_proxy.instance_eval(&defn.define_proc)
|
132
179
|
end
|
180
|
+
|
181
|
+
|
133
182
|
end
|
134
183
|
nil
|
135
184
|
end
|
@@ -171,19 +220,20 @@ module GraphQL
|
|
171
220
|
end
|
172
221
|
|
173
222
|
def ensure_defined(*method_names)
|
174
|
-
|
175
|
-
|
176
|
-
method_names.each do |method_name|
|
177
|
-
define_method(method_name) { |*args, &block|
|
178
|
-
ensure_defined
|
179
|
-
super(*args, &block)
|
180
|
-
}
|
181
|
-
end
|
182
|
-
}
|
183
|
-
self.prepend(ensure_defined_module)
|
223
|
+
@ensure_defined_method_names ||= []
|
224
|
+
@ensure_defined_method_names.concat(method_names)
|
184
225
|
nil
|
185
226
|
end
|
186
227
|
|
228
|
+
def ensure_defined_method_names
|
229
|
+
own_method_names = @ensure_defined_method_names || []
|
230
|
+
if superclass.respond_to?(:ensure_defined_method_names)
|
231
|
+
superclass.ensure_defined_method_names + own_method_names
|
232
|
+
else
|
233
|
+
own_method_names
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
187
237
|
# @return [Hash] combined definitions for self and ancestors
|
188
238
|
def dictionary
|
189
239
|
if superclass.respond_to?(:dictionary)
|