syntropy 0.11 → 0.12
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 +9 -0
- data/Rakefile +1 -1
- data/TODO.md +180 -135
- data/bin/syntropy +8 -3
- data/lib/syntropy/app.rb +227 -111
- data/lib/syntropy/errors.rb +40 -12
- data/lib/syntropy/markdown.rb +4 -2
- data/lib/syntropy/module.rb +9 -10
- data/lib/syntropy/request_extensions.rb +112 -2
- data/lib/syntropy/routing_tree.rb +553 -0
- data/lib/syntropy/version.rb +1 -1
- data/lib/syntropy.rb +1 -1
- data/syntropy.gemspec +1 -1
- data/test/app/params/[foo].rb +3 -0
- data/test/helper.rb +18 -2
- data/test/test_app.rb +17 -25
- data/test/test_errors.rb +38 -0
- data/test/test_request_extensions.rb +163 -0
- data/test/test_routing_tree.rb +427 -0
- metadata +8 -6
- data/lib/syntropy/router.rb +0 -245
- data/test/test_router.rb +0 -116
- data/test/test_validation.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18d6c37aa12dcf2315d95bc656d1cbf196ada1dd0e2309b0f1bf1ca8b65a1cbb
|
4
|
+
data.tar.gz: 04f12a324d704814f45d9218e6e02d0d38469dbed753dc8202009568f447d400
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c62d9b26c9754c23bfc53dd25c1d698733e1d01b703d580db17cfeed1505c36f3f382fcd762ef0e91e641505cda26af03a328e549a840f9f7af4b2ab7c582bc0
|
7
|
+
data.tar.gz: 0577b8d2fda40cb40c81419c31c7f515230f7948ab49158a7ca2d647fe71a323d51b176003d976b1e5428fe4ead6415064321340c0e20b5c84806d1e52266ce0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# 0.12 2025-08-28
|
2
|
+
|
3
|
+
- Add routing info to request: `#route`, `#route_params`
|
4
|
+
- Improve validations
|
5
|
+
- Improve errors
|
6
|
+
- Reimplement `App`
|
7
|
+
- Add support for parametric routes
|
8
|
+
- Reimplement `RoutingTree` (was `Router`)
|
9
|
+
|
1
10
|
## 0.11 2025-08-17
|
2
11
|
|
3
12
|
- Upgrade to P2 2.8
|
data/Rakefile
CHANGED
data/TODO.md
CHANGED
@@ -1,137 +1,182 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
-
|
8
|
-
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
1
|
+
## Support for applets
|
2
|
+
|
3
|
+
- can be implemented as separate gems
|
4
|
+
- can route requests to a different directory (i.e. inside the gem directory)
|
5
|
+
- simple way to instantiate and setup the applet
|
6
|
+
- as a first example, implement an auth/signin applet:
|
7
|
+
- session hook
|
8
|
+
- session persistence
|
9
|
+
- login page
|
10
|
+
- support for custom behaviour and custom workflows (2FA, signin using OTP etc.)
|
11
|
+
|
12
|
+
Example usage:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# /admin+.rb
|
16
|
+
require 'syntropy/admin'
|
17
|
+
|
18
|
+
export Syntropy::Admin.new(@ref, @env)
|
19
|
+
```
|
20
|
+
|
21
|
+
Implementation:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# syntropy-admin/lib/syntropy/admin.rb
|
25
|
+
APP_ROOT = File.expand_path(File.join(__dir__, '../../app'))
|
26
|
+
|
27
|
+
class Syntropy::Admin < Syntropy::App
|
28
|
+
def new(mount_path, env)
|
29
|
+
super(env[:machine], APP_ROOT, mount_path, env)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
## Response: cookies and headers
|
35
|
+
|
36
|
+
We need a way to inject cookies into the response. This probably should be done
|
37
|
+
in the TP2 code:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
@@default_set_cookie_attr = 'HttpOnly'
|
41
|
+
def self.default_set_cookie_attr=(v)
|
42
|
+
@@default_set_cookie_attr = v
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_cookie(key, value, attr = @@default_set_cookie_attr)
|
46
|
+
@buffered_headers ||= +''
|
47
|
+
@buffered_headers << format(
|
48
|
+
"Set-Cookie: %<key>s=%<value>s; %<attr>s\n",
|
49
|
+
key:, value:, attr:
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_headers(headers)
|
54
|
+
@buffered_headers ||= +''
|
55
|
+
@buffered_headers << format_headers(headers)
|
56
|
+
end
|
57
|
+
|
58
|
+
...
|
59
|
+
|
60
|
+
req.set_cookie('at', 'foobar', 'SameSite=none; Secure; HttpOnly')
|
61
|
+
```
|
62
|
+
|
63
|
+
## Middleware
|
64
|
+
|
65
|
+
Some standard middleware:
|
66
|
+
|
67
|
+
- request rewriter
|
68
|
+
- logger
|
69
|
+
- auth
|
70
|
+
- selector + terminator
|
71
|
+
|
72
|
+
```Ruby
|
73
|
+
# For the chainable DSL shown below, we need to create a custom class:
|
74
|
+
class Syntropy::Middleware::Selector
|
75
|
+
def initialize(select_proc, terminator_proc = nil)
|
76
|
+
@select_proc = select_proc
|
77
|
+
@terminator_proc = terminator_proc
|
38
78
|
end
|
39
79
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
80
|
+
def to_proc
|
81
|
+
->(req, proc) {
|
82
|
+
@select_proc.(req) ? @terminator_proc.(req) : proc(req)
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def terminate(&proc)
|
87
|
+
@terminator_proc = proc
|
88
|
+
end
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
```Ruby
|
93
|
+
# a _site.rb file can be used to wrap a whole app
|
94
|
+
# site/_site.rb
|
95
|
+
|
96
|
+
# this means we route according to the host header, with each
|
97
|
+
export Syntropy.route_by_host
|
98
|
+
|
99
|
+
# we can also rewrite requests:
|
100
|
+
rewriter = Syntropy
|
101
|
+
.select { it.host =~ /^tolkora\.(org|com)$/ }
|
102
|
+
.terminate { it.redirect_permanent('https://tolkora.net') }
|
103
|
+
|
104
|
+
# This is actuall a pretty interesting DSL design:
|
105
|
+
# a chain of operations that compose functions. So, we can select a
|
106
|
+
export rewriter.wrap(default_app)
|
107
|
+
|
108
|
+
# composing
|
109
|
+
export rewriter.wrap(Syntropy.some_custom_app.wrap(app))
|
110
|
+
|
111
|
+
# or maybe
|
112
|
+
export rewriter << some_other_middleware << app
|
113
|
+
```
|
114
|
+
|
115
|
+
## CLI tool for setting up a site repo:
|
116
|
+
|
117
|
+
```bash
|
118
|
+
# clone a newly created repo
|
119
|
+
~/repo$ git clone https://github.com/foo/bar
|
120
|
+
...
|
121
|
+
~/repo$ syntropy setup bar
|
122
|
+
|
123
|
+
(syntropy banner)
|
124
|
+
|
125
|
+
Setting up Syntropy project in /home/sharon/repo/bar:
|
126
|
+
|
127
|
+
bar/
|
128
|
+
bin/
|
129
|
+
start
|
130
|
+
stop
|
131
|
+
restart
|
132
|
+
console
|
133
|
+
server
|
134
|
+
docker-compose.yml
|
135
|
+
Dockerfile
|
136
|
+
Gemfile
|
137
|
+
proxy/
|
138
|
+
README.md
|
139
|
+
site/
|
140
|
+
_layout/
|
141
|
+
default.rb
|
142
|
+
_lib/
|
143
|
+
about.md
|
144
|
+
articles/
|
145
|
+
long-form.md
|
146
|
+
assets/
|
147
|
+
js/
|
148
|
+
css/
|
149
|
+
style.css
|
150
|
+
img/
|
151
|
+
syntropy.png
|
152
|
+
index.rb
|
153
|
+
```
|
154
|
+
|
155
|
+
Some of the files might need templating, but we can maybe do without, or at
|
156
|
+
least make it as generic as possible.
|
157
|
+
|
158
|
+
`syntropy setup` steps:
|
159
|
+
|
160
|
+
1. Verify existence of target directory
|
161
|
+
2. Copy files from Syntropy template to target directory
|
162
|
+
3. Do chmod +x for bin/*
|
163
|
+
4. Do bundle install in the target directory
|
164
|
+
5. Show some information with regard to how to get started working with the
|
165
|
+
repo
|
166
|
+
|
167
|
+
`syntropy provision` steps:
|
168
|
+
|
169
|
+
1. Verify Ubuntu 22.x or higher
|
170
|
+
2. Install git, docker, docker-compose
|
171
|
+
|
172
|
+
`syntropy deploy` steps:
|
173
|
+
|
174
|
+
1. Verify no uncommitted changes.
|
175
|
+
2. SSH to remote machine.
|
176
|
+
2.1. If not exists, clone repo
|
177
|
+
2.2. Otherwise, verify remote machine repo is on same branch as local repo
|
178
|
+
2.3. Do a git pull (what about credentials?)
|
179
|
+
2.4. If gem bundle has changed, do a docker compose build
|
180
|
+
2.5. If docker compose services are running, restart
|
181
|
+
2.6. Otherwise, start services
|
182
|
+
2.7. Verify service is running correctly
|
data/bin/syntropy
CHANGED
@@ -5,6 +5,7 @@ require 'syntropy'
|
|
5
5
|
require 'optparse'
|
6
6
|
|
7
7
|
opts = {
|
8
|
+
mount_path: '/',
|
8
9
|
banner: Syntropy::BANNER,
|
9
10
|
logger: true
|
10
11
|
}
|
@@ -32,6 +33,10 @@ parser = OptionParser.new do |o|
|
|
32
33
|
exit
|
33
34
|
end
|
34
35
|
|
36
|
+
o.on('-m', '--mount', 'Set mount path (default: /)') do
|
37
|
+
opts[:mount_path] = it
|
38
|
+
end
|
39
|
+
|
35
40
|
o.on('-v', '--version', 'Show version') do
|
36
41
|
require 'syntropy/version'
|
37
42
|
puts "Syntropy version #{Syntropy::VERSION}"
|
@@ -49,10 +54,10 @@ rescue StandardError => e
|
|
49
54
|
exit
|
50
55
|
end
|
51
56
|
|
52
|
-
opts[:
|
57
|
+
opts[:root_dir] = ARGV.shift || '.'
|
53
58
|
|
54
|
-
if !File.directory?(opts[:
|
55
|
-
puts "#{File.expand_path(opts[:
|
59
|
+
if !File.directory?(opts[:root_dir])
|
60
|
+
puts "#{File.expand_path(opts[:root_dir])} Not a directory"
|
56
61
|
exit
|
57
62
|
end
|
58
63
|
|