Skip to content

Commit 9178113

Browse files
committed
Cleanup, some docs
1 parent 2d5b492 commit 9178113

4 files changed

Lines changed: 201 additions & 142 deletions

File tree

TODO.md

Lines changed: 186 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,188 @@
1-
- a way to inject cookies into the response
2-
3-
- add support for applets
4-
5-
- can be implemented as separate gems
6-
- can route requests to a different directory (i.e. inside the gem directory)
7-
- simple way to instantiate and setup the applet
8-
- as a first example, implement an auth/signin applet:
9-
- session hook
10-
- session persistence
11-
- login page
12-
- support for custom behaviour and custom workflows (2FA, signin using OTP etc.)
13-
14-
- Some standard middleware:
15-
16-
- request rewriter
17-
- logger
18-
- auth
19-
- selector + terminator
20-
21-
```Ruby
22-
# For the chainable DSL shown below, we need to create a custom class:
23-
class Syntropy::Middleware::Selector
24-
def initialize(select_proc, terminator_proc = nil)
25-
@select_proc = select_proc
26-
@terminator_proc = terminator_proc
27-
end
28-
29-
def to_proc
30-
->(req, proc) {
31-
@select_proc.(req) ? @terminator_proc.(req) : proc(req)
32-
}
33-
end
34-
35-
def terminate(&proc)
36-
@terminator_proc = proc
37-
end
1+
## Routing: add support for parameter routes
2+
3+
(following the SvelteKit convention: https://svelte.dev/docs/kit/routing):
4+
5+
Implementation in docs repo. The routing tree should be compiled.
6+
7+
## Support for applets
8+
9+
- can be implemented as separate gems
10+
- can route requests to a different directory (i.e. inside the gem directory)
11+
- simple way to instantiate and setup the applet
12+
- as a first example, implement an auth/signin applet:
13+
- session hook
14+
- session persistence
15+
- login page
16+
- support for custom behaviour and custom workflows (2FA, signin using OTP etc.)
17+
18+
Example usage:
19+
20+
```ruby
21+
# /admin+.rb
22+
require 'syntropy/admin'
23+
24+
export Syntropy::Admin.new(@ref, @env)
25+
```
26+
27+
Implementation:
28+
29+
```ruby
30+
# syntropy-admin/lib/syntropy/admin.rb
31+
APP_ROOT = File.expand_path(File.join(__dir__, '../../app'))
32+
33+
class Syntropy::Admin < Syntropy::App
34+
def new(mount_path, env)
35+
super(env[:machine], APP_ROOT, mount_path, env)
3836
end
37+
end
38+
```
39+
40+
## Response: cookies and headers
41+
42+
We need a way to inject cookies into the response. This probably should be done
43+
in the TP2 code:
44+
45+
```ruby
46+
@@default_set_cookie_attr = 'HttpOnly'
47+
def self.default_set_cookie_attr=(v)
48+
@@default_set_cookie_attr = v
49+
end
3950

