graphql 1.9.14 → 1.9.15

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/templates/base_field.erb +0 -4
  3. data/lib/generators/graphql/templates/graphql_controller.erb +1 -1
  4. data/lib/generators/graphql/templates/schema.erb +1 -1
  5. data/lib/graphql.rb +3 -0
  6. data/lib/graphql/analysis/ast/analyzer.rb +1 -1
  7. data/lib/graphql/base_type.rb +1 -1
  8. data/lib/graphql/function.rb +1 -1
  9. data/lib/graphql/input_object_type.rb +2 -1
  10. data/lib/graphql/language/document_from_schema_definition.rb +1 -1
  11. data/lib/graphql/language/lexer.rb +49 -48
  12. data/lib/graphql/language/lexer.rl +49 -48
  13. data/lib/graphql/language/nodes.rb +11 -8
  14. data/lib/graphql/language/parser.rb +4 -1
  15. data/lib/graphql/language/parser.y +4 -1
  16. data/lib/graphql/language/token.rb +1 -1
  17. data/lib/graphql/query/arguments.rb +6 -1
  18. data/lib/graphql/query/literal_input.rb +2 -1
  19. data/lib/graphql/relay/base_connection.rb +3 -3
  20. data/lib/graphql/relay/relation_connection.rb +9 -5
  21. data/lib/graphql/schema.rb +8 -8
  22. data/lib/graphql/schema/argument.rb +1 -11
  23. data/lib/graphql/schema/build_from_definition.rb +1 -1
  24. data/lib/graphql/schema/directive/feature.rb +1 -1
  25. data/lib/graphql/schema/field.rb +19 -2
  26. data/lib/graphql/schema/input_object.rb +4 -0
  27. data/lib/graphql/schema/loader.rb +3 -3
  28. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -2
  29. data/lib/graphql/schema/member/has_fields.rb +15 -6
  30. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  31. data/lib/graphql/schema/resolver.rb +14 -1
  32. data/lib/graphql/schema/wrapper.rb +1 -1
  33. data/lib/graphql/static_validation/definition_dependencies.rb +21 -12
  34. data/lib/graphql/subscriptions.rb +5 -5
  35. data/lib/graphql/upgrader/member.rb +1 -1
  36. data/lib/graphql/version.rb +1 -1
  37. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23da143cfa3964ded042927d8b52fa98598387b3ab471b2ae66a2d25282af9b4
4
- data.tar.gz: e1a2924a847a05c1214b350bb316f093ab36a09b4be20c7e4cb66a2ee429058f
3
+ metadata.gz: 2f0505024fefcdebc975dad9259c3f3470f883b4251380e759de60ba380b3314
4
+ data.tar.gz: d92149e0da6aa4e110525a38badaf2163bf887205e2c6dd75c327302e0cfabb4
5
5
  SHA512:
6
- metadata.gz: c354cc5db0bf2072f25adb3798c90e011b9998cbb287ec825bdcc2f4733070af552810e28e46045b0eace05b9c8db571823a84be5e770c0be76eeb44dcd499fd
7
- data.tar.gz: 8d7d75c3dfa2b001e36d30554eaf313b3f489625519462e53fa7c479a1f0baf4fc7d4c27aec8cc78ceb9ef28dd7182bae61a6c2c768e8a30915657379c2832c7
6
+ metadata.gz: 47b920de6ba95b4c15ebc4aace97d74931cc24efee8c3fb6a6291ea68a13b3ef46b9c613658306e1ff100d0917433431c129baba12553a94dc96a4184cbc5435
7
+ data.tar.gz: 0e9c03e45fd3b3cb40a9b6b948ee1f71952f618511254acb3840195ff9c054036308636c7b2a42858096e70d1764e38458b88b0f2f369f699c7f71cdd0afd415
@@ -1,9 +1,5 @@
1
1
  module Types
2
2
  class BaseField < GraphQL::Schema::Field
3
3
  argument_class Types::BaseArgument
4
-
5
- def resolve_field(obj, args, ctx)
6
- resolve(obj, args, ctx)
7
- end
8
4
  end
9
5
  end
@@ -2,7 +2,7 @@ class GraphqlController < ApplicationController
2
2
  # If accessing from outside this domain, nullify the session
3
3
  # This allows for outside API access while preventing CSRF attacks,
4
4
  # but you'll have to authenticate your user separately
5
- protect_from_forgery with: :null_session
5
+ # protect_from_forgery with: :null_session
6
6
 
7
7
  def execute
8
8
  variables = ensure_hash(params[:variables])
@@ -24,7 +24,7 @@ class <%= schema_name %> < GraphQL::Schema
24
24
  def self.resolve_type(type, obj, ctx)
25
25
  # TODO: Implement this function
26
26
  # to return the correct type for `obj`
27
- raise(NotImplementedError)
27
+ raise(GraphQL::RequiredImplementationMissingError)
28
28
  end
29
29
  <% end %><% if options[:batch] %>
30
30
  # GraphQL::Batch setup:
@@ -10,6 +10,9 @@ module GraphQL
10
10
  class Error < StandardError
11
11
  end
12
12
 
13
+ class RequiredImplementationMissingError < Error
14
+ end
15
+
13
16
  # Turn a query string or schema definition into an AST
14
17
  # @param graphql_string [String] a GraphQL query string or schema definition
15
18
  # @return [GraphQL::Language::Nodes::Document]
@@ -22,7 +22,7 @@ module GraphQL
22
22
  # in a query error.
23
23
  # @return [Any] The analyzer result
24
24
  def result
25
- raise NotImplementedError
25
+ raise GraphQL::RequiredImplementationMissingError
26
26
  end
27
27
 
28
28
  class << self
@@ -167,7 +167,7 @@ module GraphQL
167
167
  end
168
168
 
169
169
  def coerce_result(value, ctx)
170
- raise NotImplementedError
170
+ raise GraphQL::RequiredImplementationMissingError
171
171
  end
172
172
 
173
173
  # Types with fields may override this
@@ -43,7 +43,7 @@ module GraphQL
43
43
 
44
44
  # @return [Object] This function's resolver
45
45
  def call(obj, args, ctx)
46
- raise NotImplementedError
46
+ raise GraphQL::RequiredImplementationMissingError
47
47
  end
48
48
 
49
49
  # @return [String, nil]
@@ -99,7 +99,8 @@ module GraphQL
99
99
  end
100
100
  end
101
101
 
102
- arguments_class.new(input_values, context: ctx, defaults_used: defaults_used)
102
+ result = arguments_class.new(input_values, context: ctx, defaults_used: defaults_used)
103
+ result.prepare
103
104
  end
