syntropy 0.31.0 → 0.33.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +20 -0
  4. data/TODO.md +7 -1
  5. data/cmd/console.rb +77 -0
  6. data/cmd/serve.rb +1 -3
  7. data/cmd/test.rb +76 -20
  8. data/examples/blog/app/_layout/default.rb +11 -0
  9. data/examples/blog/app/_lib/post_store.rb +47 -0
  10. data/examples/blog/app/_schema/2026-01-01-initial.rb +9 -0
  11. data/examples/blog/app/_setup.rb +4 -0
  12. data/examples/blog/app/index.rb +7 -0
  13. data/examples/blog/app/posts/[id]/edit.rb +33 -0
  14. data/examples/blog/app/posts/[id]/index.rb +61 -0
  15. data/examples/blog/app/posts/index.rb +40 -0
  16. data/examples/blog/app/posts/new.rb +29 -0
  17. data/examples/mcp-oauth/README.md +3 -3
  18. data/examples/mcp-oauth/app/mcp.rb +55 -8
  19. data/examples/mcp-oauth/app/oauth/authorize.rb +0 -8
  20. data/examples/mcp-oauth/app/oauth/register.rb +0 -1
  21. data/examples/mcp-oauth/test/test_app.rb +2 -20
  22. data/examples/mcp-oauth/test/test_oauth.rb +93 -217
  23. data/lib/syntropy/app.rb +23 -9
  24. data/lib/syntropy/db/connection_pool.rb +71 -0
  25. data/lib/syntropy/db/schema.rb +92 -0
  26. data/lib/syntropy/db/store.rb +31 -0
  27. data/lib/syntropy/http/io_extensions.rb +33 -5
  28. data/lib/syntropy/http/server_connection.rb +21 -62
  29. data/lib/syntropy/{module.rb → module_loader.rb} +48 -8
  30. data/lib/syntropy/request/request_info.rb +3 -4
  31. data/lib/syntropy/request/response.rb +2 -2
  32. data/lib/syntropy/request/session.rb +113 -0
  33. data/lib/syntropy/request/validation.rb +1 -2
  34. data/lib/syntropy/request.rb +9 -0
  35. data/lib/syntropy/test.rb +84 -1
  36. data/lib/syntropy/version.rb +1 -1
  37. data/lib/syntropy.rb +4 -2
  38. data/syntropy.gemspec +3 -1
  39. data/test/app/_hook.rb +1 -1
  40. data/test/app/by_method.rb +9 -0
  41. data/test/app_setup/_setup.rb +7 -0
  42. data/test/app_setup/index.rb +1 -0
  43. data/test/app_with_schema/_schema/2026-01-02-foo.rb +12 -0
  44. data/test/app_with_schema/_schema/2026-05-30-bar.rb +7 -0
  45. data/test/schema/2026-01-02-foo.rb +12 -0
  46. data/test/schema/2026-05-30-bar.rb +7 -0
  47. data/test/test_app.rb +58 -3
  48. data/test/{test_connection_pool.rb → test_db_connection_pool.rb} +7 -2
  49. data/test/test_db_schema.rb +96 -0
  50. data/test/test_db_store.rb +24 -0
  51. data/test/test_http_protocol.rb +250 -0
  52. data/test/test_http_server_connection.rb +18 -24
  53. data/test/test_json_api.rb +1 -1
  54. data/test/{test_module.rb → test_module_loader.rb} +31 -0
  55. data/test/test_request.rb +7 -4
  56. data/test/test_request_session.rb +254 -0
  57. data/test/test_server.rb +9 -13
  58. metadata +63 -12
  59. data/examples/mcp-oauth/test/helper.rb +0 -9
  60. data/lib/syntropy/connection_pool.rb +0 -61
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class RequestSessionTest < Minitest::Test
6
+ def make_socket_pair
7
+ port = SecureRandom.random_number(10000..40000)
8
+ server_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
9
+ @machine.setsockopt(server_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
10
+ @machine.bind(server_fd, '127.0.0.1', port)
11
+ @machine.listen(server_fd, UM::SOMAXCONN)
12
+
13
+ client_conn_fd = @machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
14
+ @machine.connect(client_conn_fd, '127.0.0.1', port)
15
+
16
+ server_conn_fd = @machine.accept(server_fd)
17
+
18
+ @machine.close(server_fd)
19
+ [client_conn_fd, server_conn_fd]
20
+ end
21
+
22
+ def setup
23
+ @machine = UM.new
24
+ @c_fd, @s_fd = make_socket_pair
25
+ # s = @machine.io(@s_fd, :socket)
26
+
27
+ @app = ->(req) { req.respond(nil, ':status' => Syntropy::HTTP::INTERNAL_SERVER_ERROR) }
28
+ @env = {}
29
+ @connection = Syntropy::HTTP::ServerConnection.new(@machine, @s_fd, @env) { |req| @app.(req) }
30
+ end
31
+
32
+ def teardown
33
+ @machine.close(@c_fd) rescue nil
34
+ @machine.close(@s_fd) rescue nil
35
+ end
36
+
37
+ def write_http_request(msg, shutdown_wr = true)
38
+ @machine.send(@c_fd, msg, msg.bytesize, UM::MSG_WAITALL)
39
+ @machine.shutdown(@c_fd, UM::SHUT_WR) if shutdown_wr
40
+ end
41
+
42
+ def write_client_side(msg)
43
+ @machine.send(@c_fd, msg, msg.bytesize, UM::MSG_WAITALL)
44
+ end
45
+
46
+ def read_client_side(len = 65536)
47
+ buf = +''
48
+ res = @machine.recv(@c_fd, buf, len, 0)
49
+ res == 0 ? nil : buf
50
+ end
51
+
52
+ def test_session_kv_access
53
+ current = :something
54
+
55
+ @app = ->(req) {
56
+ req.session['foo'] = 'bar'
57
+ current = req.session['foo']
58
+ req.respond(nil)
59
+ }
60
+
61
+ write_http_request "GET / HTTP/1.1\r\n\r\n"
62
+ @connection.serve_request
63
+
64
+ assert_equal 'bar', current
65
+
66
+ response = read_client_side
67
+ data = Base64.strict_encode64(JSON.dump({ 'foo' => 'bar' }))
68
+ assert_equal "HTTP/1.1 204\r\nSet-Cookie: __syntropy_session__=#{data}; Path=/; HttpOnly\r\n\r\n", response
69
+ end
70
+
71
+ def test_session_kv_multi
72
+ @app = ->(req) {
73
+ req.session['foo'] = 'bar'
74
+ req.session['bar'] = 'baz'
75
+ req.respond(nil)
76
+ }
77
+
78
+ write_http_request "GET / HTTP/1.1\r\n\r\n"
79
+ @connection.serve_request
80
+
81
+ response = read_client_side
82
+ data = Base64.strict_encode64(JSON.dump({ 'foo' => 'bar', 'bar' => 'baz' }))
83
+ assert_equal "HTTP/1.1 204\r\nSet-Cookie: __syntropy_session__=#{data}; Path=/; HttpOnly\r\n\r\n", response
84
+ end
85
+
86
+ def test_session_kv_sequence
87
+ counter = 0
88
+
89
+ @app = ->(req) {
90
+ counter += 1
91
+ case counter
92
+ when 1
93
+ req.session['foo'] = 'bar'
94
+ when 2
95
+ req.session['foo'] = req.session['foo'] + 'baz'
96
+ end
97
+ req.respond(nil)
98
+ }
99
+
100
+ write_http_request "GET / HTTP/1.1\r\n\r\n", false
101
+ @connection.serve_request
102
+ response = read_client_side
103
+ data = Base64.strict_encode64(JSON.dump({ 'foo' => 'bar' }))
104
+ assert_equal "HTTP/1.1 204\r\nSet-Cookie: __syntropy_session__=#{data}; Path=/; HttpOnly\r\n\r\n", response
105
+
106
+ write_http_request "GET / HTTP/1.1\r\nCookie: __syntropy_session__=#{data}\r\n\r\n"
107
+ @connection.serve_request
108
+ response = read_client_side
109
+ data = Base64.strict_encode64(JSON.dump({ 'foo' => 'barbaz' }))
110
+ assert_equal "HTTP/1.1 204\r\nSet-Cookie: __syntropy_session__=#{data}; Path=/; HttpOnly\r\n\r\n", response
111
+ end
112
+
113
+ def test_session_kv_delete
114
+ counter = 0
115
+
116
+ @app = ->(req) {
117
+ counter += 1
118
+ case counter
119
+ when 1
120
+ req.session['foo'] = 'bar'
121
+ when 2
122
+ req.session.delete('foo')
123
+ end
124
+ req.respond(nil)
125
+ }
126
+
127
+ write_http_request "GET / HTTP/1.1\r\n\r\n", false
128
+ @connection.serve_request
129
+ response = read_client_side
130
+ data = Base64.strict_encode64(JSON.dump({ 'foo' => 'bar' }))
131
+ assert_equal "HTTP/1.1 204\r\nSet-Cookie: __syntropy_session__=#{data}; Path=/; HttpOnly\r\n\r\n", response
132
+
133
+ write_http_request "GET / HTTP/1.1\r\nCookie: __syntropy_session__=#{data}\r\n\r\n"
134
+ @connection.serve_request
135
+ response = read_client_side
136
+ assert_equal "HTTP/1.1 204\r\nSet-Cookie: __syntropy_session__=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/; Max-Age=0; HttpOnly\r\n\r\n", response
137
+ end
138
+
139
+ def test_session_discard
140
+ counter = 0
141
+
142
+ @app = ->(req) {
143
+ counter += 1
144
+ case counter
145
+ when 1
146
+ req.session['foo'] = 'bar'
147
+ when 2
148
+ req.session.discard
149
+ end
150
+ req.respond(nil)
151
+ }
152
+
153
+ write_http_request "GET / HTTP/1.1\r\n\r\n", false
154
+ @connection.serve_request
155
+ response = read_client_side
156
+ data = Base64.strict_encode64(JSON.dump({ 'foo' => 'bar' }))
157
+ assert_equal "HTTP/1.1 204\r\nSet-Cookie: __syntropy_session__=#{data}; Path=/; HttpOnly\r\n\r\n", response
158
+
159
+ write_http_request "GET / HTTP/1.1\r\nCookie: __syntropy_session__=#{data}\r\n\r\n"
160
+ @connection.serve_request
161
+ response = read_client_side
162
+ assert_equal "HTTP/1.1 204\r\nSet-Cookie: __syntropy_session__=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/; Max-Age=0; HttpOnly\r\n\r\n", response
163
+ end
164
+
165
+ def test_flash_simple
166
+ counter = 0
167
+ flash_notices = []
168
+
169
+ @app = ->(req) do
170
+ counter += 1
171
+ case counter
172
+ when 1
173
+ req.session.flash[:notice] = "Hello flash!"
174
+ flash_notices << req.session.flash[:notice]
175
+ when 2
176
+ flash_notices << req.session.flash[:notice]
177
+ when 3
178
+ flash_notices << req.session.flash[:notice]
179
+ end
180
+ req.respond(nil)
181
+ end
182
+
183
+ parse_cookie = ->(response) {
184
+ m = response.match(/Set-Cookie: __syntropy_session__=([^\s;]*)/)
185
+ m && m[1]
186
+ }
187
+
188
+ cookie = nil
189
+
190
+ 3.times {
191
+ request = cookie ? "GET / HTTP/1.1\r\nCookie: __syntropy_session__=#{cookie}\r\n\r\n" : "GET / HTTP/1.1\r\n\r\n"
192
+ write_http_request request, false
193
+ @connection.serve_request
194
+ response = read_client_side
195
+ v = parse_cookie.(response)
196
+ if v
197
+ cookie = v.empty? ? nil : v
198
+ end
199
+ }
200
+
201
+ assert_equal [nil, 'Hello flash!', nil], flash_notices
202
+ end
203
+
204
+ def test_flash_each
205
+ counter = 0
206
+ flash_content = []
207
+
208
+ @app = ->(req) do
209
+ counter += 1
210
+ case counter
211
+ when 1
212
+ req.session.flash[:notice] = "Hello flash!"
213
+ a = []
214
+ req.session.flash.each { |k, v| a << [k, v] }
215
+ flash_content << a
216
+ when 2
217
+ a = []
218
+ req.session.flash.each { |k, v| a << [k, v] }
219
+ flash_content << a
220
+ when 3
221
+ a = []
222
+ req.session.flash.each { |k, v| a << [k, v] }
223
+ flash_content << a
224
+ end
225
+ req.respond(nil)
226
+ end
227
+
228
+ parse_cookie = ->(response) {
229
+ m = response.match(/Set-Cookie: __syntropy_session__=([^\s;]*)/)
230
+ m && m[1]
231
+ }
232
+
233
+ set_cookies = []
234
+ cookie = nil
235
+
236
+ 3.times {
237
+ request = cookie ? "GET / HTTP/1.1\r\nCookie: __syntropy_session__=#{cookie}\r\n\r\n" : "GET / HTTP/1.1\r\n\r\n"
238
+ write_http_request request, false
239
+ @connection.serve_request
240
+ response = read_client_side
241
+ v = parse_cookie.(response)
242
+ if v
243
+ cookie = v.empty? ? nil : v
244
+ end
245
+ set_cookies << v ? cookie : nil
246
+ }
247
+
248
+ assert_equal [
249
+ [],
250
+ [[:notice, 'Hello flash!']],
251
+ []
252
+ ], flash_content
253
+ end
254
+ end
data/test/test_server.rb CHANGED
@@ -89,12 +89,12 @@ class ServerTest < Minitest::Test
89
89
  req.respond("method: #{req.method}")
90
90
  }
