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 +4 -4
- data/Gemfile.lock +43 -43
- data/docs/generated_modules.md +56 -10
- data/docs/getting_started.md +5 -2
- data/docs/upgrading.md +19 -1
- data/examples/github/generated/star_mutation.rb +1 -1
- data/examples/github/generated/stargazers_query.rb +1 -1
- data/examples/github/generated/starred_query.rb +1 -1
- data/lib/graph_weaver/graph.rb +12 -1
- data/lib/graph_weaver/internal.rb +17 -0
- data/lib/graph_weaver/railtie.rb +250 -11
- data/lib/graph_weaver/version.rb +1 -1
- data/lib/graph_weaver.rb +17 -8
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a7b665c1263db52f315217a6298ff638d963b926e44f94c16dc2972de4140320
|
|
4
|
+
data.tar.gz: a9712a11f712adfa8846b180b1e22874f63d57fd00514d311b9cb0e60c9f6ebf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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 (
|
|
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.
|
|
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
|
|
162
|
+
rbi (0.4.3)
|
|
163
163
|
prism (~> 1.0)
|
|
164
164
|
rbs (>= 4.0.1)
|
|
165
|
-
rbs (4.0
|
|
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.
|
|
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.
|
|
194
|
-
rubydex (0.
|
|
195
|
-
rubydex (0.
|
|
196
|
-
rubydex (0.
|
|
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.
|
|
200
|
-
sorbet-static (= 0.6.
|
|
201
|
-
sorbet-runtime (0.6.
|
|
202
|
-
sorbet-static (0.6.
|
|
203
|
-
sorbet-static (0.6.
|
|
204
|
-
sorbet-static (0.6.
|
|
205
|
-
sorbet-static-and-runtime (0.6.
|
|
206
|
-
sorbet (= 0.6.
|
|
207
|
-
sorbet-runtime (= 0.6.
|
|
208
|
-
spoom (1.8.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 (
|
|
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.
|
|
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
|
|
336
|
-
rbs (4.0
|
|
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.
|
|
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.
|
|
349
|
-
rubydex (0.
|
|
350
|
-
rubydex (0.
|
|
351
|
-
rubydex (0.
|
|
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.
|
|
355
|
-
sorbet-runtime (0.6.
|
|
356
|
-
sorbet-static (0.6.
|
|
357
|
-
sorbet-static (0.6.
|
|
358
|
-
sorbet-static (0.6.
|
|
359
|
-
sorbet-static-and-runtime (0.6.
|
|
360
|
-
spoom (1.8.
|
|
361
|
-
tapioca (0.
|
|
354
|
+
sorbet (0.6.13506) sha256=b6fd9f9ab2563c709b9308437cb40754e190667fddfe841723a7dda430978cd6
|
|
355
|
+
sorbet-runtime (0.6.13506) sha256=94a7ff795cf2a67818a954883c0301de0b7222a9f732b24ab30ea5253e7568f6
|
|
356
|
+
sorbet-static (0.6.13506-aarch64-linux) sha256=04405070206de265a87e6544454092cd4c7b275415dccf76a4ab41649819ae9f
|
|
357
|
+
sorbet-static (0.6.13506-universal-darwin) sha256=744918a125841c84d0804410bfda068a6dc8182285ba50ddb1e8ce5955b24700
|
|
358
|
+
sorbet-static (0.6.13506-x86_64-linux) sha256=b57d36d1db374e10109bbd5af3bec0edf6f94f3e86dbffe69199763909ad2c91
|
|
359
|
+
sorbet-static-and-runtime (0.6.13506) sha256=12c6abbc1d988267452c97e4cff75c563a480e3e8b7a1a633e2f714856e5c313
|
|
360
|
+
spoom (1.8.7) sha256=81960502cdf23aa991f015988aa5dd43325e23ae4318b8e00d541299cbdb70f6
|
|
361
|
+
tapioca (0.20.0) sha256=b375ed7bf51d2c86c09543eafedd0de85513634187c0f7a062615af8bb9347a0
|
|
362
362
|
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
|
|
363
363
|
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
|
364
364
|
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
|
data/docs/generated_modules.md
CHANGED
|
@@ -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::
|
|
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
|
|
837
|
-
|
|
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
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
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
|
|
data/docs/getting_started.md
CHANGED
|
@@ -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: `
|
|
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. **
|
|
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.
|
|
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
|
data/lib/graph_weaver/graph.rb
CHANGED
|
@@ -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
|
|
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)
|
data/lib/graph_weaver/railtie.rb
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
#
|
|
145
|
-
#
|
|
146
|
-
|
|
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
|
-
|
|
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) } -
|
|
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
|
-
|
|
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?(
|
|
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!
|
data/lib/graph_weaver/version.rb
CHANGED
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 ||
|
|
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,
|
|
707
|
-
#
|
|
708
|
-
#
|
|
709
|
-
#
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|