ruby-object-framework 1.0.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 +7 -0
- data/API.md +78 -0
- data/README.md +250 -0
- data/THIRD_PARTY_NOTICES.md +45 -0
- data/examples/guides/01_plain_use_case.rb +13 -0
- data/examples/guides/02_composed_application.rb +25 -0
- data/examples/guides/03_order_domain.rb +14 -0
- data/examples/guides/04_scoped_resources.rb +38 -0
- data/examples/guides/05_mapping.rb +39 -0
- data/examples/guides/06_associations.rb +49 -0
- data/examples/guides/07_http.rb +17 -0
- data/examples/guides/08_errors.rb +20 -0
- data/examples/guides/09_sqlite.rb +38 -0
- data/examples/guides/10_grpc.rb +26 -0
- data/examples/guides/11_ractor.rb +15 -0
- data/examples/guides/12_box.rb +11 -0
- data/examples/guides/13_testing.rb +24 -0
- data/examples/guides/manifest.yml +62 -0
- data/examples/orders/application.rb +51 -0
- data/examples/orders/contracts.rb +19 -0
- data/examples/orders/domain.rb +35 -0
- data/examples/orders/export_proto.rb +5 -0
- data/examples/orders/grpc_client.py +24 -0
- data/examples/orders/grpc_server.rb +23 -0
- data/examples/orders/server.rb +18 -0
- data/lib/object_framework/application.rb +136 -0
- data/lib/object_framework/capabilities.rb +59 -0
- data/lib/object_framework/errors.rb +85 -0
- data/lib/object_framework/execution.rb +184 -0
- data/lib/object_framework/grpc/messages.rb +30 -0
- data/lib/object_framework/grpc/server.rb +264 -0
- data/lib/object_framework/grpc/service.rb +55 -0
- data/lib/object_framework/http/io.rb +127 -0
- data/lib/object_framework/http/presentation.rb +55 -0
- data/lib/object_framework/http/protocol.rb +209 -0
- data/lib/object_framework/http/router.rb +80 -0
- data/lib/object_framework/http/server.rb +279 -0
- data/lib/object_framework/http2/connection.rb +399 -0
- data/lib/object_framework/http2/frame.rb +21 -0
- data/lib/object_framework/http2/hpack.rb +206 -0
- data/lib/object_framework/http2/hpack_tables.rb +333 -0
- data/lib/object_framework/http2/inbound.rb +75 -0
- data/lib/object_framework/memory/storage.rb +97 -0
- data/lib/object_framework/persistence/mapping.rb +150 -0
- data/lib/object_framework/persistence/query.rb +97 -0
- data/lib/object_framework/persistence/session.rb +173 -0
- data/lib/object_framework/persistence/transaction.rb +54 -0
- data/lib/object_framework/protobuf.rb +268 -0
- data/lib/object_framework/sqlite/compiler.rb +77 -0
- data/lib/object_framework/sqlite/schema.rb +95 -0
- data/lib/object_framework/sqlite/storage.rb +220 -0
- data/lib/object_framework/sqlite.rb +6 -0
- data/lib/object_framework/version.rb +5 -0
- data/lib/object_framework.rb +31 -0
- metadata +105 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Protocol tables from RFC 7541, Appendices A and B.
|
|
4
|
+
# Copyright (c) 2015 IETF Trust and the persons identified as the document authors.
|
|
5
|
+
# See THIRD_PARTY_NOTICES.md for the applicable Simplified BSD license.
|
|
6
|
+
module ObjectFramework
|
|
7
|
+
module HTTP2
|
|
8
|
+
module HPACK
|
|
9
|
+
STATIC_TABLE = [
|
|
10
|
+
[":authority", ""].freeze,
|
|
11
|
+
[":method", "GET"].freeze,
|
|
12
|
+
[":method", "POST"].freeze,
|
|
13
|
+
[":path", "/"].freeze,
|
|
14
|
+
[":path", "/index.html"].freeze,
|
|
15
|
+
[":scheme", "http"].freeze,
|
|
16
|
+
[":scheme", "https"].freeze,
|
|
17
|
+
[":status", "200"].freeze,
|
|
18
|
+
[":status", "204"].freeze,
|
|
19
|
+
[":status", "206"].freeze,
|
|
20
|
+
[":status", "304"].freeze,
|
|
21
|
+
[":status", "400"].freeze,
|
|
22
|
+
[":status", "404"].freeze,
|
|
23
|
+
[":status", "500"].freeze,
|
|
24
|
+
["accept-charset", ""].freeze,
|
|
25
|
+
["accept-encoding", "gzip, deflate"].freeze,
|
|
26
|
+
["accept-language", ""].freeze,
|
|
27
|
+
["accept-ranges", ""].freeze,
|
|
28
|
+
["accept", ""].freeze,
|
|
29
|
+
["access-control-allow-origin", ""].freeze,
|
|
30
|
+
["age", ""].freeze,
|
|
31
|
+
["allow", ""].freeze,
|
|
32
|
+
["authorization", ""].freeze,
|
|
33
|
+
["cache-control", ""].freeze,
|
|
34
|
+
["content-disposition", ""].freeze,
|
|
35
|
+
["content-encoding", ""].freeze,
|
|
36
|
+
["content-language", ""].freeze,
|
|
37
|
+
["content-length", ""].freeze,
|
|
38
|
+
["content-location", ""].freeze,
|
|
39
|
+
["content-range", ""].freeze,
|
|
40
|
+
["content-type", ""].freeze,
|
|
41
|
+
["cookie", ""].freeze,
|
|
42
|
+
["date", ""].freeze,
|
|
43
|
+
["etag", ""].freeze,
|
|
44
|
+
["expect", ""].freeze,
|
|
45
|
+
["expires", ""].freeze,
|
|
46
|
+
["from", ""].freeze,
|
|
47
|
+
["host", ""].freeze,
|
|
48
|
+
["if-match", ""].freeze,
|
|
49
|
+
["if-modified-since", ""].freeze,
|
|
50
|
+
["if-none-match", ""].freeze,
|
|
51
|
+
["if-range", ""].freeze,
|
|
52
|
+
["if-unmodified-since", ""].freeze,
|
|
53
|
+
["last-modified", ""].freeze,
|
|
54
|
+
["link", ""].freeze,
|
|
55
|
+
["location", ""].freeze,
|
|
56
|
+
["max-forwards", ""].freeze,
|
|
57
|
+
["proxy-authenticate", ""].freeze,
|
|
58
|
+
["proxy-authorization", ""].freeze,
|
|
59
|
+
["range", ""].freeze,
|
|
60
|
+
["referer", ""].freeze,
|
|
61
|
+
["refresh", ""].freeze,
|
|
62
|
+
["retry-after", ""].freeze,
|
|
63
|
+
["server", ""].freeze,
|
|
64
|
+
["set-cookie", ""].freeze,
|
|
65
|
+
["strict-transport-security", ""].freeze,
|
|
66
|
+
["transfer-encoding", ""].freeze,
|
|
67
|
+
["user-agent", ""].freeze,
|
|
68
|
+
["vary", ""].freeze,
|
|
69
|
+
["via", ""].freeze,
|
|
70
|
+
["www-authenticate", ""].freeze,
|
|
71
|
+
].freeze
|
|
72
|
+
HUFFMAN_CODES = [
|
|
73
|
+
[0x1ff8, 13].freeze,
|
|
74
|
+
[0x7fffd8, 23].freeze,
|
|
75
|
+
[0xfffffe2, 28].freeze,
|
|
76
|
+
[0xfffffe3, 28].freeze,
|
|
77
|
+
[0xfffffe4, 28].freeze,
|
|
78
|
+
[0xfffffe5, 28].freeze,
|
|
79
|
+
[0xfffffe6, 28].freeze,
|
|
80
|
+
[0xfffffe7, 28].freeze,
|
|
81
|
+
[0xfffffe8, 28].freeze,
|
|
82
|
+
[0xffffea, 24].freeze,
|
|
83
|
+
[0x3ffffffc, 30].freeze,
|
|
84
|
+
[0xfffffe9, 28].freeze,
|
|
85
|
+
[0xfffffea, 28].freeze,
|
|
86
|
+
[0x3ffffffd, 30].freeze,
|
|
87
|
+
[0xfffffeb, 28].freeze,
|
|
88
|
+
[0xfffffec, 28].freeze,
|
|
89
|
+
[0xfffffed, 28].freeze,
|
|
90
|
+
[0xfffffee, 28].freeze,
|
|
91
|
+
[0xfffffef, 28].freeze,
|
|
92
|
+
[0xffffff0, 28].freeze,
|
|
93
|
+
[0xffffff1, 28].freeze,
|
|
94
|
+
[0xffffff2, 28].freeze,
|
|
95
|
+
[0x3ffffffe, 30].freeze,
|
|
96
|
+
[0xffffff3, 28].freeze,
|
|
97
|
+
[0xffffff4, 28].freeze,
|
|
98
|
+
[0xffffff5, 28].freeze,
|
|
99
|
+
[0xffffff6, 28].freeze,
|
|
100
|
+
[0xffffff7, 28].freeze,
|
|
101
|
+
[0xffffff8, 28].freeze,
|
|
102
|
+
[0xffffff9, 28].freeze,
|
|
103
|
+
[0xffffffa, 28].freeze,
|
|
104
|
+
[0xffffffb, 28].freeze,
|
|
105
|
+
[0x14, 6].freeze,
|
|
106
|
+
[0x3f8, 10].freeze,
|
|
107
|
+
[0x3f9, 10].freeze,
|
|
108
|
+
[0xffa, 12].freeze,
|
|
109
|
+
[0x1ff9, 13].freeze,
|
|
110
|
+
[0x15, 6].freeze,
|
|
111
|
+
[0xf8, 8].freeze,
|
|
112
|
+
[0x7fa, 11].freeze,
|
|
113
|
+
[0x3fa, 10].freeze,
|
|
114
|
+
[0x3fb, 10].freeze,
|
|
115
|
+
[0xf9, 8].freeze,
|
|
116
|
+
[0x7fb, 11].freeze,
|
|
117
|
+
[0xfa, 8].freeze,
|
|
118
|
+
[0x16, 6].freeze,
|
|
119
|
+
[0x17, 6].freeze,
|
|
120
|
+
[0x18, 6].freeze,
|
|
121
|
+
[0x0, 5].freeze,
|
|
122
|
+
[0x1, 5].freeze,
|
|
123
|
+
[0x2, 5].freeze,
|
|
124
|
+
[0x19, 6].freeze,
|
|
125
|
+
[0x1a, 6].freeze,
|
|
126
|
+
[0x1b, 6].freeze,
|
|
127
|
+
[0x1c, 6].freeze,
|
|
128
|
+
[0x1d, 6].freeze,
|
|
129
|
+
[0x1e, 6].freeze,
|
|
130
|
+
[0x1f, 6].freeze,
|
|
131
|
+
[0x5c, 7].freeze,
|
|
132
|
+
[0xfb, 8].freeze,
|
|
133
|
+
[0x7ffc, 15].freeze,
|
|
134
|
+
[0x20, 6].freeze,
|
|
135
|
+
[0xffb, 12].freeze,
|
|
136
|
+
[0x3fc, 10].freeze,
|
|
137
|
+
[0x1ffa, 13].freeze,
|
|
138
|
+
[0x21, 6].freeze,
|
|
139
|
+
[0x5d, 7].freeze,
|
|
140
|
+
[0x5e, 7].freeze,
|
|
141
|
+
[0x5f, 7].freeze,
|
|
142
|
+
[0x60, 7].freeze,
|
|
143
|
+
[0x61, 7].freeze,
|
|
144
|
+
[0x62, 7].freeze,
|
|
145
|
+
[0x63, 7].freeze,
|
|
146
|
+
[0x64, 7].freeze,
|
|
147
|
+
[0x65, 7].freeze,
|
|
148
|
+
[0x66, 7].freeze,
|
|
149
|
+
[0x67, 7].freeze,
|
|
150
|
+
[0x68, 7].freeze,
|
|
151
|
+
[0x69, 7].freeze,
|
|
152
|
+
[0x6a, 7].freeze,
|
|
153
|
+
[0x6b, 7].freeze,
|
|
154
|
+
[0x6c, 7].freeze,
|
|
155
|
+
[0x6d, 7].freeze,
|
|
156
|
+
[0x6e, 7].freeze,
|
|
157
|
+
[0x6f, 7].freeze,
|
|
158
|
+
[0x70, 7].freeze,
|
|
159
|
+
[0x71, 7].freeze,
|
|
160
|
+
[0x72, 7].freeze,
|
|
161
|
+
[0xfc, 8].freeze,
|
|
162
|
+
[0x73, 7].freeze,
|
|
163
|
+
[0xfd, 8].freeze,
|
|
164
|
+
[0x1ffb, 13].freeze,
|
|
165
|
+
[0x7fff0, 19].freeze,
|
|
166
|
+
[0x1ffc, 13].freeze,
|
|
167
|
+
[0x3ffc, 14].freeze,
|
|
168
|
+
[0x22, 6].freeze,
|
|
169
|
+
[0x7ffd, 15].freeze,
|
|
170
|
+
[0x3, 5].freeze,
|
|
171
|
+
[0x23, 6].freeze,
|
|
172
|
+
[0x4, 5].freeze,
|
|
173
|
+
[0x24, 6].freeze,
|
|
174
|
+
[0x5, 5].freeze,
|
|
175
|
+
[0x25, 6].freeze,
|
|
176
|
+
[0x26, 6].freeze,
|
|
177
|
+
[0x27, 6].freeze,
|
|
178
|
+
[0x6, 5].freeze,
|
|
179
|
+
[0x74, 7].freeze,
|
|
180
|
+
[0x75, 7].freeze,
|
|
181
|
+
[0x28, 6].freeze,
|
|
182
|
+
[0x29, 6].freeze,
|
|
183
|
+
[0x2a, 6].freeze,
|
|
184
|
+
[0x7, 5].freeze,
|
|
185
|
+
[0x2b, 6].freeze,
|
|
186
|
+
[0x76, 7].freeze,
|
|
187
|
+
[0x2c, 6].freeze,
|
|
188
|
+
[0x8, 5].freeze,
|
|
189
|
+
[0x9, 5].freeze,
|
|
190
|
+
[0x2d, 6].freeze,
|
|
191
|
+
[0x77, 7].freeze,
|
|
192
|
+
[0x78, 7].freeze,
|
|
193
|
+
[0x79, 7].freeze,
|
|
194
|
+
[0x7a, 7].freeze,
|
|
195
|
+
[0x7b, 7].freeze,
|
|
196
|
+
[0x7ffe, 15].freeze,
|
|
197
|
+
[0x7fc, 11].freeze,
|
|
198
|
+
[0x3ffd, 14].freeze,
|
|
199
|
+
[0x1ffd, 13].freeze,
|
|
200
|
+
[0xffffffc, 28].freeze,
|
|
201
|
+
[0xfffe6, 20].freeze,
|
|
202
|
+
[0x3fffd2, 22].freeze,
|
|
203
|
+
[0xfffe7, 20].freeze,
|
|
204
|
+
[0xfffe8, 20].freeze,
|
|
205
|
+
[0x3fffd3, 22].freeze,
|
|
206
|
+
[0x3fffd4, 22].freeze,
|
|
207
|
+
[0x3fffd5, 22].freeze,
|
|
208
|
+
[0x7fffd9, 23].freeze,
|
|
209
|
+
[0x3fffd6, 22].freeze,
|
|
210
|
+
[0x7fffda, 23].freeze,
|
|
211
|
+
[0x7fffdb, 23].freeze,
|
|
212
|
+
[0x7fffdc, 23].freeze,
|
|
213
|
+
[0x7fffdd, 23].freeze,
|
|
214
|
+
[0x7fffde, 23].freeze,
|
|
215
|
+
[0xffffeb, 24].freeze,
|
|
216
|
+
[0x7fffdf, 23].freeze,
|
|
217
|
+
[0xffffec, 24].freeze,
|
|
218
|
+
[0xffffed, 24].freeze,
|
|
219
|
+
[0x3fffd7, 22].freeze,
|
|
220
|
+
[0x7fffe0, 23].freeze,
|
|
221
|
+
[0xffffee, 24].freeze,
|
|
222
|
+
[0x7fffe1, 23].freeze,
|
|
223
|
+
[0x7fffe2, 23].freeze,
|
|
224
|
+
[0x7fffe3, 23].freeze,
|
|
225
|
+
[0x7fffe4, 23].freeze,
|
|
226
|
+
[0x1fffdc, 21].freeze,
|
|
227
|
+
[0x3fffd8, 22].freeze,
|
|
228
|
+
[0x7fffe5, 23].freeze,
|
|
229
|
+
[0x3fffd9, 22].freeze,
|
|
230
|
+
[0x7fffe6, 23].freeze,
|
|
231
|
+
[0x7fffe7, 23].freeze,
|
|
232
|
+
[0xffffef, 24].freeze,
|
|
233
|
+
[0x3fffda, 22].freeze,
|
|
234
|
+
[0x1fffdd, 21].freeze,
|
|
235
|
+
[0xfffe9, 20].freeze,
|
|
236
|
+
[0x3fffdb, 22].freeze,
|
|
237
|
+
[0x3fffdc, 22].freeze,
|
|
238
|
+
[0x7fffe8, 23].freeze,
|
|
239
|
+
[0x7fffe9, 23].freeze,
|
|
240
|
+
[0x1fffde, 21].freeze,
|
|
241
|
+
[0x7fffea, 23].freeze,
|
|
242
|
+
[0x3fffdd, 22].freeze,
|
|
243
|
+
[0x3fffde, 22].freeze,
|
|
244
|
+
[0xfffff0, 24].freeze,
|
|
245
|
+
[0x1fffdf, 21].freeze,
|
|
246
|
+
[0x3fffdf, 22].freeze,
|
|
247
|
+
[0x7fffeb, 23].freeze,
|
|
248
|
+
[0x7fffec, 23].freeze,
|
|
249
|
+
[0x1fffe0, 21].freeze,
|
|
250
|
+
[0x1fffe1, 21].freeze,
|
|
251
|
+
[0x3fffe0, 22].freeze,
|
|
252
|
+
[0x1fffe2, 21].freeze,
|
|
253
|
+
[0x7fffed, 23].freeze,
|
|
254
|
+
[0x3fffe1, 22].freeze,
|
|
255
|
+
[0x7fffee, 23].freeze,
|
|
256
|
+
[0x7fffef, 23].freeze,
|
|
257
|
+
[0xfffea, 20].freeze,
|
|
258
|
+
[0x3fffe2, 22].freeze,
|
|
259
|
+
[0x3fffe3, 22].freeze,
|
|
260
|
+
[0x3fffe4, 22].freeze,
|
|
261
|
+
[0x7ffff0, 23].freeze,
|
|
262
|
+
[0x3fffe5, 22].freeze,
|
|
263
|
+
[0x3fffe6, 22].freeze,
|
|
264
|
+
[0x7ffff1, 23].freeze,
|
|
265
|
+
[0x3ffffe0, 26].freeze,
|
|
266
|
+
[0x3ffffe1, 26].freeze,
|
|
267
|
+
[0xfffeb, 20].freeze,
|
|
268
|
+
[0x7fff1, 19].freeze,
|
|
269
|
+
[0x3fffe7, 22].freeze,
|
|
270
|
+
[0x7ffff2, 23].freeze,
|
|
271
|
+
[0x3fffe8, 22].freeze,
|
|
272
|
+
[0x1ffffec, 25].freeze,
|
|
273
|
+
[0x3ffffe2, 26].freeze,
|
|
274
|
+
[0x3ffffe3, 26].freeze,
|
|
275
|
+
[0x3ffffe4, 26].freeze,
|
|
276
|
+
[0x7ffffde, 27].freeze,
|
|
277
|
+
[0x7ffffdf, 27].freeze,
|
|
278
|
+
[0x3ffffe5, 26].freeze,
|
|
279
|
+
[0xfffff1, 24].freeze,
|
|
280
|
+
[0x1ffffed, 25].freeze,
|
|
281
|
+
[0x7fff2, 19].freeze,
|
|
282
|
+
[0x1fffe3, 21].freeze,
|
|
283
|
+
[0x3ffffe6, 26].freeze,
|
|
284
|
+
[0x7ffffe0, 27].freeze,
|
|
285
|
+
[0x7ffffe1, 27].freeze,
|
|
286
|
+
[0x3ffffe7, 26].freeze,
|
|
287
|
+
[0x7ffffe2, 27].freeze,
|
|
288
|
+
[0xfffff2, 24].freeze,
|
|
289
|
+
[0x1fffe4, 21].freeze,
|
|
290
|
+
[0x1fffe5, 21].freeze,
|
|
291
|
+
[0x3ffffe8, 26].freeze,
|
|
292
|
+
[0x3ffffe9, 26].freeze,
|
|
293
|
+
[0xffffffd, 28].freeze,
|
|
294
|
+
[0x7ffffe3, 27].freeze,
|
|
295
|
+
[0x7ffffe4, 27].freeze,
|
|
296
|
+
[0x7ffffe5, 27].freeze,
|
|
297
|
+
[0xfffec, 20].freeze,
|
|
298
|
+
[0xfffff3, 24].freeze,
|
|
299
|
+
[0xfffed, 20].freeze,
|
|
300
|
+
[0x1fffe6, 21].freeze,
|
|
301
|
+
[0x3fffe9, 22].freeze,
|
|
302
|
+
[0x1fffe7, 21].freeze,
|
|
303
|
+
[0x1fffe8, 21].freeze,
|
|
304
|
+
[0x7ffff3, 23].freeze,
|
|
305
|
+
[0x3fffea, 22].freeze,
|
|
306
|
+
[0x3fffeb, 22].freeze,
|
|
307
|
+
[0x1ffffee, 25].freeze,
|
|
308
|
+
[0x1ffffef, 25].freeze,
|
|
309
|
+
[0xfffff4, 24].freeze,
|
|
310
|
+
[0xfffff5, 24].freeze,
|
|
311
|
+
[0x3ffffea, 26].freeze,
|
|
312
|
+
[0x7ffff4, 23].freeze,
|
|
313
|
+
[0x3ffffeb, 26].freeze,
|
|
314
|
+
[0x7ffffe6, 27].freeze,
|
|
315
|
+
[0x3ffffec, 26].freeze,
|
|
316
|
+
[0x3ffffed, 26].freeze,
|
|
317
|
+
[0x7ffffe7, 27].freeze,
|
|
318
|
+
[0x7ffffe8, 27].freeze,
|
|
319
|
+
[0x7ffffe9, 27].freeze,
|
|
320
|
+
[0x7ffffea, 27].freeze,
|
|
321
|
+
[0x7ffffeb, 27].freeze,
|
|
322
|
+
[0xffffffe, 28].freeze,
|
|
323
|
+
[0x7ffffec, 27].freeze,
|
|
324
|
+
[0x7ffffed, 27].freeze,
|
|
325
|
+
[0x7ffffee, 27].freeze,
|
|
326
|
+
[0x7ffffef, 27].freeze,
|
|
327
|
+
[0x7fffff0, 27].freeze,
|
|
328
|
+
[0x3ffffee, 26].freeze,
|
|
329
|
+
[0x3fffffff, 30].freeze,
|
|
330
|
+
].freeze
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ObjectFramework
|
|
4
|
+
module HTTP2
|
|
5
|
+
class Inbound
|
|
6
|
+
def initialize(cancellation:, on_consume: ->(_) {}, buffer_limit: 65_535)
|
|
7
|
+
@cancellation, @on_consume, @limit = cancellation, on_consume, buffer_limit
|
|
8
|
+
@mutex, @changed, @queue = Mutex.new, ConditionVariable.new, []
|
|
9
|
+
@bytes, @finished, @discard = 0, false, false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def push(bytes)
|
|
13
|
+
discarded = @mutex.synchronize do
|
|
14
|
+
if @discard
|
|
15
|
+
bytes.bytesize
|
|
16
|
+
else
|
|
17
|
+
raise ProtocolError.new("Inbound stream exceeds flow control", code: 3) if @bytes + bytes.bytesize > @limit
|
|
18
|
+
@queue << bytes.b.freeze unless bytes.empty?
|
|
19
|
+
@bytes += bytes.bytesize
|
|
20
|
+
@changed.broadcast
|
|
21
|
+
0
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
@on_consume.call(discarded) if discarded.positive?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def finish
|
|
28
|
+
@mutex.synchronize { @finished = true; @changed.broadcast }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def discard
|
|
32
|
+
bytes = @mutex.synchronize do
|
|
33
|
+
@discard, @finished = true, true
|
|
34
|
+
discarded, @bytes = @bytes, 0
|
|
35
|
+
@queue.clear
|
|
36
|
+
@changed.broadcast
|
|
37
|
+
discarded
|
|
38
|
+
end
|
|
39
|
+
@on_consume.call(bytes) if bytes.positive?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def cancel
|
|
43
|
+
@cancellation.cancel
|
|
44
|
+
@mutex.synchronize { @changed.broadcast }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def read_exact(count, allow_eof: false)
|
|
48
|
+
result = +"".b
|
|
49
|
+
while result.bytesize < count
|
|
50
|
+
part = @mutex.synchronize do
|
|
51
|
+
loop do
|
|
52
|
+
@cancellation.check!
|
|
53
|
+
unless @queue.empty?
|
|
54
|
+
first = @queue.shift
|
|
55
|
+
length = [count - result.bytesize, first.bytesize].min
|
|
56
|
+
@queue.unshift(first.byteslice(length..)) if length < first.bytesize
|
|
57
|
+
@bytes -= length
|
|
58
|
+
break first.byteslice(0, length)
|
|
59
|
+
end
|
|
60
|
+
if @finished
|
|
61
|
+
return nil if allow_eof && result.empty?
|
|
62
|
+
raise InvalidInput, "Truncated gRPC message"
|
|
63
|
+
end
|
|
64
|
+
remaining = @cancellation.deadline ? @cancellation.deadline - HTTP::Timing.now : 0.1
|
|
65
|
+
@changed.wait(@mutex, [[remaining, 0].max, 0.1].min)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
result << part
|
|
69
|
+
@on_consume.call(part.bytesize)
|
|
70
|
+
end
|
|
71
|
+
result
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ObjectFramework
|
|
4
|
+
module Memory
|
|
5
|
+
class Storage
|
|
6
|
+
def initialize
|
|
7
|
+
@mutex, @tables, @revision, @closed = Mutex.new, {}, 0, false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def begin_transaction(mode:)
|
|
11
|
+
@mutex.synchronize do
|
|
12
|
+
raise Unavailable, "Storage is closed" if @closed
|
|
13
|
+
Transaction.new(storage: self, tables: @tables.transform_values(&:dup), revision: @revision, mode:)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def publish(tables, revision:)
|
|
18
|
+
@mutex.synchronize do
|
|
19
|
+
raise Unavailable, "Storage is closed" if @closed
|
|
20
|
+
raise Conflict, "Snapshot revision changed" unless revision == @revision
|
|
21
|
+
# Publishing the whole table set under one lock makes multi-row commits atomic.
|
|
22
|
+
@tables = tables.transform_values { |rows| rows.dup.freeze }.freeze
|
|
23
|
+
@revision += 1
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def close
|
|
28
|
+
@mutex.synchronize { @closed = true; @tables = {} }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Transaction < Persistence::Transaction
|
|
33
|
+
def initialize(storage:, tables:, revision:, mode:)
|
|
34
|
+
super(mode:)
|
|
35
|
+
@storage, @tables, @revision, @dirty = storage, tables, revision, false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def find(table, id)
|
|
39
|
+
active!
|
|
40
|
+
row = rows(table)[id]
|
|
41
|
+
Values.detach(row) if row
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def query(query)
|
|
45
|
+
active!
|
|
46
|
+
Values.detach(query.apply(rows(query.table).values))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def insert(table, row)
|
|
50
|
+
writable!
|
|
51
|
+
value = row_value(row)
|
|
52
|
+
raise Duplicate, "Duplicate identity" if rows(table).key?(value[:id])
|
|
53
|
+
rows(table)[value[:id]] = value
|
|
54
|
+
@dirty = true
|
|
55
|
+
value[:id]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def update(table, id, row)
|
|
59
|
+
writable!
|
|
60
|
+
raise NotFound, "Unknown identity" unless rows(table).key?(id)
|
|
61
|
+
rows(table)[id] = row_value(row, id:)
|
|
62
|
+
@dirty = true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def delete(table, id)
|
|
66
|
+
writable!
|
|
67
|
+
raise NotFound, "Unknown identity" unless rows(table).key?(id)
|
|
68
|
+
rows(table).delete(id)
|
|
69
|
+
@dirty = true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def commit
|
|
73
|
+
active!
|
|
74
|
+
@storage.publish(@tables, revision: @revision) if @dirty
|
|
75
|
+
@state = :committed
|
|
76
|
+
rescue Conflict
|
|
77
|
+
@state = :rolled_back
|
|
78
|
+
@tables.clear
|
|
79
|
+
raise
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def rows(table)
|
|
85
|
+
@tables[Persistence::Query.identifier(table)] ||= {}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def rollback_storage
|
|
89
|
+
@tables.clear
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def close_storage
|
|
93
|
+
@tables.clear
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ObjectFramework
|
|
4
|
+
module Persistence
|
|
5
|
+
class Converter
|
|
6
|
+
def initialize(dump: ->(value) { value }, load: ->(value) { value })
|
|
7
|
+
@dump, @load = dump, load
|
|
8
|
+
freeze
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def dump(value); @dump.call(value); end
|
|
12
|
+
def load(value); @load.call(value); end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Field = Data.define(:name, :column, :type, :converter, :reader, :writer)
|
|
16
|
+
Association = Data.define(:name, :kind, :target, :foreign_key, :reader, :writer)
|
|
17
|
+
|
|
18
|
+
module AssociationDSL
|
|
19
|
+
%i[belongs_to has_many].each do |kind|
|
|
20
|
+
define_method(kind) do |name, target:, foreign_key:, read: nil, write: nil|
|
|
21
|
+
mutable!
|
|
22
|
+
name = Query.identifier(name)
|
|
23
|
+
raise ConfigurationError, "Duplicate mapped attribute" if (@fields + @associations).any? { |item| item.name == name }
|
|
24
|
+
foreign_key = Query.identifier(foreign_key)
|
|
25
|
+
if kind == :belongs_to && columns.include?(foreign_key)
|
|
26
|
+
raise ConfigurationError, "Duplicate storage column"
|
|
27
|
+
end
|
|
28
|
+
@associations << Association.new(name:, kind:, target:, foreign_key:,
|
|
29
|
+
reader: read || ->(entity) { entity.public_send(name) },
|
|
30
|
+
writer: write || ->(entity, value) { entity.public_send("#{name}=", value) })
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Mapping
|
|
37
|
+
include AssociationDSL
|
|
38
|
+
TYPES = %i[string integer float boolean blob].freeze
|
|
39
|
+
attr_reader :entity_class, :table, :fields, :associations
|
|
40
|
+
|
|
41
|
+
def initialize(entity_class, table:)
|
|
42
|
+
@entity_class, @table = entity_class, Query.identifier(table)
|
|
43
|
+
@fields, @associations = [], []
|
|
44
|
+
@builder = ->(attributes) { entity_class.new(**attributes) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def field(name, column: name, type: :string, converter: Converter.new, read: nil, write: nil)
|
|
48
|
+
mutable!
|
|
49
|
+
name, column = Query.identifier(name), Query.identifier(column)
|
|
50
|
+
raise ConfigurationError, "Unsupported column type" unless TYPES.include?(type)
|
|
51
|
+
raise ConfigurationError, "Duplicate mapped column or attribute" if columns.include?(column) || (@fields + @associations).any? { |item| item.name == name }
|
|
52
|
+
@fields << Field.new(name:, column:, type:, converter:,
|
|
53
|
+
reader: read || ->(entity) { entity.public_send(name) },
|
|
54
|
+
writer: write || ->(entity, value) { entity.public_send("#{name}=", value) })
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def build(&builder)
|
|
59
|
+
mutable!
|
|
60
|
+
@builder = builder
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def identity
|
|
65
|
+
fields.find { |field| field.name == :id } || raise(ConfigurationError, "Mapping requires an id field")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def columns
|
|
69
|
+
fields.map(&:column) + associations.select { |item| item.kind == :belongs_to }.map(&:foreign_key)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def schema
|
|
73
|
+
fields.to_h { |field| [field.column, field.type] }.merge(
|
|
74
|
+
associations.select { |item| item.kind == :belongs_to }.to_h { |item| [item.foreign_key, :string] }
|
|
75
|
+
).freeze
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def hydrate(row)
|
|
79
|
+
@builder.call(fields.to_h { |field| [field.name, field.converter.load(Values.mutable_copy(row[field.column]))] })
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def dump(entity, baseline: nil, loaded: [])
|
|
83
|
+
row = fields.to_h { |field| [field.column, field.converter.dump(field.reader.call(entity))] }
|
|
84
|
+
associations.select { |association| association.kind == :belongs_to }.each do |association|
|
|
85
|
+
target = association.reader.call(entity)
|
|
86
|
+
row[association.foreign_key] = if target
|
|
87
|
+
target.public_send(:id) || raise(InvalidInput, "Associated entities must be registered before flush")
|
|
88
|
+
elsif baseline && !loaded.include?(association.name)
|
|
89
|
+
baseline[association.foreign_key]
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
Values.detach(baseline ? baseline.merge(row) : row)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def finalize!
|
|
96
|
+
identity
|
|
97
|
+
raise ConfigurationError, "Identity must map to the id column" unless identity.column == :id
|
|
98
|
+
fields.freeze
|
|
99
|
+
associations.freeze
|
|
100
|
+
freeze
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def mutable!
|
|
106
|
+
raise ConfigurationError, "Mapping is finalized" if frozen?
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class Mappings
|
|
111
|
+
include Enumerable
|
|
112
|
+
|
|
113
|
+
def initialize
|
|
114
|
+
@mappings = {}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def define(entity_class, table:, &description)
|
|
118
|
+
raise ConfigurationError, "Mappings are finalized" if @mappings.frozen?
|
|
119
|
+
raise ConfigurationError, "Duplicate entity mapping" if @mappings.key?(entity_class)
|
|
120
|
+
raise ConfigurationError, "Duplicate table mapping" if @mappings.values.any? { |mapping| mapping.table == table.to_sym }
|
|
121
|
+
mapping = Mapping.new(entity_class, table:)
|
|
122
|
+
mapping.instance_eval(&description) if description
|
|
123
|
+
@mappings[entity_class] = mapping
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def each(&block)
|
|
127
|
+
@mappings.values.each(&block)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def fetch(entity_class)
|
|
131
|
+
@mappings.fetch(entity_class) { raise ConfigurationError, "Unmapped entity: #{entity_class}" }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def validate!
|
|
135
|
+
each do |mapping|
|
|
136
|
+
mapping.identity
|
|
137
|
+
mapping.associations.each do |association|
|
|
138
|
+
target = fetch(association.target)
|
|
139
|
+
if association.kind == :has_many && !target.columns.include?(association.foreign_key)
|
|
140
|
+
raise ConfigurationError, "Collection foreign key is not mapped"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
each(&:finalize!)
|
|
145
|
+
@mappings.freeze
|
|
146
|
+
self
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|