syntropy 0.1 → 0.2
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/.rubocop.yml +196 -0
- data/CHANGELOG.md +6 -0
- data/TODO.md +94 -8
- data/bin/syntropy +61 -0
- data/lib/syntropy/app.rb +32 -7
- data/lib/syntropy/connection_pool.rb +60 -0
- data/lib/syntropy/file_watch.rb +27 -0
- data/lib/syntropy/module.rb +64 -0
- data/lib/syntropy/rpc_api.rb +4 -0
- data/lib/syntropy/version.rb +1 -1
- data/lib/syntropy.rb +23 -1
- data/syntropy.gemspec +23 -18
- data/test/app/_lib/callable.rb +13 -0
- data/test/app/_lib/klass.rb +15 -0
- data/test/app/_lib/missing-export.rb +7 -0
- data/test/app/about/index.rb +1 -1
- data/test/app/api+.rb +4 -3
- data/test/app/bar.rb +1 -1
- data/test/app/tmp.rb +6 -0
- data/test/test_app.rb +20 -1
- data/test/test_connection_pool.rb +81 -0
- data/test/test_file_watch.rb +36 -0
- data/test/test_module.rb +32 -0
- data/test/test_rpc_api.rb +1 -1
- metadata +67 -13
- data/lib/syntropy/context.rb +0 -58
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9a2e580917d4b05e8470695a75ab399326c6b8f7ee3b576526181c36791ee81
|
|
4
|
+
data.tar.gz: eb95e089e426bdb618981694cf8240911f30821a90d5ae9ef8f4bf4d79670a44
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a85e2502bd313a10b5533ec5b8f124cfa72a3c4494f7ddcd912e0939973e2f72df458e6891543c5cbcf106718254561523c7f561ee1fd23bf348cab701696d4a
|
|
7
|
+
data.tar.gz: 60391470703441eccb1bdba52edfba754225171bcf65a08d146d01742e1ab8f7b8f6215cfc093ed4aee4b6e99c0f967cb9a6bf1f19274f6f4d34f8ae95a6e2c4
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.2
|
|
3
|
+
RubyInterpreters:
|
|
4
|
+
- ruby
|
|
5
|
+
Exclude:
|
|
6
|
+
- "**/*.gemspec"
|
|
7
|
+
- "test/**/*.rb"
|
|
8
|
+
- "examples/**/*.rb"
|
|
9
|
+
- "Gemfile*"
|
|
10
|
+
# Style/LambdaCall:
|
|
11
|
+
# Enabled: false
|
|
12
|
+
# Style/ModuleFunction:
|
|
13
|
+
# Enabled: false
|
|
14
|
+
# Style/RegexpLiteral:
|
|
15
|
+
# Enabled: false
|
|
16
|
+
|
|
17
|
+
# Naming/MemoizedInstanceVariableName:
|
|
18
|
+
# Enabled: false
|
|
19
|
+
|
|
20
|
+
# Style/Alias:
|
|
21
|
+
# EnforcedStyle: prefer_alias_method
|
|
22
|
+
|
|
23
|
+
# Style/SpecialGlobalVars:
|
|
24
|
+
# Enabled: false
|
|
25
|
+
|
|
26
|
+
# Style/ClassAndModuleChildren:
|
|
27
|
+
# Enabled: false
|
|
28
|
+
|
|
29
|
+
# Metrics/AbcSize:
|
|
30
|
+
# Enabled: false
|
|
31
|
+
|
|
32
|
+
# Style/MixinUsage:
|
|
33
|
+
# Enabled: false
|
|
34
|
+
|
|
35
|
+
# Style/MultilineBlockChain:
|
|
36
|
+
# Enabled: false
|
|
37
|
+
|
|
38
|
+
# Lint/RescueException:
|
|
39
|
+
# Enabled: false
|
|
40
|
+
|
|
41
|
+
# Lint/InheritException:
|
|
42
|
+
# Enabled: false
|
|
43
|
+
|
|
44
|
+
Style/NumericPredicate:
|
|
45
|
+
Enabled: false
|
|
46
|
+
|
|
47
|
+
# Style/TrivialAccessors:
|
|
48
|
+
# Enabled: false
|
|
49
|
+
|
|
50
|
+
# Lint/MissingSuper:
|
|
51
|
+
# Enabled: false
|
|
52
|
+
|
|
53
|
+
# Style/GlobalVars:
|
|
54
|
+
# Exclude:
|
|
55
|
+
# - lib/polyphony/auto_run.rb
|
|
56
|
+
# - lib/polyphony/extensions/core.rb
|
|
57
|
+
# - examples/**/*.rb
|
|
58
|
+
|
|
59
|
+
Layout/HashAlignment:
|
|
60
|
+
Enabled: false
|
|
61
|
+
EnforcedColonStyle: table
|
|
62
|
+
EnforcedHashRocketStyle: table
|
|
63
|
+
|
|
64
|
+
# Naming/AccessorMethodName:
|
|
65
|
+
# Exclude:
|
|
66
|
+
# - lib/polyphony/extensions/fiber.rb
|
|
67
|
+
# - examples/**/*.rb
|
|
68
|
+
|
|
69
|
+
# Naming/MethodName:
|
|
70
|
+
# Exclude:
|
|
71
|
+
# - test/test_signal.rb
|
|
72
|
+
|
|
73
|
+
# Lint/SuppressedException:
|
|
74
|
+
# Exclude:
|
|
75
|
+
# - examples/**/*.rb
|
|
76
|
+
|
|
77
|
+
Metrics/MethodLength:
|
|
78
|
+
Max: 14
|
|
79
|
+
# Exclude:
|
|
80
|
+
# - lib/polyphony/extensions/io.rb
|
|
81
|
+
# - lib/polyphony/extensions/fiber.rb
|
|
82
|
+
# - lib/polyphony/extensions/thread.rb
|
|
83
|
+
# - lib/polyphony/adapters/open3.rb
|
|
84
|
+
# - test/**/*.rb
|
|
85
|
+
# - examples/**/*.rb
|
|
86
|
+
|
|
87
|
+
# Metrics/ModuleLength:
|
|
88
|
+
# Exclude:
|
|
89
|
+
# - lib/polyphony/core/global_api.rb
|
|
90
|
+
# - examples/**/*.rb
|
|
91
|
+
|
|
92
|
+
# Metrics/ClassLength:
|
|
93
|
+
# Exclude:
|
|
94
|
+
# - lib/polyphony/extensions/io.rb
|
|
95
|
+
# - lib/polyphony/extensions/fiber.rb
|
|
96
|
+
# - lib/polyphony/extensions/object.rb
|
|
97
|
+
# - lib/polyphony/extensions/thread.rb
|
|
98
|
+
# - test/**/*.rb
|
|
99
|
+
# - examples/**/*.rb
|
|
100
|
+
|
|
101
|
+
# Metrics/CyclomaticComplexity:
|
|
102
|
+
# Exclude:
|
|
103
|
+
# - lib/polyphony/extensions/fiber.rb
|
|
104
|
+
|
|
105
|
+
# Metrics/PerceivedComplexity:
|
|
106
|
+
# Exclude:
|
|
107
|
+
# - lib/polyphony/extensions/fiber.rb
|
|
108
|
+
|
|
109
|
+
# Style/RegexpLiteral:
|
|
110
|
+
# Enabled: false
|
|
111
|
+
|
|
112
|
+
Style/RescueModifier:
|
|
113
|
+
Enabled: false
|
|
114
|
+
# Style/Documentation:
|
|
115
|
+
# Exclude:
|
|
116
|
+
# - test/**/*.rb
|
|
117
|
+
# - examples/**/*.rb
|
|
118
|
+
# - lib/polyphony/adapters/**/*.rb
|
|
119
|
+
|
|
120
|
+
# Style/FormatString:
|
|
121
|
+
# Exclude:
|
|
122
|
+
# - test/**/*.rb
|
|
123
|
+
# - examples/**/*.rb
|
|
124
|
+
|
|
125
|
+
# Style/FormatStringToken:
|
|
126
|
+
# Exclude:
|
|
127
|
+
# - test/**/*.rb
|
|
128
|
+
# - examples/**/*.rb
|
|
129
|
+
|
|
130
|
+
Naming/MethodParameterName:
|
|
131
|
+
Enabled: false
|
|
132
|
+
|
|
133
|
+
# Security/MarshalLoad:
|
|
134
|
+
# Exclude:
|
|
135
|
+
# - examples/**/*.rb
|
|
136
|
+
|
|
137
|
+
# Lint/ShadowedArgument:
|
|
138
|
+
# Exclude:
|
|
139
|
+
# - lib/polyphony/extensions/fiber.rb
|
|
140
|
+
|
|
141
|
+
# Style/HashEachMethods:
|
|
142
|
+
# Enabled: true
|
|
143
|
+
|
|
144
|
+
# Style/HashTransformKeys:
|
|
145
|
+
# Enabled: true
|
|
146
|
+
|
|
147
|
+
# Style/HashTransformValues:
|
|
148
|
+
# Enabled: true
|
|
149
|
+
|
|
150
|
+
# Layout/EmptyLinesAroundAttributeAccessor:
|
|
151
|
+
# Enabled: true
|
|
152
|
+
|
|
153
|
+
# Layout/SpaceAroundMethodCallOperator:
|
|
154
|
+
# Enabled: true
|
|
155
|
+
|
|
156
|
+
# Lint/DeprecatedOpenSSLConstant:
|
|
157
|
+
# Enabled: true
|
|
158
|
+
|
|
159
|
+
# Lint/MixedRegexpCaptureTypes:
|
|
160
|
+
# Enabled: true
|
|
161
|
+
|
|
162
|
+
# Lint/RaiseException:
|
|
163
|
+
# Enabled: true
|
|
164
|
+
|
|
165
|
+
# Lint/StructNewOverride:
|
|
166
|
+
# Enabled: true
|
|
167
|
+
|
|
168
|
+
Style/NegatedIf:
|
|
169
|
+
Enabled: false
|
|
170
|
+
# Style/NegatedWhile:
|
|
171
|
+
# Enabled: false
|
|
172
|
+
|
|
173
|
+
# Style/CombinableLoops:
|
|
174
|
+
# Enabled: false
|
|
175
|
+
|
|
176
|
+
# Style/InfiniteLoop:
|
|
177
|
+
# Enabled: false
|
|
178
|
+
|
|
179
|
+
# Style/RedundantReturn:
|
|
180
|
+
# Enabled: false
|
|
181
|
+
|
|
182
|
+
# Style/ExponentialNotation:
|
|
183
|
+
# Enabled: true
|
|
184
|
+
|
|
185
|
+
# Style/RedundantRegexpCharacterClass:
|
|
186
|
+
# Enabled: true
|
|
187
|
+
|
|
188
|
+
# Style/RedundantRegexpEscape:
|
|
189
|
+
# Enabled: true
|
|
190
|
+
|
|
191
|
+
# Style/SlicingWithRange:
|
|
192
|
+
# Enabled: true
|
|
193
|
+
|
|
194
|
+
# Style/RaiseArgs:
|
|
195
|
+
# Exclude:
|
|
196
|
+
# - lib/polyphony/extensions/fiber.rb
|
data/CHANGELOG.md
CHANGED
data/TODO.md
CHANGED
|
@@ -1,11 +1,97 @@
|
|
|
1
|
-
|
|
1
|
+
## Server tool
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
```bash
|
|
4
|
+
$ bundle exec syntropy --dev ./site
|
|
5
|
+
$ bundle exec syntropy --workers 4 ./site
|
|
6
|
+
```
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
And also a config file:
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
```bash
|
|
11
|
+
$ bundle exec syntropy site.rb
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
And the config file:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
# site.rb
|
|
18
|
+
Syntropy.config do
|
|
19
|
+
root './site'
|
|
20
|
+
workers 4
|
|
21
|
+
log { |req| }
|
|
22
|
+
end
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Lightweight model API on top of Extralite
|
|
26
|
+
|
|
27
|
+
- DB connection pool
|
|
28
|
+
- Lightweight means 90% features with 10% effort:
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
Posts = Syntropy::Relation.new('posts')
|
|
32
|
+
|
|
33
|
+
posts = Posts.order_by(:stamp, :desc).all(db)
|
|
34
|
+
|
|
35
|
+
post = Posts.where(id: 13).first(db)
|
|
36
|
+
|
|
37
|
+
id = Posts.insert(db, title: 'foo', body: 'bar')
|
|
38
|
+
|
|
39
|
+
Posts.where(id: id).update(db, body: 'baz')
|
|
40
|
+
|
|
41
|
+
Posts.where(id: id).delete(db)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The whole `db` argument thing is very limiting. For easier usage we integrate
|
|
45
|
+
the db connection pool as dependency injection the model:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
db_pool = E2::ConnectionPool.new(fn)
|
|
49
|
+
Posts = Syntropy::Dataset.new(db_pool, 'posts')
|
|
50
|
+
|
|
51
|
+
Posts[id: 1] #=> { id: 1, title: 'foo', body: 'bar' }
|
|
52
|
+
Posts.find(id: 1) #=>
|
|
53
|
+
|
|
54
|
+
Posts.to_a #=> [...]
|
|
55
|
+
Posts.order_by(:stamp, :desc).to_a #=> [...]
|
|
56
|
+
|
|
57
|
+
id = Posts.insert(title: 'foo', body: 'bar')
|
|
58
|
+
|
|
59
|
+
post = Posts.where(id: id)
|
|
60
|
+
post.values #=> { id: 1, title: 'foo', body: 'bar' }
|
|
61
|
+
post.update(body: 'baz') #=> 1
|
|
62
|
+
post.delete
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
So basically it's a bit similar to Sequel datasets, but there's no "object instance as single row". The instance is the entire set of rows in the table, or a subset thereof:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
Posts.where(...).order_by(...).select(...).from(rowset)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
How about CTEs?
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
Users = Syntrop::Dataset.new(db_pool, 'users')
|
|
75
|
+
|
|
76
|
+
GroupIdRowset = Syntropy::Dataset {
|
|
77
|
+
with(
|
|
78
|
+
foo: Users,
|
|
79
|
+
bar: -> {
|
|
80
|
+
select user_id, group
|
|
81
|
+
from foo
|
|
82
|
+
},
|
|
83
|
+
baz: -> {
|
|
84
|
+
select id
|
|
85
|
+
from bar
|
|
86
|
+
where user_id == bar.select(:user_id)
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
select_all
|
|
91
|
+
from baz
|
|
92
|
+
where id == :group_id
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
users = GroupIdRowset.bind(group_id: 5).to_a
|
|
96
|
+
|
|
97
|
+
```
|
data/bin/syntropy
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'syntropy'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
|
|
7
|
+
opts = {
|
|
8
|
+
banner: Syntropy::BANNER,
|
|
9
|
+
logger: true
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
parser = OptionParser.new do |o|
|
|
13
|
+
o.banner = 'Usage: syntropy [options] DIR'
|
|
14
|
+
|
|
15
|
+
o.on('-b', '--bind BIND', String,
|
|
16
|
+
'Bind address (default: http://0.0.0.0:1234). You can specify this flag multiple times to bind to multiple addresses.') do
|
|
17
|
+
opts[:bind] ||= []
|
|
18
|
+
opts[:bind] << it
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
o.on('-s', '--silent', 'Silent mode') do
|
|
22
|
+
opts[:banner] = nil
|
|
23
|
+
opts[:logger] = nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
o.on('-w', '--watch', 'Watch for changed files') do
|
|
27
|
+
opts[:watch_files] = 0.1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
o.on('-h', '--help', 'Show this help message') do
|
|
31
|
+
puts o
|
|
32
|
+
exit
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
o.on('-v', '--version', 'Show version') do
|
|
36
|
+
require 'syntropy/version'
|
|
37
|
+
puts "Syntropy version #{Syntropy::VERSION}"
|
|
38
|
+
exit
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
RubyVM::YJIT.enable rescue nil
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
parser.parse!
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
puts e.message
|
|
48
|
+
puts e.backtrace.join("\n")
|
|
49
|
+
exit
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
opts[:location] = ARGV.shift || '.'
|
|
53
|
+
|
|
54
|
+
if !File.directory?(opts[:location])
|
|
55
|
+
puts "#{File.expand_path(opts[:location])} Not a directory"
|
|
56
|
+
exit
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
opts[:machine] = UM.new
|
|
60
|
+
app = Syntropy::App.new(opts[:machine], opts[:location], '/', watch_files: 0.05)
|
|
61
|
+
TP2.run(opts) { app.(it) }
|
data/lib/syntropy/app.rb
CHANGED
|
@@ -1,20 +1,34 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'qeweney'
|
|
4
|
-
require 'syntropy/errors'
|
|
5
4
|
require 'json'
|
|
6
5
|
require 'papercraft'
|
|
7
6
|
|
|
7
|
+
require 'syntropy/errors'
|
|
8
|
+
require 'syntropy/file_watch'
|
|
9
|
+
require 'syntropy/module'
|
|
10
|
+
|
|
8
11
|
module Syntropy
|
|
9
12
|
class App
|
|
10
13
|
attr_reader :route_cache
|
|
11
14
|
|
|
12
|
-
def initialize(src_path, mount_path)
|
|
15
|
+
def initialize(machine, src_path, mount_path, env = {})
|
|
16
|
+
@machine = machine
|
|
13
17
|
@src_path = src_path
|
|
14
18
|
@mount_path = mount_path
|
|
15
19
|
@route_cache = {}
|
|
20
|
+
@env = env
|
|
16
21
|
|
|
17
22
|
@relative_path_re = calculate_relative_path_re(mount_path)
|
|
23
|
+
if (wf = env[:watch_files])
|
|
24
|
+
period = wf.is_a?(Numeric) ? wf : 0.1
|
|
25
|
+
machine.spin do
|
|
26
|
+
Syntropy.file_watch(@machine, src_path, period: period) { invalidate_cache(it) }
|
|
27
|
+
rescue Exception => e
|
|
28
|
+
p e
|
|
29
|
+
p e.backtrace
|
|
30
|
+
end
|
|
31
|
+
end
|
|
18
32
|
end
|
|
19
33
|
|
|
20
34
|
def find_route(path, cache: true)
|
|
@@ -28,6 +42,15 @@ module Syntropy
|
|
|
28
42
|
entry
|
|
29
43
|
end
|
|
30
44
|
|
|
45
|
+
def invalidate_cache(fn)
|
|
46
|
+
invalidated_keys = []
|
|
47
|
+
@route_cache.each do |k, v|
|
|
48
|
+
invalidated_keys << k if v[:fn] == fn
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
invalidated_keys.each { @route_cache.delete(it) }
|
|
52
|
+
end
|
|
53
|
+
|
|
31
54
|
def call(req)
|
|
32
55
|
entry = find_route(req.path)
|
|
33
56
|
render_entry(req, entry)
|
|
@@ -67,7 +90,7 @@ module Syntropy
|
|
|
67
90
|
|
|
68
91
|
entry = find_file_entry_with_extension(fs_path)
|
|
69
92
|
return entry if entry[:kind] != :not_found
|
|
70
|
-
|
|
93
|
+
|
|
71
94
|
find_up_tree_module(path)
|
|
72
95
|
end
|
|
73
96
|
|
|
@@ -138,13 +161,15 @@ module Syntropy
|
|
|
138
161
|
rescue StandardError => e
|
|
139
162
|
p e
|
|
140
163
|
p e.backtrace
|
|
141
|
-
req.respond(nil, ':status' => Qeweney::
|
|
164
|
+
req.respond(nil, ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
|
|
142
165
|
end
|
|
143
166
|
|
|
144
167
|
def load_module(entry)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
o =
|
|
168
|
+
loader = Syntropy::ModuleLoader.new(@src_path, @env)
|
|
169
|
+
ref = entry[:fn].gsub(%r{^#{@src_path}\/}, '').gsub(/\.rb$/, '')
|
|
170
|
+
o = loader.load(ref)
|
|
171
|
+
# klass = Class.new
|
|
172
|
+
# o = klass.instance_eval(body, entry[:fn], 1)
|
|
148
173
|
|
|
149
174
|
if o.is_a?(Papercraft::HTML)
|
|
150
175
|
return wrap_template(o)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'extralite'
|
|
4
|
+
|
|
5
|
+
module Syntropy
|
|
6
|
+
class ConnectionPool
|
|
7
|
+
attr_reader :count
|
|
8
|
+
|
|
9
|
+
def initialize(machine, fn, max_conn)
|
|
10
|
+
@machine = machine
|
|
11
|
+
@fn = fn
|
|
12
|
+
@count = 0
|
|
13
|
+
@max_conn = max_conn
|
|
14
|
+
@queue = UM::Queue.new
|
|
15
|
+
@key = :"db_#{fn}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def with_db
|
|
19
|
+
if (db = Thread.current[@key])
|
|
20
|
+
@machine.snooze
|
|
21
|
+
return yield(db)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
db = checkout
|
|
25
|
+
begin
|
|
26
|
+
Thread.current[@key] = db
|
|
27
|
+
yield(db)
|
|
28
|
+
ensure
|
|
29
|
+
Thread.current[@key] = nil
|
|
30
|
+
checkin(db)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def checkout
|
|
37
|
+
if @queue.count == 0 && @count < @max_conn
|
|
38
|
+
return create_db
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@machine.shift(@queue)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def checkin(db)
|
|
45
|
+
@machine.push(@queue, db)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def create_db
|
|
49
|
+
db = Extralite::Database.new(@fn, wal: true)
|
|
50
|
+
setup_db(db)
|
|
51
|
+
@count += 1
|
|
52
|
+
db
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def setup_db(db)
|
|
56
|
+
# setup WAL, sync
|
|
57
|
+
# setup concurrency stuff
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Syntropy
|
|
4
|
+
def self.file_watch(machine, *roots, period: 0.1, &block)
|
|
5
|
+
raise 'Missing root paths' if roots.empty?
|
|
6
|
+
|
|
7
|
+
require 'listen'
|
|
8
|
+
|
|
9
|
+
queue = Thread::Queue.new
|
|
10
|
+
listener = Listen.to(*roots) do |modified, added, removed|
|
|
11
|
+
fns = (modified + added + removed).uniq
|
|
12
|
+
fns.each { queue.push(it) }
|
|
13
|
+
end
|
|
14
|
+
listener.start
|
|
15
|
+
|
|
16
|
+
loop do
|
|
17
|
+
machine.sleep(period) while queue.empty?
|
|
18
|
+
fn = queue.shift
|
|
19
|
+
block.call(fn)
|
|
20
|
+
end
|
|
21
|
+
rescue StandardError => e
|
|
22
|
+
p e
|
|
23
|
+
p e.backtrace
|
|
24
|
+
ensure
|
|
25
|
+
listener.stop
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Syntropy
|
|
4
|
+
class ModuleLoader
|
|
5
|
+
def initialize(root, env)
|
|
6
|
+
@root = root
|
|
7
|
+
@env = env
|
|
8
|
+
@loaded = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def load(ref)
|
|
12
|
+
@loaded[ref] ||= load_module(ref)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def load_module(ref)
|
|
18
|
+
fn = File.join(@root, "#{ref}.rb")
|
|
19
|
+
raise RuntimeError, "File not found #{fn}" if !File.file?(fn)
|
|
20
|
+
|
|
21
|
+
mod_body = IO.read(fn)
|
|
22
|
+
mod_ctx = Class.new(Syntropy::Module)
|
|
23
|
+
mod_ctx.loader = self
|
|
24
|
+
# mod_ctx = .new(self, @env)
|
|
25
|
+
mod_ctx.module_eval(mod_body, fn, 1)
|
|
26
|
+
|
|
27
|
+
export_value = mod_ctx.__export_value__
|
|
28
|
+
|
|
29
|
+
case export_value
|
|
30
|
+
when nil
|
|
31
|
+
raise RuntimeError, 'No export found'
|
|
32
|
+
when Symbol
|
|
33
|
+
# TODO: verify export_value denotes a valid method
|
|
34
|
+
mod_ctx.new(@env)
|
|
35
|
+
when Proc
|
|
36
|
+
export_value
|
|
37
|
+
else
|
|
38
|
+
export_value.new(@env)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class Module
|
|
44
|
+
def initialize(env)
|
|
45
|
+
@env = env
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.loader=(loader)
|
|
49
|
+
@loader = loader
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.import(ref)
|
|
53
|
+
@loader.load(ref)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.export(ref)
|
|
57
|
+
@__export_value__ = ref
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.__export_value__
|
|
61
|
+
@__export_value__
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/syntropy/rpc_api.rb
CHANGED
data/lib/syntropy/version.rb
CHANGED
data/lib/syntropy.rb
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'qeweney'
|
|
4
|
+
require 'uringmachine'
|
|
5
|
+
require 'tp2'
|
|
4
6
|
|
|
5
7
|
require 'syntropy/errors'
|
|
6
|
-
|
|
8
|
+
require 'syntropy/connection_pool'
|
|
9
|
+
require 'syntropy/module'
|
|
7
10
|
require 'syntropy/rpc_api'
|
|
8
11
|
require 'syntropy/app'
|
|
9
12
|
|
|
@@ -57,4 +60,23 @@ class Qeweney::Request
|
|
|
57
60
|
end
|
|
58
61
|
|
|
59
62
|
module Syntropy
|
|
63
|
+
def colorize(color_code)
|
|
64
|
+
"\e[#{color_code}m#{self}\e[0m"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
GREEN = "\e[32m"
|
|
68
|
+
WHITE = "\e[0m"
|
|
69
|
+
YELLOW = "\e[33m"
|
|
70
|
+
|
|
71
|
+
BANNER = (
|
|
72
|
+
"\n"\
|
|
73
|
+
" #{GREEN}\n"\
|
|
74
|
+
" #{GREEN} ooo\n"\
|
|
75
|
+
" #{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"\
|
|
79
|
+
" #{GREEN} :#{YELLOW}|#{GREEN}:::#{YELLOW}|#{GREEN}::#{YELLOW}|#{GREEN}:\n"\
|
|
80
|
+
" #{YELLOW}++++++++++++\e[0m\n\n"
|
|
81
|
+
)
|
|
60
82
|
end
|
data/syntropy.gemspec
CHANGED
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
require_relative './lib/syntropy/version'
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
|
-
s.
|
|
5
|
-
s.
|
|
6
|
-
s.
|
|
7
|
-
s.
|
|
8
|
-
s.
|
|
4
|
+
s.name = 'syntropy'
|
|
5
|
+
s.summary = 'Syntropic Web Framework'
|
|
6
|
+
s.version = Syntropy::VERSION
|
|
7
|
+
s.licenses = ['MIT']
|
|
8
|
+
s.author = 'Sharon Rosner'
|
|
9
|
+
s.email = 'sharon@noteflakes.com'
|
|
10
|
+
s.files = `git ls-files`.split
|
|
9
11
|
|
|
10
|
-
s.homepage
|
|
11
|
-
s.metadata
|
|
12
|
+
s.homepage = 'https://github.com/noteflakes/syntropy'
|
|
13
|
+
s.metadata = {
|
|
12
14
|
'homepage_uri' => 'https://github.com/noteflakes/syntropy',
|
|
13
15
|
'documentation_uri' => 'https://www.rubydoc.info/gems/syntropy',
|
|
14
16
|
'changelog_uri' => 'https://github.com/noteflakes/syntropy/blob/master/CHANGELOG.md'
|
|
15
17
|
}
|
|
16
|
-
s.rdoc_options
|
|
18
|
+
s.rdoc_options = ['--title', 'Extralite', '--main', 'README.md']
|
|
17
19
|
s.extra_rdoc_files = ['README.md']
|
|
18
20
|
s.require_paths = ['lib']
|
|
19
|
-
s.required_ruby_version = '>= 3.
|
|
21
|
+
s.required_ruby_version = '>= 3.4'
|
|
22
|
+
s.executables = ['syntropy']
|
|
20
23
|
|
|
21
|
-
s.add_dependency
|
|
22
|
-
s.add_dependency
|
|
23
|
-
s.add_dependency
|
|
24
|
-
s.add_dependency
|
|
25
|
-
s.add_dependency
|
|
24
|
+
s.add_dependency 'extralite', '2.12'
|
|
25
|
+
s.add_dependency 'json', '2.12.2'
|
|
26
|
+
s.add_dependency 'papercraft', '1.4'
|
|
27
|
+
s.add_dependency 'qeweney', '0.21'
|
|
28
|
+
s.add_dependency 'tp2', '0.12.2'
|
|
29
|
+
s.add_dependency 'uringmachine', '0.15'
|
|
26
30
|
|
|
27
|
-
s.
|
|
28
|
-
s.
|
|
31
|
+
s.add_dependency 'listen', '3.9.0'
|
|
32
|
+
s.add_dependency 'logger', '1.7.0'
|
|
33
|
+
|
|
34
|
+
s.add_development_dependency 'minitest', '5.25.5'
|
|
35
|
+
s.add_development_dependency 'rake', '13.3.0'
|
|
29
36
|
|
|
30
|
-
s.name = 'syntropy'
|
|
31
|
-
s.summary = 'Syntropic Web Framework'
|
|
32
37
|
end
|
data/test/app/about/index.rb
CHANGED
data/test/app/api+.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
class API < Syntropy::RPCAPI
|
|
2
|
-
def initialize
|
|
2
|
+
def initialize(env)
|
|
3
|
+
super(env)
|
|
3
4
|
@count = 0
|
|
4
5
|
end
|
|
5
6
|
|
|
@@ -9,11 +10,11 @@ class API < Syntropy::RPCAPI
|
|
|
9
10
|
|
|
10
11
|
def incr!(req)
|
|
11
12
|
if req.path != '/test/api'
|
|
12
|
-
raise Syntropy::Error.new(Qeweney::Status::TEAPOT, 'Teapot')
|
|
13
|
+
raise Syntropy::Error.new(Qeweney::Status::TEAPOT, 'Teapot')
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
@count += 1
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
API
|
|
20
|
+
export API
|
data/test/app/bar.rb
CHANGED
data/test/app/tmp.rb
ADDED
data/test/test_app.rb
CHANGED
|
@@ -6,7 +6,12 @@ class AppRoutingTest < Minitest::Test
|
|
|
6
6
|
APP_ROOT = File.join(__dir__, 'app')
|
|
7
7
|
|
|
8
8
|
def setup
|
|
9
|
-
@
|
|
9
|
+
@machine = UM.new
|
|
10
|
+
|
|
11
|
+
@tmp_path = '/test/tmp'
|
|
12
|
+
@tmp_fn = File.join(APP_ROOT, "tmp.rb")
|
|
13
|
+
|
|
14
|
+
@app = Syntropy::App.new(@machine, APP_ROOT, '/test', watch_files: 0.05)
|
|
10
15
|
end
|
|
11
16
|
|
|
12
17
|
def full_path(fn)
|
|
@@ -100,7 +105,21 @@ class AppRoutingTest < Minitest::Test
|
|
|
100
105
|
|
|
101
106
|
req = make_request(':method' => 'GET', ':path' => '/test/about/foo/bar')
|
|
102
107
|
assert_equal Qeweney::Status::NOT_FOUND, req.response_status
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_app_file_watching
|
|
111
|
+
@machine.sleep 0.2
|
|
112
|
+
|
|
113
|
+
req = make_request(':method' => 'GET', ':path' => @tmp_path)
|
|
114
|
+
assert_equal 'foo', req.response_body
|
|
103
115
|
|
|
116
|
+
orig_body = IO.read(@tmp_fn)
|
|
117
|
+
IO.write(@tmp_fn, orig_body.gsub('foo', 'bar'))
|
|
118
|
+
@machine.sleep(0.5)
|
|
104
119
|
|
|
120
|
+
req = make_request(':method' => 'GET', ':path' => @tmp_path)
|
|
121
|
+
assert_equal 'bar', req.response_body
|
|
122
|
+
ensure
|
|
123
|
+
IO.write(@tmp_fn, orig_body) if orig_body
|
|
105
124
|
end
|
|
106
125
|
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'helper'
|
|
4
|
+
|
|
5
|
+
class ConnectionPoolTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@machine = UM.new
|
|
8
|
+
@fn = "/tmp/#{rand(100000)}.db"
|
|
9
|
+
@cp = Syntropy::ConnectionPool.new(@machine, @fn, 4)
|
|
10
|
+
|
|
11
|
+
FileUtils.rm(@fn) rescue nil
|
|
12
|
+
@standalone_db = Extralite::Database.new(@fn)
|
|
13
|
+
@standalone_db.execute("create table foo (x,y, z)")
|
|
14
|
+
@standalone_db.execute("insert into foo values (1, 2, 3)")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_with_db
|
|
18
|
+
assert_equal 0, @cp.count
|
|
19
|
+
|
|
20
|
+
@cp.with_db do |db|
|
|
21
|
+
assert_kind_of Extralite::Database, db
|
|
22
|
+
|
|
23
|
+
records = db.query("select * from foo")
|
|
24
|
+
assert_equal [{x: 1, y: 2, z: 3}], records
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
assert_equal 1, @cp.count
|
|
28
|
+
@cp.with_db { |db| assert_kind_of Extralite::Database, db }
|
|
29
|
+
assert_equal 1, @cp.count
|
|
30
|
+
|
|
31
|
+
dbs = []
|
|
32
|
+
ff = (1..2).map { |i|
|
|
33
|
+
@machine.spin {
|
|
34
|
+
@cp.with_db { |db|
|
|
35
|
+
dbs << db
|
|
36
|
+
@machine.sleep(0.05)
|
|
37
|
+
db.execute("insert into foo values (?, ?, ?)", i * 10 + 1, i * 10 + 2, i * 10 + 3)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
@machine.join(*ff)
|
|
42
|
+
|
|
43
|
+
assert_equal 2, dbs.size
|
|
44
|
+
assert_equal 2, dbs.uniq.size
|
|
45
|
+
assert_equal 2, @cp.count
|
|
46
|
+
|
|
47
|
+
records = @standalone_db.query("select * from foo order by x")
|
|
48
|
+
assert_equal [
|
|
49
|
+
{x: 1, y: 2, z: 3},
|
|
50
|
+
{x: 11, y: 12, z: 13},
|
|
51
|
+
{x: 21, y: 22, z: 23},
|
|
52
|
+
], records
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
dbs = []
|
|
56
|
+
ff = (1..10).map { |i|
|
|
57
|
+
@machine.spin {
|
|
58
|
+
@cp.with_db { |db|
|
|
59
|
+
dbs << db
|
|
60
|
+
@machine.sleep(0.05 + rand * 0.05)
|
|
61
|
+
db.execute("insert into foo values (?, ?, ?)", i * 10 + 1, i * 10 + 2, i * 10 + 3)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
@machine.join(*ff)
|
|
66
|
+
|
|
67
|
+
assert_equal 10, dbs.size
|
|
68
|
+
assert_equal 4, dbs.uniq.size
|
|
69
|
+
assert_equal 4, @cp.count
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_with_db_reentrant
|
|
73
|
+
dbs = @cp.with_db do |db1|
|
|
74
|
+
@cp.with_db do |db2|
|
|
75
|
+
[db1, db2]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
assert_equal 1, dbs.uniq.size
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require_relative 'helper'
|
|
5
|
+
|
|
6
|
+
class FileWatchTest < Minitest::Test
|
|
7
|
+
def setup
|
|
8
|
+
@machine = UM.new
|
|
9
|
+
@root = "/tmp/syntropy/#{rand(1000000).to_s(16)}"
|
|
10
|
+
FileUtils.mkdir_p(@root)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_file_watch
|
|
14
|
+
queue = UM::Queue.new
|
|
15
|
+
|
|
16
|
+
f = @machine.spin do
|
|
17
|
+
Syntropy.file_watch(@machine, @root, period: 0.01) { @machine.push(queue, it) }
|
|
18
|
+
end
|
|
19
|
+
@machine.sleep(0.05)
|
|
20
|
+
assert_equal 0, queue.count
|
|
21
|
+
|
|
22
|
+
fn = File.join(@root, 'foo.bar')
|
|
23
|
+
IO.write(fn, 'abc')
|
|
24
|
+
assert_equal fn, @machine.shift(queue)
|
|
25
|
+
|
|
26
|
+
fn = File.join(@root, 'foo.bar')
|
|
27
|
+
IO.write(fn, 'def')
|
|
28
|
+
assert_equal fn, @machine.shift(queue)
|
|
29
|
+
|
|
30
|
+
FileUtils.rm(fn)
|
|
31
|
+
assert_equal fn, @machine.shift(queue)
|
|
32
|
+
ensure
|
|
33
|
+
@machine.schedule(f, UM::Terminate)
|
|
34
|
+
# @machine.join(f)
|
|
35
|
+
end
|
|
36
|
+
end
|
data/test/test_module.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'helper'
|
|
4
|
+
|
|
5
|
+
class ModuleTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@machine = UM.new
|
|
8
|
+
@root = File.join(__dir__, 'app')
|
|
9
|
+
@env = { baz: 42 }
|
|
10
|
+
@loader = Syntropy::ModuleLoader.new(@root, @env)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_module_loading
|
|
14
|
+
mod = @loader.load('_lib/klass')
|
|
15
|
+
assert_equal :bar, mod.foo
|
|
16
|
+
assert_equal 42, mod.bar
|
|
17
|
+
|
|
18
|
+
assert_raises(RuntimeError) { @loader.load('_lib/foo') }
|
|
19
|
+
assert_raises(RuntimeError) { @loader.load('_lib/missing-export') }
|
|
20
|
+
|
|
21
|
+
mod = @loader.load('_lib/callable')
|
|
22
|
+
assert_kind_of Syntropy::Module, mod
|
|
23
|
+
assert_equal 'barbarbar', mod.call(3)
|
|
24
|
+
assert_raises(NoMethodError) { mod.foo(2) }
|
|
25
|
+
assert_equal 42, mod.bar
|
|
26
|
+
|
|
27
|
+
mod = @loader.load('_lib/klass')
|
|
28
|
+
assert_equal :bar, mod.foo
|
|
29
|
+
@env[:baz] += 1
|
|
30
|
+
assert_equal 43, mod.bar
|
|
31
|
+
end
|
|
32
|
+
end
|
data/test/test_rpc_api.rb
CHANGED
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.
|
|
4
|
+
version: '0.2'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sharon Rosner
|
|
@@ -9,6 +9,20 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: extralite
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - '='
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.12'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - '='
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.12'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: json
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -23,6 +37,20 @@ dependencies:
|
|
|
23
37
|
- - '='
|
|
24
38
|
- !ruby/object:Gem::Version
|
|
25
39
|
version: 2.12.2
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: papercraft
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - '='
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.4'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - '='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.4'
|
|
26
54
|
- !ruby/object:Gem::Dependency
|
|
27
55
|
name: qeweney
|
|
28
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -38,47 +66,61 @@ dependencies:
|
|
|
38
66
|
- !ruby/object:Gem::Version
|
|
39
67
|
version: '0.21'
|
|
40
68
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
69
|
+
name: tp2
|
|
42
70
|
requirement: !ruby/object:Gem::Requirement
|
|
43
71
|
requirements:
|
|
44
72
|
- - '='
|
|
45
73
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
74
|
+
version: 0.12.2
|
|
47
75
|
type: :runtime
|
|
48
76
|
prerelease: false
|
|
49
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
78
|
requirements:
|
|
51
79
|
- - '='
|
|
52
80
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
81
|
+
version: 0.12.2
|
|
54
82
|
- !ruby/object:Gem::Dependency
|
|
55
|
-
name:
|
|
83
|
+
name: uringmachine
|
|
56
84
|
requirement: !ruby/object:Gem::Requirement
|
|
57
85
|
requirements:
|
|
58
86
|
- - '='
|
|
59
87
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 0.
|
|
88
|
+
version: '0.15'
|
|
61
89
|
type: :runtime
|
|
62
90
|
prerelease: false
|
|
63
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
92
|
requirements:
|
|
65
93
|
- - '='
|
|
66
94
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 0.
|
|
95
|
+
version: '0.15'
|
|
68
96
|
- !ruby/object:Gem::Dependency
|
|
69
|
-
name:
|
|
97
|
+
name: listen
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - '='
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 3.9.0
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - '='
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: 3.9.0
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: logger
|
|
70
112
|
requirement: !ruby/object:Gem::Requirement
|
|
71
113
|
requirements:
|
|
72
114
|
- - '='
|
|
73
115
|
- !ruby/object:Gem::Version
|
|
74
|
-
version:
|
|
116
|
+
version: 1.7.0
|
|
75
117
|
type: :runtime
|
|
76
118
|
prerelease: false
|
|
77
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
120
|
requirements:
|
|
79
121
|
- - '='
|
|
80
122
|
- !ruby/object:Gem::Version
|
|
81
|
-
version:
|
|
123
|
+
version: 1.7.0
|
|
82
124
|
- !ruby/object:Gem::Dependency
|
|
83
125
|
name: minitest
|
|
84
126
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,36 +150,48 @@ dependencies:
|
|
|
108
150
|
- !ruby/object:Gem::Version
|
|
109
151
|
version: 13.3.0
|
|
110
152
|
email: sharon@noteflakes.com
|
|
111
|
-
executables:
|
|
153
|
+
executables:
|
|
154
|
+
- syntropy
|
|
112
155
|
extensions: []
|
|
113
156
|
extra_rdoc_files:
|
|
114
157
|
- README.md
|
|
115
158
|
files:
|
|
116
159
|
- ".github/workflows/test.yml"
|
|
117
160
|
- ".gitignore"
|
|
161
|
+
- ".rubocop.yml"
|
|
118
162
|
- CHANGELOG.md
|
|
119
163
|
- Gemfile
|
|
120
164
|
- LICENSE
|
|
121
165
|
- README.md
|
|
122
166
|
- Rakefile
|
|
123
167
|
- TODO.md
|
|
168
|
+
- bin/syntropy
|
|
124
169
|
- lib/syntropy.rb
|
|
125
170
|
- lib/syntropy/app.rb
|
|
126
|
-
- lib/syntropy/
|
|
171
|
+
- lib/syntropy/connection_pool.rb
|
|
127
172
|
- lib/syntropy/errors.rb
|
|
173
|
+
- lib/syntropy/file_watch.rb
|
|
174
|
+
- lib/syntropy/module.rb
|
|
128
175
|
- lib/syntropy/rpc_api.rb
|
|
129
176
|
- lib/syntropy/version.rb
|
|
130
177
|
- syntropy.gemspec
|
|
131
178
|
- test/app/_layout/default.rb
|
|
179
|
+
- test/app/_lib/callable.rb
|
|
180
|
+
- test/app/_lib/klass.rb
|
|
181
|
+
- test/app/_lib/missing-export.rb
|
|
132
182
|
- test/app/about/foo.md
|
|
133
183
|
- test/app/about/index.rb
|
|
134
184
|
- test/app/api+.rb
|
|
135
185
|
- test/app/assets/style.css
|
|
136
186
|
- test/app/bar.rb
|
|
137
187
|
- test/app/index.html
|
|
188
|
+
- test/app/tmp.rb
|
|
138
189
|
- test/helper.rb
|
|
139
190
|
- test/run.rb
|
|
140
191
|
- test/test_app.rb
|
|
192
|
+
- test/test_connection_pool.rb
|
|
193
|
+
- test/test_file_watch.rb
|
|
194
|
+
- test/test_module.rb
|
|
141
195
|
- test/test_rpc_api.rb
|
|
142
196
|
- test/test_validation.rb
|
|
143
197
|
homepage: https://github.com/noteflakes/syntropy
|
|
@@ -158,7 +212,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
158
212
|
requirements:
|
|
159
213
|
- - ">="
|
|
160
214
|
- !ruby/object:Gem::Version
|
|
161
|
-
version: '3.
|
|
215
|
+
version: '3.4'
|
|
162
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
217
|
requirements:
|
|
164
218
|
- - ">="
|
data/lib/syntropy/context.rb
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# # frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# require 'syntropy/errors'
|
|
4
|
-
|
|
5
|
-
# module Syntropy
|
|
6
|
-
# class Context
|
|
7
|
-
# attr_reader :request
|
|
8
|
-
|
|
9
|
-
# def initialize(request)
|
|
10
|
-
# @request = request
|
|
11
|
-
# end
|
|
12
|
-
|
|
13
|
-
# def params
|
|
14
|
-
# @request.query
|
|
15
|
-
# end
|
|
16
|
-
|
|
17
|
-
# def validate_param(name, *clauses)
|
|
18
|
-
# value = @request.query[name]
|
|
19
|
-
# clauses.each do |c|
|
|
20
|
-
# valid = param_is_valid?(value, c)
|
|
21
|
-
# raise(Syntropy::ValidationError, 'Validation error') if !valid
|
|
22
|
-
# value = param_convert(value, c)
|
|
23
|
-
# end
|
|
24
|
-
# value
|
|
25
|
-
# end
|
|
26
|
-
|
|
27
|
-
# BOOL_REGEXP = /^(t|f|true|false|on|off|1|0|yes|no)$/
|
|
28
|
-
# BOOL_TRUE_REGEXP = /^(t|true|on|1|yes)$/
|
|
29
|
-
# INTEGER_REGEXP = /^[\+\-]?[0-9]+$/
|
|
30
|
-
# FLOAT_REGEXP = /^[\+\-]?[0-9]+(\.[0-9]+)?$/
|
|
31
|
-
|
|
32
|
-
# def param_is_valid?(value, cond)
|
|
33
|
-
# if cond == :bool
|
|
34
|
-
# return (value && value =~ BOOL_REGEXP)
|
|
35
|
-
# elsif cond == Integer
|
|
36
|
-
# return (value && value =~ INTEGER_REGEXP)
|
|
37
|
-
# elsif cond == Float
|
|
38
|
-
# return (value && value =~ FLOAT_REGEXP)
|
|
39
|
-
# elsif cond.is_a?(Array)
|
|
40
|
-
# return cond.any? { |c| param_is_valid?(value, c) }
|
|
41
|
-
# end
|
|
42
|
-
|
|
43
|
-
# cond === value
|
|
44
|
-
# end
|
|
45
|
-
|
|
46
|
-
# def param_convert(value, klass)
|
|
47
|
-
# if klass == :bool
|
|
48
|
-
# value = value =~ BOOL_TRUE_REGEXP ? true : false
|
|
49
|
-
# elsif klass == Integer
|
|
50
|
-
# value = value.to_i
|
|
51
|
-
# elsif klass == Float
|
|
52
|
-
# value = value.to_f
|
|
53
|
-
# else
|
|
54
|
-
# value
|
|
55
|
-
# end
|
|
56
|
-
# end
|
|
57
|
-
# end
|
|
58
|
-
# end
|