91
91
 
92
- write_http_request "GET /foo HTTP/1.1\r\nServer: foo.com\r\n\r\nSCHmet /bar HTTP/1.1\r\n\r\n"
92
+ write_http_request "GET /foo HTTP/1.1\r\nServer: foo.com\r\n\r\nPUT /bar HTTP/1.1\r\n\r\n"
93
93
 
94
94
  @machine.sleep(0.1)
95
95
  response = read_client_side
96
96
  expected = "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\nb\r\nmethod: get\r\n0\r\n\r\n" +
97
- "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\ne\r\nmethod: schmet\r\n0\r\n\r\n"
97
+ "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\nb\r\nmethod: put\r\n0\r\n\r\n"
98
98
  assert_equal(expected, response)
99
99
  end
100
100
 
@@ -107,7 +107,7 @@ class ServerTest < Minitest::Test
107
107
  raise
108
108
  end
109
109
 
110
- write_http_request "GET /foo HTTP/1.1\r\nServer: foo.com\r\n\r\nSCHMET /bar HTTP/1.1\r\n\r\n"
110
+ write_http_request "GET /foo HTTP/1.1\r\nServer: foo.com\r\n\r\nPUT /bar HTTP/1.1\r\n\r\n"
111
111
 
112
112
  @machine.sleep(0.01)
113
113
  @server.stop!
@@ -132,7 +132,7 @@ class ServerTest < Minitest::Test
132
132
  Server: foo.com
133
133
  Content-Length: 3
134
134
 
135
- abcSCHMOST /bar HTTP/1.1
135
+ abcPOST /bar HTTP/1.1
136
136
  Server: bar.com
137
137
  Content-Length: 6
138
138
 
@@ -148,7 +148,6 @@ class ServerTest < Minitest::Test
148
148
  assert_equal({
149
149
  ':method' => 'post',
150
150
  ':path' => '/foo',
151
- ':protocol' => 'http/1.1',
152
151
  'server' => 'foo.com',
153
152
  'content-length' => '3',
154
153
  ':body-done-reading' => true,
@@ -160,13 +159,12 @@ class ServerTest < Minitest::Test
160
159
  req1 = @reqs.shift
161
160
  headers = req1.headers
162
161
  assert_equal({
163
- ':method' => 'schmost',
162
+ ':method' => 'post',
164
163
  ':path' => '/bar',
165
- ':protocol' => 'http/1.1',
166
164
  'server' => 'bar.com',
167
165
  'content-length' => '6',
168
166
  ':body-done-reading' => true,
169
- ':tx' => 59,
167
+ ':tx' => 56,
170
168
  }, headers)
171
169
  body = @bodies.shift
172
170
  assert_equal 'defghi', body
@@ -196,7 +194,7 @@ class ServerTest < Minitest::Test
196
194
  de
197
195
  0
198
196
 
199
- SCHMOST /bar HTTP/1.1
197
+ POST /bar HTTP/1.1
200
198
  Server: bar.com
201
199
  Transfer-Encoding: chunked
202
200
 
@@ -218,7 +216,6 @@ class ServerTest < Minitest::Test
218
216
  assert_equal({
219
217
  ':method' => 'post',
220
218
  ':path' => '/foo',
221
- ':protocol' => 'http/1.1',
222
219
  'server' => 'foo.com',
223
220
  'transfer-encoding' => 'chunked',
224
221
  ':body-done-reading' => true,
@@ -230,13 +227,12 @@ class ServerTest < Minitest::Test
230
227
  req1 = @reqs.shift
231
228
  headers = req1.headers
232
229
  assert_equal({
233
- ':method' => 'schmost',
230
+ ':method' => 'post',
234
231
  ':path' => '/bar',
235
- ':protocol' => 'http/1.1',
236
232
  'server' => 'bar.com',
237
233
  'transfer-encoding' => 'chunked',
238
234
  ':body-done-reading' => true,
239
- ':tx' => 59
235
+ ':tx' => 56
240
236
  }, headers)
241
237
  body = @bodies.shift
242
238
  assert_equal '123456789abcdefghijklmnopqrstuv', body
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntropy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.0
4
+ version: 0.33.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -52,21 +52,21 @@ dependencies:
52
52
  - !ruby/object:Gem::Version
53
53
  version: 1.0.2
54
54
  - !ruby/object:Gem::Dependency
55
- name: escape_utils
55
+ name: json
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - '='
58
+ - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 1.3.0
60
+ version: '0'
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - '='
65
+ - - ">="
66
66
  - !ruby/object:Gem::Version
67
- version: 1.3.0
67
+ version: '0'
68
68
  - !ruby/object:Gem::Dependency
69
- name: json
69
+ name: base64
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - ">="
@@ -93,6 +93,20 @@ dependencies:
93
93
  - - ">="
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: irb
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
96
110
  - !ruby/object:Gem::Dependency
97
111
  name: minitest
98
112
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +135,20 @@ dependencies:
121
135
  - - "~>"
122
136
  - !ruby/object:Gem::Version
123
137
  version: 13.3.1
138
+ - !ruby/object:Gem::Dependency
139
+ name: solargraph
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
124
152
  email: sharon@noteflakes.com
125
153
  executables:
126
154
  - syntropy
@@ -140,6 +168,7 @@ files:
140
168
  - TODO.md
141
169
  - bin/syntropy
142
170
  - cmd/_banner.rb
171
+ - cmd/console.rb
143
172
  - cmd/help.rb
144
173
  - cmd/serve.rb
145
174
  - cmd/setup/template/site/.gitignore
@@ -175,6 +204,15 @@ files:
175
204
  - examples/basic/favicon.ico
176
205
  - examples/basic/index.md
177
206
  - examples/basic/templates.rb
207
+ - examples/blog/app/_layout/default.rb
208
+ - examples/blog/app/_lib/post_store.rb
209
+ - examples/blog/app/_schema/2026-01-01-initial.rb
210
+ - examples/blog/app/_setup.rb
211
+ - examples/blog/app/index.rb
212
+ - examples/blog/app/posts/[id]/edit.rb
213
+ - examples/blog/app/posts/[id]/index.rb
214
+ - examples/blog/app/posts/index.rb
215
+ - examples/blog/app/posts/new.rb
178
216
  - examples/mcp-oauth/.ruby-version
179
217
  - examples/mcp-oauth/Gemfile
180
218
  - examples/mcp-oauth/README.md
@@ -188,7 +226,6 @@ files:
188
226
  - examples/mcp-oauth/app/oauth/register.rb
189
227
  - examples/mcp-oauth/app/oauth/token.rb
190
228
  - examples/mcp-oauth/app/signin.rb
191
- - examples/mcp-oauth/test/helper.rb
192
229
  - examples/mcp-oauth/test/test_app.rb
193
230
  - examples/mcp-oauth/test/test_oauth.rb
194
231
  - lib/syntropy.rb
@@ -202,7 +239,9 @@ files:
202
239
  - lib/syntropy/applets/builtin/json_api.js
203
240
  - lib/syntropy/applets/builtin/ping.rb
204
241
  - lib/syntropy/applets/builtin/req.rb
205
- - lib/syntropy/connection_pool.rb
242
+ - lib/syntropy/db/connection_pool.rb
243
+ - lib/syntropy/db/schema.rb
244
+ - lib/syntropy/db/store.rb
206
245
  - lib/syntropy/dev_mode.rb
207
246
  - lib/syntropy/errors.rb
208
247
  - lib/syntropy/http.rb
@@ -216,12 +255,13 @@ files:
216
255
  - lib/syntropy/logger.rb
217
256
  - lib/syntropy/markdown.rb
218
257
  - lib/syntropy/mime_types.rb
219
- - lib/syntropy/module.rb
258
+ - lib/syntropy/module_loader.rb
220
259
  - lib/syntropy/papercraft_extensions.rb
221
260
  - lib/syntropy/request.rb
222
261
  - lib/syntropy/request/mock_adapter.rb
223
262
  - lib/syntropy/request/request_info.rb
224
263
  - lib/syntropy/request/response.rb
264
+ - lib/syntropy/request/session.rb
225
265
  - lib/syntropy/request/validation.rb
226
266
  - lib/syntropy/routing_tree.rb
227
267
  - lib/syntropy/side_run.rb
@@ -247,6 +287,7 @@ files:
247
287
  - test/app/bad_mod.rb
248
288
  - test/app/bar.rb
249
289
  - test/app/baz.rb
290
+ - test/app/by_method.rb
250
291
  - test/app/deps.rb
251
292
  - test/app/index.html
252
293
  - test/app/mod/bar/index+.rb
@@ -260,21 +301,31 @@ files:
260
301
  - test/app_multi_site/_site.rb
261
302
  - test/app_multi_site/bar.baz/index.html
262
303
  - test/app_multi_site/foo.bar/index.html
304
+ - test/app_setup/_setup.rb
305
+ - test/app_setup/index.rb
306
+ - test/app_with_schema/_schema/2026-01-02-foo.rb
307
+ - test/app_with_schema/_schema/2026-05-30-bar.rb
263
308
  - test/bm_router_proc.rb
264
309
  - test/bm_server.rb
265
310
  - test/helper.rb
266
311
  - test/run.rb
312
+ - test/schema/2026-01-02-foo.rb
313
+ - test/schema/2026-05-30-bar.rb
267
314
  - test/test_app.rb
268
315
  - test/test_caching.rb
269
- - test/test_connection_pool.rb
316
+ - test/test_db_connection_pool.rb
317
+ - test/test_db_schema.rb
318
+ - test/test_db_store.rb
270
319
  - test/test_errors.rb
271
320
  - test/test_http_client.rb
272
321
  - test/test_http_client_connection.rb
322
+ - test/test_http_protocol.rb
273
323
  - test/test_http_server_connection.rb
274
324
  - test/test_json_api.rb
275
325
  - test/test_mock_adapter.rb
276
- - test/test_module.rb
326
+ - test/test_module_loader.rb
277
327
  - test/test_request.rb
328
+ - test/test_request_session.rb
278
329
  - test/test_response.rb
279
330
  - test/test_routing_tree.rb
280
331
  - test/test_server.rb
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # require 'bundler/setup'
4
- # require 'syntropy'
5
- # require 'syntropy/test'
6
- # require 'minitest/autorun'
7
-
8
- # STDOUT.sync = true
9
- # STDERR.sync = true
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'extralite'
4
-
5
- module Syntropy
6
- class ConnectionPool
7
- attr_reader :count
8
-
9
- def initialize(machine, fn, max_conn)
10
- @machine = machine
11
- @fn = fn
12
- @count = 0
13
- @max_conn = max_conn
14
- @queue = UM::Queue.new
15
- @key = :"db_#{fn}"
16
- end
17
-
18
- def with_db
19
- if (db = Thread.current[@key])
20
- @machine.snooze
21
- return yield(db)
22
- end
23
-
24
- db = checkout
25
- begin
26
- Thread.current[@key] = db
27
- yield(db)
28
- ensure
29
- Thread.current[@key] = nil
30
- checkin(db)
31
- end
32
- end
33
-
34
- def query(sql, *, **, &)
35
- with_db { it.query(sql, *, **, &) }
36
- end
37
-
38
- def execute(sql, *, **)
39
- with_db { it.execute(sql, *, **) }
40
- end
41
-
42
- private
43
-
44
- def checkout
45
- return make_db_instance if @queue.count == 0 && @count < @max_conn
46
-
47
- @machine.shift(@queue)
48
- end
49
-
50
- def checkin(db)
51
- @machine.push(@queue, db)
52
- end
53
-
54
- def make_db_instance
55
- Extralite::Database.new(@fn, wal: true).tap do
56
- @count += 1
57
- it.on_progress(mode: :at_least_once, period: 320, tick: 10) { @machine.snooze }
58
- end
59
- end
60
- end
61
- end