syntropy 0.2 → 0.4

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -4
  3. data/CHANGELOG.md +12 -0
  4. data/README.md +30 -11
  5. data/TODO.md +89 -97
  6. data/bin/syntropy +7 -3
  7. data/cmd/setup/template/site/.gitignore +57 -0
  8. data/cmd/setup/template/site/Dockerfile +32 -0
  9. data/cmd/setup/template/site/Gemfile +3 -0
  10. data/cmd/setup/template/site/README.md +0 -0
  11. data/cmd/setup/template/site/bin/console +0 -0
  12. data/cmd/setup/template/site/bin/restart +0 -0
  13. data/cmd/setup/template/site/bin/server +0 -0
  14. data/cmd/setup/template/site/bin/start +0 -0
  15. data/cmd/setup/template/site/bin/stop +0 -0
  16. data/cmd/setup/template/site/docker-compose.yml +51 -0
  17. data/cmd/setup/template/site/proxy/Dockerfile +5 -0
  18. data/cmd/setup/template/site/proxy/etc/Caddyfile +7 -0
  19. data/cmd/setup/template/site/proxy/etc/tls_auto +2 -0
  20. data/cmd/setup/template/site/proxy/etc/tls_cloudflare +4 -0
  21. data/cmd/setup/template/site/proxy/etc/tls_custom +1 -0
  22. data/cmd/setup/template/site/proxy/etc/tls_selfsigned +1 -0
  23. data/cmd/setup/template/site/site/_layout/default.rb +11 -0
  24. data/cmd/setup/template/site/site/about.md +6 -0
  25. data/cmd/setup/template/site/site/articles/cage.rb +29 -0
  26. data/cmd/setup/template/site/site/articles/index.rb +3 -0
  27. data/cmd/setup/template/site/site/assets/css/style.css +40 -0
  28. data/cmd/setup/template/site/site/assets/img/syntropy.png +0 -0
  29. data/cmd/setup/template/site/site/index.rb +15 -0
  30. data/docker-compose.yml +51 -0
  31. data/lib/syntropy/app.rb +134 -50
  32. data/lib/syntropy/errors.rb +16 -2
  33. data/lib/syntropy/module.rb +26 -5
  34. data/lib/syntropy/request_extensions.rb +96 -0
  35. data/lib/syntropy/rpc_api.rb +26 -9
  36. data/lib/syntropy/side_run.rb +46 -0
  37. data/lib/syntropy/version.rb +1 -1
  38. data/lib/syntropy.rb +14 -49
  39. data/syntropy.gemspec +1 -1
  40. data/test/app/baz.rb +3 -0
  41. data/test/test_app.rb +57 -7
  42. data/test/test_side_run.rb +43 -0
  43. data/test/test_validation.rb +1 -1
  44. metadata +31 -3
data/lib/syntropy.rb CHANGED
@@ -8,64 +8,29 @@ require 'syntropy/errors'
8
8
  require 'syntropy/connection_pool'
9
9
  require 'syntropy/module'
10
10
  require 'syntropy/rpc_api'
11
+ require 'syntropy/side_run'
11
12
  require 'syntropy/app'
13
+ require 'syntropy/request_extensions'
12
14
 
13
- class Qeweney::Request
14
- def ctx
15
- @ctx ||= {}
16
- end
17
-
18
- def validate_param(name, *clauses)
19
- value = query[name]
20
- clauses.each do |c|
21
- valid = param_is_valid?(value, c)
22
- raise(Syntropy::ValidationError, 'Validation error') if !valid
23
- value = param_convert(value, c)
24
- end
25
- value
26
- end
27
-
28
- private
29
-
30
- BOOL_REGEXP = /^(t|f|true|false|on|off|1|0|yes|no)$/
31
- BOOL_TRUE_REGEXP = /^(t|true|on|1|yes)$/
32
- INTEGER_REGEXP = /^[\+\-]?[0-9]+$/
33
- FLOAT_REGEXP = /^[\+\-]?[0-9]+(\.[0-9]+)?$/
15
+ module Syntropy
16
+ Status = Qeweney::Status
34
17
 
35
- def param_is_valid?(value, cond)
36
- if cond == :bool
37
- return (value && value =~ BOOL_REGEXP)
38
- elsif cond == Integer
39
- return (value && value =~ INTEGER_REGEXP)
40
- elsif cond == Float
41
- return (value && value =~ FLOAT_REGEXP)
42
- elsif cond.is_a?(Array)
43
- return cond.any? { |c| param_is_valid?(value, c) }
44
- end
18
+ class << self
19
+ attr_accessor :machine
45
20
 
46
- cond === value
47
- end
21
+ def side_run(&block)
22
+ raise 'Syntropy.machine not set' if !@machine
48
23
 
