Skip to content

Commit 30e8bdc

Browse files
committed
Implement basic routing
1 parent b6614b4 commit 30e8bdc

12 files changed

Lines changed: 220 additions & 6 deletions

File tree

lib/syntropy.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'syntropy/errors'
44
require 'syntropy/context'
55
require 'syntropy/rpc_api'
6+
require 'syntropy/app'
67

78
module Syntropy
89
end

lib/syntropy/app.rb

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# frozen_string_literal: true
2+
3+
require 'qeweney'
4+
require 'syntropy/errors'
5+
require 'json'
6+
7+
module Syntropy
8+
class App
9+
attr_reader :route_cache
10+
11+
def initialize(src_path, mount_path)
12+
@src_path = src_path
13+
@mount_path = mount_path
14+
@route_cache = {}
15+
16+
@relative_path_re = calculate_relative_path_re(mount_path)
17+
end
18+
19+
def find_route(path, cache: true)
20+
cached = @route_cache[path]
21+
return cached if cached
22+
23+
entry = calculate_route(path)
24+
if entry[:kind] != :not_found
25+
@route_cache[path] = entry if cache
26+
end
27+
entry
28+
end
29+
30+
def call(req)
31+
entry = find_route(req.path)
32+
render_entry(req, entry)
33+
rescue StandardError => e
34+
p e
35+
p e.backtrace
36+
req.respond(e.message, ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
37+
end
38+
39+
private
40+
41+
def calculate_relative_path_re(mount_path)
42+
mount_path = '' if mount_path == '/'
43+
/^#{mount_path}(?:\/(.*))?$/
44+
end
45+
46+
FILE_KINDS = {
47+
'.rb' => :module,
48+
'.md' => :markdown
49+
}
50+
NOT_FOUND = { kind: :not_found }
51+
52+
# We don't allow access to path with /.., or entries that start with _
53+
FORBIDDEN_RE = /(\/_)|((\/\.\.)\/?)/
54+
55+
def calculate_route(path)
56+
return NOT_FOUND if path =~ FORBIDDEN_RE
57+
58+
m = path.match(@relative_path_re)
59+
return NOT_FOUND if !m
60+
61+
relative_path = m[1] || ''
62+
fs_path = File.join(@src_path, relative_path)
63+
64+
return file_entry(fs_path) if File.file?(fs_path)
65+
return find_index_entry(fs_path) if File.directory?(fs_path)
66+
67+
entry = find_file_entry_with_extension(fs_path)
68+
return entry if entry[:kind] != :not_found
69+
70+
find_up_tree_module(path)
71+
end
72+
73+
def file_entry(fn)
74+
{ fn: fn, kind: FILE_KINDS[File.extname(fn)] || :static }
75+
end
76+
77+
def find_index_entry(dir)
78+
find_file_entry_with_extension(File.join(dir, 'index'))
79+
end
80+
81+
def find_file_entry_with_extension(path)
82+
fn = "#{path}.html"
83+
return file_entry(fn) if File.file?(fn)
84+
85+
fn = "#{path}.md"
86+
return file_entry(fn) if File.file?(fn)
87+
88+
fn = "#{path}.rb"
89+
return file_entry(fn) if File.file?(fn)
90+
91+
fn = "#{path}+.rb"
92+
return file_entry(fn) if File.file?(fn)
93+
94+
NOT_FOUND
95+
end
96+
97+
def find_up_tree_module(path)
98+
parent = parent_path(path)
99+
return NOT_FOUND if !parent
100+
101+
entry = find_route("#{parent}+.rb", cache: false)
102+
entry[:kind] == :module ? entry : NOT_FOUND
103+
end
104+
105+
UP_TREE_PATH_RE = /^(.+)?\/[^\/]+$/
106+
107+
def parent_path(path)
108+
m = path.match(UP_TREE_PATH_RE)
109+
m && m[1]
110+
end
111+
112+
def render_entry(req, entry)
113+
p entry: entry
114+
case entry[:kind]
115+
when :not_found
116+
req.respond('Not found', ':status' => Qeweney::Status::NOT_FOUND)
117+
when :static
118+
entry[:mime_type] ||= Qeweney::MimeTypes[File.extname(entry[:fn])]
119+
req.respond(IO.read(entry[:fn]), 'Content-Type' => entry[:mime_type])
120+
when :markdown
121+
body = render_markdown(markdown)
122+
req.respond(body, 'Content-Type' => 'text/html')
123+
when :module
124+
ctx = Syntropy::Context.new(req)
125+
call_module(entry, ctx)
126+
else
127+
raise "Invalid entry kind"
128+
end
129+
end
130+
end
131+
end

syntropy.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
2020

2121
s.add_dependency 'json', '2.12.2'
2222
s.add_dependency 'qeweney', '0.21'
23+
s.add_dependency 'papercraft', '1.4'
2324
s.add_dependency 'tp2', '0.11.3'
2425
s.add_dependency 'uringmachine', '0.14'
2526

test/app/_layout/default.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
->(**props) {
2+
header {
3+
h1 'Foo'
4+
}
5+
content {
6+
emit_yield **props
7+
}
8+
}

test/app/about/foo.md

Whitespace-only changes.

test/app/about/index.rb

Whitespace-only changes.

test/app/api+.rb

Whitespace-only changes.

test/app/assets/style.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* { color: beige }

test/app/bar.rb

Whitespace-only changes.

test/app/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>Hello, world!</h1>

0 commit comments

Comments
 (0)