graph_weaver 0.6.1 → 0.7.0
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/CHANGELOG.md +1447 -1
- data/Gemfile +8 -0
- data/Gemfile.lock +151 -2
- data/README.md +20 -6
- data/docs/alternatives.md +201 -0
- data/docs/cassettes.md +17 -1
- data/docs/errors.md +382 -17
- data/docs/federation.md +469 -63
- data/docs/generated_modules.md +231 -15
- data/docs/getting_started.md +497 -104
- data/docs/i18n.md +234 -0
- data/docs/logging.md +160 -24
- data/docs/real_world.md +28 -0
- data/docs/scalars.md +190 -26
- data/docs/testing.md +457 -58
- data/docs/transports.md +164 -19
- data/docs/upgrading.md +328 -3
- data/graph_weaver.gemspec +7 -0
- data/lib/generators/graph_weaver/install_generator.rb +138 -4
- data/lib/graph_weaver/client.rb +47 -10
- data/lib/graph_weaver/codegen/aliases.rb +7 -5
- data/lib/graph_weaver/codegen/emit.rb +98 -29
- data/lib/graph_weaver/codegen/enum_type.rb +2 -1
- data/lib/graph_weaver/codegen/nodes.rb +39 -6
- data/lib/graph_weaver/codegen/registry.rb +175 -0
- data/lib/graph_weaver/codegen/scalar_type.rb +123 -30
- data/lib/graph_weaver/codegen/type_helpers.rb +56 -11
- data/lib/graph_weaver/codegen.rb +404 -197
- data/lib/graph_weaver/coerce.rb +155 -26
- data/lib/graph_weaver/errors.rb +264 -34
- data/lib/graph_weaver/federation.rb +119 -26
- data/lib/graph_weaver/graph.rb +315 -0
- data/lib/graph_weaver/hints.rb +100 -24
- data/lib/graph_weaver/in_process.rb +17 -11
- data/lib/graph_weaver/input_struct.rb +119 -32
- data/lib/graph_weaver/internal/endpoint.rb +78 -0
- data/lib/graph_weaver/internal/headers.rb +51 -0
- data/lib/graph_weaver/internal/overrides.rb +67 -5
- data/lib/graph_weaver/internal/planner.rb +45 -15
- data/lib/graph_weaver/internal/refusal.rb +49 -0
- data/lib/graph_weaver/internal/schemas.rb +23 -9
- data/lib/graph_weaver/internal/selection.rb +34 -0
- data/lib/graph_weaver/internal/server_input.rb +251 -0
- data/lib/graph_weaver/internal/test_clients.rb +276 -0
- data/lib/graph_weaver/internal/unused.rb +287 -0
- data/lib/graph_weaver/internal/values.rb +40 -4
- data/lib/graph_weaver/internal.rb +183 -1
- data/lib/graph_weaver/log_subscriber.rb +66 -0
- data/lib/graph_weaver/logging.rb +136 -12
- data/lib/graph_weaver/query_module.rb +36 -3
- data/lib/graph_weaver/railtie.rb +237 -17
- data/lib/graph_weaver/representation.rb +55 -17
- data/lib/graph_weaver/result_struct.rb +90 -0
- data/lib/graph_weaver/retry.rb +33 -5
- data/lib/graph_weaver/rspec.rb +404 -93
- data/lib/graph_weaver/schema_loader.rb +221 -49
- data/lib/graph_weaver/tasks.rb +380 -89
- data/lib/graph_weaver/testing/cassette.rb +6 -5
- data/lib/graph_weaver/testing/endpoint.rb +106 -0
- data/lib/graph_weaver/testing/failure.rb +69 -12
- data/lib/graph_weaver/testing/fake_client.rb +133 -44
- data/lib/graph_weaver/testing/router.rb +58 -11
- data/lib/graph_weaver/testing.rb +200 -58
- data/lib/graph_weaver/transport/faraday.rb +41 -8
- data/lib/graph_weaver/transport/http.rb +46 -4
- data/lib/graph_weaver/transport.rb +109 -26
- data/lib/graph_weaver/version.rb +1 -1
- data/lib/graph_weaver.rb +474 -106
- metadata +56 -1
data/Gemfile
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
source "https://rubygems.org"
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
|
+
|
|
5
|
+
# Mutation testing — not in the gemspec: it is a local quality tool, not a
|
|
6
|
+
# development dependency contributors need to install the gem's suite.
|
|
7
|
+
# Proprietary EULA, free for opensource projects (see `usage:` in mutant.yml).
|
|
8
|
+
group :development do
|
|
9
|
+
gem "mutant", "0.16.3"
|
|
10
|
+
gem "mutant-rspec", "0.16.3"
|
|
11
|
+
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,24 +1,63 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
graph_weaver (0.
|
|
4
|
+
graph_weaver (0.7.0)
|
|
5
5
|
graphql (>= 2.6.7)
|
|
6
6
|
sorbet-runtime
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
+
actionpack (8.1.3.1)
|
|
12
|
+
actionview (= 8.1.3.1)
|
|
13
|
+
activesupport (= 8.1.3.1)
|
|
14
|
+
nokogiri (>= 1.8.5)
|
|
15
|
+
rack (>= 2.2.4)
|
|
16
|
+
rack-session (>= 1.0.1)
|
|
17
|
+
rack-test (>= 0.6.3)
|
|
18
|
+
rails-dom-testing (~> 2.2)
|
|
19
|
+
rails-html-sanitizer (~> 1.6)
|
|
20
|
+
useragent (~> 0.16)
|
|
21
|
+
actionview (8.1.3.1)
|
|
22
|
+
activesupport (= 8.1.3.1)
|
|
23
|
+
builder (~> 3.1)
|
|
24
|
+
erubi (~> 1.11)
|
|
25
|
+
rails-dom-testing (~> 2.2)
|
|
26
|
+
rails-html-sanitizer (~> 1.6)
|
|
27
|
+
activesupport (8.1.3.1)
|
|
28
|
+
base64
|
|
29
|
+
bigdecimal
|
|
30
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
31
|
+
connection_pool (>= 2.2.5)
|
|
32
|
+
drb
|
|
33
|
+
i18n (>= 1.6, < 2)
|
|
34
|
+
json
|
|
35
|
+
logger (>= 1.4.2)
|
|
36
|
+
minitest (>= 5.1)
|
|
37
|
+
securerandom (>= 0.3)
|
|
38
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
39
|
+
uri (>= 0.13.1)
|
|
40
|
+
addressable (2.9.0)
|
|
41
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
11
42
|
apollo-federation (3.10.3)
|
|
12
43
|
google-protobuf (< 5)
|
|
13
44
|
graphql
|
|
45
|
+
ast (2.4.3)
|
|
14
46
|
base64 (0.3.0)
|
|
15
47
|
benchmark (0.5.0)
|
|
16
48
|
bigdecimal (4.1.2)
|
|
49
|
+
builder (3.3.0)
|
|
17
50
|
concurrent-ruby (1.3.7)
|
|
51
|
+
connection_pool (3.0.2)
|
|
52
|
+
crack (1.0.1)
|
|
53
|
+
bigdecimal
|
|
54
|
+
rexml
|
|
55
|
+
crass (1.0.7)
|
|
18
56
|
debug (1.11.1)
|
|
19
57
|
irb (~> 1.10)
|
|
20
58
|
reline (>= 0.3.8)
|
|
21
59
|
diff-lcs (1.6.2)
|
|
60
|
+
drb (2.2.3)
|
|
22
61
|
erb (6.0.4)
|
|
23
62
|
erubi (1.13.1)
|
|
24
63
|
faker (3.8.0)
|
|
@@ -46,6 +85,7 @@ GEM
|
|
|
46
85
|
base64
|
|
47
86
|
fiber-storage
|
|
48
87
|
logger
|
|
88
|
+
hashdiff (1.2.1)
|
|
49
89
|
i18n (1.15.2)
|
|
50
90
|
concurrent-ruby (~> 1.0)
|
|
51
91
|
io-console (0.8.2)
|
|
@@ -56,14 +96,68 @@ GEM
|
|
|
56
96
|
reline (>= 0.4.2)
|
|
57
97
|
json (2.21.2)
|
|
58
98
|
logger (1.7.0)
|
|
99
|
+
loofah (2.25.2)
|
|
100
|
+
crass (~> 1.0.2)
|
|
101
|
+
nokogiri (>= 1.12.0)
|
|
102
|
+
minitest (6.0.6)
|
|
103
|
+
drb (~> 2.0)
|
|
104
|
+
prism (~> 1.5)
|
|
105
|
+
mutant (0.16.3)
|
|
106
|
+
diff-lcs (>= 1.6, < 3)
|
|
107
|
+
irb (~> 1.15)
|
|
108
|
+
parser (~> 3.3.10)
|
|
109
|
+
regexp_parser (~> 2.10)
|
|
110
|
+
securerandom (>= 0.3)
|
|
111
|
+
sorbet-runtime (~> 0.6.0)
|
|
112
|
+
unparser (>= 0.8.2, < 0.10)
|
|
113
|
+
mutant-rspec (0.16.3)
|
|
114
|
+
mutant (= 0.16.3)
|
|
115
|
+
rspec-core (>= 3.8.0, < 5.0.0)
|
|
59
116
|
net-http (0.9.1)
|
|
60
117
|
uri (>= 0.11.1)
|
|
61
118
|
netrc (0.11.0)
|
|
119
|
+
nokogiri (1.19.4-aarch64-linux-gnu)
|
|
120
|
+
racc (~> 1.4)
|
|
121
|
+
nokogiri (1.19.4-arm64-darwin)
|
|
122
|
+
racc (~> 1.4)
|
|
123
|
+
nokogiri (1.19.4-x86_64-darwin)
|
|
124
|
+
racc (~> 1.4)
|
|
125
|
+
nokogiri (1.19.4-x86_64-linux-gnu)
|
|
126
|
+
racc (~> 1.4)
|
|
62
127
|
parallel (2.1.0)
|
|
128
|
+
parser (3.3.12.0)
|
|
129
|
+
ast (~> 2.4.1)
|
|
130
|
+
racc
|
|
63
131
|
pp (0.6.4)
|
|
64
132
|
prettyprint
|
|
65
133
|
prettyprint (0.2.0)
|
|
66
134
|
prism (1.9.0)
|
|
135
|
+
public_suffix (7.0.5)
|
|
136
|
+
racc (1.8.1)
|
|
137
|
+
rack (3.2.7)
|
|
138
|
+
rack-session (2.1.2)
|
|
139
|
+
base64 (>= 0.1.0)
|
|
140
|
+
rack (>= 3.0.0)
|
|
141
|
+
rack-test (2.2.0)
|
|
142
|
+
rack (>= 1.3)
|
|
143
|
+
rackup (2.3.1)
|
|
144
|
+
rack (>= 3)
|
|
145
|
+
rails-dom-testing (2.3.0)
|
|
146
|
+
activesupport (>= 5.0.0)
|
|
147
|
+
minitest
|
|
148
|
+
nokogiri (>= 1.6)
|
|
149
|
+
rails-html-sanitizer (1.7.1)
|
|
150
|
+
loofah (~> 2.25, >= 2.25.2)
|
|
151
|
+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
152
|
+
railties (8.1.3.1)
|
|
153
|
+
actionpack (= 8.1.3.1)
|
|
154
|
+
activesupport (= 8.1.3.1)
|
|
155
|
+
irb (~> 1.13)
|
|
156
|
+
rackup (>= 1.0.0)
|
|
157
|
+
rake (>= 12.2)
|
|
158
|
+
thor (~> 1.0, >= 1.2.2)
|
|
159
|
+
tsort (>= 0.2)
|
|
160
|
+
zeitwerk (~> 2.6)
|
|
67
161
|
rake (13.4.2)
|
|
68
162
|
rbi (0.3.14)
|
|
69
163
|
prism (~> 1.0)
|
|
@@ -78,6 +172,7 @@ GEM
|
|
|
78
172
|
rbs (>= 4.0.0)
|
|
79
173
|
tsort
|
|
80
174
|
redcarpet (3.6.1)
|
|
175
|
+
regexp_parser (2.12.0)
|
|
81
176
|
reline (0.6.3)
|
|
82
177
|
io-console (~> 0.5)
|
|
83
178
|
require-hooks (0.4.0)
|
|
@@ -99,6 +194,7 @@ GEM
|
|
|
99
194
|
rubydex (0.2.7-arm64-darwin)
|
|
100
195
|
rubydex (0.2.7-x86_64-darwin)
|
|
101
196
|
rubydex (0.2.7-x86_64-linux)
|
|
197
|
+
securerandom (0.4.1)
|
|
102
198
|
simplecov (1.2.0)
|
|
103
199
|
sorbet (0.6.13485)
|
|
104
200
|
sorbet-static (= 0.6.13485)
|
|
@@ -131,9 +227,21 @@ GEM
|
|
|
131
227
|
tsort
|
|
132
228
|
thor (1.5.0)
|
|
133
229
|
tsort (0.2.0)
|
|
230
|
+
tzinfo (2.0.6)
|
|
231
|
+
concurrent-ruby (~> 1.0)
|
|
232
|
+
unparser (0.9.0)
|
|
233
|
+
diff-lcs (>= 1.6, < 3)
|
|
234
|
+
parser (>= 3.3.0)
|
|
235
|
+
prism (>= 1.5.1)
|
|
134
236
|
uri (1.1.1)
|
|
237
|
+
useragent (0.16.11)
|
|
238
|
+
webmock (3.26.4)
|
|
239
|
+
addressable (>= 2.8.0)
|
|
240
|
+
crack (>= 0.3.2)
|
|
241
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
135
242
|
webrick (1.9.2)
|
|
136
243
|
yard (0.9.44)
|
|
244
|
+
zeitwerk (2.8.3)
|
|
137
245
|
|
|
138
246
|
PLATFORMS
|
|
139
247
|
aarch64-linux
|
|
@@ -149,23 +257,38 @@ DEPENDENCIES
|
|
|
149
257
|
faker
|
|
150
258
|
faraday
|
|
151
259
|
graph_weaver!
|
|
260
|
+
mutant (= 0.16.3)
|
|
261
|
+
mutant-rspec (= 0.16.3)
|
|
262
|
+
rack
|
|
263
|
+
railties
|
|
152
264
|
rake
|
|
153
265
|
redcarpet
|
|
154
266
|
rspec
|
|
155
267
|
simplecov
|
|
156
268
|
sorbet
|
|
157
269
|
tapioca
|
|
270
|
+
webmock
|
|
158
271
|
webrick
|
|
159
272
|
yard
|
|
160
273
|
|
|
161
274
|
CHECKSUMS
|
|
275
|
+
actionpack (8.1.3.1) sha256=974cb7154548e81f470b1b0f247b99cb38e87825899dca58610596e2817723d0
|
|
276
|
+
actionview (8.1.3.1) sha256=2da68b8414c47b43bfbed1ce69c5afe1c04f78c267aacb5660a4cab5ca12cfb6
|
|
277
|
+
activesupport (8.1.3.1) sha256=85458765f25ea48b9019c46b6bb3fa5683197bf4280d9f06710a6e8d7a831376
|
|
278
|
+
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
|
|
162
279
|
apollo-federation (3.10.3) sha256=61e6d8e1fd695ee45bab5d84f86f586d99c296ad77a86c6f1e7b964ac17be3d7
|
|
280
|
+
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
163
281
|
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
164
282
|
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
|
|
165
283
|
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
|
|
284
|
+
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
|
|
166
285
|
concurrent-ruby (1.3.7) sha256=4412caec3a5ea2e5fdc52076724c071a81f2c0593d83b2ac8cbb8ca63b3151b0
|
|
286
|
+
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
|
287
|
+
crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
|
|
288
|
+
crass (1.0.7) sha256=94868719948664c89ddcaf0a37c65048413dfcb1c869470a5f7a7ceb5390b295
|
|
167
289
|
debug (1.11.1) sha256=2e0b0ac6119f2207a6f8ac7d4a73ca8eb4e440f64da0a3136c30343146e952b6
|
|
168
290
|
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
291
|
+
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
|
169
292
|
erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
|
|
170
293
|
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
|
|
171
294
|
faker (3.8.0) sha256=c147b308df73a90f27a4fc84f18d4c22ef0ad9c2a64b2b61c86fd0ca71753efc
|
|
@@ -176,24 +299,44 @@ CHECKSUMS
|
|
|
176
299
|
google-protobuf (4.35.1-arm64-darwin) sha256=d9c957df04fa89c749fa9a72a7b383eb4296efc9b2303dc6fd6fbe39c698ad6b
|
|
177
300
|
google-protobuf (4.35.1-x86_64-darwin) sha256=66b62b4df00931018a692806df66393efa960d6d2b7da69735187249f950d3ee
|
|
178
301
|
google-protobuf (4.35.1-x86_64-linux-gnu) sha256=c786439087512a3fbd199e9897d265b855f951d4027e218ea55e858d45969edd
|
|
179
|
-
graph_weaver (0.
|
|
302
|
+
graph_weaver (0.7.0)
|
|
180
303
|
graphql (2.6.10) sha256=9b7c8633767f516ff9d48a8d6305b2a00a2101c82aa871b92e69086944f9f83e
|
|
304
|
+
hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
|
|
181
305
|
i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
|
|
182
306
|
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
|
183
307
|
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
|
|
184
308
|
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
|
|
185
309
|
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
310
|
+
loofah (2.25.2) sha256=2007f746959ac65552456e04b433e83deb22759ab38c838b4445c70e43425918
|
|
311
|
+
minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
|
|
312
|
+
mutant (0.16.3) sha256=7e10fb09b80a78d82f8e38283fc1e4d562cbb1392451e2a21611ea2bc44b5023
|
|
313
|
+
mutant-rspec (0.16.3) sha256=006ee9640800f205278604bb2160ca4b1d0013464fb216a48b7c09571a1fc547
|
|
186
314
|
net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
|
|
187
315
|
netrc (0.11.0) sha256=de1ce33da8c99ab1d97871726cba75151113f117146becbe45aa85cb3dabee3f
|
|
316
|
+
nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f
|
|
317
|
+
nokogiri (1.19.4-arm64-darwin) sha256=a46db9853286e6597b36ebc6953817d15acf3a299583eb3f89fdc6f91dd63527
|
|
318
|
+
nokogiri (1.19.4-x86_64-darwin) sha256=7fd17057d3e1f00e9954a74b3cd76595d3d4a5ef233b7ed9599047c204f70551
|
|
319
|
+
nokogiri (1.19.4-x86_64-linux-gnu) sha256=379fae440b28915e3f19d752ce2dcf8465ed2b2fbefd2a7ca0dd497bc981a06a
|
|
188
320
|
parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
|
|
321
|
+
parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
|
|
189
322
|
pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570
|
|
190
323
|
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
|
|
191
324
|
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
325
|
+
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
|
|
326
|
+
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
|
327
|
+
rack (3.2.7) sha256=93e13e1c24f93556671d85d2d79fa228c3485815c50d7e2f265b5330c6528fb7
|
|
328
|
+
rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8
|
|
329
|
+
rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463
|
|
330
|
+
rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
|
|
331
|
+
rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d
|
|
332
|
+
rails-html-sanitizer (1.7.1) sha256=e797a7c9b01e567307e317c576b49ab4168017e63eea4dba9ce3cb587e2f22c2
|
|
333
|
+
railties (8.1.3.1) sha256=2388a232579a00cefea4487de66c8553c3408c1300abdc6cf1799d86ffb04487
|
|
192
334
|
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
193
335
|
rbi (0.3.14) sha256=932ed904adbb5ca645c285c8b36da1b7ff6321ec1870d187d6325248cb637658
|
|
194
336
|
rbs (4.0.3) sha256=5a7bf70e2628549d9a1f44eae447b2cfe55968a9c60cfff52693a4bdcc020e14
|
|
195
337
|
rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469
|
|
196
338
|
redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
|
|
339
|
+
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
|
|
197
340
|
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
|
198
341
|
require-hooks (0.4.0) sha256=005f4c6435b4edae73e358cdbaba48370a4121f9ce893d5d2a3c66fce855677d
|
|
199
342
|
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
|
@@ -206,6 +349,7 @@ CHECKSUMS
|
|
|
206
349
|
rubydex (0.2.7-arm64-darwin) sha256=f0d28bbf4153568be79b671642424750053e0bea971b60ddf5cec19bf4563990
|
|
207
350
|
rubydex (0.2.7-x86_64-darwin) sha256=b002b259d118ac69de44470eff1597143318402c45630c47371f9542631447dc
|
|
208
351
|
rubydex (0.2.7-x86_64-linux) sha256=dacfade9fa42ce4469618da6dac07e69d5f3ac6a313b4caced5234c8f052419a
|
|
352
|
+
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
|
209
353
|
simplecov (1.2.0) sha256=ea6acd05eece5a41990e2a5171c57d15700d329326c7666c85ee8c6a0dd0977e
|
|
210
354
|
sorbet (0.6.13485) sha256=b885d6a2fcde57bc46c3cdd597b9014b82619dee0f811a6fdf36f57c8f82c380
|
|
211
355
|
sorbet-runtime (0.6.13485) sha256=2982504e662eb424e515b3aa1c8139070451a014b6c1af2302250ed82495b4cc
|
|
@@ -217,9 +361,14 @@ CHECKSUMS
|
|
|
217
361
|
tapioca (0.19.2) sha256=938731b07811aee8d23871b1aee8861d464fbaf2cfffbf79a62b0c869a5120ec
|
|
218
362
|
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
|
|
219
363
|
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
|
364
|
+
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
|
|
365
|
+
unparser (0.9.0) sha256=4331f174a73a23b69250b13d47da3794ed1449711ee0f9ed8947dc020ba76067
|
|
220
366
|
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
|
|
367
|
+
useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844
|
|
368
|
+
webmock (3.26.4) sha256=8d8da206d217ebe6968cfb09c77f4533c23074e1432bad865f3994eacbaad50d
|
|
221
369
|
webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131
|
|
222
370
|
yard (0.9.44) sha256=eb087e9b631ccd887b049f303d489963945452d5e2a7eb49a5a74a7cf6887f28
|
|
371
|
+
zeitwerk (2.8.3) sha256=2c85125a8467ce069e20123d1e709a08955c9d29c118c25b46b7b7fafdbb92e5
|
|
223
372
|
|
|
224
373
|
BUNDLED WITH
|
|
225
374
|
4.0.10
|
data/README.md
CHANGED
|
@@ -41,6 +41,10 @@ result.person&.nmae
|
|
|
41
41
|
`person` is `T.nilable` because the schema says the field is nullable — the `&.`
|
|
42
42
|
isn't defensive, it's the schema talking. A field you misspelled, or never
|
|
43
43
|
selected, is a typecheck error rather than a `NoMethodError` in production.
|
|
44
|
+
Static Sorbet is optional: `sorbet-runtime` is the only Sorbet gem this one
|
|
45
|
+
needs, so if your app doesn't run `srb tc`, that same typo surfaces as a
|
|
46
|
+
`NoMethodError` the first time the line runs instead of in CI — [Sorbet, with or
|
|
47
|
+
without](docs/getting_started.md#sorbet-with-or-without).
|
|
44
48
|
|
|
45
49
|
## Start here
|
|
46
50
|
|
|
@@ -63,6 +67,11 @@ You then rarely type `rake graph_weaver:generate` again: while the dev server is
|
|
|
63
67
|
up, a `.graphql` edit regenerates and reloads before the next request, the way a
|
|
64
68
|
route change does. Run it when you're ready to commit the Ruby.
|
|
65
69
|
|
|
70
|
+
A second schema is a second `GraphWeaver.graph` — its own queries, output,
|
|
71
|
+
client, namespace and scalar registrations — and the same one command generates
|
|
72
|
+
and verifies the app: **[more than one
|
|
73
|
+
schema](docs/getting_started.md#more-than-one-schema)**.
|
|
74
|
+
|
|
66
75
|
Or skip the build step and poke at an API from a console —
|
|
67
76
|
anything holding a schema parses, and the module runs on what parsed it:
|
|
68
77
|
|
|
@@ -94,7 +103,7 @@ then one tag says what an example runs against:
|
|
|
94
103
|
it "shows the profile", graphql: :fake do
|
|
95
104
|
person = PersonQuery.execute!(id: "1").person
|
|
96
105
|
|
|
97
|
-
person.name # => "Shakita Stark"
|
|
106
|
+
person.name # => "Shakita Stark" with faker installed; "name-1" without
|
|
98
107
|
person.birthday # => #<Date: 2024-12-16> custom scalars included
|
|
99
108
|
person.pets.size # => 2
|
|
100
109
|
end
|
|
@@ -119,7 +128,8 @@ reads the selected fields off your factory's object and fabricates the rest. The
|
|
|
119
128
|
are checked and spellchecked, so a typo raises instead of leaving the example
|
|
120
129
|
green against random data. The tag also picks a *real* client
|
|
121
130
|
when you want one: `:in_process` runs your resolvers, `:router` runs them across a
|
|
122
|
-
federated graph
|
|
131
|
+
federated graph, and `:wire` serves either at your own endpoint, so the transport
|
|
132
|
+
you ship runs too. Field-level failure simulation and record/replay cassettes with
|
|
123
133
|
anonymization are in [testing](docs/testing.md).
|
|
124
134
|
|
|
125
135
|
## Federation without a gateway
|
|
@@ -150,8 +160,10 @@ See [federation](docs/federation.md).
|
|
|
150
160
|
|
|
151
161
|
The lifecycle is rake tasks, not a CI pipeline you assemble yourself:
|
|
152
162
|
`schema:refresh` re-introspects the committed dump, `schema:diff` names what
|
|
153
|
-
changed when
|
|
154
|
-
|
|
163
|
+
changed when whatever that dump came from — an endpoint, your own schema class —
|
|
164
|
+
has moved past it, `queries:check` names the queries that drift broke and where,
|
|
165
|
+
`unused` names the selections your app stopped reading, and `verify` fails when
|
|
166
|
+
the checked-in Ruby is stale.
|
|
155
167
|
Generation is deterministic — same schema and queries, byte-identical files — so
|
|
156
168
|
regenerating never shows a diff you didn't earn. See
|
|
157
169
|
[getting started](docs/getting_started.md#5-verify-in-ci).
|
|
@@ -162,7 +174,7 @@ upgrade, and `verify` is what tells you when you've skipped it.
|
|
|
162
174
|
|
|
163
175
|
#### Also in the box
|
|
164
176
|
|
|
165
|
-
- **Queries and mutations** with typed variable kwargs — enums as `T::Enum`s, input objects as `T::Struct`s, required vs optional falling out of nullability and defaults
|
|
177
|
+
- **Queries and mutations** with typed variable kwargs — enums as `T::Enum`s, input objects as `T::Struct`s, required vs optional falling out of nullability and defaults. Results are generated per selection set; an input object has no selection set, so [its whole closure is](docs/generated_modules.md#an-input-object-generates-its-whole-closure)
|
|
166
178
|
- **Fragments** (inline, named, type conditions), **unions and interfaces** (member structs, `__typename` dispatch), `@skip`/`@include` nullability
|
|
167
179
|
- **Any transport**: in-process execution, a zero-dependency HTTP client, or Faraday with your own middleware — plus a composable `Retry` with backoff and jitter
|
|
168
180
|
- **Structured errors**: a typed envelope that keeps partial data and extensions, an error hierarchy split by failure site, field-level reports with entity ids, and stale-schema detection
|
|
@@ -175,12 +187,14 @@ upgrade, and `verify` is what tells you when you've skipped it.
|
|
|
175
187
|
- **[Federation](docs/federation.md)** — supergraph vs API schema, the local router, what it refuses
|
|
176
188
|
- **[Transports](docs/transports.md)** — the execute contract, Faraday, retries and backoff
|
|
177
189
|
- **[Errors](docs/errors.md)** — the Response envelope, the error hierarchy, field-level reports
|
|
190
|
+
- **[Translating input errors](docs/i18n.md)** — stable keys and data for bad input, on either side of the wire
|
|
178
191
|
- **[Custom scalars](docs/scalars.md)** — what's registered already, codec inference, input coercion
|
|
179
192
|
- **[Cassettes](docs/cassettes.md)** — capture and replay real responses, anonymized
|
|
180
193
|
- **[Editor support](docs/editors.md)** — five lines of YAML for schema autocomplete in `.graphql` files, no JS project
|
|
181
194
|
- **[Against a real API](docs/real_world.md)** — introspecting a live endpoint, GitHub end to end
|
|
182
195
|
- **[Logging](docs/logging.md)** — point `GraphWeaver.logger` at any Logger
|
|
183
|
-
- **[Upgrading](docs/upgrading.md)** — regenerate on every bump, and what
|
|
196
|
+
- **[Upgrading](docs/upgrading.md)** — regenerate on every bump, and what each release moved
|
|
197
|
+
- **[Alternatives](docs/alternatives.md)** — graphql-client, graphlient, artemis, hand-rolled: what each is good at, and where this one loses
|
|
184
198
|
|
|
185
199
|
----
|
|
186
200
|
## Development
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Alternatives
|
|
2
|
+
|
|
3
|
+
Why another Ruby GraphQL client, what else exists, and where each one wins.
|
|
4
|
+
Read it before adopting — including the last section, which is where this gem
|
|
5
|
+
loses.
|
|
6
|
+
|
|
7
|
+
*Figures checked 2026-09-12. Every claim carries its source in an HTML comment
|
|
8
|
+
beside it; a maintainer re-checking this page should follow those, not trust the
|
|
9
|
+
prose.*
|
|
10
|
+
|
|
11
|
+
## Why another one
|
|
12
|
+
|
|
13
|
+
Ruby has **one** real GraphQL client — GitHub's `graphql-client` — and the two
|
|
14
|
+
maintained alternatives are wrappers around it.<!-- graphlient.gemspec deps: faraday ~>2.0, graphql-client; artemis.gemspec deps include graphql-client >= 0.13.0 — https://rubygems.org/api/v1/gems/graphlient.json, https://rubygems.org/api/v1/gems/artemis.json -->
|
|
15
|
+
All three share one design: the result object is built at runtime by
|
|
16
|
+
metaprogramming against an introspected schema, so a field you misspelled is a
|
|
17
|
+
`NoMethodError` on a production request rather than a red build.<!-- graphql-client lib/graphql/client/schema/object_type.rb:231-266 — method_missing raises UnimplementedFieldError/UnfetchedFieldError at runtime; a camelCase typo re-raises plain NoMethodError at :248-250 -->
|
|
18
|
+
And none of them ship anything to test with.<!-- graphql-client: no fakes/stubs/fixtures anywhere in lib/, no testing guide among its 14 guides — https://github.com/github-community-projects/graphql-client/tree/master/guides -->
|
|
19
|
+
|
|
20
|
+
GraphWeaver makes a different structural bet: **the schema is known at
|
|
21
|
+
generation time**, so result types can be real files on disk that `srb tc`
|
|
22
|
+
reads, and the same knowledge that makes them exact is what lets the gem
|
|
23
|
+
fabricate them for your tests. That second half is the part every code generator
|
|
24
|
+
in every language skips — and it is the reason precise types usually feel
|
|
25
|
+
expensive.
|
|
26
|
+
|
|
27
|
+
The honest counter: if your app doesn't use Sorbet, most of that value
|
|
28
|
+
evaporates, and [graphlient](#graphlient) is the better answer.
|
|
29
|
+
|
|
30
|
+
This idea was tried once before. `yogurt` generated Sorbet types from GraphQL
|
|
31
|
+
documents, shipped two versions 70 minutes apart in November 2020, and stopped —
|
|
32
|
+
its own README concedes it lacked named fragments and that the author "probably
|
|
33
|
+
got a lot of the decisions wrong".<!-- https://raw.githubusercontent.com/theorygeek/yogurt/master/README.md ; versions 0.1.1 16:52 and 0.2.0 18:02 on 2020-11-26 — https://rubygems.org/api/v1/versions/yogurt.json ; last real commit 29ef902 2020-11-26 -->
|
|
34
|
+
So the thesis is unproven, not proven wrong.
|
|
35
|
+
|
|
36
|
+
## The table
|
|
37
|
+
|
|
38
|
+
`graphql-ruby` isn't a column because it isn't a client — see
|
|
39
|
+
[graphql-ruby alone](#graphql-ruby-alone). "Hand-rolled" is `Net::HTTP` or
|
|
40
|
+
Faraday plus a query string and `response["data"]["..."]`, which is what most
|
|
41
|
+
vendor SDKs actually do.
|
|
42
|
+
|
|
43
|
+
| | **graph_weaver** | **graphql-client** | **graphlient** | **artemis** | **hand-rolled** |
|
|
44
|
+
|---|---|---|---|---|---|
|
|
45
|
+
| **Query lives in** | a `.graphql` file, one operation each<!-- README.md; DECISIONS.md "Directories organize queries" --> | a heredoc assigned to a Ruby constant (enforced)<!-- lib/graphql/client.rb:345-347 raises DynamicQueryError when definition.name is nil --> | a heredoc, a Ruby block DSL, or `parse`<!-- https://github.com/ashkan18/graphlient#usage ; lib/graphlient/query/serializer.rb --> | a `.graphql` file under `app/operations`<!-- https://github.com/yuki24/artemis#the-convention --> | a string in your code |
|
|
46
|
+
| **A result is** | a checked-in nested `T::Struct`<!-- README.md; lib/graph_weaver/result_struct.rb --> | an anonymous class, readers per selected field<!-- lib/graphql/client/schema/object_type.rb:10-22, 56-60 --> | the same (it returns graphql-client's `Response`)<!-- lib/graphlient/client.rb:41 --> | the same<!-- artemis lib/artemis/client.rb #execute delegates to client.query --> | a `Hash` |
|
|
47
|
+
| **A typo is caught** | at `srb tc`, before you run it | at runtime, on the request<!-- object_type.rb:243-266 --> | at runtime | at runtime | never |
|
|
48
|
+
| **Schema needed** | at codegen time: live class, introspection dump, SDL, or supergraph<!-- README.md; lib/graph_weaver/schema_loader.rb --> | at boot: dump recommended; SDL support merged but unreleased<!-- README.md:34-42; https://github.com/github-community-projects/graphql-client/pull/60 merged 2025-12-06, not in 0.26.0 --> | at runtime: introspects over HTTP lazily unless you pass `schema_path`<!-- lib/graphlient/schema.rb:16; lib/graphlient/client.rb:76 --> | a checked-in dump per service<!-- lib/artemis/railtie.rb schema_path vendor/graphql/schema/<service>.json --> | none |
|
|
49
|
+
| **Codegen** | yes — you check the Ruby in | none, all runtime metaprogramming<!-- lib/graphql/client/schema.rb:68-81 --> | none | none | none |
|
|
50
|
+
| **Typing** | Sorbet `# typed: strict`, per query<!-- README.md --> | none shipped; Tapioca PR stalled since 2024-11, and schema-wide not per-operation<!-- https://github.com/github-community-projects/graphql-client/pull/7 — open, mergeable_state blocked, last touched 2024-11-07 --> | none<!-- no .rbs/.rbi/sig in the gem --> | none | none |
|
|
51
|
+
| **Testing** | schema-driven fakes, pinning, failure simulation, cassettes<!-- docs/testing.md; lib/graph_weaver/rspec.rb; lib/graph_weaver/testing/ --> | nothing ships<!-- no stub/fake/fixture in lib/ --> | documented WebMock patterns only<!-- https://github.com/ashkan18/graphlient#testing-with-graphlient-and-rspec --> | `stub_graphql` + YAML fixtures, unvalidated against the schema<!-- lib/artemis/test_helper.rb; lib/artemis/adapters/test_adapter.rb returns fixtures verbatim --> | WebMock |
|
|
52
|
+
| **Federation** | plans and runs a supergraph in-process<!-- docs/federation.md; lib/graph_weaver/federation.rb --> | none; open crash against federated routers<!-- https://github.com/github-community-projects/graphql-client/issues/78 open since 2026-01-15 --> | none | none | none |
|
|
53
|
+
| **Errors** | typed envelope keeping partial data, hierarchy by failure site<!-- docs/errors.md; lib/graph_weaver/errors.rb --> | raw hashes; HTTP errors become a fake `errors` array<!-- lib/graphql/client/http.rb:80-85; https://github.com/github-community-projects/graphql-client/issues/67 --> | raises a real class hierarchy on every failure<!-- lib/graphlient/errors/ --> | thin hierarchy, mostly never raised<!-- lib/artemis/exceptions.rb — GraphQLError/GraphQLServerError defined, never raised --> | yours to write |
|
|
54
|
+
| **Transport** | `Net::HTTP` (pooled) or Faraday, plus `Retry`<!-- docs/transports.md; lib/graph_weaver/transport/ --> | stock adapter self-described as trivial<!-- lib/graphql/client/http.rb:17-19 "Production applications should consider implementing their own network adapter" --> | Faraday 2.x, full middleware access<!-- lib/graphlient/adapters/http/faraday_adapter.rb:36-48 --> | four adapters, no middleware layer<!-- lib/artemis/adapters.rb; https://github.com/yuki24/artemis/issues/57 open since 2019 --> | whatever you picked |
|
|
55
|
+
| **Rails** | generator, railtie, reload-on-edit, rake lifecycle<!-- lib/generators/graph_weaver/install_generator.rb; lib/graph_weaver/railtie.rb; lib/graph_weaver/tasks.rb --> | opt-in railtie, no generators; docs call the boot order "a mess"<!-- lib/graphql/client/railtie.rb:34-37 TODO; https://github.com/github-community-projects/graphql-client/blob/master/guides/rails-configuration.md --> | none | the whole pitch: generators, config, callbacks<!-- lib/artemis/railtie.rb; lib/generators/artemis/ --> | n/a |
|
|
56
|
+
| **Last release** | v0.7.0, 2026-09-12 | v0.26.0, 2025-05-29<!-- https://rubygems.org/api/v1/gems/graphql-client.json --> | v0.9.0, 2026-08-02<!-- https://rubygems.org/api/v1/gems/graphlient.json --> | v1.1.0, 2024-08-16<!-- https://rubygems.org/api/v1/gems/artemis.json --> | n/a |
|
|
57
|
+
| **Downloads** | 7.5k<!-- 7,478 — https://rubygems.org/api/v1/gems/graph_weaver.json --> | 94M<!-- 94,408,657 --> | 32M<!-- 32,287,626 --> | 430k<!-- 425,737 --> | n/a |
|
|
58
|
+
|
|
59
|
+
## graphql-client
|
|
60
|
+
|
|
61
|
+
**Where it shines.** It is GitHub's, it has a decade of production use, and 94M
|
|
62
|
+
downloads means someone has hit your problem before you. The duck-typed
|
|
63
|
+
`execute:` slot is genuinely good design — point it at a graphql-ruby schema and
|
|
64
|
+
queries run in-process with no socket.<!-- https://github.com/github-community-projects/graphql-client/blob/master/guides/local-queries.md -->
|
|
65
|
+
It ships two RuboCop cops, including `GraphQL/Overfetch`, which nobody else
|
|
66
|
+
has.<!-- lib/rubocop/cop/graphql/ in the unpacked gem -->
|
|
67
|
+
|
|
68
|
+
**Where it gaps.** Custom scalars don't deserialize when the schema came from a
|
|
69
|
+
dump — the path its own README recommends — and the suggested workaround is
|
|
70
|
+
monkey-patching `GraphQL::Schema::BUILT_IN_TYPES`; the issue has been open since
|
|
71
|
+
February 2024.<!-- https://github.com/github-community-projects/graphql-client/issues/17 --> Network
|
|
72
|
+
errors are discarded: a 403 surfaces as `KeyError: key not found: "data"`.<!-- https://github.com/github-community-projects/graphql-client/issues/67 open since 2025-04-11 -->
|
|
73
|
+
Fragments enforce Relay-style data masking, so a field another fragment fetched
|
|
74
|
+
raises even though the value is right there in the response — and users file
|
|
75
|
+
issues asking for plain reuse.<!-- object_type.rb:161-168, 261-264; https://github.com/github-community-projects/graphql-client/issues/76 -->
|
|
76
|
+
Its CI matrix stops at Ruby 3.2 and Rails 7.1.<!-- https://github.com/github-community-projects/graphql-client/blob/master/.github/workflows/ci.yml -->
|
|
77
|
+
Nine issues and seven PRs are open, several waiting on a maintainer to approve a
|
|
78
|
+
CI run.<!-- gh api search/issues, repo:github-community-projects/graphql-client, 2026-09-12 -->
|
|
79
|
+
|
|
80
|
+
**Pick it over graph_weaver when** institutional safety outweighs static types,
|
|
81
|
+
or when you need data masking as a feature rather than a constraint.
|
|
82
|
+
|
|
83
|
+
## graphlient
|
|
84
|
+
|
|
85
|
+
**Where it shines.** Quietly the healthiest Ruby client: 0.9.0 shipped
|
|
86
|
+
2026-08-02, more recently than graphql-client itself, adding DSL fragments,
|
|
87
|
+
directives and scalar registration.<!-- https://github.com/ashkan18/graphlient/blob/master/CHANGELOG.md -->
|
|
88
|
+
It fixes the failure everyone hits with its substrate — it raises a real,
|
|
89
|
+
rescuable error hierarchy instead of handing you a half-populated response.<!-- lib/graphlient/errors/ --> Faraday
|
|
90
|
+
means your existing middleware just works.
|
|
91
|
+
|
|
92
|
+
**Where it gaps.** It is a wrapper, so it inherits graphql-client's result
|
|
93
|
+
model, its fragment isolation, and its untyped everything — its own README
|
|
94
|
+
offers `to_query_string` as "the escape hatch if you want to replace the
|
|
95
|
+
graphql-client dependency entirely".<!-- https://github.com/ashkan18/graphlient#readme, 0.9.0 -->
|
|
96
|
+
Read and write timeouts default to nil, i.e. none.<!-- README config table; lib/graphlient/adapters/http/adapter.rb:33-40 -->
|
|
97
|
+
No Rails integration, no field aliasing, and 18 open issues, the most-reacted
|
|
98
|
+
dating to 2017.<!-- https://github.com/ashkan18/graphlient/issues/10 ; gh api repos/ashkan18/graphlient -->
|
|
99
|
+
|
|
100
|
+
**Pick it over graph_weaver when** you want to call an API without thinking
|
|
101
|
+
about it. For that job graph_weaver is over-engineered, and this is the right
|
|
102
|
+
answer.
|
|
103
|
+
|
|
104
|
+
## artemis
|
|
105
|
+
|
|
106
|
+
**Where it shines.** The best Rails story of the three: `rails g
|
|
107
|
+
artemis:install` writes the client, the config and the schema dump; `.graphql`
|
|
108
|
+
files map to methods by convention; `before_execute`/`after_execute` are real
|
|
109
|
+
hooks; and it has the only shipped test harness among the alternatives —
|
|
110
|
+
`stub_graphql(Artsy, :artist).to_return(:yayoi_kusama)` against YAML
|
|
111
|
+
fixtures.<!-- lib/artemis/test_helper.rb; https://github.com/yuki24/artemis#testing -->
|
|
112
|
+
It also batches, via `Client.multiplex`.<!-- lib/artemis/client.rb .multiplex/MultiplexQueue -->
|
|
113
|
+
|
|
114
|
+
**Where it gaps.** Fixtures are returned verbatim — nothing checks them against
|
|
115
|
+
the schema or the query's selection set, so a fixture can drift from reality and
|
|
116
|
+
stay green.<!-- lib/artemis/adapters/test_adapter.rb#execute -->
|
|
117
|
+
Its reloader and production preload are both gated on *not* using Zeitwerk,
|
|
118
|
+
which every Rails 7+ app does, so the README's preloading claim no longer
|
|
119
|
+
applies.<!-- lib/artemis/railtie.rb — graphql.client.set_reloader and graphql.client.preload both gated on not_on_zeitwerk -->
|
|
120
|
+
Last release August 2024; Rails 8 support exists only on `main`, and there have
|
|
121
|
+
been no commits since December 2025.<!-- gh api repos/yuki24/artemis/compare/v1.1.0...main ; last commit 8b3d76a 2025-12-04 -->
|
|
122
|
+
|
|
123
|
+
**Pick it over graph_weaver when** you want convention-over-configuration Rails
|
|
124
|
+
ergonomics and don't need types.
|
|
125
|
+
|
|
126
|
+
## graphql-ruby alone
|
|
127
|
+
|
|
128
|
+
`graphql-ruby` is a **server** library and ships no HTTP client at all — its
|
|
129
|
+
only three runtime dependencies are `base64`, `fiber-storage` and `logger`, and
|
|
130
|
+
the only `Net::HTTP` call in the gem fetches a checksum for graphql-pro.<!-- gem spec graphql-2.6.10.gem dependencies; lib/graphql/rake_task/validate.rb:38-44 -->
|
|
131
|
+
The thing called "client" in its docs is
|
|
132
|
+
[JavaScript](https://graphql-ruby.org/javascript_client/overview).
|
|
133
|
+
|
|
134
|
+
What it does give a client author is the substrate everyone here builds on:
|
|
135
|
+
[`GraphQL.parse`](https://graphql-ruby.org/api-doc/2.6.10/GraphQL.html),
|
|
136
|
+
[`GraphQL::Schema.from_definition`](https://graphql-ruby.org/schema/sdl.html)
|
|
137
|
+
and `from_introspection`, and `GraphQL::StaticValidation::Validator` for
|
|
138
|
+
checking a document against a schema.<!-- lib/graphql.rb:49; lib/graphql/schema.rb:105,115; lib/graphql/static_validation/validator.rb:11-18 -->
|
|
139
|
+
GraphWeaver uses exactly these — it is a code generator on top of graphql-ruby,
|
|
140
|
+
not a reimplementation of it.
|
|
141
|
+
|
|
142
|
+
**Subscriptions are server-side only** across the whole ecosystem: graphql-ruby
|
|
143
|
+
delivers them over ActionCable and every documented consumer is
|
|
144
|
+
JavaScript,<!-- https://graphql-ruby.org/subscriptions/action_cable_implementation — "See client usage for: Apollo Client, Relay Modern, GraphiQL" -->
|
|
145
|
+
and no Ruby gem consumes GraphQL subscriptions over websockets.<!-- rubygems search graphql+websocket, graphql-ws, subscriptions-transport-ws all return 0 results, 2026-09-12 -->
|
|
146
|
+
Nobody in Ruby has this, GraphWeaver included.
|
|
147
|
+
|
|
148
|
+
## Hand-rolled HTTP — the real incumbent
|
|
149
|
+
|
|
150
|
+
Most Ruby code talking to a GraphQL API isn't using a client library. It POSTs a
|
|
151
|
+
string and reads a hash, and that includes the vendors' own SDKs.
|
|
152
|
+
|
|
153
|
+
Shopify is the sharpest example. `shopify_api` v10 **removed** graphql-client,
|
|
154
|
+
saying so in its breaking-changes doc — "There is no need to dump the schema to
|
|
155
|
+
a local JSON file before using it anymore" — and the migration example replaces
|
|
156
|
+
`result.data.shop.name` with `response.body["data"]["shop"]["name"]`.<!-- https://github.com/Shopify/shopify-api-ruby/blob/main/BREAKING_CHANGES_FOR_V10.md:3,115,140-159 -->
|
|
157
|
+
The current gemspec declares `httparty`, `oj` and `sorbet-runtime` and no
|
|
158
|
+
`graphql` at all.<!-- https://github.com/Shopify/shopify-api-ruby/blob/main/shopify_api.gemspec:35-46 -->
|
|
159
|
+
The gem is `# typed: strict` throughout — and the GraphQL payload is typed
|
|
160
|
+
`T.any(T::Hash[String, T.untyped], String, OpenStruct)`.<!-- https://github.com/Shopify/shopify-api-ruby/blob/main/lib/shopify_api/clients/http_response.rb#L15-L16 -->
|
|
161
|
+
A Sorbet shop, shipping a Sorbet-typed SDK, with untyped GraphQL. Braintree does
|
|
162
|
+
the same thing without the Sorbet.<!-- https://github.com/braintree/braintree_ruby/blob/master/lib/braintree/graphql_client.rb#L13-L26 -->
|
|
163
|
+
|
|
164
|
+
**Where it shines.** Zero dependencies, zero build step, nothing to learn, and
|
|
165
|
+
it never gets in your way. For three queries against a stable API this is
|
|
166
|
+
genuinely the correct engineering call.
|
|
167
|
+
|
|
168
|
+
**Where it gaps.** Nothing validates the query, nothing knows the schema
|
|
169
|
+
changed, every response is `T.untyped`, and the error handling is a 40-line
|
|
170
|
+
layer you write once per project. The cost is invisible until the schema moves.
|
|
171
|
+
|
|
172
|
+
**Pick it over graph_weaver when** you have a handful of queries and no
|
|
173
|
+
appetite for a build step.
|
|
174
|
+
|
|
175
|
+
## Where graph_weaver loses
|
|
176
|
+
|
|
177
|
+
Said plainly.
|
|
178
|
+
|
|
179
|
+
- **Sorbet is a hard dependency.** `sorbet-runtime` is a runtime
|
|
180
|
+
dependency,<!-- graph_weaver.gemspec: s.add_dependency "sorbet-runtime" --> and
|
|
181
|
+
the payoff — a typo caught before you run — needs `srb tc` in your build. No
|
|
182
|
+
RBS output. If Sorbet isn't in your stack, you're carrying the cost of
|
|
183
|
+
generated code for a fraction of the benefit.
|
|
184
|
+
- **Codegen is a build step.** You check generated Ruby in, and regenerate on
|
|
185
|
+
every gem upgrade, because any release can change what codegen emits. `rake
|
|
186
|
+
graph_weaver:verify` exists precisely because that step is easy to skip. The
|
|
187
|
+
alternatives have no build step at all.
|
|
188
|
+
- **No subscriptions.** A subscription document is refused outright:
|
|
189
|
+
`GraphWeaver::Error: unsupported operation: subscription`.<!-- lib/graph_weaver/internal/selection.rb:48 --> Nobody
|
|
190
|
+
in Ruby has this, but that doesn't make it present here.
|
|
191
|
+
- **No `@defer`, no file uploads, no persisted queries, no batching.** artemis
|
|
192
|
+
has multiplex batching;<!-- lib/artemis/client.rb .multiplex --> this gem has
|
|
193
|
+
none of the four.
|
|
194
|
+
- **One author, and it's new.** First commit July 2026, 4 stars, pre-1.0, and
|
|
195
|
+
about 30 breaking-change notes in the changelog so far.<!-- gh api repos/dpep/graph_weaver created_at 2026-07-07, stargazers_count 4; grep -c -i breaking CHANGELOG.md -->
|
|
196
|
+
Codegen is unforgiving and there is a lot of surface to get wrong.
|
|
197
|
+
`verify` mitigates drift; nothing mitigates the maintainer. "No static types"
|
|
198
|
+
is a cost many teams will rationally accept over that.
|
|
199
|
+
|
|
200
|
+
If you read that list and none of it stops you, the thing you get in exchange is
|
|
201
|
+
in [getting started](getting_started.md).
|
data/docs/cassettes.md
CHANGED
|
@@ -31,6 +31,17 @@ to refresh. A *request* with no recording raises
|
|
|
31
31
|
`GraphWeaver::Testing::MissingRecording`, naming the variables it was called
|
|
32
32
|
with and the ones recorded for that same query — what usually differs.
|
|
33
33
|
|
|
34
|
+
**One entry per request.** The request key is the query, its variables and the
|
|
35
|
+
operation name together, and a cassette holds one entry per key: re-recording a
|
|
36
|
+
request *replaces* its entry, and a request the file hasn't seen appends one. So
|
|
37
|
+
however often `GRAPHWEAVER_RECORD=1` runs, no cassette ends up with two entries
|
|
38
|
+
for the same request.
|
|
39
|
+
|
|
40
|
+
Editing a query changes the key, so the re-record writes a new entry and the old
|
|
41
|
+
one stays behind — a recording of a request nothing sends any more.
|
|
42
|
+
`cassettes:check` counts those ("1 not sent by any query module", below); the
|
|
43
|
+
way to clear them is to delete the cassette and record it afresh.
|
|
44
|
+
|
|
34
45
|
## Has a recording gone stale?
|
|
35
46
|
|
|
36
47
|
A cassette is the one artifact here recorded from *someone else's* server, and
|
|
@@ -50,7 +61,7 @@ belongs in the normal PR run beside `verify`, and exits non-zero on drift:
|
|
|
50
61
|
|
|
51
62
|
```
|
|
52
63
|
spec/cassettes/dashboard.yml: 1 stale (3 checked, 1 not sent by any query module)
|
|
53
|
-
DashboardQuery {"id"
|
|
64
|
+
DashboardQuery {"id":"b1"}
|
|
54
65
|
failed to cast response into DashboardQuery::Result::Me::Reviews::Book: Parameter 'price_cents': Can't set …price_cents to 4200 (instance of Integer) - need a String
|
|
55
66
|
```
|
|
56
67
|
|
|
@@ -82,6 +93,11 @@ preserving everything that makes the recording faithful:
|
|
|
82
93
|
| enums, booleans, `__typename` | numbers, dates |
|
|
83
94
|
| id *relationships* (same original id → same fake id) | the id values themselves |
|
|
84
95
|
|
|
96
|
+
**Every plain string goes**, not the PII-shaped ones — nothing here can tell a
|
|
97
|
+
user's name from a product's, so a recorded `"pikachu"` replays as `"name-1"`
|
|
98
|
+
and an assertion pinned to it fails. Leave it off for a public, non-sensitive
|
|
99
|
+
API, where the real values *are* the point of the cassette.
|
|
100
|
+
|
|
85
101
|
`data` is walked against the schema — which is why it needs one, to know which
|
|
86
102
|
values are enums, dates, ids. `errors` and `extensions` have none behind them,
|
|
87
103
|
so they're walked by shape instead: keys, nesting and structure survive, every
|