40-
def Syntropy
41-
```
42-
43-
```Ruby
44-
# a _site.rb file can be used to wrap a whole app
45-
# site/_site.rb
46-
47-
# this means we route according to the host header, with each
48-
export Syntropy.route_by_host
49-
50-
# we can also rewrite requests:
51-
rewriter = Syntropy
52-
.select { it.host =~ /^tolkora\.(org|com)$/ }
53-
.terminate { it.redirect_permanent('https://tolkora.net') }
54-
55-
# This is actuall a pretty interesting DSL design:
56-
# a chain of operations that compose functions. So, we can select a
57-
export rewriter.wrap(default_app)
58-
59-
# composing
60-
export rewriter.wrap(Syntropy.some_custom_app.wrap(app))
61-
62-
# or maybe
63-
export rewriter << some_other_middleware << app
64-
```
65-
66-
67-
68-
69-
- CLI tool for setting up a site repo:
70-
71-
```bash
72-
# clone a newly created repo
73-
~/repo$ git clone https://github.com/foo/bar
74-
...
75-
~/repo$ syntropy setup bar
76-
77-
(syntropy banner)
78-
79-
Setting up Syntropy project in /home/sharon/repo/bar:
80-
81-
bar/
82-
bin/
83-
start
84-
stop
85-
restart
86-
console
87-
server
88-
docker-compose.yml
89-
Dockerfile
90-
Gemfile
91-
proxy/
92-
93-
README.md
94-
site/
95-
_layout/
96-
default.rb
97-
_lib/
98-
about.md
99-
articles/
100-
long-form.md
101-
assets/
102-
js/
103-
css/
104-
style.css
105-
img/
106-
syntropy.png
107-
index.rb
108-
```
109-
110-
- Some of the files might need templating, but we can maybe do without, or at
111-
least make it as generic as possible.
112-
113-
- `syntropy setup` steps:
114-
115-
1. Verify existence of target directory
116-
2. Copy files from Syntropy template to target directory
117-
3. Do chmod +x for bin/*
118-
4. Do bundle install in the target directory
119-
5. Show some information with regard to how to get started working with the
120-
repo
121-
122-
- `syntropy provision` steps:
123-
124-
1. Verify Ubuntu 22.x or higher
125-
2. Install git, docker, docker-compose
126-
127-
- `syntropy deploy` steps:
128-
129-
1. Verify no uncommitted changes.
130-
2. SSH to remote machine.
131-
2.1. If not exists, clone repo
132-
2.2. Otherwise, verify remote machine repo is on same branch as local repo
133-
2.3. Do a git pull (what about credentials?)
134-
2.4. If gem bundle has changed, do a docker compose build
135-
2.5. If docker compose services are running, restart
136-
2.6. Otherwise, start services
137-
2.7. Verify service is running correctly
51+
def set_cookie(key, value, attr = @@default_set_cookie_attr)
52+
@buffered_headers ||= +''
53+
@buffered_headers << format(
54+
"Set-Cookie: %<key>s=%<value>s; %<attr>s\n",
55+
key:, value:, attr:
56+
)
57+
end
58+
59+
def set_headers(headers)
60+
@buffered_headers ||= +''
61+
@buffered_headers << format_headers(headers)
62+
end
63+
64+
...
65+
66+
req.set_cookie('at', 'foobar', 'SameSite=none; Secure; HttpOnly')
67+
```
68+
69+
## Middleware
70+
71+
Some standard middleware:
72+
73+
- request rewriter
74+
- logger
75+
- auth
76+
- selector + terminator
77+
78+
```Ruby
79+
# For the chainable DSL shown below, we need to create a custom class:
80+
class Syntropy::Middleware::Selector
81+
def initialize(select_proc, terminator_proc = nil)
82+
@select_proc = select_proc
83+
@terminator_proc = terminator_proc
84+
end
85+
86+
def to_proc
87+
->(req, proc) {
88+
@select_proc.(req) ? @terminator_proc.(req) : proc(req)
89+
}
90+
end
91+
92+
def terminate(&proc)
93+
@terminator_proc = proc
94+
end
95+
end
96+
```
97+
98+
```Ruby
99+
# a _site.rb file can be used to wrap a whole app
100+
# site/_site.rb
101+
102+
# this means we route according to the host header, with each
103+
export Syntropy.route_by_host
104+
105+
# we can also rewrite requests:
106+
rewriter = Syntropy
107+
.select { it.host =~ /^tolkora\.(org|com)$/ }
108+
.terminate { it.redirect_permanent('https://tolkora.net') }
109+
110+
# This is actuall a pretty interesting DSL design:
111+
# a chain of operations that compose functions. So, we can select a
112+
export rewriter.wrap(default_app)
113+
114+
# composing
115+
export rewriter.wrap(Syntropy.some_custom_app.wrap(app))
116+
117+
# or maybe
118+
export rewriter << some_other_middleware << app
119+
```
120+
121+
## CLI tool for setting up a site repo:
122+
123+
```bash
124+
# clone a newly created repo
125+
~/repo$ git clone https://github.com/foo/bar
126+
...
127+
~/repo$ syntropy setup bar
128+
129+
(syntropy banner)
130+
131+
Setting up Syntropy project in /home/sharon/repo/bar:
132+
133+
bar/
134+
bin/
135+
start
136+
stop
137+
restart
138+
console
139+
server
140+
docker-compose.yml
141+
Dockerfile
142+
Gemfile
143+
proxy/
144+
README.md
145+
site/
146+
_layout/
147+
default.rb
148+
_lib/
149+
about.md
150+
articles/
151+
long-form.md
152+
assets/
153+
js/
154+
css/
155+
style.css
156+
img/
157+
syntropy.png
158+
index.rb
159+
```
160+
161+
Some of the files might need templating, but we can maybe do without, or at
162+
least make it as generic as possible.
163+
164+
`syntropy setup` steps:
165+
166+
1. Verify existence of target directory
167+
2. Copy files from Syntropy template to target directory
168+
3. Do chmod +x for bin/*
169+
4. Do bundle install in the target directory
170+
5. Show some information with regard to how to get started working with the
171+
repo
172+
173+
`syntropy provision` steps:
174+
175+
1. Verify Ubuntu 22.x or higher
176+
2. Install git, docker, docker-compose
177+
178+
`syntropy deploy` steps:
179+
180+
1. Verify no uncommitted changes.
181+
2. SSH to remote machine.
182+
2.1. If not exists, clone repo
183+
2.2. Otherwise, verify remote machine repo is on same branch as local repo
184+
2.3. Do a git pull (what about credentials?)
185+
2.4. If gem bundle has changed, do a docker compose build
186+
2.5. If docker compose services are running, restart
187+
2.6. Otherwise, start services
188+
2.7. Verify service is running correctly

lib/syntropy/app.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,9 @@ def render_markdown(entry)
196196

197197
def markdown_layout_proc(layout)
198198
layout = @module_loader.load("_layout/#{layout}")
199-
puts '*' * 40
200-
puts layout.proc.compiled_code
201-
puts
202-
layout.apply { |md:, **atts| markdown(md) }
199+
layout.apply { |md:, **|
200+
markdown(md)
201+
}
203202
end
204203
end
205204
end

lib/syntropy/module.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ def load_module(ref)
3636
mod_ctx.module_eval(mod_body, fn, 1)
3737

3838
export_value = mod_ctx.__export_value__
39-
4039
wrap_module(mod_ctx, export_value)
4140
end
4241

lib/syntropy/router.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# frozen_string_literal: true
22

33
module Syntropy
4+
# The Router class implements a caching router. Routes are calculated
5+
# dynamically upon receiving an HTTP request, matching the URL path to the
6+
# corresponding file tree.
47
class Router
58
def initialize(opts, module_loader = nil)
69
raise 'Invalid location given' if !File.directory?(opts[:location])
@@ -131,11 +134,18 @@ def set_cache(path, entry)
131134
end
132135

133136
# We don't allow access to path with /.., or entries that start with _
134-
FORBIDDEN_RE = %r{(/_)|((/\.\.)/?)}
137+
NO_ACCESS_RE = %r{(/_)|((/\.\.)/?)}
135138
NOT_FOUND = { kind: :not_found }.freeze
136139

140+
# Returns a route entry for the given path using the following rules:
141+
#
142+
# - If the path matches that of a given route, return the route.
143+
# - If the path ends with an index.(rb|md|html), return the corresponding
144+
# index route.
145+
# - Search for an up-tree wildcard module and return it.
146+
# - Otherwise, return a not found entry (which will generate a 404 response).
137147
def find_route_entry(path)
138-
return NOT_FOUND if path =~ FORBIDDEN_RE
148+
return NOT_FOUND if path =~ NO_ACCESS_RE
139149

140150
@routes[path] || find_index_route(path) || find_up_tree_module(path) || NOT_FOUND
141151
end

0 commit comments

Comments
 (0)