104
105
 
105
106
  # @api private
@@ -207,7 +207,7 @@ module GraphQL
207
207
  when ListType
208
208
  default_value.to_a.map { |v| build_default_value(v, type.of_type) }
209
209
  else
210
- raise NotImplementedError, "Unexpected default value type #{type.inspect}"
210
+ raise GraphQL::RequiredImplementationMissingError, "Unexpected default value type #{type.inspect}"
211
211
  end
212
212
  end
213
213
 
@@ -314,7 +314,7 @@ def self.run_lexer(query_string)
314
314
  begin
315
315
  te = p+1;
316
316
  begin
317
- emit(:RCURLY, ts, te, meta)
317
+ emit(:RCURLY, ts, te, meta, "}")
318
318
  end
319
319
 
320
320
  end
@@ -328,7 +328,7 @@ def self.run_lexer(query_string)
328
328
  begin
329
329
  te = p+1;
330
330
  begin
331
- emit(:LCURLY, ts, te, meta)
331
+ emit(:LCURLY, ts, te, meta, "{")
332
332
  end
333
333
 
334
334
  end
@@ -342,7 +342,7 @@ def self.run_lexer(query_string)
342
342
  begin
343
343
  te = p+1;
344
344
  begin
345
- emit(:RPAREN, ts, te, meta)
345
+ emit(:RPAREN, ts, te, meta, ")")
346
346
  end
347
347
 
348
348
  end
@@ -356,7 +356,7 @@ def self.run_lexer(query_string)
356
356
  begin
357
357
  te = p+1;
358
358
  begin
359
- emit(:LPAREN, ts, te, meta)
359
+ emit(:LPAREN, ts, te, meta, "(")
360
360
  end
361
361
 
362
362
  end
@@ -370,7 +370,7 @@ def self.run_lexer(query_string)
370
370
  begin
371
371
  te = p+1;
372
372
  begin
373
- emit(:RBRACKET, ts, te, meta)
373
+ emit(:RBRACKET, ts, te, meta, "]")
374
374
  end
375
375
 
376
376
  end
@@ -384,7 +384,7 @@ def self.run_lexer(query_string)
384
384
  begin
385
385
  te = p+1;
386
386
  begin
387
- emit(:LBRACKET, ts, te, meta)
387
+ emit(:LBRACKET, ts, te, meta, "[")
388
388
  end
389
389
 
390
390
  end
@@ -398,7 +398,7 @@ def self.run_lexer(query_string)
398
398
  begin
399
399
  te = p+1;
400
400
  begin
401
- emit(:COLON, ts, te, meta)
401
+ emit(:COLON, ts, te, meta, ":")
402
402
  end
403
403
 
404
404
  end
@@ -440,7 +440,7 @@ def self.run_lexer(query_string)
440
440
  begin
441
441
  te = p+1;
442
442
  begin
443
- emit(:VAR_SIGN, ts, te, meta)
443
+ emit(:VAR_SIGN, ts, te, meta, "$")
444
444
  end
445
445
 
446
446
  end
@@ -454,7 +454,7 @@ def self.run_lexer(query_string)
454
454
  begin
455
455
  te = p+1;
456
456
  begin
457
- emit(:DIR_SIGN, ts, te, meta)
457
+ emit(:DIR_SIGN, ts, te, meta, "@")
458
458
  end
459
459
 
460
460
  end
@@ -468,7 +468,7 @@ def self.run_lexer(query_string)
468
468
  begin
469
469
  te = p+1;
470
470
  begin
471
- emit(:ELLIPSIS, ts, te, meta)
471
+ emit(:ELLIPSIS, ts, te, meta, "...")
472
472
  end
473
473
 
474
474
  end
@@ -482,7 +482,7 @@ def self.run_lexer(query_string)
482
482
  begin
483
483
  te = p+1;
484
484
  begin
485
- emit(:EQUALS, ts, te, meta)
485
+ emit(:EQUALS, ts, te, meta, "=")
486
486
  end
487
487
 
488
488
  end
@@ -496,7 +496,7 @@ def self.run_lexer(query_string)
496
496
  begin
497
497
  te = p+1;
498
498
  begin
499
- emit(:BANG, ts, te, meta)
499
+ emit(:BANG, ts, te, meta, "!")
500
500
  end
501
501
 
502
502
  end
@@ -510,7 +510,7 @@ def self.run_lexer(query_string)
510
510
  begin
511
511
  te = p+1;
512
512
  begin
513
- emit(:PIPE, ts, te, meta)
513
+ emit(:PIPE, ts, te, meta, "|")
514
514
  end
515
515
 
516
516
  end
@@ -524,7 +524,7 @@ def self.run_lexer(query_string)
524
524
  begin
525
525
  te = p+1;
526
526
  begin
527
- emit(:AMP, ts, te, meta)
527
+ emit(:AMP, ts, te, meta, "&")
528
528
  end
529
529
 
530
530
  end
@@ -738,7 +738,7 @@ def self.run_lexer(query_string)
738
738
  begin
739
739
  p = ((te))-1;
740
740
  begin
741
- emit(:ON, ts, te, meta)
741
+ emit(:ON, ts, te, meta, "on")
742
742
  end
743
743
 
744
744
  end
@@ -746,7 +746,7 @@ def self.run_lexer(query_string)
746
746
  begin
747
747
  p = ((te))-1;
748
748
  begin
749
- emit(:FRAGMENT, ts, te, meta)
749
+ emit(:FRAGMENT, ts, te, meta, "fragment")
750
750
  end
751
751
 
752
752
  end
@@ -754,7 +754,7 @@ def self.run_lexer(query_string)
754
754
  begin
755
755
  p = ((te))-1;
756
756
  begin
757
- emit(:TRUE, ts, te, meta)
757
+ emit(:TRUE, ts, te, meta, "true")
758
758
  end
759
759
 
760
760
  end
@@ -762,7 +762,7 @@ def self.run_lexer(query_string)
762
762
  begin
763
763
  p = ((te))-1;
764
764
  begin
765
- emit(:FALSE, ts, te, meta)
765
+ emit(:FALSE, ts, te, meta, "false")
766
766
  end
767
767
 
768
768
  end
@@ -770,7 +770,7 @@ def self.run_lexer(query_string)
770
770
  begin
771
771
  p = ((te))-1;
772
772
  begin
773
- emit(:NULL, ts, te, meta)
773
+ emit(:NULL, ts, te, meta, "null")
774
774
  end
775
775
 
776
776
  end
@@ -778,7 +778,7 @@ def self.run_lexer(query_string)
778
778
  begin
779
779
  p = ((te))-1;
780
780
  begin
