graph_weaver 0.1.0 → 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 +213 -0
- data/Gemfile.lock +32 -2
- data/Makefile +7 -2
- data/NOTES.md +1 -1
- data/PLAN.md +34 -1
- data/README.md +56 -41
- data/docs/cassettes.md +76 -0
- data/docs/errors.md +37 -9
- data/docs/generated_modules.md +139 -29
- data/docs/getting_started.md +134 -0
- data/docs/logging.md +33 -0
- data/docs/real_world.md +27 -20
- data/docs/scalars.md +122 -8
- data/docs/testing.md +44 -34
- data/docs/transports.md +124 -0
- data/graph_weaver.gemspec +7 -1
- data/lib/graph_weaver/client.rb +200 -0
- data/lib/graph_weaver/codegen/emit.rb +88 -39
- data/lib/graph_weaver/codegen/enum_type.rb +149 -0
- data/lib/graph_weaver/codegen/nodes.rb +109 -9
- data/lib/graph_weaver/codegen/scalar_type.rb +53 -35
- data/lib/graph_weaver/codegen.rb +301 -67
- data/lib/graph_weaver/errors.rb +37 -25
- data/lib/graph_weaver/hints.rb +63 -0
- data/lib/graph_weaver/inflect.rb +2 -1
- data/lib/graph_weaver/logging.rb +41 -0
- data/lib/graph_weaver/railtie.rb +29 -0
- data/lib/graph_weaver/retry.rb +97 -0
- data/lib/graph_weaver/rspec.rb +19 -14
- data/lib/graph_weaver/schema_loader.rb +156 -20
- data/lib/graph_weaver/selection.rb +3 -3
- data/lib/graph_weaver/tasks.rb +71 -0
- data/lib/graph_weaver/testing/cassette.rb +42 -21
- data/lib/graph_weaver/testing/failure.rb +22 -22
- data/lib/graph_weaver/testing/{fake_executor.rb → fake_client.rb} +13 -13
- data/lib/graph_weaver/testing/values.rb +2 -2
- data/lib/graph_weaver/testing.rb +32 -16
- 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 +208 -38
- metadata +73 -5
- data/lib/graph_weaver/faraday_executor.rb +0 -61
- data/lib/graph_weaver/http_executor.rb +0 -44
- data/lib/graph_weaver/testing/rspec.rb +0 -5
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# typed: true
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require "date"
|
|
5
|
+
|
|
4
6
|
class GraphWeaver::Codegen
|
|
5
7
|
|
|
6
8
|
# How one GraphQL scalar maps to Ruby: the Sorbet prop type, the
|
|
@@ -15,17 +17,17 @@ class GraphWeaver::Codegen
|
|
|
15
17
|
# from the Ruby type when it is a real class, by probing for a known
|
|
16
18
|
# deserializer and pairing its serializer (see CODECS) — so the common
|
|
17
19
|
# case needs no more than a class:
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
+
# type: Money (defines .parse) => Money.parse(expr) / expr.to_s
|
|
21
|
+
# type: Blob (defines .load) => Blob.load(expr) / Blob.dump(expr)
|
|
20
22
|
# Probing the *deserialize* side is deliberate: every object has #to_s,
|
|
21
23
|
# so inferring a serializer off it would wrongly wrap plain types (String,
|
|
22
24
|
# Integer) — pairing off a deserializer the type actually defines avoids
|
|
23
25
|
# that. Override with an explicit value:
|
|
24
26
|
# - a Symbol names a method, so there is no string to misspell:
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
+
# cast: :load => "Blob.load(expr)" (class method on type)
|
|
28
|
+
# serialize: :to_json => "expr.to_json" (instance method)
|
|
27
29
|
# - a Proc handles anything a Symbol can't express:
|
|
28
|
-
#
|
|
30
|
+
# cast: ->(e) { "Money.new(#{e})" }
|
|
29
31
|
# - :itself opts out — force identity pass-through even when a codec
|
|
30
32
|
# would otherwise match (rare)
|
|
31
33
|
# requires: a String or Array of paths emitted as `require`s atop the
|
|
@@ -56,23 +58,44 @@ class GraphWeaver::Codegen
|
|
|
56
58
|
|
|
57
59
|
attr_reader :graphql_name, :type, :requires
|
|
58
60
|
|
|
59
|
-
def initialize(graphql_name, type
|
|
61
|
+
def initialize(graphql_name, type, cast: nil, serialize: nil, requires: nil, coerce: nil)
|
|
60
62
|
@graphql_name = graphql_name.to_s
|
|
61
63
|
@klass = type.is_a?(Module) ? type : nil
|
|
62
64
|
@type = type_name(type)
|
|
65
|
+
# requires: load BEFORE codec probing — the probe method may come
|
|
66
|
+
# from the required file (core Time has no .parse until the "time"
|
|
67
|
+
# stdlib loads)
|
|
68
|
+
@requires = normalize_requires(requires)
|
|
63
69
|
codec = @klass && CODECS.find { |c| @klass.respond_to?(c.probe) }
|
|
64
70
|
@cast = normalize_cast(cast, codec&.cast)
|
|
65
71
|
@serialize = normalize_serialize(serialize, codec&.serialize)
|
|
66
|
-
@requires = normalize_requires(requires)
|
|
67
72
|
@coerce = coerce
|
|
68
73
|
validate_coerce!
|
|
69
74
|
end
|
|
70
75
|
|
|
76
|
+
# conversions applied to the four convertible built-ins when the
|
|
77
|
+
# global GraphWeaver.auto_coerce is on and no explicit coerce: given
|
|
78
|
+
AUTO_CONVERSIONS = {
|
|
79
|
+
"ID" => :to_s, "String" => :to_s, "Int" => :to_i, "Float" => :to_f,
|
|
80
|
+
}.freeze
|
|
81
|
+
|
|
71
82
|
def cast(expr) = @cast&.call(expr)
|
|
72
83
|
def cast? = !@cast.nil?
|
|
73
84
|
def serialize(expr) = @serialize&.call(expr)
|
|
74
85
|
def serialize? = !@serialize.nil?
|
|
75
|
-
def coerce? =
|
|
86
|
+
def coerce? = !!effective_coerce
|
|
87
|
+
|
|
88
|
+
# Explicit coerce: always wins (false means never). Left unset, the
|
|
89
|
+
# global GraphWeaver.auto_coerce decides — resolved HERE, at
|
|
90
|
+
# generation time, so registration order doesn't matter: convertible
|
|
91
|
+
# built-ins get their conversion, anything with a full cast/serialize
|
|
92
|
+
# pair gets parse-style coercion.
|
|
93
|
+
def effective_coerce
|
|
94
|
+
return @coerce unless @coerce.nil?
|
|
95
|
+
return false unless GraphWeaver.auto_coerce
|
|
96
|
+
|
|
97
|
+
AUTO_CONVERSIONS.fetch(@graphql_name) { (cast? && serialize?) || nil }
|
|
98
|
+
end
|
|
76
99
|
|
|
77
100
|
# The code that normalizes a variable input before it's serialized. Two
|
|
78
101
|
# shapes: coerce: true parses a raw value into the rich type via the cast
|
|
@@ -82,17 +105,17 @@ class GraphWeaver::Codegen
|
|
|
82
105
|
# afterward, but is identity for the conversion built-ins, so the
|
|
83
106
|
# converted value goes on the wire natively (a Float, not "5.0").
|
|
84
107
|
def coerce_input(expr)
|
|
85
|
-
case
|
|
108
|
+
case effective_coerce
|
|
86
109
|
when true then "(#{expr}.is_a?(#{@type}) ? #{expr} : #{cast(expr)})"
|
|
87
|
-
when Symbol then "#{expr}.#{
|
|
110
|
+
when Symbol then "#{expr}.#{effective_coerce}"
|
|
88
111
|
end
|
|
89
112
|
end
|
|
90
113
|
|
|
91
114
|
# the accepted Sorbet type for a coercible variable kwarg
|
|
92
115
|
def coerce_type
|
|
93
|
-
case
|
|
116
|
+
case effective_coerce
|
|
94
117
|
when true then "T.any(#{@type}, String)"
|
|
95
|
-
when Symbol then CONVERT_INPUTS.fetch(
|
|
118
|
+
when Symbol then CONVERT_INPUTS.fetch(effective_coerce, "T.untyped")
|
|
96
119
|
end
|
|
97
120
|
end
|
|
98
121
|
|
|
@@ -175,9 +198,9 @@ class GraphWeaver::Codegen
|
|
|
175
198
|
# the accepted cast:/serialize:/requires: forms. Later registrations
|
|
176
199
|
# win, so an app can override a built-in (e.g. map Date onto its own
|
|
177
200
|
# type).
|
|
178
|
-
def register_scalar(graphql_name, type
|
|
201
|
+
def register_scalar(graphql_name, type, cast: nil, serialize: nil, requires: nil, coerce: nil)
|
|
179
202
|
scalar_registry[graphql_name.to_s] =
|
|
180
|
-
ScalarType.new(graphql_name, type
|
|
203
|
+
ScalarType.new(graphql_name, type, cast:, serialize:, requires:, coerce:)
|
|
181
204
|
end
|
|
182
205
|
|
|
183
206
|
# The ScalarType for a scalar name; unknown scalars fall back to an
|
|
@@ -185,7 +208,7 @@ class GraphWeaver::Codegen
|
|
|
185
208
|
# scalars outside the table.
|
|
186
209
|
def scalar(graphql_name)
|
|
187
210
|
scalar_registry.fetch(graphql_name.to_s) do
|
|
188
|
-
ScalarType.new(graphql_name,
|
|
211
|
+
ScalarType.new(graphql_name, "T.untyped")
|
|
189
212
|
end
|
|
190
213
|
end
|
|
191
214
|
|
|
@@ -201,14 +224,12 @@ class GraphWeaver::Codegen
|
|
|
201
224
|
end
|
|
202
225
|
|
|
203
226
|
# Drop every custom registration and restore the built-in scalars — the
|
|
204
|
-
# clean slate to reach for between tests, or to undo overrides.
|
|
205
|
-
#
|
|
206
|
-
#
|
|
207
|
-
|
|
208
|
-
# built-in by hand.
|
|
209
|
-
def reset_scalars!(coerce: false)
|
|
227
|
+
# clean slate to reach for between tests, or to undo overrides. (Want
|
|
228
|
+
# the built-ins to coerce loose input? That's GraphWeaver.auto_coerce,
|
|
229
|
+
# resolved at generation time — no re-registering.)
|
|
230
|
+
def reset_scalars!
|
|
210
231
|
clear_scalars!
|
|
211
|
-
register_builtin_scalars!
|
|
232
|
+
register_builtin_scalars!
|
|
212
233
|
self
|
|
213
234
|
end
|
|
214
235
|
|
|
@@ -218,19 +239,16 @@ class GraphWeaver::Codegen
|
|
|
218
239
|
# matches nothing and leaves them identity — which is exactly why we
|
|
219
240
|
# can name them with the real class constants. Date deserializes via
|
|
220
241
|
# ISO-8601 (it *does* define .parse, but we want iso8601 specifically,
|
|
221
|
-
# so it's explicit).
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
register_scalar "
|
|
229
|
-
register_scalar "
|
|
230
|
-
register_scalar "
|
|
231
|
-
register_scalar "Float", type: Float, coerce: (:to_f if coerce)
|
|
232
|
-
register_scalar "Boolean", type: "T::Boolean"
|
|
233
|
-
register_scalar "Date", type: Date, cast: :iso8601, serialize: :iso8601, requires: "date"
|
|
242
|
+
# so it's explicit). Input coercion is a generation-time concern:
|
|
243
|
+
# GraphWeaver.auto_coerce gives the convertible built-ins their
|
|
244
|
+
# conversion (see ScalarType::AUTO_CONVERSIONS).
|
|
245
|
+
def register_builtin_scalars!
|
|
246
|
+
register_scalar "ID", String
|
|
247
|
+
register_scalar "String", String
|
|
248
|
+
register_scalar "Int", Integer
|
|
249
|
+
register_scalar "Float", Float
|
|
250
|
+
register_scalar "Boolean", "T::Boolean"
|
|
251
|
+
register_scalar "Date", Date, cast: :iso8601, serialize: :iso8601, requires: "date"
|
|
234
252
|
end
|
|
235
253
|
end
|
|
236
254
|
|