graph_weaver 0.7.5 → 0.7.7

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +43 -43
  3. data/docs/errors.md +7 -3
  4. data/docs/generated_modules.md +66 -16
  5. data/docs/getting_started.md +39 -19
  6. data/docs/logging.md +79 -35
  7. data/docs/migrating.md +11 -4
  8. data/docs/scalars.md +12 -3
  9. data/docs/testing.md +75 -13
  10. data/docs/upgrading.md +43 -3
  11. data/examples/README.md +4 -2
  12. data/examples/github/generate.rb +22 -8
  13. data/examples/github/generated/star_mutation.rb +2 -2
  14. data/examples/github/generated/stargazers_query.rb +2 -2
  15. data/examples/github/generated/starred_query.rb +2 -2
  16. data/examples/github/run.rb +1 -0
  17. data/examples/github/setup.rb +16 -8
  18. data/graph_weaver.gemspec +15 -6
  19. data/lib/generators/graph_weaver/install_generator.rb +20 -2
  20. data/lib/graph_weaver/client.rb +0 -23
  21. data/lib/graph_weaver/codegen/aliases.rb +15 -3
  22. data/lib/graph_weaver/codegen/emit.rb +7 -6
  23. data/lib/graph_weaver/codegen/enum_type.rb +26 -2
  24. data/lib/graph_weaver/codegen/nodes.rb +21 -2
  25. data/lib/graph_weaver/codegen.rb +106 -54
  26. data/lib/graph_weaver/graph.rb +67 -6
  27. data/lib/graph_weaver/in_process.rb +1 -1
  28. data/lib/graph_weaver/input_struct.rb +29 -4
  29. data/lib/graph_weaver/internal/overrides.rb +126 -14
  30. data/lib/graph_weaver/internal/test_clients.rb +29 -7
  31. data/lib/graph_weaver/internal/unused.rb +30 -11
  32. data/lib/graph_weaver/internal/values.rb +17 -5
  33. data/lib/graph_weaver/internal.rb +23 -6
  34. data/lib/graph_weaver/log_subscriber.rb +27 -17
  35. data/lib/graph_weaver/logging.rb +89 -53
  36. data/lib/graph_weaver/parsing.rb +32 -3
  37. data/lib/graph_weaver/query_module.rb +48 -8
  38. data/lib/graph_weaver/railtie.rb +250 -11
  39. data/lib/graph_weaver/rspec.rb +41 -17
  40. data/lib/graph_weaver/schema_diff.rb +24 -5
  41. data/lib/graph_weaver/schema_loader.rb +22 -9
  42. data/lib/graph_weaver/tasks.rb +71 -44
  43. data/lib/graph_weaver/testing/fake_client.rb +24 -21
  44. data/lib/graph_weaver/testing.rb +27 -7
  45. data/lib/graph_weaver/transport.rb +1 -1
  46. data/lib/graph_weaver/version.rb +1 -1
  47. data/lib/graph_weaver.rb +76 -43
  48. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53034719b6dcd597d098091606e08b6f4a33a15a89826374cf6b14df841aa0fc
4
- data.tar.gz: cb64c3e12070f522a44353066aaac80ea7faf9df7d8a564a1abfcd053584049b
3
+ metadata.gz: a7b665c1263db52f315217a6298ff638d963b926e44f94c16dc2972de4140320
4
+ data.tar.gz: a9712a11f712adfa8846b180b1e22874f63d57fd00514d311b9cb0e60c9f6ebf
5
5
  SHA512:
6
- metadata.gz: 6b320bc47a5839ef8561f44ac12b78eba36cc8b33078397f2818db59ce1268fef5f3b6b4aa347ef1e21443c9e213dd1ea6c5a297d14d8fa5c651ff8bcacd3052
7
- data.tar.gz: 35abfec468ca9f50327c7f6a33a7f881884c004daf294019dd0a27c12498c010ec4383404a86d7015ed74616d114978e62e081703acffbcba58b9b3abad69add
6
+ metadata.gz: 693fa12dd8117988b35adcf7f88234b382bae9a8d82307a23ce7ffa3f2837c1304a529155dedab414691b13c3d982314cb70c8d5587660f0766988c7defc0f83
7
+ data.tar.gz: 2933a687d3c85faa3d514e6daddd5fe9ea62ee6e48f146e847f492a63e91b6c43c6986964f52f1fa458f1f6a60b92038058357e21062e966c47c8fef0a0235c4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graph_weaver (0.7.5)
4
+ graph_weaver (0.7.7)
5
5
  graphql (>= 2.6.7)