781
- emit(:QUERY, ts, te, meta)
781
+ emit(:QUERY, ts, te, meta, "query")
782
782
  end
783
783
 
784
784
  end
@@ -786,7 +786,7 @@ def self.run_lexer(query_string)
786
786
  begin
787
787
  p = ((te))-1;
788
788
  begin
789
- emit(:MUTATION, ts, te, meta)
789
+ emit(:MUTATION, ts, te, meta, "mutation")
790
790
  end
791
791
 
792
792
  end
@@ -794,7 +794,7 @@ def self.run_lexer(query_string)
794
794
  begin
795
795
  p = ((te))-1;
796
796
  begin
797
- emit(:SUBSCRIPTION, ts, te, meta)
797
+ emit(:SUBSCRIPTION, ts, te, meta, "subscription")
798
798
  end
799
799
 
800
800
  end
@@ -1371,11 +1371,11 @@ end
1371
1371
 
1372
1372
  def self.record_comment(ts, te, meta)
1373
1373
  token = GraphQL::Language::Token.new(
1374
- name: :COMMENT,
1375
- value: meta[:data][ts, te - ts].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
1376
- line: meta[:line],
1377
- col: meta[:col],
1378
- prev_token: meta[:previous_token],
1374
+ :COMMENT,
1375
+ meta[:data][ts, te - ts].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
1376
+ meta[:line],
1377
+ meta[:col],
1378
+ meta[:previous_token],
1379
1379
  )
1380
1380
 
1381
1381
  meta[:previous_token] = token
@@ -1383,13 +1383,14 @@ meta[:previous_token] = token
1383
1383
  meta[:col] += te - ts
1384
1384
  end
1385
1385
 
1386
- def self.emit(token_name, ts, te, meta)
1386
+ def self.emit(token_name, ts, te, meta, token_value = nil)
1387
+ token_value ||= meta[:data][ts, te - ts].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING)
1387
1388
  meta[:tokens] << token = GraphQL::Language::Token.new(
1388
- name: token_name,
1389
- value: meta[:data][ts, te - ts].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
1390
- line: meta[:line],
1391
- col: meta[:col],
1392
- prev_token: meta[:previous_token],
1389
+ token_name,
1390
+ token_value,
1391
+ meta[:line],
1392
+ meta[:col],
1393
+ meta[:previous_token],
1393
1394
  )
1394
1395
  meta[:previous_token] = token
1395
1396
  # Bump the column counter for the next token
@@ -1428,30 +1429,30 @@ end
1428
1429
  # (It's faster: https://bugs.ruby-lang.org/issues/8110)
1429
1430
  if !value.valid_encoding? || value !~ VALID_STRING
1430
1431
  meta[:tokens] << token = GraphQL::Language::Token.new(
1431
- name: :BAD_UNICODE_ESCAPE,
1432
- value: value,
1433
- line: meta[:line],
1434
- col: meta[:col],
1435
- prev_token: meta[:previous_token],
1432
+ :BAD_UNICODE_ESCAPE,
1433
+ value,
1434
+ meta[:line],
1435
+ meta[:col],
1436
+ meta[:previous_token],
1436
1437
  )
1437
1438
  else
1438
1439
  replace_escaped_characters_in_place(value)
1439
1440
 
1440
1441
  if !value.valid_encoding?
1441
1442
  meta[:tokens] << token = GraphQL::Language::Token.new(
1442
- name: :BAD_UNICODE_ESCAPE,
1443
- value: value,
1444
- line: meta[:line],
1445
- col: meta[:col],
1446
- prev_token: meta[:previous_token],
1443
+ :BAD_UNICODE_ESCAPE,
1444
+ value,
1445
+ meta[:line],
1446
+ meta[:col],
1447
+ meta[:previous_token],
1447
1448
  )
1448
1449
  else
1449
1450
  meta[:tokens] << token = GraphQL::Language::Token.new(
1450
- name: :STRING,
1451
- value: value,
1452
- line: meta[:line],
1453
- col: meta[:col],
1454
- prev_token: meta[:previous_token],
1451
+ :STRING,
1452
+ value,
1453
+ meta[:line],
1454
+ meta[:col],
1455
+ meta[:previous_token],
1455
1456
  )
1456
1457
  end
1457
1458
  end
@@ -67,14 +67,14 @@
67
67
  main := |*
68
68
  INT => { emit(:INT, ts, te, meta) };
69
69
  FLOAT => { emit(:FLOAT, ts, te, meta) };
70
- ON => { emit(:ON, ts, te, meta) };
71
- FRAGMENT => { emit(:FRAGMENT, ts, te, meta) };
72
- TRUE => { emit(:TRUE, ts, te, meta) };
73
- FALSE => { emit(:FALSE, ts, te, meta) };
74
- NULL => { emit(:NULL, ts, te, meta) };
75
- QUERY => { emit(:QUERY, ts, te, meta) };
76
- MUTATION => { emit(:MUTATION, ts, te, meta) };
77
- SUBSCRIPTION => { emit(:SUBSCRIPTION, ts, te, meta) };
70
+ ON => { emit(:ON, ts, te, meta, "on") };
71
+ FRAGMENT => { emit(:FRAGMENT, ts, te, meta, "fragment") };
72
+ TRUE => { emit(:TRUE, ts, te, meta, "true") };
73
+ FALSE => { emit(:FALSE, ts, te, meta, "false") };
74
+ NULL => { emit(:NULL, ts, te, meta, "null") };
75
+ QUERY => { emit(:QUERY, ts, te, meta, "query") };
76
+ MUTATION => { emit(:MUTATION, ts, te, meta, "mutation") };
77
+ SUBSCRIPTION => { emit(:SUBSCRIPTION, ts, te, meta, "subscription") };
78
78
  SCHEMA => { emit(:SCHEMA, ts, te, meta) };
79
79
  SCALAR => { emit(:SCALAR, ts, te, meta) };
80
80
  TYPE => { emit(:TYPE, ts, te, meta) };
@@ -85,22 +85,22 @@
85
85
  ENUM => { emit(:ENUM, ts, te, meta) };
86
86
  INPUT => { emit(:INPUT, ts, te, meta) };
87
87
  DIRECTIVE => { emit(:DIRECTIVE, ts, te, meta) };
