graph_weaver 0.0.1 → 0.2.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/.yardopts +6 -0
- data/CHANGELOG.md +279 -0
- data/Gemfile.lock +75 -17
- data/Makefile +11 -1
- data/NOTES.md +1 -1
- data/PLAN.md +85 -22
- data/README.md +86 -26
- data/docs/cassettes.md +76 -0
- data/docs/errors.md +134 -0
- data/docs/generated_modules.md +229 -0
- data/docs/getting_started.md +134 -0
- data/docs/logging.md +33 -0
- data/docs/real_world.md +53 -0
- data/docs/scalars.md +183 -0
- data/docs/testing.md +103 -0
- data/docs/transports.md +124 -0
- data/graph_weaver.gemspec +10 -1
- data/lib/graph_weaver/client.rb +200 -0
- data/lib/graph_weaver/codegen/emit.rb +286 -0
- data/lib/graph_weaver/codegen/enum_type.rb +149 -0
- data/lib/graph_weaver/codegen/nodes.rb +356 -0
- data/lib/graph_weaver/codegen/scalar_type.rb +256 -0
- data/lib/graph_weaver/codegen.rb +396 -401
- data/lib/graph_weaver/errors.rb +322 -0
- data/lib/graph_weaver/hints.rb +63 -0
- data/lib/graph_weaver/inflect.rb +19 -0
- data/lib/graph_weaver/logging.rb +41 -0
- data/lib/graph_weaver/railtie.rb +29 -0
- data/lib/graph_weaver/response.rb +55 -0
- data/lib/graph_weaver/retry.rb +97 -0
- data/lib/graph_weaver/rspec.rb +59 -0
- data/lib/graph_weaver/schema_loader.rb +208 -8
- data/lib/graph_weaver/selection.rb +68 -0
- data/lib/graph_weaver/tasks.rb +71 -0
- data/lib/graph_weaver/testing/cassette.rb +224 -0
- data/lib/graph_weaver/testing/failure.rb +109 -0
- data/lib/graph_weaver/testing/fake_client.rb +228 -0
- data/lib/graph_weaver/testing/values.rb +98 -0
- data/lib/graph_weaver/testing.rb +106 -0
- data/lib/graph_weaver/transport/faraday.rb +56 -0
- data/lib/graph_weaver/transport/http.rb +87 -0
- data/lib/graph_weaver/transport.rb +120 -0
- data/lib/graph_weaver/version.rb +1 -1
- data/lib/graph_weaver.rb +268 -4
- metadata +132 -2
- data/lib/graph_weaver/http_executor.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ef7de8e2cd115b4c757a3dac2e8c41a0e87865914e246131c1037a231f7a60af
|
|
4
|
+
data.tar.gz: fa89ee78a04a2a07b7c173c82685490ca501a2a29045ef487aae3dd6f3d915d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ae73ed74389baa1f08a42e38b7bf2b0bca2f95ccbe247ccb68fda733c9fee849aed3d17dbba424bdb13104344771d9789401710a5add195c71873edc379530b
|
|
7
|
+
data.tar.gz: 24e15e7a6213c0cd5dee16aa73e675649748b84993be11387f638aeca21c15db77a59b1fa5a85ae406af7182be0f9f6a17164b842add3627a7547ab1670616d1
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,282 @@
|
|
|
1
|
+
### v0.2.0 (2026-07-12)
|
|
2
|
+
- Cleanup pass (staff-engineer review): scalar registrations get the
|
|
3
|
+
same typo validation as enums/types; cassette replay stops recomputing
|
|
4
|
+
its key per entry; dependency-order DFS uses hash bookkeeping (big
|
|
5
|
+
bool_exp graphs); require/vocabulary residue swept; the vestigial
|
|
6
|
+
graph_weaver/testing/rspec shim removed
|
|
7
|
+
- BREAKING: "client" replaces "executor" across the whole surface.
|
|
8
|
+
Generated modules: the per-call override is an optional POSITIONAL
|
|
9
|
+
first argument — PersonQuery.execute(github, id: "1") — so variables
|
|
10
|
+
own the entire kwarg namespace and NOTHING is reserved (a $client or
|
|
11
|
+
$executor variable is fine; only Ruby keywords refuse); per-module is
|
|
12
|
+
MyQuery.client=, the baked param is client:. GraphWeaver.executor= is
|
|
13
|
+
gone — GraphWeaver.client= is the one ambient slot (auto_fake swaps
|
|
14
|
+
it per example; explicit clients are self-contained and never see it).
|
|
15
|
+
Client#executor is now Client#transport (transport: to bring your
|
|
16
|
+
own); SchemaLoader.introspect/stale? speak transport. Renames:
|
|
17
|
+
FakeExecutor => Testing::FakeClient, SequenceExecutor =>
|
|
18
|
+
Testing::Sequence, RetryExecutor => GraphWeaver::Retry,
|
|
19
|
+
Recording/ReplayExecutor => Recorder/Replayer, Cassette.use(client:)
|
|
20
|
+
- Live federation integration: two Ruby subgraphs (apollo-federation
|
|
21
|
+
gem) composed and routed by a real Apollo gateway (node harness under
|
|
22
|
+
spec/support/federation), with GraphWeaver introspecting through the
|
|
23
|
+
router and executing a query stitched across BOTH subgraphs — part of
|
|
24
|
+
make integration. Complements the existing supergraph-SDL codegen spec
|
|
25
|
+
- graphql-over-http: a non-2xx response carrying a GraphQL errors body
|
|
26
|
+
(Apollo Server/Router send request errors as 4xx JSON) flows into the
|
|
27
|
+
Response envelope so QueryError sees the structured errors; only
|
|
28
|
+
non-GraphQL bodies (proxy pages) raise ServerError
|
|
29
|
+
- Fix: input-struct serialize used bare locals (result/value) that a
|
|
30
|
+
same-named prop silently shadowed — a field named "result" dropped
|
|
31
|
+
its value onto the wrong target; generated locals now wear the
|
|
32
|
+
reserved __gw prefix (GraphQL reserves __-names, so no collision is
|
|
33
|
+
possible)
|
|
34
|
+
- Input fields and variables whose Ruby name would be a keyword
|
|
35
|
+
(nil/def/end/...), a generated method (serialize/to_h), or the
|
|
36
|
+
reserved executor kwarg now refuse at generation with a pointed
|
|
37
|
+
error instead of emitting broken code
|
|
38
|
+
- Non-JSON 200 bodies (proxy error pages) classify as ServerError, and
|
|
39
|
+
unserializable variables (NaN/Infinity) raise GraphWeaver::Error —
|
|
40
|
+
raw JSON::* errors no longer escape the umbrella
|
|
41
|
+
- Transports redact on inspect/to_s (class + url only) — Authorization
|
|
42
|
+
headers can't leak through logs or exception dumps
|
|
43
|
+
- Narrowed `... on X` selections require at least one unconditional
|
|
44
|
+
field: with every field behind @skip/@include, a matching response is
|
|
45
|
+
{} — byte-identical to a non-match — so generation refuses rather
|
|
46
|
+
than silently dropping real matches to nil
|
|
47
|
+
- Integration spec against Hasura's PokeAPI: snake_case codegen,
|
|
48
|
+
recursive bool_exp variable filtering, untyped jsonb pass-through
|
|
49
|
+
(make integration)
|
|
50
|
+
- BREAKING: ValidationError now descends from GraphWeaver::Error (was
|
|
51
|
+
ArgumentError) — one `rescue GraphWeaver::Error` catches everything
|
|
52
|
+
- Input-struct .coerce raises on unknown hash keys with a spellchecked
|
|
53
|
+
hint — a typo'd filter key no longer silently drops off the wire
|
|
54
|
+
- Client registrations (register_type/register_enum) validate at the
|
|
55
|
+
call site when the schema is already loaded; lazy clients still
|
|
56
|
+
validate at generation
|
|
57
|
+
- Unregistered custom scalars emit bare T.untyped (not
|
|
58
|
+
T.nilable(T.untyped), an srb tc error under typed: strict)
|
|
59
|
+
- Wire log lines carry [req N OperationName] tags; long queries
|
|
60
|
+
(introspection) truncate at debug
|
|
61
|
+
- Logging: GraphWeaver.logger (any stdlib-compatible Logger; Rails.logger
|
|
62
|
+
auto-wired by the railtie) — wire traffic + timings at debug,
|
|
63
|
+
introspection/cache/codegen at info, every raised error at warn
|
|
64
|
+
- Recursive input types generate — self- and mutually-referential inputs
|
|
65
|
+
(Hasura's bool_exp filter surface) emit dependency-ordered structs with
|
|
66
|
+
runtime forward declarations for cycles, so variable-driven Hasura
|
|
67
|
+
filtering works; previously raised "recursive input type"
|
|
68
|
+
- Fix: snake_case GraphQL type names (Hasura, PostGraphile) camelize
|
|
69
|
+
into valid Ruby constants — pokemon_v2_pokemon => PokemonV2Pokemon
|
|
70
|
+
(previously generated a SyntaxError); wire names (__typename dispatch,
|
|
71
|
+
registry keys) are untouched
|
|
72
|
+
- Everything raised is rescuable: unparseable queries wrap as
|
|
73
|
+
ValidationError (GraphQL::ParseError no longer leaks), and internal
|
|
74
|
+
NotImplementedError raises (recursive inputs, unsupported kinds,
|
|
75
|
+
subscriptions) became GraphWeaver::Error
|
|
76
|
+
- Transport::HTTP takes open_timeout:/read_timeout: (defaults 10s/30s);
|
|
77
|
+
timeouts surface as retriable TransportError
|
|
78
|
+
- Transport::HTTP reuses its connection (keep-alive, mutex-serialized,
|
|
79
|
+
keep_alive_timeout: for the idle window); any failure drops the socket
|
|
80
|
+
so the next call starts fresh
|
|
81
|
+
- GraphQLError#code also reads a top-level "type" (GitHub's dialect:
|
|
82
|
+
NOT_FOUND, FORBIDDEN) when extensions.code is absent
|
|
83
|
+
- Typo'd client-scoped registrations raise at generation with a
|
|
84
|
+
spellchecked hint (register_type("Pett") => "did you mean 'Pet'?")
|
|
85
|
+
instead of silently no-oping
|
|
86
|
+
- Abstract selections narrow: __typename is only required when the
|
|
87
|
+
selection varies by concrete type. Interface-level-fields-only
|
|
88
|
+
selections generate one shared struct (no dispatch); a single
|
|
89
|
+
`... on X` condition generates X's struct, always nilable — a
|
|
90
|
+
non-matching runtime type casts to nil, so narrowing doubles as
|
|
91
|
+
filtering
|
|
92
|
+
- Zero-config rspec: require "graph_weaver/rspec" now defaults
|
|
93
|
+
auto_fake on and auto-locates the schema from the committed dump
|
|
94
|
+
(config.schema= / config.auto_fake = false to override) — one line is
|
|
95
|
+
the whole test setup in a conventional app
|
|
96
|
+
- examples/: runnable demos, all directly executable — countries.rb
|
|
97
|
+
(public API, no auth, all dynamic), rick_and_morty.rb (filtered
|
|
98
|
+
search, pagination, a block-built type helper), and github/ (auth,
|
|
99
|
+
checked-in generated modules; stars the repo ⭐ then tours the
|
|
100
|
+
stargazers, their top repos, and what else they've starred); excluded
|
|
101
|
+
from the gem package
|
|
102
|
+
- Fix: requires: now load before codec probing, so inference sees
|
|
103
|
+
methods the required file provides — register_scalar("DateTime", Time,
|
|
104
|
+
requires: "time") correctly infers Time.parse in a fresh process
|
|
105
|
+
(previously the cast was silently skipped unless "time" was already
|
|
106
|
+
loaded)
|
|
107
|
+
- docs/quickstart.md renamed to docs/getting_started.md
|
|
108
|
+
- Rails Railtie: the graph_weaver:* rake tasks self-register (no
|
|
109
|
+
Rakefile edit) and depend on :environment, and generated modules load
|
|
110
|
+
at boot (after initializers) when generated_path exists; outside
|
|
111
|
+
Rails, require "graph_weaver/tasks" and call load_generated! as before
|
|
112
|
+
- BREAKING (vs 0.1.0): reset_scalars! lost its coerce: flavor —
|
|
113
|
+
GraphWeaver.auto_coerce = true is the one way to default-coerce
|
|
114
|
+
(broader: convertible built-ins AND full cast/serialize scalars,
|
|
115
|
+
resolved lazily, per-registration coerce: still wins)
|
|
116
|
+
- GraphWeaver.client= — the blessed global wiring: assign the app's
|
|
117
|
+
default client and generated modules resolve through it (per call ->
|
|
118
|
+
per module -> baked -> executor= -> client). executor= stays as the
|
|
119
|
+
low-level override, so test fakes still win
|
|
120
|
+
- Enum mappings: register_enum("Species", PetKind) (+ bulk
|
|
121
|
+
register_enums, client-scoped variants) — generated code speaks YOUR
|
|
122
|
+
T::Enum, with the wire mapping inferred by name, pinned via map:,
|
|
123
|
+
exhaustiveness-checked at generation (fails naming gaps), and
|
|
124
|
+
fallback: to absorb unknown wire values on cast (inputs stay strict);
|
|
125
|
+
translation tables emitted into the source (X_FROM_WIRE / X_TO_WIRE)
|
|
126
|
+
- Type helpers: register_type("Pet", PetHelpers) (global or
|
|
127
|
+
client-scoped, additive) — app-owned modules included into every
|
|
128
|
+
struct generated from that GraphQL type, so derived values live as
|
|
129
|
+
methods beside the honest wire data and srb tc checks them against
|
|
130
|
+
each query's selection. Or build the mixin inline with a block
|
|
131
|
+
(module_eval'd into an auto-named GraphWeaver::TypeHelpers constant —
|
|
132
|
+
quick decoration, invisible to srb tc)
|
|
133
|
+
- BREAKING (vs 0.1.0): register_scalar takes the type positionally —
|
|
134
|
+
register_scalar("Money", Money, requires: ...) — matching the new
|
|
135
|
+
registrars: the GraphQL name + your Ruby type up front, options as
|
|
136
|
+
kwargs
|
|
137
|
+
- GraphWeaver::Client — transport, schema, and scalars for one server in
|
|
138
|
+
one object: GraphWeaver.new(url_or_schema) takes a url (transport
|
|
139
|
+
built, schema introspected lazily per cache:/ttl:) or a schema source
|
|
140
|
+
(live class — also the in-process executor — or a path/SDL/dump);
|
|
141
|
+
#parse and #execute/#execute! bind the implicit schema + transport;
|
|
142
|
+
#register_scalar scopes scalar mappings to the client (overlaying the
|
|
143
|
+
global registry), so two servers can disagree about a scalar type
|
|
144
|
+
- BREAKING: GraphWeaver.connect removed — GraphWeaver.new(url) replaces
|
|
145
|
+
it (wire generated modules with GraphWeaver.executor = client.executor)
|
|
146
|
+
- BREAKING: the one-shots are now GraphWeaver.execute(url_or_schema,
|
|
147
|
+
query, **variables) / execute! — Client#execute on a throwaway client;
|
|
148
|
+
variables are plain kwargs, as on a generated module
|
|
149
|
+
- Client#load_queries! — parse every query file into modules named like
|
|
150
|
+
generation would name them (reloadable; namespace: to scope): the
|
|
151
|
+
no-build-step analog of generate! + load_generated!
|
|
152
|
+
- Introspected schema dumps record provenance (source url + timestamp):
|
|
153
|
+
a parsable SDL header comment, a "graph_weaver" sibling key in JSON —
|
|
154
|
+
read it back with SchemaLoader.provenance(path), check drift with
|
|
155
|
+
SchemaLoader.stale?(path) or rake graph_weaver:schema:verify, rewrite
|
|
156
|
+
with rake graph_weaver:schema:refresh (GRAPHWEAVER_AUTH for tokens)
|
|
157
|
+
- generate!/verify_generated!/rake auto-locate the schema dump at
|
|
158
|
+
schema_path in any supported format; SchemaLoader.locate is public
|
|
159
|
+
- Calling a result field by its camelCase wire name raises a pointed
|
|
160
|
+
NoMethodError naming the snake_case prop that does exist
|
|
161
|
+
(result.addPet => "use 'add_pet'"), and near-miss typos in either
|
|
162
|
+
casing get a spellchecked suggestion (result.addPt => "did you mean
|
|
163
|
+
'add_pet'?") — the runtime companion to srb tc's static flag
|
|
164
|
+
- BREAKING: an operation whose only variable is a required input object
|
|
165
|
+
(the Relay convention) now flattens the input's fields into execute's
|
|
166
|
+
kwargs — AdoptQuery.execute!(name:, species:) instead of
|
|
167
|
+
execute!(input: {...}); multi-variable / nullable-input operations
|
|
168
|
+
keep the input: kwarg (struct or hash)
|
|
169
|
+
- Enum kwargs accept the T::Enum or its wire value (T.any(Enum, String))
|
|
170
|
+
everywhere — variables now match input-hash fields
|
|
171
|
+
- BREAKING: HttpExecutor / FaradayExecutor are now Transport::HTTP /
|
|
172
|
+
Transport::Faraday, subclasses of the new abstract GraphWeaver::Transport
|
|
173
|
+
base, which owns the shared flow (encode, TransportError reclassify,
|
|
174
|
+
non-2xx ServerError, parse) — a custom transport just implements
|
|
175
|
+
post(body) => [status, body]. Opt-in require moved:
|
|
176
|
+
"graph_weaver/faraday_executor" -> "graph_weaver/transport/faraday"
|
|
177
|
+
- SchemaLoader.introspect cache: reuses a fresh dump in ANY supported
|
|
178
|
+
format before re-introspecting (an existing schema.graphql wins over
|
|
179
|
+
writing schema.json), and accepts :json / :graphql / :gql to pick the
|
|
180
|
+
format at GraphWeaver.schema_path's location
|
|
181
|
+
- rubydoc.info rendering: ship .yardopts (markdown markup, docs/ guides
|
|
182
|
+
as extra files) and re-indent docstring examples so code blocks and
|
|
183
|
+
backticks render; make docs previews locally
|
|
184
|
+
- GraphWeaver.connect(url, auth:, headers:, retries:): one-shot setup —
|
|
185
|
+
best transport (Faraday when the app loads it; detection is defined?,
|
|
186
|
+
never a require), bearer/verbatim auth, opt-in RetryExecutor wrapping
|
|
187
|
+
(true / options Hash; off by default), wired in as the global executor
|
|
188
|
+
- Generation workflow: GraphWeaver.generate! (queries dir -> generated
|
|
189
|
+
dir), verify_generated! (the freshness guard — raises naming stale
|
|
190
|
+
files), load_generated! (factory_bot-style explicit loading), rake
|
|
191
|
+
tasks (require "graph_weaver/tasks": graph_weaver:generate / :verify),
|
|
192
|
+
all defaulting to configurable conventional paths (queries_path /
|
|
193
|
+
generated_path / schema_path)
|
|
194
|
+
- GraphWeaver.auto_coerce = true: default input coercion for scalars
|
|
195
|
+
without an explicit coerce:, resolved lazily at generation time (no
|
|
196
|
+
reset_scalars! ordering dance) — convertible built-ins take their
|
|
197
|
+
conversion, cast/serialize pairs take parse-style coercion
|
|
198
|
+
- SchemaLoader.introspect cache: true — caches at GraphWeaver.schema_path,
|
|
199
|
+
in the format the extension picks: .json (verbatim wire artifact) or
|
|
200
|
+
.graphql/.gql (SDL — human-readable, PR-reviewable diffs);
|
|
201
|
+
the same dump rake graph_weaver:generate reads
|
|
202
|
+
- docs/transports.md: connect, the executor contract, Faraday, retries
|
|
203
|
+
- Cassette workflow: GRAPHWEAVER_RECORD=1 / config.record force
|
|
204
|
+
re-recording; config.anonymize scrubs responses as they are recorded
|
|
205
|
+
(caller sees the anonymized data too, so assertions hold on replay);
|
|
206
|
+
rake graph_weaver:cassettes:anonymize; docs/cassettes.md guide
|
|
207
|
+
- auto_coerce reaches input-object fields: raw scalar values inside
|
|
208
|
+
input hashes coerce via the registry, mutations included
|
|
209
|
+
- RetryExecutor: composable retries over any transport — tries:,
|
|
210
|
+
exponential/linear/custom backoff with jitter and max clamp,
|
|
211
|
+
retry-by-error-class (5xx yes, 4xx no by default; retry_if: override)
|
|
212
|
+
and retry-by-GraphQL-code (retry_codes: ["THROTTLED"])
|
|
213
|
+
|
|
214
|
+
### v0.1.0 (2026-07-11)
|
|
215
|
+
- Structured errors: execute returns a typed Response envelope (#data/#data!,
|
|
216
|
+
#errors, #errors?, #extensions) instead of raising on GraphQL errors, so
|
|
217
|
+
partial data and top-level extensions (cost/throttle) survive. Error classes
|
|
218
|
+
under GraphWeaver::Error — TransportError (network), ServerError (non-2xx
|
|
219
|
+
HTTP, #status/#body), QueryError (#errors/#data/#extensions/#codes),
|
|
220
|
+
ValidationError (build-time) — plus a GraphQLError value object with #code.
|
|
221
|
+
Transport-error classification is an extensible Set (GraphWeaver.transport_errors
|
|
222
|
+
/ register_transport_error): each transport seeds its own network exceptions
|
|
223
|
+
and apps can add more (e.g. a connection-pool timeout).
|
|
224
|
+
The envelope is a single generic GraphWeaver::Response[Result] (no per-query
|
|
225
|
+
wrapper class). execute! is the shortcut for execute(...).data! — the typed
|
|
226
|
+
result or a raised QueryError — on both generated modules and the one-shot
|
|
227
|
+
GraphWeaver.execute!/execute.
|
|
228
|
+
BREAKING: module #execute returns Response; use #execute! (or #data!) for
|
|
229
|
+
the old raise-or-result behavior. GraphWeaver.execute now returns the
|
|
230
|
+
envelope too; GraphWeaver.execute! returns the result.
|
|
231
|
+
- GraphWeaver.register_scalar: custom scalar deserialization into rich Ruby
|
|
232
|
+
objects. cast/serialize inferred from a class type via paired codecs
|
|
233
|
+
(.parse/#to_s or .load/.dump), or given as a Symbol/Proc (:itself opts out);
|
|
234
|
+
requires: emits (validated, and require-checked when type: is a class)
|
|
235
|
+
requires into generated source — the built-in Date scalar carries
|
|
236
|
+
require "date" so Date-using queries are self-contained; coerce: true lets a
|
|
237
|
+
variable accept the value or its raw input (coerce: :to_f for a built-in
|
|
238
|
+
conversion), casting/converting the latter — reset_scalars!(coerce: true)
|
|
239
|
+
reloads the built-ins coercible; built-in scalars pre-registered in one
|
|
240
|
+
overridable registry (reset_scalars!/clear_scalars!)
|
|
241
|
+
- FaradayExecutor: url, Faraday connection, or middleware block
|
|
242
|
+
- GraphWeaver.executor default transport; per-module executor= override
|
|
243
|
+
- GraphWeaver.parse and GraphWeaver.execute (dynamic queries)
|
|
244
|
+
- Codegen.generate shorthand; executor: takes a constant; module_name
|
|
245
|
+
derived from operation or file name
|
|
246
|
+
- Error ergonomics: schema_stale? (validation-shaped rejections hint at
|
|
247
|
+
regeneration), errors_at(path) + each_error/errors_by_field filtering,
|
|
248
|
+
#report (field-keyed rollup with entity ids resolved from partial
|
|
249
|
+
data), #to_h across the hierarchy (JSON-ready machine output), and
|
|
250
|
+
GraphWeaver::TypeError wrapping cast failures with the failing struct
|
|
251
|
+
- SchemaLoader: introspect(executor, cache:, ttl:) fetches schemas from
|
|
252
|
+
live endpoints with file caching; load accepts introspection JSON /
|
|
253
|
+
SDL content / Hashes as well as paths (cache round-trips)
|
|
254
|
+
- GraphWeaver::Testing (require "graph_weaver/testing", or
|
|
255
|
+
"graph_weaver/rspec" for the rspec integration): FakeExecutor
|
|
256
|
+
fabricates schema-correct castable responses (mode: :faker semantic
|
|
257
|
+
values / :literal; overrides by GraphQL name; seeded; list_size /
|
|
258
|
+
null_chance), failure simulation (Failure.transport/server/graphql/
|
|
259
|
+
throttled/stale_schema, SequenceExecutor for retries, fail_at: with
|
|
260
|
+
spec-correct null propagation, corrupt: for derived type mismatches),
|
|
261
|
+
cassette record/replay above the transport, and Cassette#anonymize!
|
|
262
|
+
(shape-preserving, consistent id mapping). rspec: seed follows
|
|
263
|
+
--seed; auto_fake installs a fake executor per example
|
|
264
|
+
- one-off integration specs against live GitHub + Countries APIs
|
|
265
|
+
(make integration)
|
|
266
|
+
- Input objects: INPUT_OBJECT variables generate module-level T::Structs
|
|
267
|
+
with serialize (aliased to_h) producing the wire hash; execute kwargs
|
|
268
|
+
also accept plain hashes, normalized + type-checked via the generated
|
|
269
|
+
.coerce (underscored Symbol/String keys, enums as instances or wire
|
|
270
|
+
values, nested inputs as hashes)
|
|
271
|
+
- fields under @skip/@include generate nilable regardless of schema
|
|
272
|
+
nullability; FakeExecutor honors first/last/limit when sizing lists
|
|
273
|
+
- eval hardening for parse: module names must be constant names, and
|
|
274
|
+
QUERY heredocs can't be terminated early by block strings
|
|
275
|
+
- GraphWeaver::Selection: one shared query-walk (codegen, FakeExecutor,
|
|
276
|
+
anonymizer); codegen split into scalar_type / nodes / emit
|
|
277
|
+
- docs/: generated_modules, real_world, scalars, errors, testing;
|
|
278
|
+
README slimmed to pitch + quickstart
|
|
279
|
+
|
|
1
280
|
### v0.0.1 (2026-07-07)
|
|
2
281
|
- voila: typed codegen (T::Structs, T::Enums, typed variable kwargs)
|
|
3
282
|
- queries + mutations; fragments, unions, interfaces, enums, custom scalars
|
data/Gemfile.lock
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
graph_weaver (0.0
|
|
4
|
+
graph_weaver (0.2.0)
|
|
5
5
|
graphql (>= 2)
|
|
6
6
|
sorbet-runtime
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
+
apollo-federation (3.10.3)
|
|
12
|
+
google-protobuf (< 5)
|
|
13
|
+
graphql
|
|
11
14
|
base64 (0.3.0)
|
|
12
15
|
benchmark (0.5.0)
|
|
16
|
+
bigdecimal (4.1.2)
|
|
17
|
+
concurrent-ruby (1.3.7)
|
|
13
18
|
debug (1.11.1)
|
|
14
19
|
irb (~> 1.10)
|
|
15
20
|
reline (>= 0.3.8)
|
|
@@ -17,24 +22,50 @@ GEM
|
|
|
17
22
|
docile (1.4.1)
|
|
18
23
|
erb (6.0.4)
|
|
19
24
|
erubi (1.13.1)
|
|
25
|
+
faker (3.8.0)
|
|
26
|
+
i18n (>= 1.8.11, < 2)
|
|
27
|
+
faraday (2.14.3)
|
|
28
|
+
faraday-net_http (>= 2.0, < 3.5)
|
|
29
|
+
json
|
|
30
|
+
logger
|
|
31
|
+
faraday-net_http (3.4.4)
|
|
32
|
+
net-http (~> 0.5)
|
|
20
33
|
fiber-storage (1.0.1)
|
|
34
|
+
google-protobuf (4.35.1-aarch64-linux-gnu)
|
|
35
|
+
bigdecimal
|
|
36
|
+
rake (~> 13.3)
|
|
37
|
+
google-protobuf (4.35.1-arm64-darwin)
|
|
38
|
+
bigdecimal
|
|
39
|
+
rake (~> 13.3)
|
|
40
|
+
google-protobuf (4.35.1-x86_64-darwin)
|
|
41
|
+
bigdecimal
|
|
42
|
+
rake (~> 13.3)
|
|
43
|
+
google-protobuf (4.35.1-x86_64-linux-gnu)
|
|
44
|
+
bigdecimal
|
|
45
|
+
rake (~> 13.3)
|
|
21
46
|
graphql (2.6.5)
|
|
22
47
|
base64
|
|
23
48
|
fiber-storage
|
|
24
49
|
logger
|
|
50
|
+
i18n (1.15.2)
|
|
51
|
+
concurrent-ruby (~> 1.0)
|
|
25
52
|
io-console (0.8.2)
|
|
26
53
|
irb (1.18.0)
|
|
27
54
|
pp (>= 0.6.0)
|
|
28
55
|
prism (>= 1.3.0)
|
|
29
56
|
rdoc (>= 4.0.0)
|
|
30
57
|
reline (>= 0.4.2)
|
|
58
|
+
json (2.20.0)
|
|
31
59
|
logger (1.7.0)
|
|
60
|
+
net-http (0.9.1)
|
|
61
|
+
uri (>= 0.11.1)
|
|
32
62
|
netrc (0.11.0)
|
|
33
63
|
parallel (2.1.0)
|
|
34
64
|
pp (0.6.4)
|
|
35
65
|
prettyprint
|
|
36
66
|
prettyprint (0.2.0)
|
|
37
67
|
prism (1.9.0)
|
|
68
|
+
rake (13.4.2)
|
|
38
69
|
rbi (0.3.14)
|
|
39
70
|
prism (~> 1.0)
|
|
40
71
|
rbs (>= 4.0.1)
|
|
@@ -47,6 +78,7 @@ GEM
|
|
|
47
78
|
prism (>= 1.6.0)
|
|
48
79
|
rbs (>= 4.0.0)
|
|
49
80
|
tsort
|
|
81
|
+
redcarpet (3.6.1)
|
|
50
82
|
reline (0.6.3)
|
|
51
83
|
io-console (~> 0.5)
|
|
52
84
|
require-hooks (0.4.0)
|
|
@@ -74,15 +106,15 @@ GEM
|
|
|
74
106
|
simplecov_json_formatter (~> 0.1)
|
|
75
107
|
simplecov-html (0.13.2)
|
|
76
108
|
simplecov_json_formatter (0.1.4)
|
|
77
|
-
sorbet (0.6.
|
|
78
|
-
sorbet-static (= 0.6.
|
|
79
|
-
sorbet-runtime (0.6.
|
|
80
|
-
sorbet-static (0.6.
|
|
81
|
-
sorbet-static (0.6.
|
|
82
|
-
sorbet-static (0.6.
|
|
83
|
-
sorbet-static-and-runtime (0.6.
|
|
84
|
-
sorbet (= 0.6.
|
|
85
|
-
sorbet-runtime (= 0.6.
|
|
109
|
+
sorbet (0.6.13327)
|
|
110
|
+
sorbet-static (= 0.6.13327)
|
|
111
|
+
sorbet-runtime (0.6.13327)
|
|
112
|
+
sorbet-static (0.6.13327-aarch64-linux)
|
|
113
|
+
sorbet-static (0.6.13327-universal-darwin)
|
|
114
|
+
sorbet-static (0.6.13327-x86_64-linux)
|
|
115
|
+
sorbet-static-and-runtime (0.6.13327)
|
|
116
|
+
sorbet (= 0.6.13327)
|
|
117
|
+
sorbet-runtime (= 0.6.13327)
|
|
86
118
|
spoom (1.8.3)
|
|
87
119
|
erubi (>= 1.10.0)
|
|
88
120
|
prism (>= 0.28.0)
|
|
@@ -105,7 +137,9 @@ GEM
|
|
|
105
137
|
tsort
|
|
106
138
|
thor (1.5.0)
|
|
107
139
|
tsort (0.2.0)
|
|
140
|
+
uri (1.1.1)
|
|
108
141
|
webrick (1.9.2)
|
|
142
|
+
yard (0.9.44)
|
|
109
143
|
|
|
110
144
|
PLATFORMS
|
|
111
145
|
aarch64-linux
|
|
@@ -115,36 +149,58 @@ PLATFORMS
|
|
|
115
149
|
x86_64-linux
|
|
116
150
|
|
|
117
151
|
DEPENDENCIES
|
|
152
|
+
apollo-federation
|
|
153
|
+
bigdecimal
|
|
118
154
|
debug
|
|
155
|
+
faker
|
|
156
|
+
faraday
|
|
119
157
|
graph_weaver!
|
|
158
|
+
rake
|
|
159
|
+
redcarpet
|
|
120
160
|
rspec
|
|
121
161
|
simplecov
|
|
122
162
|
sorbet
|
|
123
163
|
tapioca
|
|
124
164
|
webrick
|
|
165
|
+
yard
|
|
125
166
|
|
|
126
167
|
CHECKSUMS
|
|
168
|
+
apollo-federation (3.10.3) sha256=61e6d8e1fd695ee45bab5d84f86f586d99c296ad77a86c6f1e7b964ac17be3d7
|
|
127
169
|
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
128
170
|
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
|
|
171
|
+
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
|
|
172
|
+
concurrent-ruby (1.3.7) sha256=4412caec3a5ea2e5fdc52076724c071a81f2c0593d83b2ac8cbb8ca63b3151b0
|
|
129
173
|
debug (1.11.1) sha256=2e0b0ac6119f2207a6f8ac7d4a73ca8eb4e440f64da0a3136c30343146e952b6
|
|
130
174
|
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
131
175
|
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
|
|
132
176
|
erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
|
|
133
177
|
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
|
|
178
|
+
faker (3.8.0) sha256=c147b308df73a90f27a4fc84f18d4c22ef0ad9c2a64b2b61c86fd0ca71753efc
|
|
179
|
+
faraday (2.14.3) sha256=1882247e6766615c8220b4392bf1d27f6ebb63d8e28267587cef1fb0bf37f278
|
|
180
|
+
faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588
|
|
134
181
|
fiber-storage (1.0.1) sha256=f48e5b6d8b0be96dac486332b55cee82240057065dc761c1ea692b2e719240e1
|
|
135
|
-
|
|
182
|
+
google-protobuf (4.35.1-aarch64-linux-gnu) sha256=50ca44d0eeff3f8475e630a1accdd974256f3510694d574e2c9d6119ea8bc9e1
|
|
183
|
+
google-protobuf (4.35.1-arm64-darwin) sha256=d9c957df04fa89c749fa9a72a7b383eb4296efc9b2303dc6fd6fbe39c698ad6b
|
|
184
|
+
google-protobuf (4.35.1-x86_64-darwin) sha256=66b62b4df00931018a692806df66393efa960d6d2b7da69735187249f950d3ee
|
|
185
|
+
google-protobuf (4.35.1-x86_64-linux-gnu) sha256=c786439087512a3fbd199e9897d265b855f951d4027e218ea55e858d45969edd
|
|
186
|
+
graph_weaver (0.2.0)
|
|
136
187
|
graphql (2.6.5) sha256=1051825bfb4aedb4293cdbcd9726c3150e69e3512556609a32c2ca19d4be3d00
|
|
188
|
+
i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
|
|
137
189
|
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
|
138
190
|
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
|
|
191
|
+
json (2.20.0) sha256=9362bc6e55a952b056abf9167cf053358181c904cb70cd6eee0808ea830fc32b
|
|
139
192
|
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
193
|
+
net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
|
|
140
194
|
netrc (0.11.0) sha256=de1ce33da8c99ab1d97871726cba75151113f117146becbe45aa85cb3dabee3f
|
|
141
195
|
parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
|
|
142
196
|
pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570
|
|
143
197
|
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
|
|
144
198
|
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
199
|
+
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
145
200
|
rbi (0.3.14) sha256=932ed904adbb5ca645c285c8b36da1b7ff6321ec1870d187d6325248cb637658
|
|
146
201
|
rbs (4.0.3) sha256=5a7bf70e2628549d9a1f44eae447b2cfe55968a9c60cfff52693a4bdcc020e14
|
|
147
202
|
rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469
|
|
203
|
+
redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
|
|
148
204
|
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
|
149
205
|
require-hooks (0.4.0) sha256=005f4c6435b4edae73e358cdbaba48370a4121f9ce893d5d2a3c66fce855677d
|
|
150
206
|
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
|
@@ -160,17 +216,19 @@ CHECKSUMS
|
|
|
160
216
|
simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
|
|
161
217
|
simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
|
|
162
218
|
simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
|
|
163
|
-
sorbet (0.6.
|
|
164
|
-
sorbet-runtime (0.6.
|
|
165
|
-
sorbet-static (0.6.
|
|
166
|
-
sorbet-static (0.6.
|
|
167
|
-
sorbet-static (0.6.
|
|
168
|
-
sorbet-static-and-runtime (0.6.
|
|
219
|
+
sorbet (0.6.13327) sha256=4b56cb04542661c6bf5075f1ca38ca00506045325fd689f460aee8466dd3d7a3
|
|
220
|
+
sorbet-runtime (0.6.13327) sha256=86f32b2df6d266bf5c0e0eb8ea81d0376ba36eece4c8610938174e403222948d
|
|
221
|
+
sorbet-static (0.6.13327-aarch64-linux) sha256=da8a575785d34641095631c50fe1d1f645ee8782c8e2aa5a0ecfd12d1fa22a8a
|
|
222
|
+
sorbet-static (0.6.13327-universal-darwin) sha256=a2a8c6b624aada3f6ef68852398de756248f71b3ea691119873515847010dcda
|
|
223
|
+
sorbet-static (0.6.13327-x86_64-linux) sha256=f3a915bab4af1979145c793a08d3b1db207526d5e2f332513da10a95e70f9028
|
|
224
|
+
sorbet-static-and-runtime (0.6.13327) sha256=7b039be023b6a7d2d17b1f751191f32307d9ace4170b6a192bb0e005d222e00e
|
|
169
225
|
spoom (1.8.3) sha256=32871fa189bbfa49cf557a50f819f23cc9a6ceefd0346caa7a6adc193becd5dd
|
|
170
226
|
tapioca (0.19.2) sha256=938731b07811aee8d23871b1aee8861d464fbaf2cfffbf79a62b0c869a5120ec
|
|
171
227
|
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
|
|
172
228
|
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
|
229
|
+
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
|
|
173
230
|
webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131
|
|
231
|
+
yard (0.9.44) sha256=eb087e9b631ccd887b049f303d489963945452d5e2a7eb49a5a74a7cf6887f28
|
|
174
232
|
|
|
175
233
|
BUNDLED WITH
|
|
176
234
|
4.0.10
|
data/Makefile
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
.PHONY: check generate test tc
|
|
1
|
+
.PHONY: check generate test tc integration docs
|
|
2
2
|
|
|
3
3
|
# full verify loop: regenerate, test, typecheck
|
|
4
4
|
check: generate test tc
|
|
5
5
|
|
|
6
|
+
# manual/one-off checks against real GraphQL APIs (network; GitHub needs
|
|
7
|
+
# `gh auth login` or GITHUB_TOKEN; federation boots an Apollo gateway — node)
|
|
8
|
+
integration:
|
|
9
|
+
INTEGRATION=1 bundle exec rspec spec/integration
|
|
10
|
+
|
|
6
11
|
generate:
|
|
7
12
|
bundle exec ruby bin/generate
|
|
8
13
|
|
|
@@ -11,3 +16,8 @@ test:
|
|
|
11
16
|
|
|
12
17
|
tc:
|
|
13
18
|
bundle exec srb tc
|
|
19
|
+
|
|
20
|
+
# preview what rubydoc.info will render
|
|
21
|
+
docs:
|
|
22
|
+
bundle exec yard doc
|
|
23
|
+
open doc/index.html
|