6
6
  sorbet-runtime
7
7
 
@@ -62,7 +62,7 @@ GEM
62
62
  erubi (1.13.1)
63
63
  faker (3.8.0)
64
64
  i18n (>= 1.8.11, < 2)
65
- faraday (2.14.3)
65
+ faraday (2.14.4)
66
66
  faraday-net_http (>= 2.0, < 3.5)
67
67
  json
68
68
  logger
@@ -94,7 +94,7 @@ GEM
94
94
  prism (>= 1.3.0)
95
95
  rdoc (>= 4.0.0)
96
96
  reline (>= 0.4.2)
97
- json (2.21.2)
97
+ json (3.0.2)
98
98
  logger (1.7.0)
99
99
  loofah (2.25.2)
100
100
  crass (~> 1.0.2)
@@ -124,7 +124,7 @@ GEM
124
124
  racc (~> 1.4)
125
125
  nokogiri (1.19.4-x86_64-linux-gnu)
126
126
  racc (~> 1.4)
127
- parallel (2.1.0)
127
+ parallel (2.2.0)
128
128
  parser (3.3.12.0)
129
129
  ast (~> 2.4.1)
130
130
  racc
@@ -159,10 +159,10 @@ GEM
159
159
  tsort (>= 0.2)
160
160
  zeitwerk (~> 2.6)
161
161
  rake (13.4.2)
162
- rbi (0.3.14)
162
+ rbi (0.4.3)
163
163
  prism (~> 1.0)
164
164
  rbs (>= 4.0.1)
165
- rbs (4.0.3)
165
+ rbs (4.2.0)
166
166
  logger
167
167
  prism (>= 1.6.0)
168
168
  tsort
@@ -175,7 +175,7 @@ GEM
175
175
  regexp_parser (2.12.0)
176
176
  reline (0.6.3)
177
177
  io-console (~> 0.5)
178
- require-hooks (0.4.0)
178
+ require-hooks (0.5.1)
179
179
  rexml (3.4.4)
180
180
  rspec (3.13.2)
181
181
  rspec-core (~> 3.13.0)
@@ -190,35 +190,35 @@ GEM
190
190
  diff-lcs (>= 1.2.0, < 2.0)
191
191
  rspec-support (~> 3.13.0)
192
192
  rspec-support (3.13.7)
193
- rubydex (0.2.7-aarch64-linux)
194
- rubydex (0.2.7-arm64-darwin)
195
- rubydex (0.2.7-x86_64-darwin)
196
- rubydex (0.2.7-x86_64-linux)
193
+ rubydex (0.4.1-aarch64-linux)
194
+ rubydex (0.4.1-arm64-darwin)
195
+ rubydex (0.4.1-x86_64-darwin)
196
+ rubydex (0.4.1-x86_64-linux)
197
197
  securerandom (0.4.1)
198
198
  simplecov (1.3.0)
199
- sorbet (0.6.13498)
200
- sorbet-static (= 0.6.13498)
201
- sorbet-runtime (0.6.13498)
202
- sorbet-static (0.6.13498-aarch64-linux)
203
- sorbet-static (0.6.13498-universal-darwin)
204
- sorbet-static (0.6.13498-x86_64-linux)
205
- sorbet-static-and-runtime (0.6.13498)
206
- sorbet (= 0.6.13498)
207
- sorbet-runtime (= 0.6.13498)
208
- spoom (1.8.3)
199
+ sorbet (0.6.13506)
200
+ sorbet-static (= 0.6.13506)
201
+ sorbet-runtime (0.6.13506)
202
+ sorbet-static (0.6.13506-aarch64-linux)
203
+ sorbet-static (0.6.13506-universal-darwin)
204
+ sorbet-static (0.6.13506-x86_64-linux)
205
+ sorbet-static-and-runtime (0.6.13506)
206
+ sorbet (= 0.6.13506)
207
+ sorbet-runtime (= 0.6.13506)
208
+ spoom (1.8.7)
209
209
  erubi (>= 1.10.0)
210
210
  prism (>= 0.28.0)
211
- rbi (>= 0.3.14)
211
+ rbi (>= 0.4.2)
212
212
  rbs (>= 4.0.0.dev.5)
213
213
  rexml (>= 3.2.6)
214
214
  sorbet-static-and-runtime (>= 0.5.10187)
215
215
  thor (>= 0.19.2)
