syntropy 0.1 → 0.3
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 +10 -0
- data/TODO.md +0 -11
- data/bin/syntropy +65 -0
- data/lib/syntropy/app.rb +59 -19
- 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: 43d7686b3b9f5f09c819bebb9e46fdceb45c8c0a66441b1e12b130fa376defc5
|
4
|
+
data.tar.gz: f616e5dcdedc8a08fb23d5a52c1a92296cd348f8555fede1ac31ba0859c1be28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f6d14df3cbbaa74d9ec897cb50fd35caed0f48b6410f2abb13fad79947b50bbf33c32aa6358ab6464ac5b90e861fffacb345805d487cd67f5bd77b36cd7295b
|
7
|
+
data.tar.gz: af07c19a78d94fe37403246b302f95f047aeb12c764e8914e43f5ed1e1b1245cc0678a01e964e895fdc7aef0e775194c6ee9744939113352db07eca166693cc1
|
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 +0,0 @@
|
|
1
|
-
- More database methods:
|
2
|
-
|
3
|
-
- `Database#quote`
|
4
|
-
- `Database#cache_flush` https://sqlite.org/c3ref/db_cacheflush.html
|
5
|
-
- `Database#release_memory` https://sqlite.org/c3ref/db_release_memory.html
|
6
|
-
|
7
|
-
- Security
|
8
|
-
|
9
|
-
- Enable extension loading by using
|
10
|
-
[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION](https://www.sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigenableloadextension)
|
11
|
-
in order to prevent usage of `load_extension()` SQL function.
|
data/bin/syntropy
ADDED
@@ -0,0 +1,65 @@
|
|
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
|
+
|
60
|
+
|
61
|
+
opts[:machine] = UM.new
|
62
|
+
opts[:logger] = opts[:logger] && TP2::Logger.new(opts[:machine], **opts)
|
63
|
+
|
64
|
+
app = Syntropy::App.new(opts[:machine], opts[:location], '/', opts)
|
65
|
+
TP2.run(opts) { app.call(it) }
|
data/lib/syntropy/app.rb
CHANGED
@@ -1,20 +1,32 @@
|
|
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)
|
13
|
-
@
|
15
|
+
def initialize(machine, src_path, mount_path, opts = {})
|
16
|
+
@machine = machine
|
17
|
+
@src_path = File.expand_path(src_path)
|
14
18
|
@mount_path = mount_path
|
15
19
|
@route_cache = {}
|
20
|
+
@opts = opts
|
16
21
|
|
17
22
|
@relative_path_re = calculate_relative_path_re(mount_path)
|
23
|
+
@machine.spin do
|
24
|
+
# we do startup stuff asynchronously, in order to first let TP2 do its
|
25
|
+
# setup tasks
|
26
|
+
@machine.sleep 0.25
|
27
|
+
@opts[:logger]&.call("Serving from #{File.expand_path(@src_path)}")
|
28
|
+
start_file_watcher if opts[:watch_files]
|
29
|
+
end
|
18
30
|
end
|
19
31
|
|
20
32
|
def find_route(path, cache: true)
|
@@ -39,6 +51,32 @@ module Syntropy
|
|
39
51
|
|
40
52
|
private
|
41
53
|
|
54
|
+
def start_file_watcher
|
55
|
+
@opts[:logger]&.call('Watching for module file changes...', nil)
|
56
|
+
wf = @opts[:watch_files]
|
57
|
+
period = wf.is_a?(Numeric) ? wf : 0.1
|
58
|
+
@machine.spin do
|
59
|
+
Syntropy.file_watch(@machine, @src_path, period: period) do
|
60
|
+
@opts[:logger]&.call("Detected changed file: #{it}")
|
61
|
+
invalidate_cache(it)
|
62
|
+
rescue Exception => e
|
63
|
+
p e
|
64
|
+
p e.backtrace
|
65
|
+
exit!
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def invalidate_cache(fn)
|
71
|
+
invalidated_keys = []
|
72
|
+
@route_cache.each do |k, v|
|
73
|
+
@opts[:logger]&.call("Invalidate cache for #{k}", nil)
|
74
|
+
invalidated_keys << k if v[:fn] == fn
|
75
|
+
end
|
76
|
+
|
77
|
+
invalidated_keys.each { @route_cache.delete(it) }
|
78
|
+
end
|
79
|
+
|
42
80
|
def calculate_relative_path_re(mount_path)
|
43
81
|
mount_path = '' if mount_path == '/'
|
44
82
|
/^#{mount_path}(?:\/(.*))?$/
|
@@ -67,12 +105,12 @@ module Syntropy
|
|
67
105
|
|
68
106
|
entry = find_file_entry_with_extension(fs_path)
|
69
107
|
return entry if entry[:kind] != :not_found
|
70
|
-
|
108
|
+
|
71
109
|
find_up_tree_module(path)
|
72
110
|
end
|
73
111
|
|
74
112
|
def file_entry(fn)
|
75
|
-
{ fn: fn, kind: FILE_KINDS[File.extname(fn)] || :static }
|
113
|
+
{ fn: File.expand_path(fn), kind: FILE_KINDS[File.extname(fn)] || :static }
|
76
114
|
end
|
77
115
|
|
78
116
|
def find_index_entry(dir)
|
@@ -115,19 +153,23 @@ module Syntropy
|
|
115
153
|
when :not_found
|
116
154
|
req.respond('Not found', ':status' => Qeweney::Status::NOT_FOUND)
|
117
155
|
when :static
|
118
|
-
|
119
|
-
req.respond(IO.read(entry[:fn]), 'Content-Type' => entry[:mime_type])
|
156
|
+
respond_static(req, entry)
|
120
157
|
when :markdown
|
121
158
|
body = render_markdown(IO.read(entry[:fn]))
|
122
159
|
req.respond(body, 'Content-Type' => 'text/html')
|
123
160
|
when :module
|
124
|
-
call_module(
|
161
|
+
call_module(req, entry)
|
125
162
|
else
|
126
|
-
raise
|
163
|
+
raise 'Invalid entry kind'
|
127
164
|
end
|
128
165
|
end
|
129
166
|
|
130
|
-
def
|
167
|
+
def respond_static(req, entry)
|
168
|
+
entry[:mime_type] ||= Qeweney::MimeTypes[File.extname(entry[:fn])]
|
169
|
+
req.respond(IO.read(entry[:fn]), 'Content-Type' => entry[:mime_type])
|
170
|
+
end
|
171
|
+
|
172
|
+
def call_module(req, entry)
|
131
173
|
entry[:code] ||= load_module(entry)
|
132
174
|
if entry[:code] == :invalid
|
133
175
|
req.respond(nil, ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
|
@@ -138,19 +180,17 @@ module Syntropy
|
|
138
180
|
rescue StandardError => e
|
139
181
|
p e
|
140
182
|
p e.backtrace
|
141
|
-
req.respond(nil, ':status' => Qeweney::
|
183
|
+
req.respond(nil, ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
|
142
184
|
end
|
143
185
|
|
144
186
|
def load_module(entry)
|
145
|
-
|
146
|
-
|
147
|
-
o =
|
187
|
+
loader = Syntropy::ModuleLoader.new(@src_path, @opts)
|
188
|
+
ref = entry[:fn].gsub(%r{^#{@src_path}\/}, '').gsub(/\.rb$/, '')
|
189
|
+
o = loader.load(ref)
|
190
|
+
# klass = Class.new
|
191
|
+
# o = klass.instance_eval(body, entry[:fn], 1)
|
148
192
|
|
149
|
-
|
150
|
-
return wrap_template(o)
|
151
|
-
else
|
152
|
-
return o
|
153
|
-
end
|
193
|
+
o.is_a?(Papercraft::HTML) ? wrap_template(o) : o
|
154
194
|
end
|
155
195
|
|
156
196
|
def wrap_template(templ)
|
@@ -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.3.1'
|
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.3'
|
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.3.1
|
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.3.1
|
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
|