graph_weaver 0.7.6 → 0.7.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a68eaeb8cd0f3139cc9e50b88d98a50e04acd0c2c94c7825233f04405d30c42
4
- data.tar.gz: '08df5051075500cb81c710cd65705f387dec16bc29a626e2b8069bbf5c61b3a1'
3
+ metadata.gz: bd9b2703206fc3145399e11794cd488a5e3c3734584ca7a56fa0e4d2afed9a77
4
+ data.tar.gz: 0e247a7b925ff0a89cb5952ac962b31545af417f865342a22b8019dcbcdbd135
5
5
  SHA512:
6
- metadata.gz: ee7b2971697409f7180e31d40569fd44108404c0f1d811b259bec6e000f7cf82142e7640de164510d1dbd253916392c6cc866619ee2d37e35e610b1ed0be9741
7
- data.tar.gz: 9d219008a1db9bc353ef3b033b9c39f64657e58d1d2548fb074fc1a60973052e4abd0c1950d93622ad1cfc0414e7ca1219d9c770e32e445cedba287636f58a73
6
+ metadata.gz: 7310d664c88a34de5fbdf9768e6a697ca6565e5ccd4c407cfea31c772f0f44001ceb1145a7e1837d1a46bcc74c16902c55c88d2be014df9c423bb2355dc882a0
7
+ data.tar.gz: abc81fbb21a1f3f26d8a7c93f1f660347e9f68088a833b37b6a66dcf05f17e6bb0bec95fc9432c6e99192ac3682e7961936d9e08b3f518b4af9e013623ef3f6c
data/Gemfile.lock CHANGED
@@ -1,16 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graph_weaver (0.7.6)
4
+ graph_weaver (0.7.8)
5
5
  graphql (>= 2.6.7)
6
6
  sorbet-runtime
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- actionpack (8.1.3.1)
12
- actionview (= 8.1.3.1)
13
- activesupport (= 8.1.3.1)
11
+ actionpack (8.1.4)
12
+ actionview (= 8.1.4)
13
+ activesupport (= 8.1.4)
14
14
  nokogiri (>= 1.8.5)
15
15
  rack (>= 2.2.4)
16
16
  rack-session (>= 1.0.1)
@@ -18,13 +18,13 @@ GEM
18
18
  rails-dom-testing (~> 2.2)
19
19
  rails-html-sanitizer (~> 1.6)
20
20
  useragent (~> 0.16)
21
- actionview (8.1.3.1)
22
- activesupport (= 8.1.3.1)
21
+ actionview (8.1.4)
22
+ activesupport (= 8.1.4)
23
23
  builder (~> 3.1)
24
24
  erubi (~> 1.11)
25
25
  rails-dom-testing (~> 2.2)
26
26
  rails-html-sanitizer (~> 1.6)
27
- activesupport (8.1.3.1)
27
+ activesupport (8.1.4)
28
28
  base64
29
29
  bigdecimal
30
30
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -45,9 +45,9 @@ GEM
45
45
  ast (2.4.3)
46
46
  base64 (0.3.0)
47
47
  benchmark (0.5.0)
48
- bigdecimal (4.1.2)
48
+ bigdecimal (4.1.3)
49
49
  builder (3.3.0)
50
- concurrent-ruby (1.3.7)
50
+ concurrent-ruby (1.3.8)
51
51
  connection_pool (3.0.2)
52
52
  crack (1.0.1)
53
53
  bigdecimal
@@ -58,11 +58,11 @@ GEM
58
58
  reline (>= 0.3.8)
59
59
  diff-lcs (1.6.2)
60
60
  drb (2.2.3)
61
- erb (6.0.4)
61
+ erb (6.0.7)
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
@@ -81,20 +81,20 @@ GEM
81
81
  google-protobuf (4.35.1-x86_64-linux-gnu)
82
82
  bigdecimal
83
83
  rake (~> 13.3)
84
- graphql (2.6.10)
84
+ graphql (2.6.11)
85
85
  base64
86
86
  fiber-storage
87
87
  logger
88
88
  hashdiff (1.2.1)
89
89
  i18n (1.15.2)
90
90
  concurrent-ruby (~> 1.0)
91
- io-console (0.8.2)
91
+ io-console (0.9.4)
92
92
  irb (1.18.0)
93
93
  pp (>= 0.6.0)
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
@@ -149,9 +149,9 @@ GEM
149
149
  rails-html-sanitizer (1.7.1)
150
150
  loofah (~> 2.25, >= 2.25.2)
151
151
  nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
152
- railties (8.1.3.1)
153
- actionpack (= 8.1.3.1)
154
- activesupport (= 8.1.3.1)
152
+ railties (8.1.4)
153
+ actionpack (= 8.1.4)
154
+ activesupport (= 8.1.4)
155
155
  irb (~> 1.13)
156
156
  rackup (>= 1.0.0)
157
157
  rake (>= 12.2)
@@ -159,23 +159,23 @@ 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
169
- rdoc (8.0.0)
169
+ rdoc (8.1.0)
170
170
  erb
171
171
  prism (>= 1.6.0)
172
172
  rbs (>= 4.0.0)
173
173
  tsort
174
174
  redcarpet (3.6.1)
175
175
  regexp_parser (2.12.0)
176
- reline (0.6.3)
176
+ reline (0.7.0)
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
- 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)
198
+ simplecov (1.3.1)
199
+ sorbet (0.6.13508)
200
+ sorbet-static (= 0.6.13508)
201
+ sorbet-runtime (0.6.13508)
202
+ sorbet-static (0.6.13508-aarch64-linux)
203
+ sorbet-static (0.6.13508-universal-darwin)
204
+ sorbet-static (0.6.13508-x86_64-linux)
205
+ sorbet-static-and-runtime (0.6.13508)
206
+ sorbet (= 0.6.13508)
207
+ sorbet-runtime (= 0.6.13508)
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)
@@ -272,40 +272,40 @@ DEPENDENCIES
272
272
  yard
273
273
 
274
274
  CHECKSUMS
275
- actionpack (8.1.3.1) sha256=974cb7154548e81f470b1b0f247b99cb38e87825899dca58610596e2817723d0
276
- actionview (8.1.3.1) sha256=2da68b8414c47b43bfbed1ce69c5afe1c04f78c267aacb5660a4cab5ca12cfb6
277
- activesupport (8.1.3.1) sha256=85458765f25ea48b9019c46b6bb3fa5683197bf4280d9f06710a6e8d7a831376
275
+ actionpack (8.1.4) sha256=245493cc51be195a7645e6fc184880fa7e5b32ff1a08e053ee31f58c76e30ea4
276
+ actionview (8.1.4) sha256=eff1efd07c4d969abad288322b423bcb67fdbab5e324c91ca37352b80f226076
277
+ activesupport (8.1.4) sha256=891dd3fe43c4fc8391934b237b1189bb1f661c932a84a840043db9d46a4e80df
278
278
  addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
279
279
  apollo-federation (3.10.3) sha256=61e6d8e1fd695ee45bab5d84f86f586d99c296ad77a86c6f1e7b964ac17be3d7
280
280
  ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
281
281
  base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
282
282
  benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
283
- bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
283
+ bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
284
284
  builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