216
- tapioca (0.19.2)
216
+ tapioca (0.20.0)
217
217
  benchmark
218
218
  bundler (>= 2.2.25)
219
219
  netrc (>= 0.11.0)
220
220
  parallel (>= 1.21.0)
221
- rbi (>= 0.3.7)
221
+ rbi (>= 0.4.1)
222
222
  require-hooks (>= 0.2.2)
223
223
  rubydex (>= 0.1.0.beta10)
224
224
  sorbet-static-and-runtime (>= 0.6.12698)
@@ -292,20 +292,20 @@ CHECKSUMS
292
292
  erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
293
293
  erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
294
294
  faker (3.8.0) sha256=c147b308df73a90f27a4fc84f18d4c22ef0ad9c2a64b2b61c86fd0ca71753efc
295
- faraday (2.14.3) sha256=1882247e6766615c8220b4392bf1d27f6ebb63d8e28267587cef1fb0bf37f278
295
+ faraday (2.14.4) sha256=9bb4408c44621b0dbaaaa39e5dbfc4dbe58cc6751b6eb94d16ae9e1f2f2fab6e
296
296
  faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588
297
297
  fiber-storage (1.0.1) sha256=f48e5b6d8b0be96dac486332b55cee82240057065dc761c1ea692b2e719240e1
298
298
  google-protobuf (4.35.1-aarch64-linux-gnu) sha256=50ca44d0eeff3f8475e630a1accdd974256f3510694d574e2c9d6119ea8bc9e1
299
299
  google-protobuf (4.35.1-arm64-darwin) sha256=d9c957df04fa89c749fa9a72a7b383eb4296efc9b2303dc6fd6fbe39c698ad6b
300
300
  google-protobuf (4.35.1-x86_64-darwin) sha256=66b62b4df00931018a692806df66393efa960d6d2b7da69735187249f950d3ee
301
301
  google-protobuf (4.35.1-x86_64-linux-gnu) sha256=c786439087512a3fbd199e9897d265b855f951d4027e218ea55e858d45969edd
302
- graph_weaver (0.7.5)
302
+ graph_weaver (0.7.7)
303
303
  graphql (2.6.10) sha256=9b7c8633767f516ff9d48a8d6305b2a00a2101c82aa871b92e69086944f9f83e
304
304
  hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
305
305
  i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
306
306
  io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
307
307
  irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
308
- json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
308
+ json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df
309
309
  logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
310
310
  loofah (2.25.2) sha256=2007f746959ac65552456e04b433e83deb22759ab38c838b4445c70e43425918
311
311
  minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
@@ -317,7 +317,7 @@ CHECKSUMS
317
317
  nokogiri (1.19.4-arm64-darwin) sha256=a46db9853286e6597b36ebc6953817d15acf3a299583eb3f89fdc6f91dd63527
318
318
  nokogiri (1.19.4-x86_64-darwin) sha256=7fd17057d3e1f00e9954a74b3cd76595d3d4a5ef233b7ed9599047c204f70551
319
319
  nokogiri (1.19.4-x86_64-linux-gnu) sha256=379fae440b28915e3f19d752ce2dcf8465ed2b2fbefd2a7ca0dd497bc981a06a
320
- parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
320
+ parallel (2.2.0) sha256=e1059c5fd7b649558a0aec38a769f06a42942bdb40503d005a59c352fe011cd8
321
321
  parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
322
322
  pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570
323
323
  prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
@@ -332,33 +332,33 @@ CHECKSUMS
332
332
  rails-html-sanitizer (1.7.1) sha256=e797a7c9b01e567307e317c576b49ab4168017e63eea4dba9ce3cb587e2f22c2
333
333
  railties (8.1.3.1) sha256=2388a232579a00cefea4487de66c8553c3408c1300abdc6cf1799d86ffb04487
334
334
  rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
335
- rbi (0.3.14) sha256=932ed904adbb5ca645c285c8b36da1b7ff6321ec1870d187d6325248cb637658
336
- rbs (4.0.3) sha256=5a7bf70e2628549d9a1f44eae447b2cfe55968a9c60cfff52693a4bdcc020e14
335
+ rbi (0.4.3) sha256=cc090ae62c3246412b992954f54077d6046c9b04f1537a4d24bfae8714503eac
336
+ rbs (4.2.0) sha256=51f7b886dcc05bc09e10b901daa6a81829f6adc03101d6ca9ea4aac6103e0674
337
337
  rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469
338
338
  redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