88
- RCURLY => { emit(:RCURLY, ts, te, meta) };
89
- LCURLY => { emit(:LCURLY, ts, te, meta) };
90
- RPAREN => { emit(:RPAREN, ts, te, meta) };
91
- LPAREN => { emit(:LPAREN, ts, te, meta) };
92
- RBRACKET => { emit(:RBRACKET, ts, te, meta) };
93
- LBRACKET => { emit(:LBRACKET, ts, te, meta) };
94
- COLON => { emit(:COLON, ts, te, meta) };
88
+ RCURLY => { emit(:RCURLY, ts, te, meta, "}") };
89
+ LCURLY => { emit(:LCURLY, ts, te, meta, "{") };
90
+ RPAREN => { emit(:RPAREN, ts, te, meta, ")") };
91
+ LPAREN => { emit(:LPAREN, ts, te, meta, "(")};
92
+ RBRACKET => { emit(:RBRACKET, ts, te, meta, "]") };
93
+ LBRACKET => { emit(:LBRACKET, ts, te, meta, "[") };
94
+ COLON => { emit(:COLON, ts, te, meta, ":") };
95
95
  QUOTED_STRING => { emit_string(ts, te, meta, block: false) };
96
96
  BLOCK_STRING => { emit_string(ts, te, meta, block: true) };
97
- VAR_SIGN => { emit(:VAR_SIGN, ts, te, meta) };
98
- DIR_SIGN => { emit(:DIR_SIGN, ts, te, meta) };
99
- ELLIPSIS => { emit(:ELLIPSIS, ts, te, meta) };
100
- EQUALS => { emit(:EQUALS, ts, te, meta) };
101
- BANG => { emit(:BANG, ts, te, meta) };
102
- PIPE => { emit(:PIPE, ts, te, meta) };
103
- AMP => { emit(:AMP, ts, te, meta) };
97
+ VAR_SIGN => { emit(:VAR_SIGN, ts, te, meta, "$") };
98
+ DIR_SIGN => { emit(:DIR_SIGN, ts, te, meta, "@") };
99
+ ELLIPSIS => { emit(:ELLIPSIS, ts, te, meta, "...") };
100
+ EQUALS => { emit(:EQUALS, ts, te, meta, "=") };
101
+ BANG => { emit(:BANG, ts, te, meta, "!") };
102
+ PIPE => { emit(:PIPE, ts, te, meta, "|") };
103
+ AMP => { emit(:AMP, ts, te, meta, "&") };
104
104
  IDENTIFIER => { emit(:IDENTIFIER, ts, te, meta) };
105
105
  COMMENT => { record_comment(ts, te, meta) };
106
106
 
@@ -163,11 +163,11 @@ module GraphQL
163
163
 
164
164
  def self.record_comment(ts, te, meta)
165
165
  token = GraphQL::Language::Token.new(
166
- name: :COMMENT,
167
- value: meta[:data][ts, te - ts].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
168
- line: meta[:line],
169
- col: meta[:col],
170
- prev_token: meta[:previous_token],
166
+ :COMMENT,
167
+ meta[:data][ts, te - ts].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
168
+ meta[:line],
169
+ meta[:col],
170
+ meta[:previous_token],
171
171
  )
172
172
 
173
173
  meta[:previous_token] = token
@@ -175,13 +175,14 @@ module GraphQL
175
175
  meta[:col] += te - ts
176
176
  end
177
177
 
178
- def self.emit(token_name, ts, te, meta)
178
+ def self.emit(token_name, ts, te, meta, token_value = nil)
179
+ token_value ||= meta[:data][ts, te - ts].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING)
179
180
  meta[:tokens] << token = GraphQL::Language::Token.new(
180
- name: token_name,
181
- value: meta[:data][ts, te - ts].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING),
182
- line: meta[:line],
183
- col: meta[:col],
184
- prev_token: meta[:previous_token],
181
+ token_name,
182
+ token_value,
183
+ meta[:line],
184
+ meta[:col],
185
+ meta[:previous_token],
185
186
  )
186
187
  meta[:previous_token] = token
187
188
  # Bump the column counter for the next token
@@ -220,30 +221,30 @@ module GraphQL
220
221
  # (It's faster: https://bugs.ruby-lang.org/issues/8110)
221
222
  if !value.valid_encoding? || value !~ VALID_STRING
222
223
  meta[:tokens] << token = GraphQL::Language::Token.new(
223
- name: :BAD_UNICODE_ESCAPE,
224
- value: value,
225
- line: meta[:line],
226
- col: meta[:col],
227
- prev_token: meta[:previous_token],
224
+ :BAD_UNICODE_ESCAPE,
225
+ value,
226
+ meta[:line],
227
+ meta[:col],
228
+ meta[:previous_token],
228
229
  )
229
230
  else
230
231
  replace_escaped_characters_in_place(value)
231
232
 
232
233
  if !value.valid_encoding?
233
234
  meta[:tokens] << token = GraphQL::Language::Token.new(
234
- name: :BAD_UNICODE_ESCAPE,
235
- value: value,
236
- line: meta[:line],
237
- col: meta[:col],
238
- prev_token: meta[:previous_token],
235
+ :BAD_UNICODE_ESCAPE,
236
+ value,
237
+ meta[:line],
238
+ meta[:col],
239
+ meta[:previous_token],
239
240
  )
240
241
  else
241
242
  meta[:tokens] << token = GraphQL::Language::Token.new(
242
- name: :STRING,
243
- value: value,
244
- line: meta[:line],
245
- col: meta[:col],
246
- prev_token: meta[:previous_token],
243
+ :STRING,
244
+ value,
245
+ meta[:line],
246
+ meta[:col],
247
+ meta[:previous_token],
247
248
  )
248
249
  end
249
250
  end
@@ -28,7 +28,8 @@ module GraphQL
28
28
  def initialize(options={})
29
29
  if options.key?(:position_source)
30
30
  position_source = options.delete(:position_source)
31
- @line, @col = position_source.line_and_column
31
+ @line = position_source.line
32
+ @col = position_source.col
32
33
  end
33
34
 
34
35
  @filename = options.delete(:filename)
@@ -66,7 +67,7 @@ module GraphQL
66
67
 
67
68
  # @return [Symbol] the method to call on {Language::Visitor} for this node
68
69
  def visit_method
69
- raise NotImplementedError, "#{self.class.name}#visit_method shold return a symbol"
70
+ raise GraphQL::RequiredImplementationMissingError, "#{self.class.name}#visit_method shold return a symbol"
70
71
  end
71
72
 
72
73
  def position
@@ -350,6 +351,8 @@ module GraphQL
350
351
 
351
352
  # A single selection in a GraphQL query.
352
353
  class Field < AbstractNode
354
+ NONE = [].freeze
355
+
353
356
  scalar_methods :name, :alias