285
- concurrent-ruby (1.3.7) sha256=4412caec3a5ea2e5fdc52076724c071a81f2c0593d83b2ac8cbb8ca63b3151b0
285
+ concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
286
286
  connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
287
287
  crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
288
288
  crass (1.0.7) sha256=94868719948664c89ddcaf0a37c65048413dfcb1c869470a5f7a7ceb5390b295
289
289
  debug (1.11.1) sha256=2e0b0ac6119f2207a6f8ac7d4a73ca8eb4e440f64da0a3136c30343146e952b6
290
290
  diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
291
291
  drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
292
- erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
292
+ erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92
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.6)
303
- graphql (2.6.10) sha256=9b7c8633767f516ff9d48a8d6305b2a00a2101c82aa871b92e69086944f9f83e
302
+ graph_weaver (0.7.8)
303
+ graphql (2.6.11) sha256=9a9cde910e805c4c08fdf8d22bf129e935affc9ca8b4f9b2ec26b4b63666b15d
304
304
  hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
305
305
  i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
306
- io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
306
+ io-console (0.9.4) sha256=a403304f32928ee85e56d457b4612ca43f2703b561a3258844992bd24085fbb1
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
@@ -330,35 +330,35 @@ CHECKSUMS
330
330
  rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
331
331
  rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d
332
332
  rails-html-sanitizer (1.7.1) sha256=e797a7c9b01e567307e317c576b49ab4168017e63eea4dba9ce3cb587e2f22c2
333
- railties (8.1.3.1) sha256=2388a232579a00cefea4487de66c8553c3408c1300abdc6cf1799d86ffb04487
333
+ railties (8.1.4) sha256=b5e7a8e8984e33114d68af62227971960aae1e6c02bd7c9d826ffad97fff583c
334
334
  rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
335
- rbi (0.3.14) sha256=932ed904adbb5ca645c285c8b36da1b7ff6321ec1870d187d6325248cb637658
336
- rbs (4.0.3) sha256=5a7bf70e2628549d9a1f44eae447b2cfe55968a9c60cfff52693a4bdcc020e14
337
- rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469
335
+ rbi (0.4.3) sha256=cc090ae62c3246412b992954f54077d6046c9b04f1537a4d24bfae8714503eac
336
+ rbs (4.2.0) sha256=51f7b886dcc05bc09e10b901daa6a81829f6adc03101d6ca9ea4aac6103e0674
337
+ rdoc (8.1.0) sha256=befc37880a5cfd36dcb176c748b5fd5ee39dd3536a75e81824e4972ce230f60a
338
338
  redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
339
339
  regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
340
- reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
341
- require-hooks (0.4.0) sha256=005f4c6435b4edae73e358cdbaba48370a4121f9ce893d5d2a3c66fce855677d
340
+ reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
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
- 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
353
+ simplecov (1.3.1) sha256=b326d38e819053499aa2ebe35d544d8bd780eba143a50f48a624b450ad87feec
354
+ sorbet (0.6.13508) sha256=de67c75424731d39ac529639ff6849c3fe33d91c94608765d912d35efa5941a2
355
+ sorbet-runtime (0.6.13508) sha256=011ee266adead459077ebc0058d36aeb75d6c90968db8524916050ce3c62e526
356
+ sorbet-static (0.6.13508-aarch64-linux) sha256=1c54603399d40d78d6e73c853c5e0b6dcfc34105a92f6630f4a5329d1e6ed3c0
357
+ sorbet-static (0.6.13508-universal-darwin) sha256=65280392d193ca85828b13d07bb8eb90f41ad57f8d9bc79c5edcd078aa7dc308
358
+ sorbet-static (0.6.13508-x86_64-linux) sha256=57fd620b1a747baad432ef5c2bc2cd6218664e00626fc4e7fb5ed5f633e3a286
359
+ sorbet-static-and-runtime (0.6.13508) sha256=874d9fe5d98eebddd322629ee3d27021a6da32594edfa170295c0b4bcd285d66
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
@@ -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
@@ -833,10 +835,8 @@ has the other attributes it reads.
833
835
 
834
836
  ### Loading what it wrote
835
837
 
836
- In Rails, loading is automatic — the Railtie requires every generated file at
837
- the end of boot, after your initializers and after any registrations of your own
838
- in a `to_prepare` block, and again after each development reload. Elsewhere it's
839
- 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:
840
840
  `GraphWeaver.load_generated!` requires every file under `generated_paths`.
841
841
 
842
842
  **Outside Rails, four things have to agree**, and nothing wires them together for
@@ -865,11 +865,57 @@ list would name a dump nothing ever opens. A relative path resolves against
865
865
  `GraphWeaver.root` — `Rails.root` in a Rails app, the working directory otherwise
866
866
  — so where you started the process doesn't change which files it reads.
867
867
 