339
339
  regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
340
340
  reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
341
- require-hooks (0.4.0) sha256=005f4c6435b4edae73e358cdbaba48370a4121f9ce893d5d2a3c66fce855677d
341
+ require-hooks (0.5.1) sha256=49a6b14518b91c5b8006d0644ef257af0ca033df23625bd4e314a8c5282191d9
342
342
  rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
343
343
  rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
344
344
  rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
345
345
  rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
346
346
  rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
347
347
  rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
348
- rubydex (0.2.7-aarch64-linux) sha256=9a016039f7e94e55cd812e073d310de7effcce4cac560edd1185e1ce6b5b4eb0
349
- rubydex (0.2.7-arm64-darwin) sha256=f0d28bbf4153568be79b671642424750053e0bea971b60ddf5cec19bf4563990
350
- rubydex (0.2.7-x86_64-darwin) sha256=b002b259d118ac69de44470eff1597143318402c45630c47371f9542631447dc
351
- rubydex (0.2.7-x86_64-linux) sha256=dacfade9fa42ce4469618da6dac07e69d5f3ac6a313b4caced5234c8f052419a
348
+ rubydex (0.4.1-aarch64-linux) sha256=abc1c9eb22479f60dd015fa81373163c5a71f2700b76bced1282631686bd6271
349
+ rubydex (0.4.1-arm64-darwin) sha256=d23720253876d88fb98954e5d3a1ef9696a269697acf50e3ab53cdfbb2b66b96
350
+ rubydex (0.4.1-x86_64-darwin) sha256=a9681db5bc9be6a62f226d96d6889b6b600d0b09e7576391702aafd29912145b
351
+ rubydex (0.4.1-x86_64-linux) sha256=4e2c8398c9fc585485b54edd28f5ecf76b5a02255e177b6f8402e9d2afffd51b
352
352
  securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
353
353
  simplecov (1.3.0) sha256=d9886307863c1ead47657dcbed869bfffd82318808b09014d8f0ff77dcfe7492
354
- sorbet (0.6.13498) sha256=cb5279f6a9dbfc8d526bc9fb71d05020afb8043c40708e4aff2244ba329ce224
355
- sorbet-runtime (0.6.13498) sha256=2da4627f9341a1fc6e4148cc891989b7b108106c206c46ca69940e10a21e9133
356
- sorbet-static (0.6.13498-aarch64-linux) sha256=cbee732c49379d38029f295b45edaeb5757ab180d308d8383dc79f03fd4991be
357
- sorbet-static (0.6.13498-universal-darwin) sha256=4cc97aa0da3dd175b8278e1617954d6dd9ba1f5aeb62981f21616b9ec791fb75
358
- sorbet-static (0.6.13498-x86_64-linux) sha256=75d02e8af7d6c1081d4693e2ebefe679760327f1304377519970784faf9f913d
359
- sorbet-static-and-runtime (0.6.13498) sha256=5f5686869dceae893b4171b05e3db159fe12f1aadf04aa8c2bf9dfa3edc0d902
360
- spoom (1.8.3) sha256=32871fa189bbfa49cf557a50f819f23cc9a6ceefd0346caa7a6adc193becd5dd
361
- tapioca (0.19.2) sha256=938731b07811aee8d23871b1aee8861d464fbaf2cfffbf79a62b0c869a5120ec
354
+ sorbet (0.6.13506) sha256=b6fd9f9ab2563c709b9308437cb40754e190667fddfe841723a7dda430978cd6
355
+ sorbet-runtime (0.6.13506) sha256=94a7ff795cf2a67818a954883c0301de0b7222a9f732b24ab30ea5253e7568f6
356
+ sorbet-static (0.6.13506-aarch64-linux) sha256=04405070206de265a87e6544454092cd4c7b275415dccf76a4ab41649819ae9f
357
+ sorbet-static (0.6.13506-universal-darwin) sha256=744918a125841c84d0804410bfda068a6dc8182285ba50ddb1e8ce5955b24700
358
+ sorbet-static (0.6.13506-x86_64-linux) sha256=b57d36d1db374e10109bbd5af3bec0edf6f94f3e86dbffe69199763909ad2c91
359
+ sorbet-static-and-runtime (0.6.13506) sha256=12c6abbc1d988267452c97e4cff75c563a480e3e8b7a1a633e2f714856e5c313
360
+ spoom (1.8.7) sha256=81960502cdf23aa991f015988aa5dd43325e23ae4318b8e00d541299cbdb70f6
361
+ tapioca (0.20.0) sha256=b375ed7bf51d2c86c09543eafedd0de85513634187c0f7a062615af8bb9347a0
362
362
  thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