354
357
  children_methods({
355
358
  arguments: GraphQL::Language::Nodes::Argument,
@@ -360,13 +363,13 @@ module GraphQL
360
363
  # @!attribute selections
361
364
  # @return [Array<Nodes::Field>] Selections on this object (or empty array if this is a scalar field)
362
365
 
363
- def initialize_node(name: nil, arguments: [], directives: [], selections: [], **kwargs)
364
- @name = name
365
- @arguments = arguments
366
- @directives = directives
367
- @selections = selections
366
+ def initialize_node(attributes)
367
+ @name = attributes[:name]
368
+ @arguments = attributes[:arguments] || NONE
369
+ @directives = attributes[:directives] || NONE
370
+ @selections = attributes[:selections] || NONE
368
371
  # oops, alias is a keyword:
369
- @alias = kwargs.fetch(:alias, nil)
372
+ @alias = attributes[:alias]
370
373
  end
371
374
 
372
375
  # Override this because default is `:fields`
@@ -21,6 +21,7 @@ def initialize(query_string, filename:, tracer: Tracing::NullTracer)
21
21
  @query_string = query_string
22
22
  @filename = filename
23
23
  @tracer = tracer
24
+ @reused_next_token = [nil, nil]
24
25
  end
25
26
 
26
27
  def parse_document
@@ -51,7 +52,9 @@ def next_token
51
52
  if lexer_token.nil?
52
53
  nil
53
54
  else
54
- [lexer_token.name, lexer_token]
55
+ @reused_next_token[0] = lexer_token.name
56
+ @reused_next_token[1] = lexer_token
57
+ @reused_next_token
55
58
  end
56
59
  end
57
60
 
@@ -440,6 +440,7 @@ def initialize(query_string, filename:, tracer: Tracing::NullTracer)
440
440
  @query_string = query_string
441
441
  @filename = filename
442
442
  @tracer = tracer
443
+ @reused_next_token = [nil, nil]
443
444
  end
444
445
 
445
446
  def parse_document
@@ -470,7 +471,9 @@ def next_token
470
471
  if lexer_token.nil?
471
472
  nil
472
473
  else
473
- [lexer_token.name, lexer_token]
474
+ @reused_next_token[0] = lexer_token.name
475
+ @reused_next_token[1] = lexer_token
476
+ @reused_next_token
474
477
  end
475
478
  end
476
479
 
@@ -14,7 +14,7 @@ module GraphQL
14
14
  attr_reader :value
15
15
  attr_reader :prev_token, :line, :col
16
16
 
17
- def initialize(value:, name:, line:, col:, prev_token:)
17
+ def initialize(name, value, line, col, prev_token)
18
18
  @name = name
19
19
  @value = -value
20
20
  @line = line
@@ -87,6 +87,10 @@ module GraphQL
87
87
 
88
88
  def_delegators :to_h, :keys, :values, :each, :any?
89
89
 
90
+ def prepare
91
+ self
92
+ end
93
+
90
94
  # Access each key, value and type for the arguments in this set.
91
95
  # @yield [argument_value] The {ArgumentValue} for each argument
92
96
  # @yieldparam argument_value [ArgumentValue]
@@ -152,7 +156,8 @@ module GraphQL
152
156
  wrap_value(value, arg_defn_type.of_type, context)
153
157
  when GraphQL::InputObjectType
154
158
  if value.is_a?(Hash)
155
- arg_defn_type.arguments_class.new(value, context: context, defaults_used: Set.new)
159
+ result = arg_defn_type.arguments_class.new(value, context: context, defaults_used: Set.new)
160
+ result.prepare
156
161
  else
157
162
  value
158
163
  end
@@ -108,7 +108,8 @@ module GraphQL
108
108
  end
109
109
  end
110
110
 
111
- argument_owner.arguments_class.new(values_hash, context: context, defaults_used: defaults_used)
111
+ result = argument_owner.arguments_class.new(values_hash, context: context, defaults_used: defaults_used)
112
+ result.prepare
112
113
  end
113
114
  end
114
115
  end
@@ -139,7 +139,7 @@ module GraphQL
139
139
 
140
140
  # An opaque operation which returns a connection-specific cursor.
141
141
  def cursor_from_node(object)
142
- raise NotImplementedError, "must return a cursor for this object/connection pair"
142
+ raise GraphQL::RequiredImplementationMissingError, "must return a cursor for this object/connection pair"
143
143
  end
144
144
 
145
145
  def inspect
@@ -161,11 +161,11 @@ module GraphQL
161
161
  end
162
162
 
163
163
  def paged_nodes
164
- raise NotImplementedError, "must return nodes for this connection after paging"
164
+ raise GraphQL::RequiredImplementationMissingError, "must return nodes for this connection after paging"
165
165
  end
166
166
 
167
167
  def sliced_nodes
168
- raise NotImplementedError, "must return all nodes for this connection after chopping off first and last"
168
+ raise GraphQL::RequiredImplementationMissingError, "must return all nodes for this connection after chopping off first and last"
169
169
  end
170
170
  end
171
171
  end
@@ -25,12 +25,16 @@ module GraphQL
25
25
 
26
26
  def has_next_page
27
27
  if first
28
- paged_nodes.length >= first && sliced_nodes_count > first
29
- elsif GraphQL::Relay::ConnectionType.bidirectional_pagination && last
30
- sliced_nodes_count >= last
31
- else
32
- false
28
+ if defined?(ActiveRecord::Relation) && nodes.is_a?(ActiveRecord::Relation)
29
+ initial_offset = after ? offset_from_cursor(after) : 0
30
+ return paged_nodes.length >= first && nodes.offset(first + initial_offset).exists?
31
+ end
32
+ return paged_nodes.length >= first && sliced_nodes_count > first
33
+ end
34
+ if GraphQL::Relay::ConnectionType.bidirectional_pagination && last
35
+ return sliced_nodes_count >= last
33
36
  end
37
+ false
34
38
  end
35
39
 
36
40
  def has_previous_page
@@ -281,13 +281,13 @@ module GraphQL
281
281
  ensure_defined
282
282
  # Assert that all necessary configs are present:
283
283
  validation_error = Validation.validate(self)
284
- validation_error && raise(NotImplementedError, validation_error)
284
+ validation_error && raise(GraphQL::RequiredImplementationMissingError, validation_error)
285
285
  rebuild_artifacts
286
286
 
287
287
  @definition_error = nil
288
288
  nil
289
289
  rescue StandardError => err
290
- if @raise_definition_error || err.is_a?(CyclicalDefinitionError)
290
+ if @raise_definition_error || err.is_a?(CyclicalDefinitionError) || err.is_a?(GraphQL::RequiredImplementationMissingError)
291
291
  raise
292
292
  else
293
293
  # Raise this error _later_ to avoid messing with Rails constant loading
@@ -492,7 +492,7 @@ module GraphQL
492
492
  def resolve_type(type, object, ctx = :__undefined__)
493
493
  check_resolved_type(type, object, ctx) do |ok_type, ok_object, ok_ctx|
494
494
  if @resolve_type_proc.nil?
495
- raise(NotImplementedError, "Can't determine GraphQL type for: #{ok_object.inspect}, define `resolve_type (type, obj, ctx) -> { ... }` inside `Schema.define`.")
495
+ raise(GraphQL::RequiredImplementationMissingError, "Can't determine GraphQL type for: #{ok_object.inspect}, define `resolve_type (type, obj, ctx) -> { ... }` inside `Schema.define`.")
496
496
  end
497
497
  @resolve_type_proc.call(ok_type, ok_object, ok_ctx)
498
498
  end
@@ -553,7 +553,7 @@ module GraphQL
553
553
  # @return [Any] The application object identified by `id`
554
554
  def object_from_id(id, ctx)
555
555
  if @object_from_id_proc.nil?
556
- raise(NotImplementedError, "Can't fetch an object for id \"#{id}\" because the schema's `object_from_id (id, ctx) -> { ... }` function is not defined")
556
+ raise(GraphQL::RequiredImplementationMissingError, "Can't fetch an object for id \"#{id}\" because the schema's `object_from_id (id, ctx) -> { ... }` function is not defined")
557
557
  else
558
558
  @object_from_id_proc.call(id, ctx)
559
559
  end
@@ -620,7 +620,7 @@ module GraphQL
620
620
  # @return [String] a unique identifier for `object` which clients can use to refetch it
621
621
  def id_from_object(object, type, ctx)
622
622
  if @id_from_object_proc.nil?
623
- raise(NotImplementedError, "Can't generate an ID for #{object.inspect} of type #{type}, schema's `id_from_object` must be defined")
623
+ raise(GraphQL::RequiredImplementationMissingError, "Can't generate an ID for #{object.inspect} of type #{type}, schema's `id_from_object` must be defined")
624
624
  else
625
625
  @id_from_object_proc.call(object, type, ctx)
626
626
  end
@@ -956,16 +956,16 @@ module GraphQL
956
956
  if type.kind.object?
957
957
  type
958
958
  else
959
- raise NotImplementedError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
959
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
960
960
  end
961
961
  end
962
962
 
963
963
  def object_from_id(node_id, ctx)
964
- raise NotImplementedError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)"
964
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)"
965
965
  end
