graph_weaver 0.7.4 → 0.7.6
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 +2 -2
- data/README.md +1 -0
- data/docs/errors.md +12 -5
- data/docs/generated_modules.md +134 -17
- data/docs/getting_started.md +182 -20
- data/docs/logging.md +79 -35
- data/docs/migrating.md +126 -0
- data/docs/scalars.md +50 -6
- data/docs/testing.md +78 -14
- data/docs/upgrading.md +44 -2
- data/examples/README.md +4 -2
- data/examples/github/generate.rb +22 -8
- data/examples/github/generated/star_mutation.rb +2 -2
- data/examples/github/generated/stargazers_query.rb +2 -2
- data/examples/github/generated/starred_query.rb +2 -2
- data/examples/github/run.rb +1 -0
- data/examples/github/setup.rb +16 -8
- data/graph_weaver.gemspec +15 -6
- data/lib/generators/graph_weaver/install_generator.rb +49 -2
- data/lib/graph_weaver/client.rb +0 -23
- data/lib/graph_weaver/codegen/aliases.rb +36 -3
- data/lib/graph_weaver/codegen/emit.rb +20 -15
- data/lib/graph_weaver/codegen/enum_type.rb +52 -11
- data/lib/graph_weaver/codegen/nodes.rb +75 -32
- data/lib/graph_weaver/codegen.rb +260 -100
- data/lib/graph_weaver/coerce.rb +1 -1
- data/lib/graph_weaver/federation.rb +1 -6
- data/lib/graph_weaver/graph.rb +55 -5
- data/lib/graph_weaver/hints.rb +20 -5
- data/lib/graph_weaver/in_process.rb +2 -4
- data/lib/graph_weaver/input_struct.rb +50 -10
- data/lib/graph_weaver/internal/overrides.rb +126 -14
- data/lib/graph_weaver/internal/subgraphs.rb +1 -10
- data/lib/graph_weaver/internal/test_clients.rb +29 -7
- data/lib/graph_weaver/internal/unused.rb +62 -18
- data/lib/graph_weaver/internal/values.rb +24 -7
- data/lib/graph_weaver/internal.rb +23 -6
- data/lib/graph_weaver/log_subscriber.rb +27 -17
- data/lib/graph_weaver/logging.rb +115 -82
- data/lib/graph_weaver/parsing.rb +32 -3
- data/lib/graph_weaver/query_module.rb +67 -12
- data/lib/graph_weaver/railtie.rb +7 -2
- data/lib/graph_weaver/rspec.rb +41 -18
- data/lib/graph_weaver/schema_diff.rb +24 -5
- data/lib/graph_weaver/schema_loader.rb +29 -17
- data/lib/graph_weaver/tasks.rb +98 -16
- data/lib/graph_weaver/testing/fake_client.rb +28 -31
- data/lib/graph_weaver/testing/router.rb +26 -25
- data/lib/graph_weaver/testing.rb +27 -8
- data/lib/graph_weaver/transport.rb +1 -1
- data/lib/graph_weaver/version.rb +1 -1
- data/lib/graph_weaver.rb +72 -42
- metadata +3 -2
|
@@ -113,11 +113,11 @@ class GraphWeaver::Codegen
|
|
|
113
113
|
|
|
114
114
|
def cast(expr, depth)
|
|
115
115
|
var = "v#{depth}"
|
|
116
|
-
element =
|
|
117
|
-
@of.identity?
|
|
118
|
-
|
|
119
|
-
"#{var}&.then { |v#{depth + 1}| #{@of.cast("v#{depth + 1}", depth + 2)} }"
|
|
120
|
-
|
|
116
|
+
element =
|
|
117
|
+
if @of.identity? then var
|
|
118
|
+
elsif @of.non_null? then @of.cast(var, depth + 1)
|
|
119
|
+
else "#{var}&.then { |v#{depth + 1}| #{@of.cast("v#{depth + 1}", depth + 2)} }"
|
|
120
|
+
end
|
|
121
121
|
|
|
122
122
|
"#{expr}.map { |#{var}| #{element} }"
|
|
123
123
|
end
|
|
@@ -127,11 +127,11 @@ class GraphWeaver::Codegen
|
|
|
127
127
|
|
|
128
128
|
def serialize(expr, depth)
|
|
129
129
|
var = "v#{depth}"
|
|
130
|
-
element =
|
|
131
|
-
@of.serialize_identity?
|
|
132
|
-
|
|
133
|
-
"#{var}&.then { |v#{depth + 1}| #{@of.serialize("v#{depth + 1}", depth + 2)} }"
|
|
134
|
-
|
|
130
|
+
element =
|
|
131
|
+
if @of.serialize_identity? then var
|
|
132
|
+
elsif @of.non_null? then @of.serialize(var, depth + 1)
|
|
133
|
+
else "#{var}&.then { |v#{depth + 1}| #{@of.serialize("v#{depth + 1}", depth + 2)} }"
|
|
134
|
+
end
|
|
135
135
|
|
|
136
136
|
"#{expr}.map { |#{var}| #{element} }"
|
|
137
137
|
end
|
|
@@ -153,11 +153,11 @@ class GraphWeaver::Codegen
|
|
|
153
153
|
|
|
154
154
|
def hash_coerce(expr, depth)
|
|
155
155
|
var = "v#{depth}"
|
|
156
|
-
inner =
|
|
157
|
-
@of.hash_coerce_identity?
|
|
158
|
-
|
|
159
|
-
"#{var}&.then { |v#{depth + 1}| #{@of.hash_coerce("v#{depth + 1}", depth + 2)} }"
|
|
160
|
-
|
|
156
|
+
inner =
|
|
157
|
+
if @of.hash_coerce_identity? then var
|
|
158
|
+
elsif @of.non_null? then @of.hash_coerce(var, depth + 1)
|
|
159
|
+
else "#{var}&.then { |v#{depth + 1}| #{@of.hash_coerce("v#{depth + 1}", depth + 2)} }"
|
|
160
|
+
end
|
|
161
161
|
return "#{expr}.map { |#{var}| #{inner} }" if hash_coerce_identity?
|
|
162
162
|
|
|
163
163
|
# the index is a path segment — `where._and.0._not.species` needs the 0
|
|
@@ -207,10 +207,13 @@ class GraphWeaver::Codegen
|
|
|
207
207
|
class EnumNode < Node
|
|
208
208
|
attr_reader :class_name, :values, :aliases
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
# fallback: the name of the member unknown wire values cast to (register_enum
|
|
211
|
+
# fallback: true), or nil for a strict enum
|
|
212
|
+
def initialize(class_name, values, aliases = {}, fallback: nil)
|
|
211
213
|
@class_name = class_name
|
|
212
214
|
@values = values
|
|
213
215
|
@aliases = aliases
|
|
216
|
+
@fallback = fallback
|
|
214
217
|
end
|
|
215
218
|
|
|
216
219
|
def bare_type = class_name
|
|
@@ -220,10 +223,19 @@ class GraphWeaver::Codegen
|
|
|
220
223
|
def aliased? = !@aliases.empty?
|
|
221
224
|
def alias_const = "#{GraphWeaver::Inflect.underscore(class_name).upcase}_ALIASES"
|
|
222
225
|
|
|
226
|
+
# register_enum fallback: true — the extra member every value the schema
|
|
227
|
+
# doesn't declare casts to, and the one member a variable can't send
|
|
228
|
+
attr_reader :fallback
|
|
229
|
+
def fallback? = !@fallback.nil?
|
|
230
|
+
def fallback_const = "#{class_name}::#{@fallback}"
|
|
231
|
+
|
|
223
232
|
def cast(expr, _depth)
|
|
224
|
-
"GraphWeaver::Hints.enum(#{class_name}, #{expr}#{
|
|
233
|
+
"GraphWeaver::Hints.enum(#{class_name}, #{expr}#{runtime_args})"
|
|
225
234
|
end
|
|
226
235
|
|
|
236
|
+
# Other serializes to ENUM_FALLBACK_WIRE, which casts back to Other — so a
|
|
237
|
+
# result carrying it still round-trips through #as_json, even though no
|
|
238
|
+
# server would accept that spelling.
|
|
227
239
|
def serialize(expr, _depth)
|
|
228
240
|
"#{expr}.serialize"
|
|
229
241
|
end
|
|
@@ -235,7 +247,11 @@ class GraphWeaver::Codegen
|
|
|
235
247
|
def coerce? = true
|
|
236
248
|
|
|
237
249
|
def coerce(expr)
|
|
238
|
-
"GraphWeaver::InputStruct.enum(#{class_name}, #{expr}#{
|
|
250
|
+
"GraphWeaver::InputStruct.enum(#{class_name}, #{expr}#{runtime_args})"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def runtime_args
|
|
254
|
+
"#{", #{alias_const}" if aliased?}#{", fallback: #{fallback_const}" if fallback?}"
|
|
239
255
|
end
|
|
240
256
|
|
|
241
257
|
def input_type = "T.any(#{class_name}, String)"
|
|
@@ -277,7 +293,9 @@ class GraphWeaver::Codegen
|
|
|
277
293
|
# #as_json. Without a fallback the table is total and a miss is a real
|
|
278
294
|
# mistake, so it still raises.
|
|
279
295
|
def serialize(expr, _depth)
|
|
280
|
-
|
|
296
|
+
unless @fallback
|
|
297
|
+
return "GraphWeaver::InputStruct.enum_wire(#{@graphql_name.inspect}, #{const_prefix}_TO_WIRE, #{expr})"
|
|
298
|
+
end
|
|
281
299
|
|
|
282
300
|
"#{const_prefix}_TO_WIRE.fetch(#{expr}) { |member| member.serialize }"
|
|
283
301
|
end
|
|
@@ -358,20 +376,22 @@ class GraphWeaver::Codegen
|
|
|
358
376
|
def nested = self
|
|
359
377
|
end
|
|
360
378
|
|
|
361
|
-
# A reference to a
|
|
362
|
-
#
|
|
363
|
-
# <Name
|
|
364
|
-
#
|
|
365
|
-
#
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
379
|
+
# A reference to a type hoisted into the shared types module (a named shared
|
|
380
|
+
# fragment spread as a whole field): the query casts through <Name>.from_h,
|
|
381
|
+
# where <Name> is the alias the query module gives GraphQLTypes::<Name>. The
|
|
382
|
+
# type lives once in the shared module, so the same fragment across queries
|
|
383
|
+
# is one Ruby type — nested is nil, nothing is emitted here.
|
|
384
|
+
class HoistedRefNode < Node
|
|
385
|
+
# graphql_type is carried for refusals alone — an alias path that tries to
|
|
386
|
+
# read into the hoisted struct says which type to register itself on
|
|
387
|
+
attr_reader :class_name, :graphql_type
|
|
388
|
+
|
|
389
|
+
def initialize(class_name, graphql_type = nil)
|
|
371
390
|
@class_name = class_name
|
|
391
|
+
@graphql_type = graphql_type
|
|
372
392
|
end
|
|
373
393
|
|
|
374
|
-
def bare_type =
|
|
394
|
+
def bare_type = class_name
|
|
375
395
|
|
|
376
396
|
def cast(expr, _depth)
|
|
377
397
|
"#{class_name}.from_h(#{expr})"
|
|
@@ -382,6 +402,29 @@ class GraphWeaver::Codegen
|
|
|
382
402
|
end
|
|
383
403
|
end
|
|
384
404
|
|
|
405
|
+
# The same, on an abstract type: the hoisted name is a dispatch module, so
|
|
406
|
+
# the Ruby type is its member union rather than the module itself.
|
|
407
|
+
class UnionRefNode < HoistedRefNode
|
|
408
|
+
def bare_type = "#{class_name}::Type"
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# An abstract fragment narrowing to one member hoists to that member's
|
|
412
|
+
# struct, and narrowing filters — so the match is tested at the reference,
|
|
413
|
+
# the way NarrowedNode does it, rather than inside a T::Struct.from_h that
|
|
414
|
+
# has no way to answer nil. See NarrowedNode for what `typename` being
|
|
415
|
+
# absent means.
|
|
416
|
+
class NarrowedRefNode < HoistedRefNode
|
|
417
|
+
def initialize(class_name, graphql_type = nil, typename: nil)
|
|
418
|
+
super(class_name, graphql_type)
|
|
419
|
+
@typename = typename
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def cast(expr, depth)
|
|
423
|
+
match = @typename ? "#{expr}[\"__typename\"] == #{@typename.inspect}" : "!#{expr}.empty?"
|
|
424
|
+
"(#{match} ? #{super} : nil)"
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
385
428
|
# An input-object variable: emitted as a module-level T::Struct whose
|
|
386
429
|
# serialize produces the wire hash. Inputs never cast FROM the wire.
|
|
387
430
|
# Joins the coerce protocol so execute kwargs accept plain hashes,
|
|
@@ -454,6 +497,6 @@ class GraphWeaver::Codegen
|
|
|
454
497
|
# The IR is codegen's own vocabulary — every node type is reachable only
|
|
455
498
|
# from inside the walk.
|
|
456
499
|
private_constant :Node, :Scalar, :NonNull, :List, :ObjectNode, :EnumNode,
|
|
457
|
-
:MappedEnum, :NarrowedNode, :UnionNode, :
|
|
458
|
-
:RepresentationNode
|
|
500
|
+
:MappedEnum, :NarrowedNode, :UnionNode, :HoistedRefNode, :UnionRefNode,
|
|
501
|
+
:NarrowedRefNode, :InputNode, :RepresentationNode
|
|
459
502
|
end
|