syntropy 0.33.0 → 0.34.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 +7 -0
- data/cmd/console.rb +18 -7
- data/cmd/serve.rb +26 -18
- data/cmd/test.rb +37 -24
- data/examples/blog/.gitignore +1 -0
- data/examples/blog/app/_lib/database.rb +13 -0
- data/examples/blog/app/_lib/{post_store.rb → posts.rb} +3 -1
- data/examples/blog/app/posts/[id]/edit.rb +2 -2
- data/examples/blog/app/posts/[id]/index.rb +4 -4
- data/examples/blog/app/posts/index.rb +4 -4
- data/examples/blog/app/posts/new.rb +1 -1
- data/examples/blog/config/development.rb +5 -0
- data/examples/blog/config/production.rb +4 -0
- data/examples/blog/config/test.rb +5 -0
- data/examples/blog/test/test_posts.rb +65 -0
- data/examples/mcp-oauth/app/oauth/token.rb +1 -1
- data/lib/syntropy/app.rb +48 -40
- data/lib/syntropy/applets/builtin/auto_refresh/watch.sse.rb +1 -1
- data/lib/syntropy/db/schema.rb +1 -1
- data/lib/syntropy/db/store.rb +2 -0
- data/lib/syntropy/errors.rb +6 -2
- data/lib/syntropy/http/client.rb +1 -0
- data/lib/syntropy/http/server_connection.rb +0 -4
- data/lib/syntropy/json_api.rb +27 -1
- data/lib/syntropy/logger.rb +81 -27
- data/lib/syntropy/markdown.rb +61 -32
- data/lib/syntropy/mime_types.rb +9 -5
- data/lib/syntropy/module_loader.rb +20 -9
- data/lib/syntropy/papercraft_extensions.rb +2 -2
- data/lib/syntropy/request/mock_adapter.rb +10 -8
- data/lib/syntropy/request/request_info.rb +91 -0
- data/lib/syntropy/request/response.rb +1 -12
- data/lib/syntropy/request/validation.rb +1 -0
- data/lib/syntropy/request.rb +51 -19
- data/lib/syntropy/routing_tree.rb +27 -28
- data/lib/syntropy/session.rb +198 -0
- data/lib/syntropy/side_run.rb +25 -2
- data/lib/syntropy/test.rb +105 -10
- data/lib/syntropy/utils.rb +53 -18
- data/lib/syntropy/version.rb +1 -1
- data/lib/syntropy.rb +44 -10
- data/test/bm_router_proc.rb +4 -4
- data/test/fixtures/app/class_instance.rb +5 -0
- data/test/fixtures/app/http.rb +5 -0
- data/test/fixtures/app/post_ct.rb +5 -0
- data/test/fixtures/app/singleton.rb +3 -0
- data/test/test_app.rb +13 -52
- data/test/test_caching.rb +2 -2
- data/test/test_db_schema.rb +1 -1
- data/test/test_http_server_connection.rb +3 -3
- data/test/test_module_loader.rb +5 -2
- data/test/test_response.rb +0 -19
- data/test/test_routing_tree.rb +69 -69
- data/test/test_server.rb +5 -9
- data/test/test_test.rb +70 -0
- metadata +52 -42
- data/examples/blog/app/_setup.rb +0 -4
- data/lib/syntropy/request/session.rb +0 -113
- /data/test/{app → fixtures/app}/.well-known/foo.rb +0 -0
- /data/test/{app → fixtures/app}/_hook.rb +0 -0
- /data/test/{app → fixtures/app}/_layout/default.rb +0 -0
- /data/test/{app → fixtures/app}/_lib/callable.rb +0 -0
- /data/test/{app → fixtures/app}/_lib/dep.rb +0 -0
- /data/test/{app → fixtures/app}/_lib/env.rb +0 -0
- /data/test/{app → fixtures/app}/_lib/klass.rb +0 -0
- /data/test/{app → fixtures/app}/_lib/missing-export.rb +0 -0
- /data/test/{app → fixtures/app}/_lib/self.rb +0 -0
- /data/test/{app → fixtures/app}/about/_error.rb +0 -0
- /data/test/{app → fixtures/app}/about/foo.md +0 -0
- /data/test/{app → fixtures/app}/about/index.rb +0 -0
- /data/test/{app → fixtures/app}/about/raise.rb +0 -0
- /data/test/{app → fixtures/app}/api+.rb +0 -0
- /data/test/{app → fixtures/app}/assets/style.css +0 -0
- /data/test/{app → fixtures/app}/bad_mod.rb +0 -0
- /data/test/{app → fixtures/app}/bar.rb +0 -0
- /data/test/{app → fixtures/app}/baz.rb +0 -0
- /data/test/{app → fixtures/app}/by_method.rb +0 -0
- /data/test/{app → fixtures/app}/deps.rb +0 -0
- /data/test/{app → fixtures/app}/index.html +0 -0
- /data/test/{app → fixtures/app}/mod/bar/index+.rb +0 -0
- /data/test/{app → fixtures/app}/mod/foo/index.rb +0 -0
- /data/test/{app → fixtures/app}/mod/path/a.rb +0 -0
- /data/test/{app → fixtures/app}/mod/path/b.rb +0 -0
- /data/test/{app → fixtures/app}/params/[foo].rb +0 -0
- /data/test/{app → fixtures/app}/rss.rb +0 -0
- /data/test/{app → fixtures/app}/tmp.rb +0 -0
- /data/test/{app_custom → fixtures/app_custom}/_site.rb +0 -0
- /data/test/{app_multi_site → fixtures/app_multi_site}/_site.rb +0 -0
- /data/test/{app_multi_site → fixtures/app_multi_site}/bar.baz/index.html +0 -0
- /data/test/{app_multi_site → fixtures/app_multi_site}/foo.bar/index.html +0 -0
- /data/test/{app_setup → fixtures/app_setup}/_setup.rb +0 -0
- /data/test/{app_setup → fixtures/app_setup}/index.rb +0 -0
- /data/test/{app_with_schema → fixtures/app_with_schema}/_schema/2026-01-02-foo.rb +0 -0
- /data/test/{app_with_schema → fixtures/app_with_schema}/_schema/2026-05-30-bar.rb +0 -0
- /data/test/{schema → fixtures/schema}/2026-01-02-foo.rb +0 -0
- /data/test/{schema → fixtures/schema}/2026-05-30-bar.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 29b7fe87a7350db0c154f0ee25b1c6992c81aead3938cec537e383dc5e3f6a62
|
|
4
|
+
data.tar.gz: 7dcf8de792102d368aefed72ced1ac3c2e5afd1fe0aea3ac106150fa59862a6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 904b3740de777c83a631b4b0b37f72ee22b698b2f1618110a906c6ca5adf979f0c46c84af08d1ae228f8b9aa90bcb54093e88de9c18dcf3961d72ea7e0d94d2c
|
|
7
|
+
data.tar.gz: '0081303c531b38423d94f43423efcdcd80852201b0826f0078b276618b03dc7cde09e24dd599e8cee58ddf4ae8ce1f226d4684b4aa8592407728b9f911e30cc5'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# 0.34.0 2026-06-03
|
|
2
|
+
|
|
3
|
+
- Streamline options for CLI commands
|
|
4
|
+
- Remove `App#setup_db`
|
|
5
|
+
- Add support for config modules (e.g. `config/development.rb`)
|
|
6
|
+
- Add support for running modes: production, development, test
|
|
7
|
+
|
|
1
8
|
# 0.33.0 2026-06-02
|
|
2
9
|
|
|
3
10
|
- Fix `ModuleLoader` to load a module only once
|
data/cmd/console.rb
CHANGED
|
@@ -2,16 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative '../lib/syntropy'
|
|
4
4
|
require 'optparse'
|
|
5
|
+
require 'fileutils'
|
|
5
6
|
|
|
6
7
|
env = {
|
|
8
|
+
app_root: File.join(FileUtils.pwd, 'app'),
|
|
9
|
+
config_root: File.join(FileUtils.pwd, 'config'),
|
|
10
|
+
mode: ENV['SYNTROPY_MODE'] || 'development',
|
|
7
11
|
mount_path: '/',
|
|
8
|
-
logger: true,
|
|
9
12
|
builtin_applet_path: '/.syntropy',
|
|
13
|
+
logger: true,
|
|
10
14
|
watch_files: true
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
parser = OptionParser.new do |o|
|
|
14
|
-
o.banner = 'Usage: syntropy serve [options]
|
|
18
|
+
o.banner = 'Usage: syntropy serve [options]'
|
|
19
|
+
|
|
20
|
+
o.on('-a', '--app PATH', 'Set app directory (default: ./app') do |path|
|
|
21
|
+
env[:app_root] = path
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
o.on('-c', '--config PATH', 'Set config directory (default: ./config') do |path|
|
|
25
|
+
env[:config_root] = path
|
|
26
|
+
end
|
|
15
27
|
|
|
16
28
|
o.on('-h', '--help', 'Show this help message') do
|
|
17
29
|
puts o
|
|
@@ -19,7 +31,6 @@ parser = OptionParser.new do |o|
|
|
|
19
31
|
end
|
|
20
32
|
|
|
21
33
|
o.on('-m', '--mount PATH', 'Set mount path (default: /)') do |path|
|
|
22
|
-
p mount: path
|
|
23
34
|
env[:mount_path] = path
|
|
24
35
|
env[:builtin_applet_path] = File.join(path, '.syntropy')
|
|
25
36
|
end
|
|
@@ -43,11 +54,11 @@ rescue StandardError => e
|
|
|
43
54
|
exit
|
|
44
55
|
end
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
57
|
+
Syntropy.dev_mode = env[:mode] == 'development'
|
|
58
|
+
Syntropy.load_config(env)
|
|
48
59
|
|
|
49
|
-
if !File.directory?(env[:
|
|
50
|
-
puts "#{File.expand_path(env[:
|
|
60
|
+
if !File.directory?(env[:app_root])
|
|
61
|
+
puts "#{File.expand_path(env[:app_root])} Not a directory"
|
|
51
62
|
exit
|
|
52
63
|
end
|
|
53
64
|
|
data/cmd/serve.rb
CHANGED
|
@@ -2,19 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative '../lib/syntropy'
|
|
4
4
|
require 'optparse'
|
|
5
|
+
require 'fileutils'
|
|
5
6
|
|
|
6
7
|
env = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
app_root: File.join(FileUtils.pwd, 'app'),
|
|
9
|
+
config_root: File.join(FileUtils.pwd, 'config'),
|
|
10
|
+
mode: ENV['SYNTROPY_MODE'] || 'development',
|
|
11
|
+
mount_path: '/',
|
|
12
|
+
builtin_applet_path: '/.syntropy',
|
|
13
|
+
logger: true,
|
|
14
|
+
server_extensions: {
|
|
11
15
|
date: true,
|
|
12
16
|
name: 'Syntropy'
|
|
13
17
|
}
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
parser = OptionParser.new do |o|
|
|
17
|
-
o.banner = 'Usage: syntropy serve [options]
|
|
21
|
+
o.banner = 'Usage: syntropy serve [options]'
|
|
22
|
+
|
|
23
|
+
o.on('-a', '--app PATH', 'Set app directory (default: ./app') do |path|
|
|
24
|
+
env[:app_root] = path
|
|
25
|
+
end
|
|
18
26
|
|
|
19
27
|
o.on('-b', '--bind BIND', String,
|
|
20
28
|
'Bind address (default: http://0.0.0.0:1234). You can specify this flag multiple times to bind to multiple addresses.') do
|
|
@@ -22,14 +30,8 @@ parser = OptionParser.new do |o|
|
|
|
22
30
|
env[:bind] << it
|
|
23
31
|
end
|
|
24
32
|
|
|
25
|
-
o.on('-
|
|
26
|
-
env[:
|
|
27
|
-
env[:logger] = nil
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
o.on('-d', '--dev', 'Development mode') do
|
|
31
|
-
env[:dev_mode] = true
|
|
32
|
-
env[:watch_files] = true
|
|
33
|
+
o.on('-c', '--config PATH', 'Set config directory (default: ./config') do |path|
|
|
34
|
+
env[:config_root] = path
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
o.on('-h', '--help', 'Show this help message') do
|
|
@@ -50,6 +52,11 @@ parser = OptionParser.new do |o|
|
|
|
50
52
|
env[:server_extensions] = nil
|
|
51
53
|
end
|
|
52
54
|
|
|
55
|
+
o.on('-s', '--silent', 'Silent mode') do
|
|
56
|
+
env[:banner] = nil
|
|
57
|
+
env[:logger] = nil
|
|
58
|
+
end
|
|
59
|
+
|
|
53
60
|
o.on('-v', '--version', 'Show version') do
|
|
54
61
|
require 'syntropy/version'
|
|
55
62
|
puts "Syntropy version #{Syntropy::VERSION}"
|
|
@@ -71,11 +78,12 @@ rescue StandardError => e
|
|
|
71
78
|
exit
|
|
72
79
|
end
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
Syntropy.dev_mode = env[:mode] == 'development'
|
|
82
|
+
Syntropy.load_config(env)
|
|
83
|
+
env[:watch_files] = Syntropy.dev_mode
|
|
76
84
|
|
|
77
|
-
if !File.directory?(env[:
|
|
78
|
-
puts "#{File.expand_path(env[:
|
|
85
|
+
if !File.directory?(env[:app_root])
|
|
86
|
+
puts "#{File.expand_path(env[:app_root])} Not a directory"
|
|
79
87
|
exit
|
|
80
88
|
end
|
|
81
89
|
|
|
@@ -87,7 +95,7 @@ env[:machine] = Syntropy.machine = UM.new
|
|
|
87
95
|
env[:logger] = env[:logger] && Syntropy::Logger.new(env[:machine], **env)
|
|
88
96
|
|
|
89
97
|
require 'syntropy/version'
|
|
90
|
-
require 'syntropy/dev_mode' if
|
|
98
|
+
require 'syntropy/dev_mode' if Syntropy.dev_mode
|
|
91
99
|
|
|
92
100
|
app = Syntropy::App.load(env)
|
|
93
101
|
Syntropy.run(env) { app.call(it) }
|
data/cmd/test.rb
CHANGED
|
@@ -5,15 +5,25 @@ require 'optparse'
|
|
|
5
5
|
|
|
6
6
|
pwd = FileUtils.pwd
|
|
7
7
|
env = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
app_root: File.join(FileUtils.pwd, 'app'),
|
|
9
|
+
config_root: File.join(FileUtils.pwd, 'config'),
|
|
10
|
+
test_root: File.join(pwd, 'test'),
|
|
11
|
+
mode: 'test',
|
|
12
|
+
mount_path: '/'
|
|
11
13
|
}
|
|
12
14
|
MINITEST_ARGV = []
|
|
13
15
|
|
|
14
16
|
parser = OptionParser.new do |o|
|
|
15
17
|
o.banner = 'Usage: syntropy test [options]'
|
|
16
18
|
|
|
19
|
+
o.on('-a', '--app PATH', 'Set app directory (default: ./app') do |path|
|
|
20
|
+
env[:app_root] = path
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
o.on('-c', '--config PATH', 'Set config directory (default: ./config') do |path|
|
|
24
|
+
env[:config_root] = path
|
|
25
|
+
end
|
|
26
|
+
|
|
17
27
|
o.on('-h', '--help', 'Show this help message') do
|
|
18
28
|
puts o
|
|
19
29
|
exit
|
|
@@ -24,35 +34,31 @@ parser = OptionParser.new do |o|
|
|
|
24
34
|
env[:builtin_applet_path] = File.join(path, '.syntropy')
|
|
25
35
|
end
|
|
26
36
|
|
|
27
|
-
o.on('-
|
|
28
|
-
|
|
37
|
+
o.on('-n', '--name NAME', 'Specify test to run') do |name|
|
|
38
|
+
MINITEST_ARGV << '--name' << name
|
|
29
39
|
end
|
|
30
40
|
|
|
31
|
-
o.on('-
|
|
32
|
-
|
|
41
|
+
o.on('-s', '--seed SEED', 'Specify random seed') do |seed|
|
|
42
|
+
MINITEST_ARGV << '--seed' << seed
|
|
33
43
|
end
|
|
34
44
|
|
|
35
|
-
o.on('-t', '--test PATH', 'Set test
|
|
36
|
-
env[:
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
o.on('-n', '--name NAME', 'Specify test to run') do |name|
|
|
40
|
-
MINITEST_ARGV << '--name' << name
|
|
45
|
+
o.on('-t', '--test PATH', 'Set test directory (default: ./test)') do |path|
|
|
46
|
+
env[:test_root] = path
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
o.on('-V', '--verbose', 'Verbose test output') do
|
|
44
50
|
MINITEST_ARGV << '--verbose'
|
|
45
51
|
end
|
|
46
52
|
|
|
47
|
-
o.on('-s', '--seed SEED', 'Specify random seed') do |seed|
|
|
48
|
-
MINITEST_ARGV << '--seed' << seed
|
|
49
|
-
end
|
|
50
|
-
|
|
51
53
|
o.on('-v', '--version', 'Show version') do
|
|
52
54
|
require 'syntropy/version'
|
|
53
55
|
puts "Syntropy version #{Syntropy::VERSION}"
|
|
54
56
|
exit
|
|
55
57
|
end
|
|
58
|
+
|
|
59
|
+
o.on('-w', '--watch', 'Watch for file changes') do
|
|
60
|
+
env[:watch_mode] = true
|
|
61
|
+
end
|
|
56
62
|
end
|
|
57
63
|
|
|
58
64
|
argv_copy = ARGV.dup
|
|
@@ -71,26 +77,33 @@ end
|
|
|
71
77
|
require_relative '../lib/syntropy'
|
|
72
78
|
require_relative '../lib/syntropy/test'
|
|
73
79
|
|
|
80
|
+
Syntropy.load_config(env)
|
|
81
|
+
|
|
74
82
|
$stdout.sync = true
|
|
75
83
|
$stderr.sync = true
|
|
76
84
|
|
|
77
|
-
Dir.glob("#{env[:
|
|
85
|
+
Dir.glob("#{File.expand_path(env[:test_root])}/test_*.rb").each { require(it) }
|
|
86
|
+
|
|
87
|
+
def restart_on_file_change(machine, dir, restart_argv)
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
machine.write(UM::STDOUT_FILENO, "Waiting for file changes in #{FileUtils.pwd}\n")
|
|
81
|
-
machine.file_watch(FileUtils.pwd, UM::IN_CREATE | UM::IN_DELETE | UM::IN_CLOSE_WRITE) {
|
|
89
|
+
machine.file_watch(dir, UM::IN_CREATE | UM::IN_DELETE | UM::IN_CLOSE_WRITE) {
|
|
82
90
|
machine.write(UM::STDOUT_FILENO, "File changed: #{it[:fn]}\n")
|
|
83
91
|
break
|
|
84
92
|
}
|
|
93
|
+
exec('ruby', __FILE__, *restart_argv)
|
|
85
94
|
end
|
|
86
95
|
|
|
87
|
-
Syntropy::Test.env=(env)
|
|
96
|
+
Syntropy::Test.env = (env)
|
|
88
97
|
Minitest.run MINITEST_ARGV
|
|
89
98
|
|
|
90
99
|
if env[:watch_mode]
|
|
91
100
|
m = UM.new(size: 4)
|
|
92
101
|
m.write(UM::STDOUT_FILENO, "\n")
|
|
93
102
|
trap('SIGINT') { m.write(UM::STDOUT_FILENO, "\n"); exit! }
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
|
|
104
|
+
machine.write(UM::STDOUT_FILENO, "Waiting for file changes...\n")
|
|
105
|
+
m.join(
|
|
106
|
+
m.spin { restart_on_file_change(m, env[:app_root], argv_copy) },
|
|
107
|
+
m.spin { restart_on_file_change(m, env[:test_root], argv_copy) }
|
|
108
|
+
)
|
|
96
109
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
storage/*
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export self
|
|
2
|
+
|
|
3
|
+
def connection_pool
|
|
4
|
+
@connection_pool ||= DB::ConnectionPool.new(@machine, @env[:config][:storage][:path], 4)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def schema
|
|
8
|
+
DB::Schema.new(module_loader: @module_loader, schema_root: '_schema')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def migrate!
|
|
12
|
+
schema.apply(connection_pool)
|
|
13
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
DB = import '/_lib/database'
|
|
2
|
+
|
|
1
3
|
class PostStore < Syntropy::DB::Store
|
|
2
4
|
# @return [Integer] post id
|
|
3
5
|
def create(title, body)
|
|
@@ -44,4 +46,4 @@ class PostStore < Syntropy::DB::Store
|
|
|
44
46
|
end
|
|
45
47
|
end
|
|
46
48
|
|
|
47
|
-
export PostStore.new(
|
|
49
|
+
export PostStore.new(DB.connection_pool)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
@
|
|
1
|
+
@posts = import '/_lib/posts'
|
|
2
2
|
@layout = import '/_layout/default'
|
|
3
3
|
|
|
4
4
|
export http_methods
|
|
5
5
|
|
|
6
6
|
def get(req)
|
|
7
7
|
id = req.route_params['id'].to_i
|
|
8
|
-
post = @
|
|
8
|
+
post = @posts.get(id)
|
|
9
9
|
raise Syntropy::Error.not_found if !post
|
|
10
10
|
|
|
11
11
|
req.respond_html(
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
@
|
|
1
|
+
@posts = import '/_lib/posts'
|
|
2
2
|
@layout = import '/_layout/default'
|
|
3
3
|
|
|
4
4
|
export http_methods
|
|
5
5
|
|
|
6
6
|
def get(req)
|
|
7
7
|
id = req.route_params['id'].to_i
|
|
8
|
-
post = @
|
|
8
|
+
post = @posts.get(id)
|
|
9
9
|
raise Syntropy::Error.not_found if !post
|
|
10
10
|
|
|
11
11
|
req.respond_html(
|
|
@@ -21,7 +21,7 @@ def post(req)
|
|
|
21
21
|
title = req.validate(data['title'], String, /.+/)
|
|
22
22
|
body = req.validate(data['body'], String, /.+/)
|
|
23
23
|
|
|
24
|
-
updated = @
|
|
24
|
+
updated = @posts.update(id, title, body)
|
|
25
25
|
raise BadRequestError, "Failed to update post" if updated != 1
|
|
26
26
|
|
|
27
27
|
req.flash[:notice] = 'Post was successfully updated.'
|
|
@@ -31,7 +31,7 @@ end
|
|
|
31
31
|
def delete(req)
|
|
32
32
|
id = req.route_params['id'].to_i
|
|
33
33
|
|
|
34
|
-
deleted = @
|
|
34
|
+
deleted = @posts.delete(id)
|
|
35
35
|
raise BadRequestError, "Failed to delete post" if deleted != 1
|
|
36
36
|
|
|
37
37
|
req.flash[:notice] = 'Post was successfully destroyed.'
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
@
|
|
1
|
+
@posts = import '_lib/posts'
|
|
2
2
|
@layout = import '_layout/default'
|
|
3
3
|
|
|
4
4
|
export http_methods
|
|
5
5
|
|
|
6
6
|
def get(req)
|
|
7
|
-
posts = @
|
|
7
|
+
posts = @posts.get_all
|
|
8
8
|
req.respond_html(
|
|
9
9
|
@template.render(posts:, req:)
|
|
10
10
|
)
|
|
@@ -14,14 +14,14 @@ def post(req)
|
|
|
14
14
|
data = req.get_form_data
|
|
15
15
|
title = req.validate(data['title'], String, /.+/)
|
|
16
16
|
body = req.validate(data['body'], String, /.+/)
|
|
17
|
-
id = @
|
|
17
|
+
id = @posts.create(title, body)
|
|
18
18
|
|
|
19
19
|
req.flash[:notice] = 'Post was successfully created.'
|
|
20
20
|
req.redirect("posts/#{id}")
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
@template = @layout.apply { |**props|
|
|
24
|
-
h1 "My blog"
|
|
24
|
+
h1 "My awesome blog"
|
|
25
25
|
p props[:req]&.flash[:notice], style: 'color: green'
|
|
26
26
|
props[:posts].each { |post|
|
|
27
27
|
div {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class PostsTest < Syntropy::Test
|
|
4
|
+
def setup
|
|
5
|
+
super
|
|
6
|
+
@posts = load_module('/_lib/posts')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def test_get_all
|
|
10
|
+
assert_equal [], @posts.get_all
|
|
11
|
+
|
|
12
|
+
@posts.create('foo', 'bar')
|
|
13
|
+
|
|
14
|
+
assert_equal [
|
|
15
|
+
{ id: 1, title: 'foo', body: 'bar' }
|
|
16
|
+
], @posts.get_all
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_get
|
|
20
|
+
assert_nil @posts.get(1)
|
|
21
|
+
assert_nil @posts.get(2)
|
|
22
|
+
|
|
23
|
+
@posts.create('foo', 'bar')
|
|
24
|
+
|
|
25
|
+
assert_equal(
|
|
26
|
+
{ id: 1, title: 'foo', body: 'bar' },
|
|
27
|
+
@posts.get(1)
|
|
28
|
+
)
|
|
29
|
+
assert_nil @posts.get(2)
|
|
30
|
+
|
|
31
|
+
@posts.create('bar', 'baz')
|
|
32
|
+
|
|
33
|
+
assert_equal(
|
|
34
|
+
{ id: 1, title: 'foo', body: 'bar' },
|
|
35
|
+
@posts.get(1)
|
|
36
|
+
)
|
|
37
|
+
assert_equal(
|
|
38
|
+
{ id: 2, title: 'bar', body: 'baz' },
|
|
39
|
+
@posts.get(2)
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_update
|
|
44
|
+
assert_equal 0, @posts.update(1, 'qqq', 'ttt')
|
|
45
|
+
|
|
46
|
+
@posts.create('foo', 'bar')
|
|
47
|
+
assert_equal 1, @posts.update(1, 'qqq', 'ttt')
|
|
48
|
+
|
|
49
|
+
assert_equal [
|
|
50
|
+
{ id: 1, title: 'qqq', body: 'ttt' }
|
|
51
|
+
], @posts.get_all
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_delete
|
|
55
|
+
assert_equal 0, @posts.delete(1)
|
|
56
|
+
|
|
57
|
+
@posts.create('foo', 'bar')
|
|
58
|
+
@posts.create('bar', 'baz')
|
|
59
|
+
|
|
60
|
+
assert_equal 1, @posts.delete(1)
|
|
61
|
+
assert_equal [
|
|
62
|
+
{ id: 2, title: 'bar', body: 'baz' }
|
|
63
|
+
], @posts.get_all
|
|
64
|
+
end
|
|
65
|
+
end
|