49
- def param_convert(value, klass)
50
- if klass == :bool
51
- value = value =~ BOOL_TRUE_REGEXP ? true : false
52
- elsif klass == Integer
53
- value = value.to_i
54
- elsif klass == Float
55
- value = value.to_f
56
- else
57
- value
24
+ SideRun.call(@machine, &block)
58
25
  end
59
26
  end
60
- end
61
27
 
62
- module Syntropy
63
28
  def colorize(color_code)
64
29
  "\e[#{color_code}m#{self}\e[0m"
65
30
  end
66
31
 
67
32
  GREEN = "\e[32m"
68
- WHITE = "\e[0m"
33
+ CLEAR = "\e[0m"
69
34
  YELLOW = "\e[33m"
70
35
 
71
36
  BANNER = (
@@ -73,10 +38,10 @@ module Syntropy
73
38
  " #{GREEN}\n"\
74
39
  " #{GREEN} ooo\n"\
75
40
  " #{GREEN}ooooo\n"\
76
- " #{GREEN} ooo vvv #{WHITE}Syntropy - a web framework for Ruby\n"\
77
- " #{GREEN} o vvvvv #{WHITE}--------------------------------------\n"\
78
- " #{GREEN} #{YELLOW}|#{GREEN} vvv o #{WHITE}https://github.com/noteflakes/syntropy\n"\
41
+ " #{GREEN} ooo vvv #{CLEAR}Syntropy - a web framework for Ruby\n"\
42
+ " #{GREEN} o vvvvv #{CLEAR}--------------------------------------\n"\
43
+ " #{GREEN} #{YELLOW}|#{GREEN} vvv o #{CLEAR}https://github.com/noteflakes/syntropy\n"\
79
44
  " #{GREEN} :#{YELLOW}|#{GREEN}:::#{YELLOW}|#{GREEN}::#{YELLOW}|#{GREEN}:\n"\
80
- " #{YELLOW}++++++++++++\e[0m\n\n"
45
+ "#{YELLOW}+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\e[0m\n\n"
81
46
  )
82
47
  end
data/syntropy.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
25
25
  s.add_dependency 'json', '2.12.2'
26
26
  s.add_dependency 'papercraft', '1.4'
27
27
  s.add_dependency 'qeweney', '0.21'
28
- s.add_dependency 'tp2', '0.12.2'
28
+ s.add_dependency 'tp2', '0.13.2'
29
29
  s.add_dependency 'uringmachine', '0.15'
30
30
 
31
31
  s.add_dependency 'listen', '3.9.0'
data/test/app/baz.rb ADDED
@@ -0,0 +1,3 @@
1
+ export ->(req) {
2
+ req.respond_on_get('foobar')
3
+ }
data/test/test_app.rb CHANGED
@@ -3,13 +3,15 @@
3
3
  require_relative 'helper'
4
4
 
5
5
  class AppRoutingTest < Minitest::Test
6
+ Status = Qeweney::Status
7
+
6
8
  APP_ROOT = File.join(__dir__, 'app')
7
9
 
8
10
  def setup
9
11
  @machine = UM.new
10
12
 
11
13
  @tmp_path = '/test/tmp'
12
- @tmp_fn = File.join(APP_ROOT, "tmp.rb")
14
+ @tmp_fn = File.join(APP_ROOT, 'tmp.rb')
13
15
 
14
16
  @app = Syntropy::App.new(@machine, APP_ROOT, '/test', watch_files: 0.05)
15
17
  end
@@ -63,39 +65,87 @@ class AppRoutingTest < Minitest::Test
63
65
 
64
66
  def test_app_rendering
65
67
  req = make_request(':method' => 'GET', ':path' => '/')
66
- assert_equal Qeweney::Status::NOT_FOUND, req.response_status
68
+ assert_equal 'Not found', req.response_body
69
+ assert_equal Status::NOT_FOUND, req.response_status
70
+
71
+ req = make_request(':method' => 'HEAD', ':path' => '/')
72
+ assert_nil req.response_body
73
+ assert_equal Status::NOT_FOUND, req.response_status
74
+
75
+ req = make_request(':method' => 'POST', ':path' => '/')
76
+ assert_equal 'Not found', req.response_body
77
+ assert_equal Status::NOT_FOUND, req.response_status
67
78
 
68
79
  req = make_request(':method' => 'GET', ':path' => '/test')
69
- assert_equal Qeweney::Status::OK, req.response_status
80
+ assert_equal Status::OK, req.response_status
70
81
  assert_equal '<h1>Hello, world!</h1>', req.response_body
71
82
 
83
+ req = make_request(':method' => 'HEAD', ':path' => '/test')
84
+ assert_equal Status::OK, req.response_status
85
+ assert_nil req.response_body
86
+
87
+ req = make_request(':method' => 'POST', ':path' => '/test')
88
+ assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
89
+ assert_nil req.response_body
90
+
72
91
  req = make_request(':method' => 'GET', ':path' => '/test/index')