363
363
  tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
364
364
  tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
data/docs/errors.md CHANGED
@@ -106,6 +106,7 @@ subclass says where it failed:
106
106
  | `InputError` | the variables wouldn't build into the generated input structs — unknown/typo'd key, missing required field, out-of-range enum, wrong-typed field, wrong number of @oneOf fields — `#kind`, `#path`, `#coordinate`, `#value`, `#details`, `#field`, `#struct` |
107
107
  | `QueryValidationError` | build time: the query didn't validate against the schema |
108
108
  | `Codegen::Aliases::UnknownSegment` | build time: an [`alias:`](generated_modules.md#flat-accessors-with-alias) path names a field no type here has — a typo, so `optional: true` won't skip it |
109
+ | `Codegen::Aliases::HoistedSegment` | build time: an [`alias:`](generated_modules.md#flat-accessors-with-alias) path reads *into* a struct [hoisted](generated_modules.md#a-shared-fragment-is-one-type) from a shared fragment — `optional: true` skips it, but only at the cost of the accessor for every query that spreads the fragment |
109
110
  | `ConfigurationError` | setup judged against your schema — which Ruby schema serves which subgraph (`Testing::Router`, `federation:diff`) |
110
111
  | `Testing::Unplannable` | the local test router won't plan this operation — `#category`, `#detail` |
111
112
  | `Testing::MissingRecording` | a [cassette](cassettes.md) holds no entry for this request — the message prints the variables, and the ones it did record |
@@ -219,9 +220,12 @@ a form keyed on `e.field` finds the same slot for a refusal raised before the
219
220
  request left and for one the server sent back.
220
221
 
221
222
  The **prop** (`issued_on`) is what you type in Ruby — `.new`, `.coerce`, the kwargs
222
- of `execute` — and it is `#message`, the developer's line, that names it. In a
223
- Rails form the field names are the props, so underscore on the way in — and give
224
- the nil case a home, because **`#field` is `nil` whenever nothing named a slot**:
223
+ of `execute` — and it is `#message`, the developer's line, that names it, with the
224
+ schema's spelling beside it where the two differ (`issued_on (issuedOn): expected
225
+ a String`): the prop is what you fix, and the wire name is what you grep the
226
+ `.graphql` for. In a Rails form the field names are the props, so underscore on
227
+ the way in — and give the nil case a home, because **`#field` is `nil` whenever
228
+ nothing named a slot**:
225
229
 
226
230
  ```ruby
227
231
  form.errors.add(e.field&.underscore || :base, render_input_error(e))
@@ -142,7 +142,9 @@ file, and `verify` fails until you regenerate.
142
142
  **A graph's `namespace:` nests what it generates**, and is the answer when two
143
143
  schemas in one app each have a `person.graphql`: `namespace: "Billing"` makes
144
144
  that one `Billing::PersonQuery` in the same `person_query.rb`, and its shared
145
- types module `Billing::GraphQLTypes`. Nothing else about the rule changes. See
145
+ types module `Billing::Types` (plain `Types` inside a namespace, where nothing
146
+ collides with graphql-ruby's own `Types::`; `GraphQLTypes` without one). Nothing
147
+ else about the rule changes. See
146
148
  [getting started](getting_started.md#more-than-one-schema).
147
149
 
148
150
  Parsing a raw query *string* has no file to name it after, so it uses the
@@ -461,10 +463,12 @@ off `__typename` when the selection carries one unaliased and unguarded, and off
461
463
  narrowed fragment, or one whose `__typename` is itself guarded, is refused: a
462
464
  match would be indistinguishable from a miss.
463
465
 
464
- A whole union field selected as one named *shared* fragment
466
+ A whole abstract field selected as one named *shared* fragment
465
467
  (`{ ...FeedItemFields }`) is [hoisted](#a-shared-fragment-is-one-type) into
466
- `GraphQLTypes` like any other, so the same union is one Ruby type family across
467
- queries rather than a fresh dispatch module per query.
468
+ `GraphQLTypes` like any other, in whichever of the three shapes above the
469
+ fragment has — a dispatch module, one struct, or a nilable narrowed struct. So
470
+ which shape it is never decides whether consuming queries share a type, and
471
+ adding a second `... on` to a shared fragment doesn't move their constant.
468
472
 
469
473
  ### Consuming a union — dispatch on the class, not `__typename`
470
474
 
@@ -673,7 +677,7 @@ to replace it; what the *example* says still wins.
673
677
  generated module). A generated file says nothing about transport — only a private
674
678
  `GRAPH` naming its graph, which is also how `graphql: :fake` fabricates each
675
679
  module's own schema with two graphs in play, and the
676
- `:graph` on every [instrumentation event](logging.md#the-payload) the module's
680
+ `:graph` on every [instrumentation event](logging.md#instrumentation) the module's
677
681
  `execute` produces.
678
682
 
679
683
  ## Deserializing a response from another client
@@ -831,10 +835,8 @@ has the other attributes it reads.
831
835
 
832
836
  ### Loading what it wrote
833
837
 
834
- In Rails, loading is automatic — the Railtie requires every generated file at
835
- the end of boot, after your initializers and after any registrations of your own
836
- in a `to_prepare` block, and again after each development reload. Elsewhere it's
837
- explicit, factory_bot-style:
838
+ In Rails, loading is automatic — [one rule](#the-rule-in-rails) decides whether
839
+ Zeitwerk does it or the Railtie does. Elsewhere it's explicit, factory_bot-style:
838
840
  `GraphWeaver.load_generated!` requires every file under `generated_paths`.
839
841
 
840
842
  **Outside Rails, four things have to agree**, and nothing wires them together for
@@ -848,8 +850,10 @@ you — a script that generates its own modules sets all four:
848
850
  other [client](#clients) to reach for.
849
851
 
850
852
  Miss (3) and the script gets a `NameError` for its own module; miss (4) and it
851
- gets `PersonQuery: client must respond to #execute(query, variables:), got
852
- NilClass` from a module that otherwise looks fine.
853
+ gets `no client configured — set GraphWeaver.client= or pass a client` from a
854
+ module that otherwise looks fine. Set the slot to something that can't execute
855
+ and the module names the contract instead: `PersonQuery: client must respond to
856
+ #execute(query, variables:, operation_name:), got Hash`.
853
857
 
854
858
  Every directory setting is a list — `queries_paths`, `generated_paths`,
855
859
  `fragments_paths` — and every entry is read (entries may be globs; the generated
@@ -861,11 +865,57 @@ list would name a dump nothing ever opens. A relative path resolves against
861
865
  `GraphWeaver.root` — `Rails.root` in a Rails app, the working directory otherwise
862
866
  — so where you started the process doesn't change which files it reads.
863
867
 
864
- Plain requires, not Zeitwerk: Zeitwerk would expect `Generated::PersonQuery` from
865
- `generated/person_query.rb`. In development a query edit regenerates and reloads
866
- before the next request; everywhere else generated code changes only on
867
- regeneration — restart, like a schema migration, or call
868
- `GraphWeaver.reload_generated!` after regenerating in another terminal.
868
+ #### The rule in Rails
869
+
870
+ **A generated tree Zeitwerk would name correctly is left to Zeitwerk; any other
871
+ is hidden from it and required at boot.**
872
+
873
+ The conventional layout is the second one: `generated/person_query.rb` defines
874
+ `::PersonQuery` where Zeitwerk reads `Generated::PersonQuery` off the path. So
875
+ the Railtie hides that directory from the autoloader and requires every file at
876
+ the end of boot — after your initializers and after any `to_prepare`
877
+ registrations of your own, and again after each development reload.
878
+
879
+ A tree is named correctly when the graph's [`namespace`](getting_started.md#more-than-one-schema)
880
+ is the constant its output path spells:
881
+
882
+ ```ruby
883
+ GraphWeaver.graph :reports do
884
+ output "app/graphql/queries/reports/generated"
885
+ namespace "Queries::Reports::Generated"
886
+ end
887
+ ```
888
+
889
+ `report_query.rb` then defines `Queries::Reports::Generated::ReportQuery`, which
890
+ is exactly what Zeitwerk expects there — your own `inflect`/`acronym` rules
891
+ included, since graph_weaver asks the loader rather than guessing. Nothing is
892
+ hidden and nothing is required: the modules autoload on first reference and
893
+ reload with the rest of the app, and **no part of the parent namespace chain is
894
+ defined until a query is used**. Leave `types_module` unset for it — the shared
895
+ manifest is `types.rb`, which Zeitwerk reads as `<Namespace>::Types`, and `Types`
896
+ is what a namespaced graph already calls it.
897
+
898
+ The Railtie says which side each graph fell on, one line apiece at debug, naming
899
+ the constant that disagreed:
900
+
901
+ ```
902
+ graph :reports's output app/graphql/queries/reports/generated is hidden from
903
+ Zeitwerk and required at boot — types.rb defines …::GraphQLTypes, Zeitwerk
904
+ expects …::Types
905
+ ```
906
+
907
+ **The settings have two lifetimes.** `schema`, `queries`, `output` and
908
+ `namespace` matter only when `generate`/`verify` runs. `extend_type`,
909
+ `register_scalar` and `register_enum` are needed wherever generated code *loads*
910
+ — which under autoloading is the first request that uses a query, long after
911
+ boot. So generation settings can live wherever generation happens, and
912
+ registrations belong in `config/initializers` or a `to_prepare` block, where
913
+ every boot runs them.
914
+
915
+ In development a query edit regenerates and reloads before the next request;
916
+ everywhere else generated code changes only on regeneration — restart, like a
917
+ schema migration, or call `GraphWeaver.reload_generated!` after regenerating in
918
+ another terminal.
869
919
 
870
920
  ## Dynamic mode
871
921
 
@@ -292,9 +292,11 @@ omits it. Any drift exits non-zero — whether a change matters is yours to judg
292
292
  (`#breaking`, `#compatible`, `#to_h`, `#empty?`).
293
293
 
294
294
  `queries:check` answers the question that matters when the schema *has* moved:
295
- **which of your queries no longer validate, and why.** It re-introspects the url
296
- the dump records (without rewriting the dump) and validates every `.graphql` file
297
- against the schema as it is right now, naming each error's line and column:
295
+ **which of your queries no longer validate, and why.** It re-introspects
296
+ whatever is behind each graph's dump (without rewriting the dump) — the same
297
+ source `schema:refresh` rewrites from and `schema:diff` compares against — and
298
+ validates every `.graphql` file against the schema as it is right now, naming
299
+ each error's line and column:
298
300
 
299
301
  ```
300
302
  app/graphql/queries/person.graphql
@@ -355,8 +357,11 @@ named `generated`, plus `vendor`, `node_modules`, `tmp` and `log`, is skipped,
355
357
  as is any file defining a graphql-ruby **type** — a `field :sku` there is your
356
358
  *server* offering a field, not this app reading one back. A module handed whole
357
359
  to a serializer (`render json:`, `to_h`, `as_json`, a local and all) counts every
358
- prop as read, and the report quotes the line it followed. Nothing is edited and
359
- the exit is 0; `STRICT=1` exits 1 when anything is unread.
360
+ prop as read, and the report quotes the line it followed — a plain local stands
361
+ for the module only inside the method it was assigned in, so a same-named block
362
+ param in the next method credits nothing, while an `@ivar` crosses that boundary
363
+ the way a `before_action` does. Nothing is edited and the exit is 0;
364
+ `STRICT=1` exits 1 when anything is unread.
360
365
 
361
366
  **It is a lint, not a proof**, and the task's own footer says so. It matches
362
367
  names as text, so a prop called `name` counts as read the moment anything says
@@ -441,18 +446,25 @@ the client the graph names, rewrites the dump, and records the source, so every
441
446
  later refresh and `schema:diff` re-read the right server. `URL=` names the
442
447
  endpoint instead, if you'd rather say it once than configure the client first.
443
448
 
444
- Do it early. Until the dump records a source, `queries:check` has nothing to
445
- re-introspect, so it validates against the committed file — a real check, but
446
- `verify`'s question rather than this one's — and the verdict says which:
449
+ Until there is something behind the dump to re-read — the url it records, the
450
+ server the graph's client posts to, or the graphql-ruby class that client runs
451
+ in-process — `queries:check` validates against the committed file: a real check,
452
+ but `verify`'s question rather than this one's, and the verdict says which:
447
453
 
448
454
  ```
449
455
  every query validates against db/schema.graphql as committed — not the server (rake graph_weaver:schema:diff asks whether the server moved)
450
456
  ```
451
457
 
452
- A dump with no recorded url and no client behind it *is* the schema — a
453
- hand-maintained SDL nothing serves. `schema:refresh` leaves that one alone
454
- (`records no source url and the graph names no client — left as checked in`) and
455
- refreshes the rest, rather than taking the whole task down with it.
458
+ A dump with none of those behind it *is* the schema — a hand-maintained SDL
459
+ nothing serves. All three tasks say so in one sentence:
460
+
461
+ ```
462
+ db/schema.graphql records no source url and the graph names no server behind it — no client posting to one, and no graphql-ruby schema class in this process — so the file is the schema and nothing here can re-read it
463
+ ```
464
+
465
+ `schema:refresh` steps over that graph and refreshes the rest; `schema:diff`
466
+ still exits 1, because a gate that passes on having compared nothing is worse
467
+ than one that says so.
456
468
 
457
469
  ## More than one schema
458
470
 
@@ -484,7 +496,7 @@ plus `register_scalar`, `register_enum` and `extend_type`).
484
496
  | `output` | one directory — where this graph's generated Ruby is written |
485
497
  | `client` | what this graph's modules execute against — a client, or the name of the constant holding one |
486
498
  | `namespace` | a constant, or its name — what every constant this graph generates nests under |
487
- | `types_module` | a constant name for the shared types module (default: `GraphQLTypes`, under `namespace`) |
499
+ | `types_module` | a constant name for the shared types module (default: `Types` under a `namespace`, `GraphQLTypes` without one) |
488
500
 
489
501
  **Declaring graph two means declaring graph one.** A declared graph *replaces*
490
502
  the implicit one the top-level settings describe, so the moment any graph is
@@ -506,15 +518,19 @@ they belong to and nothing about transport, so they read it when they execute.
506
518
  A graph with no `client` falls back to `GraphWeaver.client`, the app default.
507
519
  Name the object (`client GraphWeaver.new(url, auth: …)`) or, when the constant
508
520
  holding it is defined later than the graph block, its name (`client "GITHUB"`),
509
- which is resolved on first use. `schema "x"` sets and a bare `schema` reads
510
- back; there is no `schema = "x"` form, since the block is `instance_eval`'d and
511
- that would be a local variable that silently does nothing.
521
+ which is resolved on first use — and not a lambda, which `schema` needs and this
522
+ doesn't, since the name is resolved at call time already. `schema "x"` sets and
523
+ a bare `schema` reads back; there is no `schema = "x"` form, since the block is
524
+ `instance_eval`'d and that would be a local variable that silently does nothing.
512
525
 
513
526
  **`namespace` nests everything that graph generates** — `person.graphql` becomes
514
527
  `Billing::PersonQuery` ([naming](generated_modules.md#naming)). Constants are
515
528
  global, so two schemas that both have a `person.graphql` would otherwise fight
516
529
  over one name; without a namespace the collision is refused at generation,
517
- naming both files. **The block's registrations reach that graph alone**, laid
530
+ naming both files. **A namespace that is the constant the output path spells is
531
+ also what makes the tree autoloadable** — graph_weaver then leaves it to Zeitwerk
532
+ instead of hiding it and requiring it at boot
533
+ ([loading](generated_modules.md#the-rule-in-rails)). **The block's registrations reach that graph alone**, laid
518
534
  over the top-level ones — so `Money` is checked against the schema it was
519
535
  registered for, and against no other.
520
536
 
@@ -522,8 +538,9 @@ An app that is a pure client of someone else's GraphQL owns no schema class, so
522
538
  every graph's `schema` is a dump. Give each the file you want and a `client`
523
539
  that can fetch it: `rake graph_weaver:schema:refresh` introspects each graph's
524
540
  client into its own dump, recording the url so every later `schema:refresh` and
525
- `schema:diff` re-reads the right server. A graph with neither a recorded url nor
526
- a client is left as checked in and the rest still refresh. (`URL=` is for the
541
+ `schema:diff` re-reads the right server. Every graph is its own job — one with
542
+ no server behind its dump is left as checked in, one whose server is down is
543
+ reported under its own heading, and the rest still refresh. (`URL=` is for the
527
544
  app that has one dump and no graphs.)
528
545
 
529
546
  In specs, `graph:` is how an example says which graph a helper stands in for —
@@ -680,6 +697,9 @@ GraphWeaver.client = GraphWeaver.new(
680
697
  GraphWeaver.load_generated! # no Railtie to require the generated files
681
698
  ```
682
699
 
700
+ **A public API needs no `auth:`** — leave the line out. It is in the example
701
+ because most endpoints want a token, not because the constructor asks for one.
702
+
683
703
  **`load_generated!` goes before your own requires** when anything your app loads
684
704
  names a generated constant as it loads — a `STATUS_LABELS` table keyed on
685
705
  `GraphQLTypes::ShipmentStatus` raises `uninitialized constant` otherwise, and