966
966
 
967
967
  def id_from_object(object, type, ctx)
968
- raise NotImplementedError, "#{self.name}.id_from_object(object, type, ctx) must be implemented to create global ids (tried to create an id for `#{object.inspect}`)"
968
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.id_from_object(object, type, ctx) must be implemented to create global ids (tried to create an id for `#{object.inspect}`)"
969
969
  end
970
970
 
971
971
  def visible?(member, context)
@@ -94,17 +94,7 @@ module GraphQL
94
94
  end
95
95
 
96
96
  def authorized?(obj, ctx)
97
- arg_type = type.unwrap
98
- if arg_type.kind.input_object? && arg_type != @owner
99
- arg_type.arguments.each do |_name, input_obj_arg|
100
- if !input_obj_arg.authorized?(obj, ctx)
101
- return false
102
- end
103
- end
104
- true
105
- else
106
- true
107
- end
97
+ true
108
98
  end
109
99
 
110
100
  def to_graphql
@@ -111,7 +111,7 @@ module GraphQL
111
111
  end
112
112
 
113
113
  NullResolveType = ->(type, obj, ctx) {
114
- raise(NotImplementedError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
114
+ raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
115
115
  }
116
116
 
117
117
  NullScalarCoerce = ->(val, _ctx) { val }
@@ -58,7 +58,7 @@ module GraphQL
58
58
  # @param context [GraphQL::Query::Context]
59
59
  # @return [Boolean] If truthy, execution will continue
60
60
  def self.enabled?(flag_name, object, context)
61
- raise NotImplementedError, "Implement `.enabled?(flag_name, object, context)` to return true or false for the feature flag (#{flag_name.inspect})"
61
+ raise GraphQL::RequiredImplementationMissingError, "Implement `.enabled?(flag_name, object, context)` to return true or false for the feature flag (#{flag_name.inspect})"
62
62
  end
63
63
  end
64
64
  end
@@ -14,6 +14,7 @@ module GraphQL
14
14
  include GraphQL::Schema::Member::AcceptsDefinition
15
15
  include GraphQL::Schema::Member::HasArguments
16
16
  include GraphQL::Schema::Member::HasPath
17
+ extend GraphQL::Schema::FindInheritedValue
17
18
 
18
19
  # @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
19
20
  attr_reader :name
@@ -135,6 +136,22 @@ module GraphQL
135
136
  end
136
137
  end
137
138
 
139
+ # This extension is applied to fields when {#connection?} is true.
140
+ #
141
+ # You can override it in your base field definition.
142
+ # @return [Class] A {FieldExtension} subclass for implementing pagination behavior.
143
+ # @example Configuring a custom extension
144
+ # class Types::BaseField < GraphQL::Schema::Field
145
+ # connection_extension(MyCustomExtension)
146
+ # end
147
+ def self.connection_extension(new_extension_class = nil)
148
+ if new_extension_class
149
+ @connection_extension = new_extension_class
150
+ else
151
+ @connection_extension ||= find_inherited_value(:connection_extension, ConnectionExtension)
152
+ end
153
+ end
154
+
138
155
  # @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
139
156
  # @param type [Class, GraphQL::BaseType, Array] The return type of this field
140
157
  # @param owner [Class] The type that this field belongs to
@@ -246,7 +263,7 @@ module GraphQL
246
263
  # The problem with putting this after the definition_block
247
264
  # is that it would override arguments
248
265
  if connection?
249
- self.extension(ConnectionExtension)
266
+ self.extension(self.class.connection_extension)
250
267
  end
251
268
 
252
269
  if definition_block
@@ -582,7 +599,7 @@ module GraphQL
582
599
  elsif ctx.respond_to?(extra_name)
583
600
  ctx.public_send(extra_name)
584
601
  else
585
- raise NotImplementedError, "Unknown field extra for #{self.path}: #{extra_name.inspect}"
602
+ raise GraphQL::RequiredImplementationMissingError, "Unknown field extra for #{self.path}: #{extra_name.inspect}"
586
603
  end
587
604
  end
588
605
 
@@ -57,6 +57,10 @@ module GraphQL
57
57
  to_h
58
58
  end
59
59
 
60
+ def prepare
61
+ self
62
+ end
63
+
60
64
  def unwrap_value(value)
61
65
  case value
62
66
  when Array
@@ -34,7 +34,7 @@ module GraphQL
34
34
  end
35
35
 
36
36
  NullResolveType = ->(type, obj, ctx) {
37
- raise(NotImplementedError, "This schema was loaded from string, so it can't resolve types for objects")
37
+ raise(GraphQL::RequiredImplementationMissingError, "This schema was loaded from string, so it can't resolve types for objects")
38
38
  }
39
39
 
40
40
  NullScalarCoerce = ->(val, _ctx) { val }
@@ -51,7 +51,7 @@ module GraphQL
51
51
  when "NON_NULL"
52
52
  NonNullType.new(of_type: resolve_type(types, type.fetch("ofType")))
53
53
  else
54
- fail NotImplementedError, "#{kind} not implemented"
54
+ fail GraphQL::RequiredImplementationMissingError, "#{kind} not implemented"
55
55
  end
56
56
  end
57
57
 
@@ -171,7 +171,7 @@ module GraphQL
171
171
  }