868
- Plain requires, not Zeitwerk: Zeitwerk would expect `Generated::PersonQuery` from
869
- `generated/person_query.rb`. In development a query edit regenerates and reloads
870
- before the next request; everywhere else generated code changes only on
871
- regeneration — restart, like a schema migration, or call
872
- `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.
873
919
 
874
920
  ## Dynamic mode
875
921
 
@@ -466,6 +466,15 @@ db/schema.graphql records no source url and the graph names no server behind it
466
466
  still exits 1, because a gate that passes on having compared nothing is worse
467
467
  than one that says so.
468
468
 
469
+ **A large dump costs nothing until something asks for it.** A client built from
470
+ a file stats the path at construction — a typo fails at boot, naming it — and
471
+ parses the file the first time anything wants the schema: codegen, a test mode,
472
+ `check_query`. Executing a generated module never does, so a production boot
473
+ never parses.
474
+
475
+ Keep one client per server rather than building one per call — a client parses
476
+ its dump once and keeps the schema.
477
+
469
478
  ## More than one schema
470
479
 
471
480
  The five steps above describe one graph — a schema, its queries, its output. An
@@ -496,7 +505,7 @@ plus `register_scalar`, `register_enum` and `extend_type`).
496
505
  | `output` | one directory — where this graph's generated Ruby is written |
497
506
  | `client` | what this graph's modules execute against — a client, or the name of the constant holding one |
498
507
  | `namespace` | a constant, or its name — what every constant this graph generates nests under |
499
- | `types_module` | a constant name for the shared types module (default: `GraphQLTypes`, under `namespace`) |
508
+ | `types_module` | a constant name for the shared types module (default: `Types` under a `namespace`, `GraphQLTypes` without one) |
500
509
 
501
510
  **Declaring graph two means declaring graph one.** A declared graph *replaces*
502
511
  the implicit one the top-level settings describe, so the moment any graph is
@@ -527,7 +536,10 @@ a bare `schema` reads back; there is no `schema = "x"` form, since the block is
527
536
  `Billing::PersonQuery` ([naming](generated_modules.md#naming)). Constants are
528
537
  global, so two schemas that both have a `person.graphql` would otherwise fight
529
538
  over one name; without a namespace the collision is refused at generation,
530
- naming both files. **The block's registrations reach that graph alone**, laid
539
+ naming both files. **A namespace that is the constant the output path spells is
540
+ also what makes the tree autoloadable** — graph_weaver then leaves it to Zeitwerk
541
+ instead of hiding it and requiring it at boot
542
+ ([loading](generated_modules.md#the-rule-in-rails)). **The block's registrations reach that graph alone**, laid
531
543
  over the top-level ones — so `Money` is checked against the schema it was
532
544
  registered for, and against no other.
533
545
 
data/docs/upgrading.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # Upgrading
2
2
 
3
3
  [Regenerate](#regenerate-on-every-upgrade) whichever version you're on, then read
4
- the one section that is yours: from [0.7.5](#upgrading-from-075), from
4
+ the one section that is yours: from [0.7.7](#upgrading-from-077), from
5
+ [0.7.6](#upgrading-from-076), from
6
+ [0.7.5](#upgrading-from-075), from
5
7
  [0.7.4](#upgrading-from-074), from [0.7.3](#upgrading-from-073), from
6
8
  [0.7.1](#upgrading-from-071), from [0.7.0](#upgrading-from-070) or from
7
9
  [0.6.1](#upgrading-from-061). Coming from 0.6.0 or older, the path is that
@@ -24,6 +26,42 @@ That's the reminder working, not a false alarm. Generation is deterministic, so
24
26
  the diff is exactly what the new version emits differently and nothing else —
25
27
  worth reading rather than rubber-stamping.
26
28
 
29
+ ## Upgrading from 0.7.7
30
+
31
+ A patch release about load time. Nothing in it reaches an app that reads its
32
+ dump and generates; one edge moved, below. Read the left column and skip what
33
+ isn't yours; the [changelog](../CHANGELOG.md) says why each one moved.
34
+
35
+ | applies if you… | what changed |
36
+ |---|---|
37
+ | build a client from a schema dump — `GraphWeaver.new("schema.graphql")`, or a graph naming one | it is parsed on first use, not at construction, so a production boot never parses it. A missing or unreadable path still refuses at construction, naming it; a file that exists but isn't a schema refuses at the first `schema`, `parse` or `check_query`. `rake graph_weaver:verify` catches that one in CI |
38
+ | call `client.check_query` per query against a supergraph client | the routing table is built once per file version rather than per call — nothing to change, ~200 ms back per call on a 2000-type supergraph |
39
+ | generate anonymous operations whose comments carry non-ASCII text, or check out with CRLF | they generate under any parser graphql-ruby is configured with; nothing to change unless you had named an operation to dodge a "could not name the anonymous operation" refusal |
40
+
41
+ Then regenerate, and the gate:
42
+
43
+ ```sh
44
+ rake graph_weaver:generate
45
+ rake graph_weaver:verify
46
+ ```
47
+
48
+ ## Upgrading from 0.7.6
49
+
50
+ Read the left column and skip what isn't yours; the
51
+ [changelog](../CHANGELOG.md) says why each one moved.
52
+
53
+ | applies if you… | what changed |
54
+ |---|---|
55
+ | set `namespace:` on any graph — `rake graph_weaver:graphs` lists them | its shared types module is `<Namespace>::Types`, not `<Namespace>::GraphQLTypes`. **Regenerate**, then move app code naming `<Namespace>::GraphQLTypes::X` to `<Namespace>::Types::X` — `srb tc` finds every site. Keep the old name with `types_module "GraphQLTypes"` in that graph's block, at the cost of the autoloadable layout below |
56
+ | run Rails, and have a graph whose `namespace:` is the constant its output path spells | that tree is [left to Zeitwerk](generated_modules.md#the-rule-in-rails) — autoloaded on first reference, reloaded with the app, and no longer required at boot, so booting stops defining its parent namespace chain. Nothing to do; the Railtie says which side each graph fell on at debug. Every other tree is hidden and required exactly as before |
57
+
58
+ Then regenerate, and the gate:
59
+
60
+ ```sh
61
+ rake graph_weaver:generate
62
+ rake graph_weaver:verify
63
+ ```
64
+
27
65
  ## Upgrading from 0.7.5
28
66
 
29
67
  A patch release. The regenerate is what changes behaviour — every module gains
@@ -1,7 +1,7 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- # Generated by GraphWeaver 0.7.6 — do not edit.
4
+ # Generated by GraphWeaver 0.7.8 — do not edit.
5
5
 
6
6
  module StarMutation
7
7
  extend T::Sig
@@ -1,7 +1,7 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- # Generated by GraphWeaver 0.7.6 — do not edit.
4
+ # Generated by GraphWeaver 0.7.8 — do not edit.
5
5
 
6
6
  require "time"
7
7
 
@@ -1,7 +1,7 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- # Generated by GraphWeaver 0.7.6 — do not edit.
4
+ # Generated by GraphWeaver 0.7.8 — do not edit.
5
5
 
6
6
  module StarredQuery
7
7
  extend T::Sig
@@ -75,8 +75,18 @@ class GraphWeaver::Client
75
75
  end
76
76
 
77
77
  # a live schema class doubles as an in-process transport; a loaded
78
- # dump has no resolvers, so it is type information only
79
- @schema = source.is_a?(Module) ? source : GraphWeaver::SchemaLoader.load(source)
78
+ # dump has no resolvers, so it is type information only — and it is
79
+ # read on first use, the rule a url client already follows. An app
80
+ # that builds one only to name its supergraph (what `graphql: :router`
81
+ # plans against) then never parses the file at all.
82
+ if source.is_a?(Module)
83
+ @schema = source
84
+ else
85
+ # a path is checked now — a stat, not a parse — so a wrong one
86
+ # refuses where it was written rather than at the first request
87
+ GraphWeaver::SchemaLoader.check_path!(source) unless GraphWeaver::SchemaLoader.content?(source)
88
+ @dump = source
89
+ end
80
90
  # A supergraph's routing table lives in the file, not in the loaded
81
91
  # schema, so the path is the only thing that can name one later. Told
82
92
  # from SDL by its extension, as SchemaLoader tells it.
@@ -147,8 +157,9 @@ class GraphWeaver::Client
147
157
  FAILURE_TTL = 1.0
148
158
  private_constant :FAILURE_TTL
149
159
 
150
- # The schema, introspecting through the transport on first use (cached
151
- # per the client's cache:/ttl:) unless one was given up front.
160
+ # The schema, on first use: a dump is read and built, a url is
161
+ # introspected through the transport (cached per the client's cache:/ttl:).
162
+ # A schema class is the schema, and was there all along.
152
163
  #
153
164
  # Locked because a cold Puma process serves its first requests
154
165
  # concurrently: a bare ||= there is one full introspection round trip per
@@ -156,6 +167,7 @@ class GraphWeaver::Client
156
167
  def schema
157
168
  @schema_lock.synchronize do
158
169
  next @schema if @schema
170
+ next @schema = GraphWeaver::SchemaLoader.load(@dump) if @dump
159
171
  raise @schema_error if @schema_error && Process.clock_gettime(Process::CLOCK_MONOTONIC) < @schema_error_until
160
172
 
161
173
  begin
@@ -458,19 +458,51 @@ class GraphWeaver::Codegen
458
458
  declares!(named, name)
459
459
  end
460
460
 
461
- # Where the operation starts in @query, in characters. graphql-ruby subtracts
462
- # a CHARACTER line start from the lexer's BYTE position
463
- # (Language::Parser#column_at over Lexer's @scanner.pos), so line start plus
464
- # col is a byte offset — and col falls back to a whole-document offset for a
465
- # token with no newline after it, which .strip guarantees for the last line,
466
- # hence the copy that ends in one.
461
+ # What an operation definition opens with.
462
+ OPERATION_START = %i[QUERY MUTATION SUBSCRIPTION LCURLY].to_set.freeze
463
+ private_constant :OPERATION_START
464
+
465
+ # Where the operation starts in @query, in characters — lexed, not derived
466
+ # from a reported position, because no two parsers report the same one:
467
+ # graphql-ruby's own gives a BYTE position minus a CHARACTER line start
468
+ # (Language::Parser#column_at), its C parser gives a true column and counts
469
+ # \r\n as two lines. Language::Lexer is the same implementation whichever
470
+ # parser is default, and its #pos is an exact byte offset, so nothing here is
471
+ # left to interpret.
472
+ #
473
+ # The operation is the first one of those tokens outside every fragment
474
+ # definition, selection set and argument list — an argument's braces are
475
+ # not a body, and a fragment's body is not the operation's.
467
476
  def operation_offset
468
- operation = sole_operation("#{@query}\n")
469
- bytes = @query.lines.first(operation.line - 1).sum(&:length) + operation.col - 1
470
- @query.byteslice(0, bytes).length
477
+ lexer = GraphQL::Language::Lexer.new(@query)
478
+ depth = 0
479
+ parens = 0
480
+ fragment = T.let(false, T::Boolean)
481
+
482
+ while (kind = lexer.advance)
483
+ parens += 1 if kind == :LPAREN
484
+ parens -= 1 if kind == :RPAREN
485
+ next unless parens.zero?
486
+
487
+ if depth.zero? && !fragment && OPERATION_START.include?(kind)
488
+ return @query.byteslice(0, lexer.pos).length
489
+ end
490
+
491
+ case kind
492
+ when :LCURLY
493
+ depth += 1
494
+ fragment = false
495
+ when :RCURLY then depth -= 1
496
+ when :FRAGMENT then fragment = true if depth.zero?
497
+ end
498
+ end
499
+
500
+ # The lexer and the parser disagree about what this document holds. Splice
501
+ # past the end, which declares! can only refuse.
502
+ @query.length
471
503
  end
472
504
 
473
- # The splice above is arithmetic over a position graphql-ruby reports, so it
505
+ # The splice above lands on an offset this file worked out for itself, so it
474
506
  # can land wrong and still emit a module that looks fine — and OPERATION_NAME
475
507
  # would then be an operationName the document doesn't declare, which every
476
508
  # server rejects. Nothing else re-reads the query, so this is the only place
@@ -93,8 +93,19 @@ module GraphWeaver
93
93
  # constant; this is the one knob that settles both.
94
94
  def namespace = @namespace
95
95
 
96
+ # The shared types module. Inside a namespace the default is plain `Types`,
97
+ # so `namespace:` alone leaves a tree Zeitwerk names correctly — types.rb
98
+ # holds it, and Zeitwerk reads that file as `<Namespace>::Types`. At the top
99
+ # level it stays GraphQLTypes, where a bare ::Types would collide with
100
+ # graphql-ruby's own Types:: convention.
101
+ NESTED_TYPES_MODULE = "Types"
102
+ private_constant :NESTED_TYPES_MODULE
103
+
96
104
  def types_module
97
- @types_module || (namespace ? "#{namespace}::#{GraphWeaver.types_module}" : GraphWeaver.types_module)
105
+ return @types_module if @types_module
106
+ return GraphWeaver.types_module unless namespace
107
+
108
+ "#{namespace}::#{GraphWeaver.types_module(NESTED_TYPES_MODULE)}"
98
109
  end
99
110
 
100
111
  # The graphql-ruby schema, however it was named: a class, a Client, a path
@@ -192,6 +192,23 @@ module GraphWeaver
192
192
  GraphWeaver.generated_paths | extra
193
193
  end
194
194
 
195
+ # The generated directories this process has to require ITSELF:
196
+ # generated_dirs, less any tree the Railtie left to Zeitwerk. Those
197
+ # load on first reference and reload with the app, and requiring them
198
+ # is the one thing an autoloadable layout exists to avoid.
199
+ def required_dirs
200
+ generated_dirs.reject { |dir| left_to_zeitwerk?(dir) }
201
+ end
202
+
203
+ # by name, not spelled: the railtie is Rails-only and `typed: ignore`,
204
+ # so sorbet has no constant to resolve. Outside Rails there is no
205
+ # autoloader and the answer is always no.
206
+ def left_to_zeitwerk?(path)
207
+ return false unless GraphWeaver.const_defined?(:Railtie, false)
208
+
209
+ GraphWeaver.const_get(:Railtie, false).left_to_zeitwerk?(path)
210
+ end
211
+
195
212
  # Every query document under these directories, sorted — the files
196
213
  # generate!, verify_generated!, check_queries and load_queries! read.
197
214
  def query_files(paths = GraphWeaver.queries_paths)
@@ -358,10 +375,24 @@ module GraphWeaver
358
375
  path = path.to_path if path.respond_to?(:to_path)
359
376
  return unless path&.end_with?(".graphql", ".gql")
360
377
 
361
- sdl = File.read(path)
362
- SchemaLoader.routing_table(sdl) if SchemaLoader.federation_sdl?(sdl)
378
+ # once per file version: check_query is called per query, and the
379
+ # table behind a 2000-type supergraph costs ~200 ms to read against
380
+ # a fraction of a millisecond to stat — mtime and size in the key
381
+ # are what let schema:refresh's rewrite be seen without a reload
382
+ stat = File.stat(path)
383
+ key = [File.expand_path(path), stat.mtime, stat.size]
384
+ TABLES.synchronize do
385
+ return TABLES_BY_FILE[key] if TABLES_BY_FILE.key?(key)
386
+
387
+ sdl = File.read(path)
388
+ TABLES_BY_FILE[key] = SchemaLoader.routing_table(sdl) if SchemaLoader.federation_sdl?(sdl)
389
+ end
363
390
  end
364
391
 
392
+ TABLES = Mutex.new
393
+ TABLES_BY_FILE = {}
394
+ private_constant :TABLES, :TABLES_BY_FILE
395
+
365
396
  private
366
397
 
367
398
  # Which subgraphs a validation error is about, on a federated schema:
@@ -85,6 +85,12 @@ class GraphWeaver::Railtie < Rails::Railtie
85
85
  # calling ignore again — check_generated_ignored! refuses instead.
86
86
  attr_accessor :ignored_dirs
87
87
 
88
+ # The outputs left to Zeitwerk, resolved — the trees whose every module is
89
+ # the constant Zeitwerk infers from its path, so hiding them would be
90
+ # hiding code that already works. Decided beside the sweep, because it is
91
+ # what the sweep must not hide.
92
+ attr_accessor :autoloaded_dirs
93
+
88
94
  # Whether hiding an output right now would actually hide it: false until
89
95
  # the initializer below has swept (it picks up anything declared so far),
90
96
  # and false again from the moment Zeitwerk sets the main autoloader up.
@@ -108,14 +114,24 @@ class GraphWeaver::Railtie < Rails::Railtie
108
114
  GraphWeaver::Railtie.hide_generated!
109
115
  end
110
116
 
117
+ # ONE rule: a generated tree Zeitwerk would name correctly is left to
118
+ # Zeitwerk; any other is hidden from it and required at boot.
119
+ #
111
120
  # generated/person_query.rb defines ::PersonQuery, but Zeitwerk infers
112
121
  # Generated::PersonQuery from the path — and app/graphql/generated is inside
113
122
  # an autoload root by default, so eager loading raised "uninitialized
114
123
  # constant Generated::PersonQuery" in production while development (lazy) was
115
- # fine. prepare_generated! requires them instead.
124
+ # fine. prepare_generated! requires those instead. A graph whose `namespace:`
125
+ # is the constant its output path spells has no such mismatch, and requiring
126
+ # it at boot would define its whole parent chain before a query is ever used.
116
127
  def self.hide_generated!
117
128
  self.ignored_dirs = []
129
+ self.autoloaded_dirs = []
118
130
  self.hiding_outputs = true
131
+ # the aligned trees first: they are what the sweep must NOT hide, and
132
+ # generated_paths' own globs cover a graph's output (app/graphql/*/generated
133
+ # matches app/graphql/billing/generated)
134
+ GraphWeaver.graphs.each { |graph| consider_output!(graph) }
119
135
  GraphWeaver::Internal::Util.generated_dirs.each { |path| ignore_output!(path) }
120
136
 
121
137
  # Zeitwerk reads its ignore list once, at setup, and nothing in Rails
@@ -124,12 +140,189 @@ class GraphWeaver::Railtie < Rails::Railtie
124
140
  # check_generated_ignored! has to be the one to say so.
125
141
  main = Rails.autoloaders.main if Rails.autoloaders.respond_to?(:main)
126
142
  if main.respond_to?(:on_setup)
127
- main.on_setup { GraphWeaver::Railtie.hiding_outputs = false }
143
+ main.on_setup do
144
+ GraphWeaver::Railtie.hiding_outputs = false
145
+ GraphWeaver::Railtie.check_alignment!
146
+ end
128
147
  else
129
148
  self.hiding_outputs = false
130
149
  end
131
150
  end
132
151
 
152
+ # Which side of the rule one graph's output falls, and say so in a line at
153
+ # debug: a user who set `namespace:` and expected laziness learns here why
154
+ # they didn't get it. True when the tree is Zeitwerk's to load.
155
+ def self.consider_output!(graph)
156
+ dir = autoload_path(graph.output)
157
+ return true if autoloaded_dirs.include?(dir)
158
+ # already hidden by an earlier pass, and an ignore can't be taken back
159
+ return false if ignored_dirs.include?(dir)
160
+
161
+ reason = misalignment(graph, dir)
162
+ subject, = describe_output(dir)
163
+ if reason
164
+ GraphWeaver::Internal::Log.log(:debug) do
165
+ "#{subject} is hidden from Zeitwerk and required at boot — #{reason}"
166
+ end
167
+ # here rather than back in ignore_output!, so the decision is recorded
168
+ # the moment it is made and the sweep doesn't reach it a second time
169
+ hide!(dir)
170
+ return false
171
+ end
172
+
173
+ autoloaded_dirs << dir
174
+ # types.rb requires its own directory — one file per type, in the order the
175
+ # runtime needs — so Zeitwerk never autoloads those files one at a time.
176
+ # Only eager loading walks them, and it would want a constant name for each
177
+ # that the generator's own inflection doesn't promise: HTTPHeaderInput
178
+ # writes httpheader_input.rb, which Zeitwerk reads as HttpheaderInput.
179
+ # Loading types.rb is what loads them, and types.rb is eager loaded.
180
+ Rails.autoloaders.each { |loader| loader.do_not_eager_load(File.join(dir, "types")) }
181
+ GraphWeaver::Internal::Log.log(:debug) do
182
+ "#{subject} is left to Zeitwerk — every module it generates is the constant Zeitwerk reads " \
183
+ "off the path, so it autoloads on first reference and reloads with the app"
184
+ end
185
+ true
186
+ end
187
+
188
+ # Why Zeitwerk would NOT name what this graph generates the way its files do
189
+ # — nil when it would, which is the aligned case. The first disagreement is
190
+ # the whole answer: one namespace settles every file, so the fix for one is
191
+ # the fix for all of them.
192
+ def self.misalignment(graph, dir)
193
+ main = Rails.autoloaders.main if Rails.respond_to?(:autoloaders) && Rails.autoloaders.respond_to?(:main)
194
+ # 2.6.2 is where cpath_expected_at arrives, and with it the only answer
195
+ # that can confirm this one at setup — without it nothing is left to
196
+ # Zeitwerk, which is what every app got before.
197
+ unless main.respond_to?(:cpath_expected_at)
198
+ return "no autoloader here answers cpath_expected_at (Zeitwerk 2.6.2 and up does)"
199
+ end
200
+
201
+ if ignored_by_app?(dir)
202
+ return "it is already hidden from autoloading — a GraphWeaver.generated_paths pattern reaches " \
203
+ "it, or the app ignored it itself"
204
+ end
205
+
206
+ namespace = expected_cpath(dir)
207
+ return "it is not under an autoload path, so nothing but this would load it" unless namespace
208
+ unless namespace == graph.namespace
209
+ return "Zeitwerk reads that directory as #{namespace}, and this graph's constants are " \
210
+ "#{graph.namespace || "top-level"} (namespace #{namespace.inspect} makes them agree)"
211
+ end
212
+
213
+ types = expected_cpath(File.join(dir, "types.rb"))
214
+ return "types.rb defines #{graph.types_module}, Zeitwerk expects #{types}" unless types == graph.types_module
215
+
216
+ first_mismatch(graph, dir)
217
+ end
218
+
219
+ # The first query module whose name isn't the one Zeitwerk reads off its
220
+ # file. Only reached once the namespace agrees, so what is left is a file
221
+ # name the app's inflector camelizes differently from the generator.
222
+ def self.first_mismatch(graph, dir)
223
+ GraphWeaver::Internal::Util.query_files(graph.queries).each do |query|
224
+ name, filename = graph.generated_names(query, File.read(query))
225
+ expected = expected_cpath(File.join(dir, filename))
226
+ next if expected == name
227
+
228
+ return "#{filename} defines #{name}, Zeitwerk expects #{expected}"
229
+ end
230
+ nil
231
+ end
232
+
233
+ # The constant Zeitwerk will expect at an absolute path, or nil when nothing
234
+ # will autoload it.
235
+ #
236
+ # Zeitwerk answers this itself, with cpath_expected_at — but only once Rails
237
+ # has pushed the main loader's roots, and Rails does that INSIDE
238
+ # :setup_main_autoloader, whose start is the deadline for `ignore`. So the
239
+ # roots here are the list Rails is about to push, and every constant name
240
+ # comes from the loader's own inflector, which an app's `inflect`/`acronym`
241
+ # is already on. check_alignment! re-asks Zeitwerk the moment it can answer.
242
+ def self.expected_cpath(path)
243
+ main = Rails.autoloaders.main
244
+ roots = autoload_roots
245
+ cnames = []
246
+ dir = path
247
+ if path.end_with?(".rb")
248
+ cnames << main.inflector.camelize(File.basename(path, ".rb"), path)
249
+ dir = File.dirname(path)
250
+ end
251
+ until roots.key?(dir)
252
+ parent = File.dirname(dir)
253
+ return if parent == dir || ignored_by_app?(dir)
254
+
255
+ cnames << main.inflector.camelize(File.basename(dir), dir)
256
+ dir = parent
257
+ end
258
+ return if ignored_by_app?(dir)
259
+
260
+ [roots[dir], *cnames.reverse].compact.join("::")
261
+ end
262
+
263
+ # Every directory the main autoloader will have as a root, and the name of
264
+ # the namespace each sits under (nil for Object): the paths Rails is about to
265
+ # push, plus any the app pushed itself — which is the only way a root gets a
266
+ # namespace, and the one thing Rails leaves alone when it pushes the rest.
267
+ def self.autoload_roots
268
+ roots = {}
269
+ if defined?(ActiveSupport::Dependencies) && ActiveSupport::Dependencies.respond_to?(:autoload_paths)
270
+ once = Rails.autoloaders.respond_to?(:once) ? Rails.autoloaders.once.dirs : []
271
+ ActiveSupport::Dependencies.autoload_paths.each do |path|
272
+ path = path.to_s
273
+ # through autoload_path, so a root and an output under it are spelled
274
+ # the same way — a symlinked checkout (Capistrano's current/) otherwise
275
+ # has the root as Rails wrote it and the output as realpath saw it
276
+ roots[autoload_path(path)] = nil unless once.include?(path)
277
+ end
278
+ end
279
+ Rails.autoloaders.main.dirs(namespaces: true).each do |dir, namespace|
280
+ roots[autoload_path(dir)] = (namespace == Object) ? nil : namespace.name
281
+ end
282
+ roots
283
+ end
284
+
285
+ # An app that hid the directory itself has a tree Zeitwerk will not load
286
+ # however it is named — so it is one to require at boot, exactly as before.
287
+ def self.ignored_by_app?(dir)
288
+ asked = IGNORES.find { |name| Rails.autoloaders.main.respond_to?(name) }
289
+ asked && Rails.autoloaders.main.public_send(asked, dir)
290
+ end
291
+
292
+ # The prediction above, against the one answer that is authoritative. A
293
+ # `collapse` between the autoload root and the output, or a root pushed under
294
+ # its own namespace after this railtie looked, changes the name — and neither
295
+ # is readable before setup. Left unsaid, that surfaces as Zeitwerk's own
296
+ # error on the first eager load in production.
297
+ def self.check_alignment!
298
+ main = Rails.autoloaders.main
299
+ Array(autoloaded_dirs).each do |dir|
300
+ # the top level only: types/ is excluded from eager loading, so what
301
+ # Zeitwerk reads off those files never comes up
302
+ Dir[File.join(dir, "*.rb")].sort.each do |file|
303
+ expected = main.cpath_expected_at(file)
304
+ # nil is "I don't see that file" — which is what Zeitwerk answers for a
305
+ # path spelled through a symlink its own roots aren't, so it is not an
306
+ # answer to compare against
307
+ next if expected.nil? || expected == expected_cpath(file)
308
+
309
+ subject, short = describe_output(dir)
310
+ raise GraphWeaver::Error,
311
+ "#{subject} was left to Zeitwerk because the constants it generates are the ones its path " \
312
+ "spells — but Zeitwerk reads #{GraphWeaver::Internal::Util.relative(file)} as " \
313
+ "#{expected.inspect}, not #{expected_cpath(file).inspect}. A `collapse`, or an autoload root " \
314
+ "pushed under its own namespace, over #{short} changes that name: move the output out from " \
315
+ "under it, or set namespace: to what Zeitwerk reads."
316
+ end
317
+ end
318
+ end
319
+
320
+ # Whether this generated directory is one Zeitwerk is loading — what
321
+ # Internal::Util.required_dirs asks, so the boot-time require skips it.
322
+ def self.left_to_zeitwerk?(path)
323
+ Array(autoloaded_dirs).include?(autoload_path(path))
324
+ end
325
+
133
326
  # Hide one output from Zeitwerk, the moment it is named — a graph declared in
134
327
  # config/initializers says where it writes long after the sweep above ran.
135
328
  # Waiting for it instead is what `after: :load_config_initializers` used to
@@ -141,9 +334,17 @@ class GraphWeaver::Railtie < Rails::Railtie
141
334
  def self.ignore_output!(path)
142
335
  return unless hiding_outputs
143
336
 
144
- # patterns, not paths — generated_paths may be globs, and Zeitwerk expands
145
- # its own at setup, which is what the window stays open until
146
- dir = autoload_path(path)
337
+ # a graph saying where it writes is also the moment the rule can be applied
338
+ # to it: config/initializers is later than the sweep above, so this is
339
+ # where an app's second graph is first heard of
340
+ graph = GraphWeaver.graphs.find { |candidate| autoload_path(candidate.output) == autoload_path(path) }
341
+ return if graph && consider_output!(graph)
342
+
343
+ hidden_for(path).each { |dir| hide!(dir) }
344
+ end
345
+
346
+ # Hide one resolved directory, once.
347
+ def self.hide!(dir)
147
348
  return if ignored_dirs.include?(dir)
148
349
 
149
350
  check_autoload_once!([dir])
@@ -151,6 +352,25 @@ class GraphWeaver::Railtie < Rails::Railtie
151
352
  Rails.autoloaders.each { |loader| loader.ignore(dir) }
152
353
  end
153
354
 
355
+ # What hiding one generated path means. Normally the pattern itself —
356
+ # generated_paths may be globs, and Zeitwerk expands its own at setup, so a
357
+ # directory a pattern will match is hidden however late it appears; that is
358
+ # what the window stays open until. The exception is a pattern that also
359
+ # covers a tree Zeitwerk is keeping (app/graphql/*/generated matches a
360
+ # namespaced graph's own output), since a glob can't say "all but this one":
361
+ # then it is expanded here and the aligned directories left out.
362
+ def self.hidden_for(path)
363
+ dir = autoload_path(path)
364
+ kept = Array(autoloaded_dirs)
365
+ return [dir] if kept.empty?
366
+
367
+ expanded = Dir[GraphWeaver::Internal::Util.resolve(path)]
368
+ .select { |found| File.directory?(found) }.map { |found| autoload_path(found) }
369
+ return [dir] if (expanded & kept).empty?
370
+
371
+ expanded - kept
372
+ end
373
+
154
374
  # The `once` autoloader is set up in bootstrap, before any of this — and
155
375
  # Zeitwerk reads its ignore list only at setup, so `loader.ignore` hides
156
376
  # nothing from it however the output is spelled. The generic advice ("name it
@@ -211,7 +431,9 @@ class GraphWeaver::Railtie < Rails::Railtie
211
431
  def self.describe_output(dir)
212
432
  graph = GraphWeaver.graphs.find { autoload_path(_1.output) == dir }
213
433
  short = GraphWeaver::Internal::Util.relative(GraphWeaver::Internal::Util.resolve(graph&.output || dir))
214
- ["#{graph ? "graph :#{graph.name}'s output" : "generated path"} #{short}", short]
434
+ # the default graph has no name to say, and "graph :'s output" is worse
435
+ # than the path on its own
436
+ ["#{graph&.name ? "graph #{graph.name.inspect}'s output" : "generated path"} #{short}", short]
215
437
  end
216
438
 
217
439
  # A graph declared from to_prepare — what the docs say to do when its block
@@ -219,16 +441,20 @@ class GraphWeaver::Railtie < Rails::Railtie
219
441
  # Zeitwerk reads its ignore list only then. So an output that arrives that
220
442
  # late can't be hidden: its files load as ordinary autoloads and raise on the
221
443
  # constant they don't define, in a Zeitwerk error that blames a dropped
222
- # extend_type. Refuse, and name what actually happened.
444
+ # extend_type. Refuse, and name what actually happened — unless the tree is
445
+ # one Zeitwerk names correctly, which needed no hiding at any point and is as
446
+ # true declared late as declared early.
223
447
  def self.check_generated_ignored!
224
448
  # no autoloaders, no Zeitwerk, nothing to refuse
225
449
  return unless Rails.respond_to?(:autoloaders)
226
450
 
227
- late = GraphWeaver::Internal::Util.generated_dirs.map { autoload_path(_1) } - Array(ignored_dirs)
451
+ late = GraphWeaver::Internal::Util.generated_dirs.map { autoload_path(_1) } -
452
+ Array(ignored_dirs) - Array(autoloaded_dirs)
228
453
  return if late.empty?
229
454
 
230
455
  late.each do |dir|
231
456
  next unless Rails.autoloaders.any? { |loader| autoloaded?(loader, dir) }
457
+ next if aligned_late?(dir)
232
458
 
233
459
  subject, short = describe_output(dir)
234
460
  raise GraphWeaver::Error,
@@ -239,6 +465,17 @@ class GraphWeaver::Railtie < Rails::Railtie
239
465
  end
240
466
  end
241
467
 
468
+ # A graph declared after setup, whose tree Zeitwerk already names correctly:
469
+ # nothing had to be hidden, so nothing was missed. Recorded on the way past,
470
+ # so the boot-time require skips it too.
471
+ def self.aligned_late?(dir)
472
+ graph = GraphWeaver.graphs.find { autoload_path(_1.output) == dir }
473
+ return false unless graph && misalignment(graph, dir).nil?
474
+
475
+ self.autoloaded_dirs = Array(autoloaded_dirs) | [dir]
476
+ true
477
+ end
478
+
242
479
  # The two auto-wires an app gets for free, and the only two it can turn off
243
480
  # by assigning nil.
244
481
  #
@@ -408,8 +645,10 @@ class GraphWeaver::Railtie < Rails::Railtie
408
645
  # reloaded what it wrote.
409
646
  return if watcher&.execute_if_updated
410
647
 
411
- # entries may be globs, so Dir[] rather than Dir.exist?
412
- generated = GraphWeaver::Internal::Util.generated_dirs.any? do |dir|
648
+ # entries may be globs, so Dir[] rather than Dir.exist?. required_dirs, not
649
+ # generated_dirs: a tree left to Zeitwerk loads on first reference, and not
650
+ # requiring its whole parent chain here is the point of leaving it there.
651
+ generated = GraphWeaver::Internal::Util.required_dirs.any? do |dir|
413
652
  Dir[GraphWeaver::Internal::Util.resolve(dir)].any?
414
653
  end
415
654
  return unless generated
@@ -422,7 +661,7 @@ class GraphWeaver::Railtie < Rails::Railtie
422
661
  # request 500s on "uninitialized constant Accounts::PersonQuery" until a
423
662
  # .graphql edit happens to trigger the watcher. An un-namespaced module
424
663
  # defines a top-level constant Zeitwerk never manages, so it survives.
425
- if GraphWeaver.graphs.any?(&:namespace)
664
+ if GraphWeaver.graphs.any? { |graph| graph.namespace && !left_to_zeitwerk?(graph.output) }
426
665
  GraphWeaver.reload_generated!
427
666
  else
428
667
  GraphWeaver.load_generated!
@@ -49,6 +49,14 @@ module GraphWeaver::SchemaLoader
49
49
  source.match?(SDL_CONTENT)
50
50
  end
51
51
 
52
+ # Schema content rather than a path: introspection JSON, SDL, or anything
53
+ # spanning lines (which `load` refuses as content it doesn't recognize).
54
+ def self.content?(source)
55
+ source = source.to_path if source.respond_to?(:to_path)
56
+ source = source.to_s
57
+ source.lstrip.start_with?("{") || sdl_content?(source) || source.include?("\n")
58
+ end
59
+
52
60
  def self.load_path(path)
53
61
  case File.extname(path)
54
62
  when ".json"
@@ -56,13 +64,33 @@ module GraphWeaver::SchemaLoader
56
64
  when ".graphql", ".gql"
57
65
  build_sdl(read_schema(path))
58
66
  else
59
- raise GraphWeaver::Error, url_error(path) ||
60
- "unsupported schema format: #{truncate(path)} — expected a .json (introspection) or " \
61
- ".graphql/.gql (SDL) path, or the content itself"
67
+ unsupported_path!(path)
62
68
  end
63
69
  end
64
70
  private_class_method :load_path
65
71
 
72
+ # Fail fast on a path `load` would refuse later — the extension and a
73
+ # stat, no read and no parse. A client defers building the schema to first
74
+ # use, so this is what still refuses at the line that named the file: a
75
+ # wrong path is known at boot, a malformed file at first use.
76
+ def self.check_path!(path)
77
+ path = path.to_path if path.respond_to?(:to_path)
78
+ unsupported_path!(path) unless dump_path?(path)
79
+
80
+ resolved = GraphWeaver::Internal::Util.resolve(path)
81
+ File.stat(resolved)
82
+ raise GraphWeaver::Error, "can't read the schema at #{path}: permission denied" unless File.readable?(resolved)
83
+ rescue SystemCallError => e
84
+ raise GraphWeaver::Error, "can't read the schema at #{path}: #{e.message}"
85
+ end
86
+
87
+ def self.unsupported_path!(path)
88
+ raise GraphWeaver::Error, url_error(path) ||
89
+ "unsupported schema format: #{truncate(path)} — expected a .json (introspection) or " \
90
+ ".graphql/.gql (SDL) path, or the content itself"
91
+ end
92
+ private_class_method :unsupported_path!
93
+
66
94
  def self.read_schema(path)
67
95
  File.read(GraphWeaver::Internal::Util.resolve(path))
68
96
  rescue SystemCallError => e
@@ -123,10 +151,23 @@ module GraphWeaver::SchemaLoader
123
151
  else sdl
124
152
  end
125
153
 
126
- build(kind) { GraphQL::Schema.from_definition(prepared) }
154
+ build(kind) { build_schema(prepared) }
127
155
  end
128
156
  private_class_method :build_sdl
129
157
 
158
+ # A supergraph arrives here as the AST strip_federation filtered, anything
159
+ # else as SDL text. `from_definition` is parse-then-build, so an AST takes
160
+ # the build half directly — printing it back and parsing it again is a
161
+ # round trip through text that can only cost. spec/schema_loader_spec.rb
162
+ # pins the entry point, so a graphql-ruby that moves it fails there rather
163
+ # than as a NoMethodError inside someone's load.
164
+ def self.build_schema(prepared)
165
+ return GraphQL::Schema.from_definition(prepared) if prepared.is_a?(String)
166
+
167
+ GraphQL::Schema::BuildFromDefinition.from_document(GraphQL::Schema, prepared, default_resolve: nil)
168
+ end
169
+ private_class_method :build_schema
170
+
130
171
  def self.build_introspection(result)
131
172
  build(:introspection) { GraphQL::Schema.from_introspection(result) }.tap do |schema|
132
173
  restore_one_of!(schema, result)
@@ -519,9 +560,9 @@ module GraphWeaver::SchemaLoader
519
560
  # join__*/link__* type and directive definitions, and every @join__*/@link
520
561
  # application on the types that remain. What's left is the merged graph's
521
562
  # ordinary type shapes — exactly what codegen reads. Parsing is lenient (it's
522
- # schema *building* that rejects the join directives), so we parse, filter the
523
- # AST, and reprint clean SDL for from_definition — no graphql-ruby monkeypatch
524
- # and no join__* leaking into schema.types.
563
+ # schema *building* that rejects the join directives), so we parse and filter
564
+ # the AST, and hand that document to the builder — no graphql-ruby
565
+ # monkeypatch and no join__* leaking into schema.types.
525
566
  def self.strip_federation(sdl)
526
567
  doc = GraphQL.parse(sdl)
527
568
  ns = link_namespaces(doc)
@@ -535,7 +576,7 @@ module GraphWeaver::SchemaLoader
535
576
  "is the whole schema behind @inaccessible?"
536
577
  end
537
578
 
538
- GraphQL::Language::Nodes::Document.new(definitions: defs).to_query_string
579
+ GraphQL::Language::Nodes::Document.new(definitions: defs)
539
580
  end
540
581
  private_class_method :strip_federation
541
582
 
@@ -1259,10 +1300,18 @@ module GraphWeaver::SchemaLoader
1259
1300
 
1260
1301
  # A @key/@requires/@provides field set is a selection set. Flattened to
1261
1302
  # dotted paths, so a nested one is recognizable as nested by its shape.
1303
+ # Memoized on the text: a supergraph repeats the same few spellings
1304
+ # thousands of times ("id" on every entity), and each was a parse.
1262
1305
  def self.parse_field_set(text)
1263
- flatten(GraphQL.parse("{ #{text} }").definitions.first.selections, [])
1306
+ FIELD_SETS.synchronize do
1307
+ FIELD_SETS_BY_TEXT[text] ||= flatten(GraphQL.parse("{ #{text} }").definitions.first.selections, []).freeze
1308
+ end
1264
1309
  end
1265
1310
 
1311
+ FIELD_SETS = Mutex.new
1312
+ FIELD_SETS_BY_TEXT = {}
1313
+ private_constant :FIELD_SETS, :FIELD_SETS_BY_TEXT
1314
+
1266
1315
  def self.flatten(selections, prefix)
1267
1316
  selections.flat_map do |node|
1268
1317
  unless node.is_a?(GraphQL::Language::Nodes::Field)
@@ -1,3 +1,3 @@
1
1
  module GraphWeaver
2
- VERSION = "0.7.6"
2
+ VERSION = "0.7.8"
3
3
  end
data/lib/graph_weaver.rb CHANGED
@@ -219,9 +219,15 @@ module GraphWeaver
219
219
  # from where you put the files: set it globally, or pass types_module: per
220
220
  # generate!. A multi-schema layout names it in the same initializer that
221
221
  # sets its paths.
222
+ #
223
+ # Unset, the default is whichever name is free where the constants land:
224
+ # GraphQLTypes at the top level, where a bare ::Types would collide with
225
+ # graphql-ruby's own Types:: convention, and plain Types inside a graph's
226
+ # namespace, where it can't — which is what leaves types.rb a file Zeitwerk
227
+ # names correctly (see Graph#types_module).
222
228
  attr_writer :types_module
223
229
 
224
- def types_module = @types_module || "GraphQLTypes"
230
+ def types_module(default = "GraphQLTypes") = @types_module || default
225
231
 
226
232
  # Declare a second schema — and a third, and the rest. Everything a graph
227
233
  # knows is said inside its block, in call style:
@@ -703,12 +709,13 @@ module GraphWeaver
703
709
  #
704
710
  # GraphWeaver.load_generated!
705
711
  #
706
- # In Rails, prefer this over autoloading: Zeitwerk would expect
707
- # Generated::PersonQuery from generated/person_query.rb, and
708
- # generated code only changes on regeneration anyway (restart, like
709
- # a schema migration).
712
+ # In Rails this is automatic, and a tree Zeitwerk already names correctly
713
+ # is skipped: it autoloads on first reference (see the Railtie). Everything
714
+ # else is required — Zeitwerk would expect Generated::PersonQuery from
715
+ # generated/person_query.rb, and generated code only changes on
716
+ # regeneration anyway (restart, like a schema migration).
710
717
  def load_generated!(path = nil)
711
- paths = path ? [path] : Internal::Util.generated_dirs
718
+ paths = path ? [path] : Internal::Util.required_dirs
712
719
  files = paths.flat_map { |dir| Dir[File.join(Internal::Util.resolve(dir), "**/*.rb")].sort }.uniq
713
720
  files.each do |file|
714
721
  require file
@@ -761,7 +768,9 @@ module GraphWeaver
761
768
  # whose query was just deleted keeps its old constant until restart —
762
769
  # nothing on disk says what it was called any more.
763
770
  def reload_generated!
764
- names = graphs.flat_map do |graph|
771
+ # a graph Zeitwerk owns unloads and reloads itself; undefining its
772
+ # constants here would only take them out from under it
773
+ names = graphs.reject { |graph| Internal::Util.left_to_zeitwerk?(graph.output) }.flat_map do |graph|
765
774
  modules = Internal::Util.query_files(graph.queries).map do |path|
766
775
  graph.generated_names(path, File.read(path)).first
767
776
  end
@@ -769,7 +778,7 @@ module GraphWeaver
769
778
  end
770
779
  names.each { |name| undefine(name) }
771
780
 
772
- Internal::Util.generated_dirs.each do |dir|
781
+ Internal::Util.required_dirs.each do |dir|
773
782
  Dir[File.join(Internal::Util.resolve(dir), "**/*.rb")].each do |file|
774
783
  # require stores the realpath; the path load_generated! passed is
775
784
  # the other one under a symlinked checkout
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graph_weaver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.6
4
+ version: 0.7.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Pepper
@@ -364,7 +364,7 @@ licenses:
364
364
  - MIT
365
365
  metadata:
366
366
  bug_tracker_uri: https://github.com/dpep/graph_weaver/issues
367
- changelog_uri: https://github.com/dpep/graph_weaver/blob/v0.7.6/CHANGELOG.md
367
+ changelog_uri: https://github.com/dpep/graph_weaver/blob/v0.7.8/CHANGELOG.md
368
368
  documentation_uri: https://github.com/dpep/graph_weaver/tree/main/docs
369
369
  rubygems_mfa_required: 'true'
370
370
  source_code_uri: https://github.com/dpep/graph_weaver