graph_weaver 0.7.6 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a68eaeb8cd0f3139cc9e50b88d98a50e04acd0c2c94c7825233f04405d30c42
4
- data.tar.gz: '08df5051075500cb81c710cd65705f387dec16bc29a626e2b8069bbf5c61b3a1'
3
+ metadata.gz: a7b665c1263db52f315217a6298ff638d963b926e44f94c16dc2972de4140320
4
+ data.tar.gz: a9712a11f712adfa8846b180b1e22874f63d57fd00514d311b9cb0e60c9f6ebf
5
5
  SHA512:
6
- metadata.gz: ee7b2971697409f7180e31d40569fd44108404c0f1d811b259bec6e000f7cf82142e7640de164510d1dbd253916392c6cc866619ee2d37e35e610b1ed0be9741
7
- data.tar.gz: 9d219008a1db9bc353ef3b033b9c39f64657e58d1d2548fb074fc1a60973052e4abd0c1950d93622ad1cfc0414e7ca1219d9c770e32e445cedba287636f58a73
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.6)
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.6)
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
@@ -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
 
@@ -496,7 +496,7 @@ plus `register_scalar`, `register_enum` and `extend_type`).
496
496
  | `output` | one directory — where this graph's generated Ruby is written |
497
497
  | `client` | what this graph's modules execute against — a client, or the name of the constant holding one |
498
498
  | `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`) |
499
+ | `types_module` | a constant name for the shared types module (default: `Types` under a `namespace`, `GraphQLTypes` without one) |
500
500
 
501
501
  **Declaring graph two means declaring graph one.** A declared graph *replaces*
502
502
  the implicit one the top-level settings describe, so the moment any graph is
@@ -527,7 +527,10 @@ a bare `schema` reads back; there is no `schema = "x"` form, since the block is
527
527
  `Billing::PersonQuery` ([naming](generated_modules.md#naming)). Constants are
528
528
  global, so two schemas that both have a `person.graphql` would otherwise fight
529
529
  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
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
531
534
  over the top-level ones — so `Money` is checked against the schema it was
532
535
  registered for, and against no other.
533
536
 
data/docs/upgrading.md CHANGED
@@ -1,7 +1,8 @@
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.6](#upgrading-from-076), from
5
+ [0.7.5](#upgrading-from-075), from
5
6
  [0.7.4](#upgrading-from-074), from [0.7.3](#upgrading-from-073), from
6
7
  [0.7.1](#upgrading-from-071), from [0.7.0](#upgrading-from-070) or from
7
8
  [0.6.1](#upgrading-from-061). Coming from 0.6.0 or older, the path is that
@@ -24,6 +25,23 @@ That's the reminder working, not a false alarm. Generation is deterministic, so
24
25
  the diff is exactly what the new version emits differently and nothing else —
25
26
  worth reading rather than rubber-stamping.
26
27
 
28
+ ## Upgrading from 0.7.6
29
+
30
+ Read the left column and skip what isn't yours; the
31
+ [changelog](../CHANGELOG.md) says why each one moved.
32
+
33
+ | applies if you… | what changed |
34
+ |---|---|
35
+ | 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 |
36
+ | 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 |
37
+
38
+ Then regenerate, and the gate:
39
+
40
+ ```sh
41
+ rake graph_weaver:generate
42
+ rake graph_weaver:verify
43
+ ```
44
+
27
45
  ## Upgrading from 0.7.5
28
46
 
29
47
  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.7 — 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.7 — 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.7 — do not edit.
5
5
 
6
6
  module StarredQuery
7
7
  extend T::Sig
@@ -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)
@@ -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!
@@ -1,3 +1,3 @@
1
1
  module GraphWeaver
2
- VERSION = "0.7.6"
2
+ VERSION = "0.7.7"
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.7
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.7/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