172
172
  )
173
173
  else
174
- fail NotImplementedError, "#{type["kind"]} not implemented"
174
+ fail GraphQL::RequiredImplementationMissingError, "#{type["kind"]} not implemented"
175
175
  end
176
176
  end
177
177
  end
@@ -74,7 +74,7 @@ module GraphQL
74
74
 
75
75
  # @return [GraphQL::BaseType] Convert this type to a legacy-style object.
76
76
  def to_graphql
77
- raise NotImplementedError
77
+ raise GraphQL::RequiredImplementationMissingError
78
78
  end
79
79
 
80
80
  alias :unwrap :itself
@@ -88,7 +88,7 @@ module GraphQL
88
88
  # without any namespaces and with any `-Type` suffix removed
89
89
  def default_graphql_name
90
90
  @default_graphql_name ||= begin
91
- raise NotImplementedError, 'Anonymous class should declare a `graphql_name`' if name.nil?
91
+ raise GraphQL::RequiredImplementationMissingError, 'Anonymous class should declare a `graphql_name`' if name.nil?
92
92
 
93
93
  name.split("::").last.sub(/Type\Z/, "")
94
94
  end
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
+ require 'irb/ruby-token'
3
+
2
4
  module GraphQL
3
5
  class Schema
4
6
  class Member
@@ -38,16 +40,23 @@ module GraphQL
38
40
  end
39
41
  end
40
42
 
43
+ # A list of Ruby keywords.
44
+ #
45
+ # @api private
46
+ RUBY_KEYWORDS = RubyToken::TokenDefinitions.select { |definition| definition[1] == RubyToken::TkId }
47
+ .map { |definition| definition[2] }
48
+ .compact
49
+
50
+ # A list of GraphQL-Ruby keywords.
51
+ #
52
+ # @api private
53
+ GRAPHQL_RUBY_KEYWORDS = [:context, :object, :method]
54
+
41
55
  # A list of field names that we should advise users to pick a different
42
56
  # resolve method name.
43
57
  #
44
58
  # @api private
45
- CONFLICT_FIELD_NAMES = Set.new([
46
- # GraphQL-Ruby conflicts
47
- :context, :object,
48
- # Ruby built-ins conflicts
49
- :method, :class
50
- ])
59
+ CONFLICT_FIELD_NAMES = Set.new(GRAPHQL_RUBY_KEYWORDS + RUBY_KEYWORDS)
51
60
 
52
61
  # Register this field with the class, overriding a previous one if needed.
53
62
  # @param field_defn [GraphQL::Schema::Field]
@@ -30,7 +30,7 @@ module GraphQL
30
30
 
31
31
  # @return [GraphQL::TypeKinds::TypeKind]
32
32
  def kind
33
- raise NotImplementedError
33
+ raise GraphQL::RequiredImplementationMissingError
34
34
  end
35
35
  end
36
36
  end
@@ -103,7 +103,7 @@ module GraphQL
103
103
  # Do the work. Everything happens here.
104
104
  # @return [Object] An object corresponding to the return type
105
105
  def resolve(**args)
106
- raise NotImplementedError, "#{self.class.name}#resolve should execute the field's logic"
106
+ raise GraphQL::RequiredImplementationMissingError, "#{self.class.name}#resolve should execute the field's logic"
107
107
  end
108
108
 
109
109
  # Called before arguments are prepared.
@@ -267,6 +267,7 @@ module GraphQL
267
267
  arguments: arguments,
268
268
  null: null,
269
269
  complexity: complexity,
270
+ extensions: extensions,
270
271
  }
271
272
  end
272
273
 
@@ -320,6 +321,18 @@ module GraphQL
320
321
  inherited_lookups.merge(own_arguments_loads_as_type)
321
322
  end
322
323
 
324
+ # Registers new extension
325
+ # @param extension [Class] Extension class
326
+ # @param options [Hash] Optional extension options
327
+ def extension(extension, **options)
328
+ extensions << {extension => options}
329
+ end
330
+
331
+ # @api private
332
+ def extensions
333
+ @extensions ||= []
334
+ end
335
+
323
336
  private
324
337
 
325
338
  def own_arguments_loads_as_type
@@ -14,7 +14,7 @@ module GraphQL
14
14
  end
15
15
 
16
16
  def to_graphql
17
- raise NotImplementedError
17
+ raise GraphQL::RequiredImplementationMissingError
18
18
  end
19
19
 
20
20
  def unwrap
@@ -11,11 +11,11 @@ module GraphQL
11
11
  super
12
12
  @defdep_node_paths = {}
13
13
 
14
- # { name => node } pairs for fragments
15
- @defdep_fragment_definitions = {}
14
+ # { name => [node, ...] } pairs for fragments (although duplicate-named fragments are _invalid_, they are _possible_)
15
+ @defdep_fragment_definitions = Hash.new{ |h, k| h[k] = [] }
16
16
 
17
17
  # This tracks dependencies from fragment to Node where it was used
18
- # { fragment_definition_node => [dependent_node, dependent_node]}
18
+ # { fragment_definition_name => [dependent_node, dependent_node]}
19
19
  @defdep_dependent_definitions = Hash.new { |h, k| h[k] = Set.new }
20
20
 
21
21
  # First-level usages of spreads within definitions
@@ -32,7 +32,7 @@ module GraphQL
32
32
  def on_document(node, parent)
33
33
  node.definitions.each do |definition|
34
34
  if definition.is_a? GraphQL::Language::Nodes::FragmentDefinition
35
- @defdep_fragment_definitions[definition.name] = definition
35
+ @defdep_fragment_definitions[definition.name] << definition
36
36
  end
37
37
  end
38
38
  super
@@ -42,7 +42,7 @@ module GraphQL
42
42
  end
43
43
 
44
44
  def on_operation_definition(node, prev_node)
45
- @defdep_node_paths[node] = NodeWithPath.new(node, context.path)
45
+ @defdep_node_paths[node.name] = NodeWithPath.new(node, context.path)
46
46
  @defdep_current_parent = node
47
47
  super
48
48
  @defdep_current_parent = nil
@@ -59,7 +59,7 @@ module GraphQL
59
59
  @defdep_node_paths[node] = NodeWithPath.new(node, context.path)
60
60
 
61
61
  # Track both sides of the dependency
62
- @defdep_dependent_definitions[@defdep_fragment_definitions[node.name]] << @defdep_current_parent
62
+ @defdep_dependent_definitions[node.name] << @defdep_current_parent
63
63
  @defdep_immediate_dependencies[@defdep_current_parent] << node
64
64
  super
65
65
  end
@@ -116,24 +116,28 @@ module GraphQL
116
116
  dependency_map = DependencyMap.new
117
117
  # Don't allow the loop to run more times
118
118
  # than the number of fragments in the document
119
- max_loops = @defdep_fragment_definitions.size
119
+ max_loops = 0
120
+ @defdep_fragment_definitions.each_value do |v|
121
+ max_loops += v.size
122
+ end
123
+
120
124
  loops = 0
121
125
 
122
126
  # Instead of tracking independent fragments _as you visit_,
123
127
  # determine them at the end. This way, we can treat fragments with the
124
128
  # same name as if they were the same name. If _any_ of the fragments
125
129
  # with that name has a dependency, we record it.
126
- independent_fragment_nodes = @defdep_fragment_definitions.values - @defdep_immediate_dependencies.keys
130
+ independent_fragment_nodes = @defdep_fragment_definitions.values.flatten - @defdep_immediate_dependencies.keys
127
131
 
128
132
  while fragment_node = independent_fragment_nodes.pop
129
133
  loops += 1
130
134
  if loops > max_loops
131
- raise("Resolution loops exceeded the number of definitions; infinite loop detected.")
135
+ raise("Resolution loops exceeded the number of definitions; infinite loop detected. (Max: #{max_loops}, Current: #{loops})")
132
136
  end
133
137
  # Since it's independent, let's remove it from here.
134
138
  # That way, we can use the remainder to identify cycles
135
139
  @defdep_immediate_dependencies.delete(fragment_node)
136
- fragment_usages = @defdep_dependent_definitions[fragment_node]
140
+ fragment_usages = @defdep_dependent_definitions[fragment_node.name]
137
141
  if fragment_usages.empty?
138
142
  # If we didn't record any usages during the visit,
139
143
  # then this fragment is unused.
@@ -151,10 +155,15 @@ module GraphQL
151
155
  if block_given?
152
156
  yield(definition_node, removed, fragment_node)
153
157
  end
154
- if remaining.empty? && definition_node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
158
+ if remaining.empty? &&
159
+ definition_node.is_a?(GraphQL::Language::Nodes::FragmentDefinition) &&
160
+ definition_node.name != fragment_node.name
155
161
  # If all of this definition's dependencies have
156
162
  # been resolved, we can now resolve its
157
163
  # own dependents.
164
+ #
165
+ # But, it's possible to have a duplicate-named fragment here.
166
+ # Skip it in that case
158
167
  independent_fragment_nodes << definition_node
159
168
  end
160
169
  end
@@ -166,7 +175,7 @@ module GraphQL
166
175
  # then they're still in there
167
176
  @defdep_immediate_dependencies.each do |defn_node, deps|
168
177
  deps.each do |spread|
169
- if @defdep_fragment_definitions[spread.name].nil?
178
+ if !@defdep_fragment_definitions.key?(spread.name)
170
179
  dependency_map.unmet_dependencies[@defdep_node_paths[defn_node]] << @defdep_node_paths[spread]
171
180
  deps.delete(spread)
172
181
  end
@@ -124,7 +124,7 @@ module GraphQL
124
124
  # @yieldparam subscription_id [String]
125
125
  # @return [void]
126
126
  def each_subscription_id(event)
127
- raise NotImplementedError
127
+ raise GraphQL::RequiredImplementationMissingError
128
128
  end
129
129
 
130
130
  # The system wants to send an update to this subscription.
@@ -132,7 +132,7 @@ module GraphQL
132
132
  # @param subscription_id [String]
133
133
  # @return [Hash] Containing required keys
134
134
  def read_subscription(subscription_id)
135
- raise NotImplementedError
135
+ raise GraphQL::RequiredImplementationMissingError
136
136
  end
137
137
 
138
138
  # A subscription query was re-evaluated, returning `result`.
@@ -141,7 +141,7 @@ module GraphQL
141
141
  # @param result [Hash]
142
142
  # @return [void]
143
143
  def deliver(subscription_id, result)
144
- raise NotImplementedError
144
+ raise GraphQL::RequiredImplementationMissingError
145
145
  end
146
146
 
147
147
  # `query` was executed and found subscriptions to `events`.
@@ -150,7 +150,7 @@ module GraphQL
150
150
  # @param events [Array<GraphQL::Subscriptions::Event>]
151
151
  # @return [void]
152
152
  def write_subscription(query, events)
153
- raise NotImplementedError
153
+ raise GraphQL::RequiredImplementationMissingError
154
154
  end
155
155
 
156
156
  # A subscription was terminated server-side.
@@ -158,7 +158,7 @@ module GraphQL
158
158
  # @param subscription_id [String]
159
159
  # @return void.
160
160
  def delete_subscription(subscription_id)
161
- raise NotImplementedError
161
+ raise GraphQL::RequiredImplementationMissingError
162
162
  end
163
163
 
164
164
  # @return [String] A new unique identifier for a subscription
@@ -13,7 +13,7 @@ module GraphQL
13
13
  # @param input_text [String] Untransformed GraphQL-Ruby code
14
14
  # @return [String] The input text, with a transformation applied if necessary
15
15
  def apply(input_text)
16
- raise NotImplementedError, "Return transformed text here"
16
+ raise GraphQL::RequiredImplementationMissingError, "Return transformed text here"
17
17
  end
18
18
 
19
19
  # Recursively transform a `.define`-DSL-based type expression into a class-ready expression, for example:
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.9.14"
3
+ VERSION = "1.9.15"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.14
4
+ version: 1.9.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-14 00:00:00.000000000 Z
11
+ date: 2019-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips