syntropy 0.40.0 → 0.42.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/CHANGELOG.md +14 -0
- data/TODO.md +334 -31
- data/cmd/new/template/app/_lib/storage.rb +2 -2
- data/cmd/new/template/config/test.rb +1 -1
- data/examples/agent/config/test.rb +1 -1
- data/examples/blog/app/_lib/posts.rb +10 -6
- data/examples/blog/app/_lib/storage.rb +2 -2
- data/examples/blog/app/posts/[id]/index.rb +2 -2
- data/examples/blog/config/test.rb +1 -1
- data/lib/syntropy/collection.rb +206 -0
- data/lib/syntropy/connection_pool.rb +118 -0
- data/lib/syntropy/controller_extensions.rb +12 -0
- data/lib/syntropy/http/server_connection.rb +18 -30
- data/lib/syntropy/json_api.rb +3 -12
- data/lib/syntropy/kv_store.rb +57 -0
- data/lib/syntropy/logger.rb +24 -47
- data/lib/syntropy/markdown.rb +1 -1
- data/lib/syntropy/module_loader.rb +53 -14
- data/lib/syntropy/schema.rb +95 -0
- data/lib/syntropy/storage.rb +42 -4
- data/lib/syntropy/test.rb +1 -1
- data/lib/syntropy/version.rb +1 -1
- data/lib/syntropy.rb +2 -1
- data/syntropy.gemspec +2 -2
- data/test/fixtures/app/_lib/dep_invalidate.rb +5 -0
- data/test/fixtures/collection/basic/_articles/2026-06-30-foo.md +6 -0
- data/test/fixtures/collection/basic/_articles/2026-07-13-bar.md +6 -0
- data/test/fixtures/collection/basic/_articles/2026-08-07-baz.md +6 -0
- data/test/fixtures/collection/basic/index+.rb +23 -0
- data/test/fixtures/collection/docs/_pages/01-foo/01-foo.md +4 -0
- data/test/fixtures/collection/docs/_pages/01-foo/02-bar.md +4 -0
- data/test/fixtures/collection/docs/_pages/01-foo/_index.yml +1 -0
- data/test/fixtures/collection/docs/_pages/02-bar/01-bar.md +4 -0
- data/test/fixtures/collection/docs/_pages/02-bar/02-baz.md +4 -0
- data/test/fixtures/collection/docs/_pages/02-bar/_index.yml +1 -0
- data/test/fixtures/collection/docs/index+.rb +16 -0
- data/test/test_app.rb +7 -7
- data/test/test_cmd_new.rb +38 -0
- data/test/test_collection.rb +164 -0
- data/test/test_connection_pool.rb +79 -3
- data/test/test_http_server_connection.rb +6 -6
- data/test/test_json_api.rb +4 -4
- data/test/test_kv_store.rb +6 -31
- data/test/test_markdown.rb +3 -3
- data/test/test_module_loader.rb +15 -0
- data/test/test_schema.rb +4 -4
- data/test/test_storage.rb +103 -0
- metadata +24 -11
- data/lib/syntropy/storage/connection_pool.rb +0 -106
- data/lib/syntropy/storage/kv_store.rb +0 -68
- data/lib/syntropy/storage/prepared_query.rb +0 -36
- data/lib/syntropy/storage/schema.rb +0 -92
- data/lib/syntropy/storage/store.rb +0 -33
- data/test/test_store.rb +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 30756c68cd868dbde85d4f7097b2e3196de2dd60cd8954084aef1789e972b50c
|
|
4
|
+
data.tar.gz: 7e3a60536d78429dacc1f7ff5cdd7fdb5f89d3d7f584f73a60f20c1ac56927e6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6a5a253f3a9ee2b0253aa44a30381d48daafb1a468e7775af95d3dafa174104ad5cc615f800dde75164f2e83fff798725f5f1ac3c9e518018042076598048f17
|
|
7
|
+
data.tar.gz: fc7fc0874e17c04145a0df875347c144ced4cc1ecb4e2357813cd471c622546af42dc14acf7d338c3069eb00959ea87f60917d8d0c1fc1856147e0080e200bf2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# 0.42.0 2026-10-04
|
|
2
|
+
|
|
3
|
+
- Add `Syntropy::Collection`
|
|
4
|
+
- Support for nested collections
|
|
5
|
+
- Support for invalidation on collection file change
|
|
6
|
+
- Improve static file serving
|
|
7
|
+
- Read modules using `UM#file_read`
|
|
8
|
+
- Refactor log entry computation
|
|
9
|
+
- Refactor and simplify storage layer
|
|
10
|
+
|
|
11
|
+
# 0.41.0 2026-08-25
|
|
12
|
+
|
|
13
|
+
- Don't log ECONNRESET errors
|
|
14
|
+
|
|
1
15
|
# 0.40.0 2026-08-24
|
|
2
16
|
|
|
3
17
|
- Update UringMachine
|
data/TODO.md
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Extralite doc site
|
|
2
2
|
|
|
3
|
-
-
|
|
3
|
+
- Similar to Papercraft site
|
|
4
|
+
- Implement collection with code from papercraft.noteflakes.com
|
|
5
|
+
- Further take ideas from discussion below
|
|
6
|
+
- Implement auto light/dark CSS theme
|
|
7
|
+
- Write docs
|
|
8
|
+
|
|
9
|
+
## Logging
|
|
10
|
+
|
|
11
|
+
- Make it possible to use different logger implementations, or maybe chain
|
|
12
|
+
multiple logger implementations, such that we could emit for example both to
|
|
13
|
+
STDOUT and to a database.
|
|
14
|
+
- Add optional SQLite-log store (+optional log viewing in Admin interface)
|
|
15
|
+
|
|
16
|
+
## Nicer logging in development mode:
|
|
4
17
|
|
|
5
18
|
Nice terminal formatting with colors:
|
|
6
19
|
|
|
@@ -34,6 +47,128 @@ sharon@nf1:~/tmp/capatest2$ npx serve dist
|
|
|
34
47
|
INFO Gracefully shutting down. Please wait...
|
|
35
48
|
```
|
|
36
49
|
|
|
50
|
+
```
|
|
51
|
+
sharon@nf1 ~/ $ be syntropy serve
|
|
52
|
+
|
|
53
|
+
ooo
|
|
54
|
+
ooooo
|
|
55
|
+
ooo vvv Syntropy - a web framework for Ruby
|
|
56
|
+
o vvvvv --------------------------------------
|
|
57
|
+
| vvv o https://github.com/digital-fabric/syntropy
|
|
58
|
+
:|:::|::|:
|
|
59
|
+
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
60
|
+
|
|
61
|
+
2026-06-07 08:04:23 <== GET /
|
|
62
|
+
2026-06-07 08:04:23 ==> 200 (42ms)
|
|
63
|
+
2026-06-07 08:04:23 <== GET /assets/index-edfizW3i.js
|
|
64
|
+
2026-06-07 08:04:23 <== GET /assets/index-PVjztr5e.css
|
|
65
|
+
2026-06-07 08:04:23 ==> 200 (3ms)
|
|
66
|
+
2026-06-07 08:04:23 ==> 200 (12ms)
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Verbose output (`-v`):
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
2026-06-07 08:04:23 <== GET /
|
|
74
|
+
Host: localhost:3000
|
|
75
|
+
User-Agent: ...
|
|
76
|
+
Cookie: ...
|
|
77
|
+
Accept: ...
|
|
78
|
+
2026-06-07 08:04:23 ==> 200 (42ms)
|
|
79
|
+
Server: syntropy
|
|
80
|
+
Transfer-Encoding: chunked
|
|
81
|
+
...
|
|
82
|
+
2026-06-07 08:04:23 <== GET /assets/index-edfizW3i.js
|
|
83
|
+
...
|
|
84
|
+
2026-06-07 08:04:23 <== GET /assets/index-PVjztr5e.css
|
|
85
|
+
...
|
|
86
|
+
2026-06-07 08:04:23 ==> 200 (3ms)
|
|
87
|
+
...
|
|
88
|
+
2026-06-07 08:04:23 ==> 200 (12ms)
|
|
89
|
+
...
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Background jobs
|
|
93
|
+
|
|
94
|
+
- Backed by SQLite db
|
|
95
|
+
- Recurring jobs
|
|
96
|
+
- Scheduled jobs
|
|
97
|
+
- Retries
|
|
98
|
+
- CLI command `syntropy background`
|
|
99
|
+
- Run automatically when running `syntropy serve` in dev mode
|
|
100
|
+
|
|
101
|
+
## Admin Dashboard
|
|
102
|
+
|
|
103
|
+
- Server state / stats
|
|
104
|
+
- Background jobs state / stats
|
|
105
|
+
- For resources:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
# /app/admin+.rb
|
|
109
|
+
export admin_ui(
|
|
110
|
+
resources: {
|
|
111
|
+
posts: {
|
|
112
|
+
type: 'crud',
|
|
113
|
+
module: '/storage/posts',
|
|
114
|
+
primary_key: :id,
|
|
115
|
+
editor_fields: {
|
|
116
|
+
id: hidden
|
|
117
|
+
title: text,
|
|
118
|
+
body: textarea
|
|
119
|
+
},
|
|
120
|
+
actions: {
|
|
121
|
+
list: ->(s) { s.list() },
|
|
122
|
+
list:
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Or, maybe:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
# /app/admin/index.rb
|
|
133
|
+
Admin = import '/admin/.controller'
|
|
134
|
+
export Admin.homepage
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# /app/admin/.controller.rb
|
|
138
|
+
require 'syntropy/admin'
|
|
139
|
+
export Syntropy::Admin.new(
|
|
140
|
+
# config
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# /app/admin/resources/posts.rb
|
|
145
|
+
Admin = import '/admin/.controller'
|
|
146
|
+
Posts = import '/storage/posts'
|
|
147
|
+
|
|
148
|
+
Resource = Admin.resource(
|
|
149
|
+
name: 'Posts',
|
|
150
|
+
fields: {
|
|
151
|
+
id: hidden
|
|
152
|
+
title: text,
|
|
153
|
+
body: textarea
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
export Resource.controller
|
|
157
|
+
|
|
158
|
+
def list(req)
|
|
159
|
+
list = Posts.list
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
actions: {
|
|
163
|
+
list: ->(s) { s.list() },
|
|
164
|
+
list:
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# Pub/sub
|
|
171
|
+
|
|
37
172
|
- [ ] Ability to load modules from builtin applet
|
|
38
173
|
- [ ] Can we mount them on the app's module loader?
|
|
39
174
|
|
|
@@ -41,46 +176,56 @@ sharon@nf1:~/tmp/capatest2$ npx serve dist
|
|
|
41
176
|
|
|
42
177
|
- [ ] Pub/sub
|
|
43
178
|
- [ ] Ruby side
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
@firehose = @app.import('/.syntropy/firehose')
|
|
182
|
+
|
|
183
|
+
def on_change(v)
|
|
184
|
+
@firehose.publish({ kind: 'value_changed', value: v })
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
## in a pub/sub controller
|
|
188
|
+
@firehose = @app.import('/.syntropy/firehose')
|
|
189
|
+
|
|
190
|
+
def call(req)
|
|
191
|
+
@firehose.stream_sse(req)
|
|
192
|
+
end
|
|
193
|
+
```
|
|
194
|
+
|
|
44
195
|
- [ ] JS side
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
await window.syntropy.firehose.process(async (evt, next) => {
|
|
199
|
+
if (evt.kind == 'value_changed')
|
|
200
|
+
await valueChanged(evt.value)
|
|
201
|
+
else if (next)
|
|
202
|
+
await next(evt);
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
45
206
|
- [ ] Reimplement `auto_refresh` using a *default* event bus provided by
|
|
46
207
|
Syntropy
|
|
47
208
|
|
|
48
209
|
- [ ] An alternative to the pub/sub design - add streaming responses to the
|
|
49
210
|
JSON/jS API (using SSE).
|
|
50
211
|
|
|
51
|
-
##
|
|
52
|
-
|
|
53
|
-
- [ ] Collection - treat directories and files as collections of data.
|
|
212
|
+
## Scaffolding
|
|
54
213
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
formats:
|
|
214
|
+
In rails, you can generate specific parts of the app, like a model or a
|
|
215
|
+
controller, or a whole set of model, view, controller code for a given entity
|
|
216
|
+
(scaffolding.)
|
|
59
217
|
|
|
60
|
-
|
|
61
|
-
- foo.json - JSON record
|
|
62
|
-
- foo.yml - YAML record
|
|
218
|
+
Does this make sense for Syntropy? The different kinds of code generated:
|
|
63
219
|
|
|
64
|
-
|
|
220
|
+
- a model with CRUD methods for the given entity.
|
|
221
|
+
- a set of CRUD controllers (spread over multiple files).
|
|
222
|
+
- a set of CRUD views corresponding to the controller.
|
|
223
|
+
- a set of tests for model, controller, view.
|
|
65
224
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
article.title #=>
|
|
71
|
-
article.date #=>
|
|
72
|
-
article.layout #=>
|
|
73
|
-
article.render_proc #=> (load layout, apply article)
|
|
74
|
-
article.render #=> (render to HTML)
|
|
75
|
-
|
|
76
|
-
# there should also be methods for creating, updating and deleting of
|
|
77
|
-
# articles/items.
|
|
78
|
-
...
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
- [ ] Improve serving of static files:
|
|
82
|
-
- [ ] support for compression
|
|
83
|
-
- [ ] add `Request#render_static_file(route, fn)
|
|
225
|
+
But in fact they don't make much sense separately, except maybe for the model.
|
|
226
|
+
So we follow Rail's example and implement a code generator. This is especially
|
|
227
|
+
useful for working with controllers. Since the controller code is spread over
|
|
228
|
+
four files, this is not something trivial.
|
|
84
229
|
|
|
85
230
|
## Missing for a first public release
|
|
86
231
|
|
|
@@ -153,3 +298,161 @@ end
|
|
|
153
298
|
2.5. If docker compose services are running, restart
|
|
154
299
|
2.6. Otherwise, start services
|
|
155
300
|
2.7. Verify service is running correctly
|
|
301
|
+
|
|
302
|
+
## Syntropy Flow - Live Templates
|
|
303
|
+
|
|
304
|
+
The idea is to have something that works like LiveView, but without WebSocket.
|
|
305
|
+
Instead, we'll use SSE in conjunction with a controller URL that provides both
|
|
306
|
+
updates (through SSE), and receives user interaction from the browser with POST
|
|
307
|
+
requests.
|
|
308
|
+
|
|
309
|
+
The compiler needs to be able to compile:
|
|
310
|
+
|
|
311
|
+
- The HTML
|
|
312
|
+
- The initial update tree (statics + dynamics)
|
|
313
|
+
- Updated dynamic values
|
|
314
|
+
|
|
315
|
+
Supposing that the HTML and the initial update tree are rendered at once. Let's
|
|
316
|
+
say we have the following template:
|
|
317
|
+
|
|
318
|
+
```ruby
|
|
319
|
+
->(name:) {
|
|
320
|
+
html {
|
|
321
|
+
head { title "My live page" }
|
|
322
|
+
body {
|
|
323
|
+
p "Hello, #{name}!"
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Which normally compiles to:
|
|
330
|
+
|
|
331
|
+
```ruby
|
|
332
|
+
->(__buffer__, name:) {
|
|
333
|
+
__buffer__
|
|
334
|
+
.<<("<!DOCTYPE html><html><head><title>My live page</title></head><body><p>")
|
|
335
|
+
.<<(ERB::Escape.html_escape(("Hello, #{name}!")))
|
|
336
|
+
.<<("</p></body></html>")
|
|
337
|
+
__buffer__
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
A live template would look something like:
|
|
342
|
+
|
|
343
|
+
```ruby
|
|
344
|
+
# __flow_url__ is the URL used for the SSE connection, it includes an id for the
|
|
345
|
+
# specific connection.
|
|
346
|
+
initial = ->(__flow_url__, name:) {
|
|
347
|
+
__html__ = +''
|
|
348
|
+
__static__ = []; __dynamic__ = {}; __dynamic_counter__ = 0;
|
|
349
|
+
|
|
350
|
+
__html__
|
|
351
|
+
.<<(
|
|
352
|
+
s = "<!DOCTYPE html><html><head><title>My live page</title></head><body><p>"
|
|
353
|
+
__static__ << s
|
|
354
|
+
s
|
|
355
|
+
)
|
|
356
|
+
.<<(
|
|
357
|
+
s = ERB::Escape.html_escape(("Hello, #{name}!"))
|
|
358
|
+
__dynamic__[__dynamic_counter__] = s
|
|
359
|
+
__dynamic_counter__ += 1
|
|
360
|
+
s
|
|
361
|
+
)
|
|
362
|
+
.<<(
|
|
363
|
+
s = "</p></body><script src=\"#{__flow_url__}\"></script></html>"
|
|
364
|
+
__static__ << s
|
|
365
|
+
s
|
|
366
|
+
)
|
|
367
|
+
{
|
|
368
|
+
html: __html__,
|
|
369
|
+
update_tree = {
|
|
370
|
+
s: __static__,
|
|
371
|
+
d: __dynamic__
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
The controller would look something like:
|
|
378
|
+
|
|
379
|
+
```ruby
|
|
380
|
+
@template = import '/views/hello_world'
|
|
381
|
+
|
|
382
|
+
export flow_controller do
|
|
383
|
+
def setup
|
|
384
|
+
assigns[:name] = 'world'
|
|
385
|
+
assigns[:counter] ||= 1
|
|
386
|
+
@updater = @machine.spin {
|
|
387
|
+
@machine.periodically(3) {
|
|
388
|
+
assigns[:name] = "world (#{assignes[:counter] += 1})"
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def teardown
|
|
394
|
+
@machine.cancel(@updater)
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
The flow_controller can handle four kinds of requests:
|
|
400
|
+
|
|
401
|
+
- `GET /hello` - This is the initial render, where it will:
|
|
402
|
+
- generate a unique flow id which will be embedded in a flow url (see below)
|
|
403
|
+
- render `initial` compiled template, and store the update tree in a short
|
|
404
|
+
term hash (which is evacuated periodically for failed connections)
|
|
405
|
+
- respond with the HTML
|
|
406
|
+
- `GET /hello?flow=setup` - The JS payload to start the SSE connection
|
|
407
|
+
- boilerplate code for setting up the SSE connection
|
|
408
|
+
- include inline the initial update tree containing static and dynamic parts
|
|
409
|
+
- `GET /hello?sse&fid=xxxx` - the SSE long-running connection, on which
|
|
410
|
+
updates are sent. The flow id identifies the flow session, which permits
|
|
411
|
+
reconnection in case of comm error. On connection, the controller will emit an
|
|
412
|
+
initial update with the update tree (see below).
|
|
413
|
+
|
|
414
|
+
- `POST /hello?fid=xxxx` - for responding to user interaction.
|
|
415
|
+
|
|
416
|
+
Whenever a value in `assigns` changes, the *update* template is rerendered.
|
|
417
|
+
Here's how the compiled update template looks:
|
|
418
|
+
|
|
419
|
+
```ruby
|
|
420
|
+
initial = ->(name:) {
|
|
421
|
+
__dynamic__ = {}; __dynamic_counter__ = 0;
|
|
422
|
+
|
|
423
|
+
s = ERB::Escape.html_escape(("Hello, #{name}!"))
|
|
424
|
+
__dynamic__[__dynamic_counter__] = s
|
|
425
|
+
__dynamic_counter__ += 1
|
|
426
|
+
|
|
427
|
+
__dynamic__
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
So, the initial update for this template will look something like the following:
|
|
432
|
+
|
|
433
|
+
```json
|
|
434
|
+
{
|
|
435
|
+
s: [
|
|
436
|
+
"<!DOCTYPE html><html><head><title>My live page</title></head><body><p>",
|
|
437
|
+
"</p></body><script src=\"/hello?sse&fid=12345678\"></script></html>"
|
|
438
|
+
],
|
|
439
|
+
d: {
|
|
440
|
+
0: "world"
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Subsequent updates will look like:
|
|
446
|
+
|
|
447
|
+
```json
|
|
448
|
+
{
|
|
449
|
+
d: {
|
|
450
|
+
0: "world (10:27:32)"
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
On the client side, each time an update is received on the SSE connection, it
|
|
456
|
+
includes the dynamic values. Those are diffed against the previous values, the
|
|
457
|
+
entire page HTML is zipped from the static and dynamic parts, and then morphdom
|
|
458
|
+
is used to patch the DOM with the relevant changes.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export self
|
|
2
2
|
|
|
3
3
|
def connection_pool
|
|
4
|
-
@connection_pool ||=
|
|
4
|
+
@connection_pool ||= ConnectionPool.new(@machine, @env[:config][:storage][:path], 4)
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def schema
|
|
8
|
-
|
|
8
|
+
Schema.new(module_loader: @module_loader, schema_root: '_schema')
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def migrate!
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
Storage = import '/_lib/storage'
|
|
2
2
|
|
|
3
|
-
class PostStore
|
|
3
|
+
class PostStore
|
|
4
|
+
def initialize(cp)
|
|
5
|
+
@cp = cp
|
|
6
|
+
end
|
|
7
|
+
|
|
4
8
|
# @return [Integer] post id
|
|
5
9
|
def create(title, body)
|
|
6
|
-
query_single_value <<~SQL, title:, body:
|
|
10
|
+
@cp.query_single_value <<~SQL, title:, body:
|
|
7
11
|
insert into posts (title, body)
|
|
8
12
|
values (:title, :body)
|
|
9
13
|
returning id;
|
|
@@ -12,7 +16,7 @@ class PostStore < Syntropy::Storage::Store
|
|
|
12
16
|
|
|
13
17
|
# @return [void]
|
|
14
18
|
def update(id, title, body)
|
|
15
|
-
execute <<~SQL, id:, title:, body:
|
|
19
|
+
@cp.execute <<~SQL, id:, title:, body:
|
|
16
20
|
update posts
|
|
17
21
|
set title = :title, body = :body
|
|
18
22
|
where id = :id
|
|
@@ -21,7 +25,7 @@ class PostStore < Syntropy::Storage::Store
|
|
|
21
25
|
|
|
22
26
|
# @return [void]
|
|
23
27
|
def delete(id)
|
|
24
|
-
execute <<~SQL, id:
|
|
28
|
+
@cp.execute <<~SQL, id:
|
|
25
29
|
delete from posts
|
|
26
30
|
where id = :id
|
|
27
31
|
SQL
|
|
@@ -29,7 +33,7 @@ class PostStore < Syntropy::Storage::Store
|
|
|
29
33
|
|
|
30
34
|
# return [Hash]
|
|
31
35
|
def get(id)
|
|
32
|
-
query_single_row <<~SQL, id:
|
|
36
|
+
@cp.query_single_row <<~SQL, id:
|
|
33
37
|
select id, title, body
|
|
34
38
|
from posts
|
|
35
39
|
where id = :id
|
|
@@ -38,7 +42,7 @@ class PostStore < Syntropy::Storage::Store
|
|
|
38
42
|
|
|
39
43
|
# return [Array<Hash>]
|
|
40
44
|
def get_all
|
|
41
|
-
query <<~SQL
|
|
45
|
+
@cp.query <<~SQL
|
|
42
46
|
select id, title, body
|
|
43
47
|
from posts
|
|
44
48
|
order by id desc
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export self
|
|
2
2
|
|
|
3
3
|
def connection_pool
|
|
4
|
-
@connection_pool ||=
|
|
4
|
+
@connection_pool ||= ConnectionPool.new(@machine, @env[:config][:storage][:path], 4)
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def schema
|
|
8
|
-
|
|
8
|
+
Schema.new(module_loader: @module_loader, schema_root: '_schema')
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def migrate!
|
|
@@ -23,7 +23,7 @@ def post(req)
|
|
|
23
23
|
raise BadRequestError, "Failed to update post" if updated != 1
|
|
24
24
|
|
|
25
25
|
req.flash[:notice] = 'Post was successfully updated.'
|
|
26
|
-
req.redirect "/posts/#{id}",
|
|
26
|
+
req.redirect "/posts/#{id}", HTTP::SEE_OTHER
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def delete(req)
|
|
@@ -33,7 +33,7 @@ def delete(req)
|
|
|
33
33
|
raise BadRequestError, 'Failed to delete post' if deleted != 1
|
|
34
34
|
|
|
35
35
|
req.flash[:notice] = 'Post was successfully destroyed.'
|
|
36
|
-
req.redirect '..',
|
|
36
|
+
req.redirect '..', HTTP::SEE_OTHER
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
@template = @layout.apply { |post:, req:, **props|
|