73
92
  assert_equal '<h1>Hello, world!</h1>', req.response_body
74
93
 
75
94
  req = make_request(':method' => 'GET', ':path' => '/test/index.html')
76
95
  assert_equal '<h1>Hello, world!</h1>', req.response_body
96
+ assert_equal 'text/html', req.response_headers['Content-Type']
97
+
98
+ req = make_request(':method' => 'HEAD', ':path' => '/test/index.html')
99
+ assert_nil req.response_body
100
+ assert_equal 'text/html', req.response_headers['Content-Type']
101
+
102
+ req = make_request(':method' => 'POST', ':path' => '/test/index.html')
103
+ assert_nil req.response_body
104
+ assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
77
105
 
78
106
  req = make_request(':method' => 'GET', ':path' => '/test/assets/style.css')
79
107
  assert_equal '* { color: beige }', req.response_body
108
+ assert_equal 'text/css', req.response_headers['Content-Type']
80
109
 
81
110
  req = make_request(':method' => 'GET', ':path' => '/assets/style.css')
82
- assert_equal Qeweney::Status::NOT_FOUND, req.response_status
111
+ assert_equal Status::NOT_FOUND, req.response_status
83
112
 
84
113
  req = make_request(':method' => 'GET', ':path' => '/test/api?q=get')
85
114
  assert_equal({ status: 'OK', response: 0 }, req.response_json)
86
115
 
116
+ req = make_request(':method' => 'POST', ':path' => '/test/api?q=get')
117
+ assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
118
+ assert_equal({ status: 'Error', message: '' }, req.response_json)
119
+
87
120
  req = make_request(':method' => 'GET', ':path' => '/test/api/foo?q=get')
88
121
  assert_equal({ status: 'OK', response: 0 }, req.response_json)
89
122
 
90
123
  req = make_request(':method' => 'POST', ':path' => '/test/api?q=incr')
91
124
  assert_equal({ status: 'OK', response: 1 }, req.response_json)
92
125
 
126
+ req = make_request(':method' => 'GET', ':path' => '/test/api?q=incr')
127
+ assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
128
+ assert_equal({ status: 'Error', message: '' }, req.response_json)
129
+
93
130
  req = make_request(':method' => 'POST', ':path' => '/test/api/foo?q=incr')
94
- assert_equal({ status: 'Syntropy::Error', message: 'Teapot' }, req.response_json)
95
- assert_equal Qeweney::Status::TEAPOT, req.response_status
131
+ assert_equal({ status: 'Error', message: 'Teapot' }, req.response_json)
132
+ assert_equal Status::TEAPOT, req.response_status
96
133
 
97
134
  req = make_request(':method' => 'GET', ':path' => '/test/bar')
98
135
  assert_equal 'foobar', req.response_body
136
+ assert_equal Status::OK, req.response_status
137
+
138
+ req = make_request(':method' => 'POST', ':path' => '/test/bar')
139
+ assert_equal 'foobar', req.response_body
140
+ assert_equal Status::OK, req.response_status
141
+
142
+ req = make_request(':method' => 'GET', ':path' => '/test/baz')
143
+ assert_equal 'foobar', req.response_body
144
+ assert_equal Status::OK, req.response_status
145
+
146
+ req = make_request(':method' => 'POST', ':path' => '/test/baz')
147
+ assert_nil req.response_body
148
+ assert_equal Status::METHOD_NOT_ALLOWED, req.response_status
99
149
 
100
150
  req = make_request(':method' => 'GET', ':path' => '/test/about')
101
151
  assert_equal 'About', req.response_body.chomp
@@ -104,7 +154,7 @@ class AppRoutingTest < Minitest::Test
104
154
  assert_equal '<p>Hello from Markdown</p>', req.response_body.chomp
105
155
 
106
156
  req = make_request(':method' => 'GET', ':path' => '/test/about/foo/bar')
107
- assert_equal Qeweney::Status::NOT_FOUND, req.response_status
157
+ assert_equal Status::NOT_FOUND, req.response_status
108
158
  end
109
159
 
110
160
  def test_app_file_watching
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ class SideRunTest < Minitest::Test
6
+ def setup
7
+ @machine = UM.new
8
+ end
9
+
10
+ def test_side_run
11
+ x = Syntropy::SideRun.call(@machine) { 42 }
12
+ assert_equal 42, x
13
+
14
+ hits = []
15
+ f = @machine.spin {
16
+ @machine.periodically(0.01) { hits << it }
17
+ }
18
+
19
+ y = Syntropy::SideRun.call(@machine) { sleep 0.10; 43 }
20
+ @machine.schedule(f, UM::Terminate.new)
21
+ assert_in_range 9..11, hits.size
22
+ end
23
+
24
+ class Bad < Exception
25
+ end
26
+
27
+ def test_side_run_exception
28
+ assert_raises(Bad) { Syntropy::SideRun.call(@machine) { raise Bad } }
29
+ end
30
+
31
+ def test_side_run_convenience_method
32
+ Syntropy.machine = nil
33
+ assert_raises { Syntropy.side_run { 42 } }
34
+
35
+ Syntropy.machine = @machine
36
+ x = Syntropy.side_run { 42 }
37
+ assert_equal 42, x
38
+
39
+ assert_raises(Bad) { Syntropy.side_run { raise Bad } }
40
+ ensure
41
+ Syntropy.machine = nil
42
+ end
43
+ end
@@ -27,7 +27,7 @@ class ValidationTest < Minitest::Test
27
27
  assert_raises(VE) { @req.validate_param(:q, Integer) }
28
28
  assert_raises(VE) { @req.validate_param(:q, Float) }
29
29
  assert_raises(VE) { @req.validate_param(:q, nil) }
30
-
30
+
31
31
  assert_raises(VE) { @req.validate_param(:y, Integer, 1..100) }
32
32
 
33
33
  assert_raises(VE) { @req.validate_param(:y, :bool) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntropy
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.2'
4
+ version: '0.4'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -71,14 +71,14 @@ dependencies:
71
71
  requirements:
72
72
  - - '='
73
73
  - !ruby/object:Gem::Version
74
- version: 0.12.2
74
+ version: 0.13.2
75
75
  type: :runtime
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - '='
80
80
  - !ruby/object:Gem::Version
81
- version: 0.12.2
81
+ version: 0.13.2
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: uringmachine
84
84
  requirement: !ruby/object:Gem::Requirement
@@ -166,13 +166,39 @@ files:
166
166
  - Rakefile
167
167
  - TODO.md
168
168
  - bin/syntropy
169
+ - cmd/setup/template/site/.gitignore
170
+ - cmd/setup/template/site/Dockerfile
171
+ - cmd/setup/template/site/Gemfile
172
+ - cmd/setup/template/site/README.md
173
+ - cmd/setup/template/site/bin/console
174
+ - cmd/setup/template/site/bin/restart
175
+ - cmd/setup/template/site/bin/server
176
+ - cmd/setup/template/site/bin/start
177
+ - cmd/setup/template/site/bin/stop
178
+ - cmd/setup/template/site/docker-compose.yml
179
+ - cmd/setup/template/site/proxy/Dockerfile
180
+ - cmd/setup/template/site/proxy/etc/Caddyfile
181
+ - cmd/setup/template/site/proxy/etc/tls_auto
182
+ - cmd/setup/template/site/proxy/etc/tls_cloudflare
183
+ - cmd/setup/template/site/proxy/etc/tls_custom
184
+ - cmd/setup/template/site/proxy/etc/tls_selfsigned
185
+ - cmd/setup/template/site/site/_layout/default.rb
186
+ - cmd/setup/template/site/site/about.md
187
+ - cmd/setup/template/site/site/articles/cage.rb
188
+ - cmd/setup/template/site/site/articles/index.rb
189
+ - cmd/setup/template/site/site/assets/css/style.css
190
+ - cmd/setup/template/site/site/assets/img/syntropy.png
191
+ - cmd/setup/template/site/site/index.rb
192
+ - docker-compose.yml
169
193
  - lib/syntropy.rb
170
194
  - lib/syntropy/app.rb
171
195
  - lib/syntropy/connection_pool.rb
172
196
  - lib/syntropy/errors.rb
173
197
  - lib/syntropy/file_watch.rb
174
198
  - lib/syntropy/module.rb
199
+ - lib/syntropy/request_extensions.rb
175
200
  - lib/syntropy/rpc_api.rb
201
+ - lib/syntropy/side_run.rb
176
202
  - lib/syntropy/version.rb
177
203
  - syntropy.gemspec
178
204
  - test/app/_layout/default.rb
@@ -184,6 +210,7 @@ files:
184
210
  - test/app/api+.rb
185
211
  - test/app/assets/style.css
186
212
  - test/app/bar.rb
213
+ - test/app/baz.rb
187
214
  - test/app/index.html
188
215
  - test/app/tmp.rb
189
216
  - test/helper.rb
@@ -193,6 +220,7 @@ files:
193
220
  - test/test_file_watch.rb
194
221
  - test/test_module.rb
195
222
  - test/test_rpc_api.rb
223
+ - test/test_side_run.rb
196
224
  - test/test_validation.rb
197
225
  homepage: https://github.com/noteflakes/syntropy
198
226
  licenses: