syntropy 0.3 → 0.5
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 +10 -2
- data/CHANGELOG.md +14 -0
- data/README.md +30 -11
- data/TODO.md +135 -0
- data/bin/syntropy +3 -3
- data/cmd/setup/template/site/.gitignore +57 -0
- data/cmd/setup/template/site/Dockerfile +32 -0
- data/cmd/setup/template/site/Gemfile +3 -0
- data/cmd/setup/template/site/README.md +0 -0
- data/cmd/setup/template/site/bin/console +0 -0
- data/cmd/setup/template/site/bin/restart +0 -0
- data/cmd/setup/template/site/bin/server +0 -0
- data/cmd/setup/template/site/bin/start +0 -0
- data/cmd/setup/template/site/bin/stop +0 -0
- data/cmd/setup/template/site/docker-compose.yml +51 -0
- data/cmd/setup/template/site/proxy/Dockerfile +5 -0
- data/cmd/setup/template/site/proxy/etc/Caddyfile +7 -0
- data/cmd/setup/template/site/proxy/etc/tls_auto +2 -0
- data/cmd/setup/template/site/proxy/etc/tls_cloudflare +4 -0
- data/cmd/setup/template/site/proxy/etc/tls_custom +1 -0
- data/cmd/setup/template/site/proxy/etc/tls_selfsigned +1 -0
- data/cmd/setup/template/site/site/_layout/default.rb +11 -0
- data/cmd/setup/template/site/site/about.md +6 -0
- data/cmd/setup/template/site/site/articles/cage.rb +29 -0
- data/cmd/setup/template/site/site/articles/index.rb +3 -0
- data/cmd/setup/template/site/site/assets/css/style.css +40 -0
- data/cmd/setup/template/site/site/assets/img/syntropy.png +0 -0
- data/cmd/setup/template/site/site/index.rb +15 -0
- data/docker-compose.yml +51 -0
- data/lib/syntropy/app.rb +112 -134
- data/lib/syntropy/errors.rb +16 -2
- data/lib/syntropy/file_watch.rb +5 -4
- data/lib/syntropy/module.rb +26 -5
- data/lib/syntropy/request_extensions.rb +96 -0
- data/lib/syntropy/router.rb +208 -0
- data/lib/syntropy/rpc_api.rb +26 -9
- data/lib/syntropy/side_run.rb +46 -0
- data/lib/syntropy/version.rb +1 -1
- data/lib/syntropy.rb +15 -49
- data/syntropy.gemspec +1 -1
- data/test/app/baz.rb +3 -0
- data/test/app_custom/_site.rb +3 -0
- data/test/test_app.rb +96 -51
- data/test/test_file_watch.rb +4 -4
- data/test/test_router.rb +90 -0
- data/test/test_side_run.rb +43 -0
- data/test/test_validation.rb +1 -1
- metadata +34 -3
data/lib/syntropy/app.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'qeweney'
|
4
3
|
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
require 'qeweney'
|
5
7
|
require 'papercraft'
|
6
8
|
|
7
9
|
require 'syntropy/errors'
|
@@ -10,39 +12,50 @@ require 'syntropy/module'
|
|
10
12
|
|
11
13
|
module Syntropy
|
12
14
|
class App
|
13
|
-
|
15
|
+
class << self
|
16
|
+
def load(opts)
|
17
|
+
site_file_app(opts) || default_app(opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def site_file_app(opts)
|
23
|
+
site_fn = File.join(opts[:location], '_site.rb')
|
24
|
+
return nil if !File.file?(site_fn)
|
25
|
+
|
26
|
+
loader = Syntropy::ModuleLoader.new(opts[:location], opts)
|
27
|
+
loader.load('_site')
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_app(opts)
|
31
|
+
new(opts[:machine], opts[:location], opts[:mount_path] || '/', opts)
|
32
|
+
end
|
33
|
+
end
|
14
34
|
|
15
35
|
def initialize(machine, src_path, mount_path, opts = {})
|
16
36
|
@machine = machine
|
17
37
|
@src_path = File.expand_path(src_path)
|
18
38
|
@mount_path = mount_path
|
19
|
-
@route_cache = {}
|
20
39
|
@opts = opts
|
21
40
|
|
22
|
-
@
|
41
|
+
@module_loader = Syntropy::ModuleLoader.new(@src_path, @opts)
|
42
|
+
@router = Syntropy::Router.new(@opts, @module_loader)
|
43
|
+
|
23
44
|
@machine.spin do
|
24
45
|
# we do startup stuff asynchronously, in order to first let TP2 do its
|
25
46
|
# setup tasks
|
26
|
-
@machine.sleep 0.
|
47
|
+
@machine.sleep 0.15
|
27
48
|
@opts[:logger]&.call("Serving from #{File.expand_path(@src_path)}")
|
28
|
-
start_file_watcher if opts[:watch_files]
|
49
|
+
@router.start_file_watcher if opts[:watch_files]
|
29
50
|
end
|
30
51
|
end
|
31
52
|
|
32
|
-
def find_route(path, cache: true)
|
33
|
-
cached = @route_cache[path]
|
34
|
-
return cached if cached
|
35
|
-
|
36
|
-
entry = calculate_route(path)
|
37
|
-
if entry[:kind] != :not_found
|
38
|
-
@route_cache[path] = entry if cache
|
39
|
-
end
|
40
|
-
entry
|
41
|
-
end
|
42
|
-
|
43
53
|
def call(req)
|
44
|
-
entry =
|
54
|
+
entry = @router[req.path]
|
45
55
|
render_entry(req, entry)
|
56
|
+
rescue Syntropy::Error => e
|
57
|
+
msg = e.message
|
58
|
+
req.respond(msg.empty? ? nil : msg, ':status' => e.http_status)
|
46
59
|
rescue StandardError => e
|
47
60
|
p e
|
48
61
|
p e.backtrace
|
@@ -51,132 +64,59 @@ module Syntropy
|
|
51
64
|
|
52
65
|
private
|
53
66
|
|
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
|
-
|
80
|
-
def calculate_relative_path_re(mount_path)
|
81
|
-
mount_path = '' if mount_path == '/'
|
82
|
-
/^#{mount_path}(?:\/(.*))?$/
|
83
|
-
end
|
84
|
-
|
85
|
-
FILE_KINDS = {
|
86
|
-
'.rb' => :module,
|
87
|
-
'.md' => :markdown
|
88
|
-
}
|
89
|
-
NOT_FOUND = { kind: :not_found }
|
90
|
-
|
91
|
-
# We don't allow access to path with /.., or entries that start with _
|
92
|
-
FORBIDDEN_RE = /(\/_)|((\/\.\.)\/?)/
|
93
|
-
|
94
|
-
def calculate_route(path)
|
95
|
-
return NOT_FOUND if path =~ FORBIDDEN_RE
|
96
|
-
|
97
|
-
m = path.match(@relative_path_re)
|
98
|
-
return NOT_FOUND if !m
|
99
|
-
|
100
|
-
relative_path = m[1] || ''
|
101
|
-
fs_path = File.join(@src_path, relative_path)
|
102
|
-
|
103
|
-
return file_entry(fs_path) if File.file?(fs_path)
|
104
|
-
return find_index_entry(fs_path) if File.directory?(fs_path)
|
105
|
-
|
106
|
-
entry = find_file_entry_with_extension(fs_path)
|
107
|
-
return entry if entry[:kind] != :not_found
|
108
|
-
|
109
|
-
find_up_tree_module(path)
|
110
|
-
end
|
111
|
-
|
112
|
-
def file_entry(fn)
|
113
|
-
{ fn: File.expand_path(fn), kind: FILE_KINDS[File.extname(fn)] || :static }
|
114
|
-
end
|
115
|
-
|
116
|
-
def find_index_entry(dir)
|
117
|
-
find_file_entry_with_extension(File.join(dir, 'index'))
|
118
|
-
end
|
119
|
-
|
120
|
-
def find_file_entry_with_extension(path)
|
121
|
-
fn = "#{path}.html"
|
122
|
-
return file_entry(fn) if File.file?(fn)
|
123
|
-
|
124
|
-
fn = "#{path}.md"
|
125
|
-
return file_entry(fn) if File.file?(fn)
|
126
|
-
|
127
|
-
fn = "#{path}.rb"
|
128
|
-
return file_entry(fn) if File.file?(fn)
|
129
|
-
|
130
|
-
fn = "#{path}+.rb"
|
131
|
-
return file_entry(fn) if File.file?(fn)
|
132
|
-
|
133
|
-
NOT_FOUND
|
134
|
-
end
|
135
|
-
|
136
|
-
def find_up_tree_module(path)
|
137
|
-
parent = parent_path(path)
|
138
|
-
return NOT_FOUND if !parent
|
139
|
-
|
140
|
-
entry = find_route("#{parent}+.rb", cache: false)
|
141
|
-
entry[:kind] == :module ? entry : NOT_FOUND
|
142
|
-
end
|
143
|
-
|
144
|
-
UP_TREE_PATH_RE = /^(.+)?\/[^\/]+$/
|
145
|
-
|
146
|
-
def parent_path(path)
|
147
|
-
m = path.match(UP_TREE_PATH_RE)
|
148
|
-
m && m[1]
|
149
|
-
end
|
150
|
-
|
151
67
|
def render_entry(req, entry)
|
152
68
|
case entry[:kind]
|
153
69
|
when :not_found
|
154
|
-
req
|
70
|
+
respond_not_found(req, entry)
|
155
71
|
when :static
|
156
72
|
respond_static(req, entry)
|
157
73
|
when :markdown
|
158
|
-
|
159
|
-
req.respond(body, 'Content-Type' => 'text/html')
|
74
|
+
respond_markdown(req, entry)
|
160
75
|
when :module
|
161
|
-
|
76
|
+
respond_module(req, entry)
|
162
77
|
else
|
163
78
|
raise 'Invalid entry kind'
|
164
79
|
end
|
165
80
|
end
|
166
81
|
|
82
|
+
def respond_not_found(req, _entry)
|
83
|
+
headers = { ':status' => Qeweney::Status::NOT_FOUND }
|
84
|
+
case req.method
|
85
|
+
when 'head'
|
86
|
+
req.respond(nil, headers)
|
87
|
+
else
|
88
|
+
req.respond('Not found', headers)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
167
92
|
def respond_static(req, entry)
|
168
93
|
entry[:mime_type] ||= Qeweney::MimeTypes[File.extname(entry[:fn])]
|
169
|
-
|
94
|
+
headers = { 'Content-Type' => entry[:mime_type] }
|
95
|
+
req.respond_by_http_method(
|
96
|
+
'head' => [nil, headers],
|
97
|
+
'get' => -> { [IO.read(entry[:fn]), headers] }
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
def respond_markdown(req, entry)
|
102
|
+
entry[:mime_type] ||= Qeweney::MimeTypes[File.extname(entry[:fn])]
|
103
|
+
headers = { 'Content-Type' => entry[:mime_type] }
|
104
|
+
req.respond_by_http_method(
|
105
|
+
'head' => [nil, headers],
|
106
|
+
'get' => -> { [render_markdown(entry[:fn]), headers] }
|
107
|
+
)
|
170
108
|
end
|
171
109
|
|
172
|
-
def
|
173
|
-
entry[:
|
174
|
-
if entry[:
|
110
|
+
def respond_module(req, entry)
|
111
|
+
entry[:proc] ||= load_module(entry)
|
112
|
+
if entry[:proc] == :invalid
|
175
113
|
req.respond(nil, ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
|
176
114
|
return
|
177
115
|
end
|
178
116
|
|
179
|
-
entry[:
|
117
|
+
entry[:proc].call(req)
|
118
|
+
rescue Syntropy::Error => e
|
119
|
+
req.respond(nil, ':status' => e.http_status)
|
180
120
|
rescue StandardError => e
|
181
121
|
p e
|
182
122
|
p e.backtrace
|
@@ -184,24 +124,62 @@ module Syntropy
|
|
184
124
|
end
|
185
125
|
|
186
126
|
def load_module(entry)
|
187
|
-
|
188
|
-
|
189
|
-
o
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
o.is_a?(Papercraft::HTML) ? wrap_template(o) : o
|
127
|
+
ref = entry[:fn].gsub(%r{^#{@src_path}/}, '').gsub(/\.rb$/, '')
|
128
|
+
o = @module_loader.load(ref)
|
129
|
+
o.is_a?(Papercraft::Template) ? wrap_template(o) : o
|
130
|
+
rescue Exception => e
|
131
|
+
@opts[:logger]&.call("Error while loading module #{ref}: #{e.message}")
|
132
|
+
:invalid
|
194
133
|
end
|
195
134
|
|
196
135
|
def wrap_template(templ)
|
197
|
-
|
136
|
+
lambda { |req|
|
198
137
|
body = templ.render
|
199
138
|
req.respond(body, 'Content-Type' => 'text/html')
|
200
139
|
}
|
201
140
|
end
|
202
141
|
|
203
|
-
def render_markdown(
|
204
|
-
|
142
|
+
def render_markdown(fn)
|
143
|
+
atts, md = parse_markdown_file(fn)
|
144
|
+
|
145
|
+
if atts[:layout]
|
146
|
+
layout = @module_loader.load("_layout/#{atts[:layout]}")
|
147
|
+
html = layout.apply { emit_markdown(md) }.render
|
148
|
+
else
|
149
|
+
html = Papercraft.markdown(md)
|
150
|
+
end
|
151
|
+
html
|
152
|
+
end
|
153
|
+
|
154
|
+
DATE_REGEXP = /(\d{4}\-\d{2}\-\d{2})/
|
155
|
+
FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
|
156
|
+
YAML_OPTS = {
|
157
|
+
permitted_classes: [Date],
|
158
|
+
symbolize_names: true
|
159
|
+
}
|
160
|
+
|
161
|
+
# Parses the markdown file at the given path.
|
162
|
+
#
|
163
|
+
# @param path [String] file path
|
164
|
+
# @return [Array] an tuple containing properties<Hash>, contents<String>
|
165
|
+
def parse_markdown_file(path)
|
166
|
+
content = IO.read(path) || ''
|
167
|
+
atts = {}
|
168
|
+
|
169
|
+
# Parse date from file name
|
170
|
+
if (m = path.match(DATE_REGEXP))
|
171
|
+
atts[:date] ||= Date.parse(m[1])
|
172
|
+
end
|
173
|
+
|
174
|
+
if (m = content.match(FRONT_MATTER_REGEXP))
|
175
|
+
front_matter = m[1]
|
176
|
+
content = m.post_match
|
177
|
+
|
178
|
+
yaml = YAML.safe_load(front_matter, **YAML_OPTS)
|
179
|
+
atts = atts.merge(yaml)
|
180
|
+
end
|
181
|
+
|
182
|
+
[atts, content]
|
205
183
|
end
|
206
184
|
end
|
207
185
|
end
|
data/lib/syntropy/errors.rb
CHANGED
@@ -4,17 +4,31 @@ require 'qeweney'
|
|
4
4
|
|
5
5
|
module Syntropy
|
6
6
|
class Error < StandardError
|
7
|
+
Status = Qeweney::Status
|
8
|
+
|
7
9
|
attr_reader :http_status
|
8
10
|
|
9
11
|
def initialize(status, msg = '')
|
10
|
-
@http_status = status || Qeweney::Status::INTERNAL_SERVER_ERROR
|
11
12
|
super(msg)
|
13
|
+
@http_status = status || Qeweney::Status::INTERNAL_SERVER_ERROR
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# Create class methods for common errors
|
18
|
+
{
|
19
|
+
not_found: Status::NOT_FOUND,
|
20
|
+
method_not_allowed: Status::METHOD_NOT_ALLOWED,
|
21
|
+
teapot: Status::TEAPOT
|
22
|
+
}
|
23
|
+
.each { |k, v|
|
24
|
+
define_method(k) { |msg = ''| new(v, msg) }
|
25
|
+
}
|
12
26
|
end
|
13
27
|
end
|
14
28
|
|
15
29
|
class ValidationError < Error
|
16
30
|
def initialize(msg)
|
17
|
-
|
31
|
+
super(Qeweney::Status::BAD_REQUEST, msg)
|
18
32
|
end
|
19
33
|
end
|
20
34
|
end
|
data/lib/syntropy/file_watch.rb
CHANGED
@@ -8,15 +8,16 @@ module Syntropy
|
|
8
8
|
|
9
9
|
queue = Thread::Queue.new
|
10
10
|
listener = Listen.to(*roots) do |modified, added, removed|
|
11
|
-
|
12
|
-
|
11
|
+
modified.each { queue.push([:modified, it]) }
|
12
|
+
added.each { queue.push([:added, it]) }
|
13
|
+
removed.each { queue.push([:removed, it]) }
|
13
14
|
end
|
14
15
|
listener.start
|
15
16
|
|
16
17
|
loop do
|
17
18
|
machine.sleep(period) while queue.empty?
|
18
|
-
fn = queue.shift
|
19
|
-
block.call(fn)
|
19
|
+
event, fn = queue.shift
|
20
|
+
block.call(event, fn)
|
20
21
|
end
|
21
22
|
rescue StandardError => e
|
22
23
|
p e
|
data/lib/syntropy/module.rb
CHANGED
@@ -1,37 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'papercraft'
|
4
|
+
|
3
5
|
module Syntropy
|
4
6
|
class ModuleLoader
|
5
7
|
def initialize(root, env)
|
6
8
|
@root = root
|
7
9
|
@env = env
|
8
|
-
@loaded = {}
|
10
|
+
@loaded = {} # maps ref to code
|
11
|
+
@fn_map = {} # maps filename to ref
|
9
12
|
end
|
10
13
|
|
11
14
|
def load(ref)
|
12
15
|
@loaded[ref] ||= load_module(ref)
|
13
16
|
end
|
14
17
|
|
18
|
+
def invalidate(fn)
|
19
|
+
ref = @fn_map[fn]
|
20
|
+
return if !ref
|
21
|
+
|
22
|
+
@loaded.delete(ref)
|
23
|
+
@fn_map.delete(fn)
|
24
|
+
end
|
25
|
+
|
15
26
|
private
|
16
27
|
|
17
28
|
def load_module(ref)
|
18
|
-
fn = File.join(@root, "#{ref}.rb")
|
19
|
-
|
29
|
+
fn = File.expand_path(File.join(@root, "#{ref}.rb"))
|
30
|
+
@fn_map[fn] = ref
|
31
|
+
raise "File not found #{fn}" if !File.file?(fn)
|
20
32
|
|
21
33
|
mod_body = IO.read(fn)
|
22
34
|
mod_ctx = Class.new(Syntropy::Module)
|
23
35
|
mod_ctx.loader = self
|
24
|
-
# mod_ctx = .new(self, @env)
|
25
36
|
mod_ctx.module_eval(mod_body, fn, 1)
|
26
37
|
|
27
38
|
export_value = mod_ctx.__export_value__
|
28
39
|
|
40
|
+
wrap_module(mod_ctx, export_value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def wrap_module(mod_ctx, export_value)
|
29
44
|
case export_value
|
30
45
|
when nil
|
31
|
-
raise
|
46
|
+
raise 'No export found'
|
32
47
|
when Symbol
|
33
48
|
# TODO: verify export_value denotes a valid method
|
34
49
|
mod_ctx.new(@env)
|
50
|
+
when String
|
51
|
+
->(req) { req.respond(export_value) }
|
35
52
|
when Proc
|
36
53
|
export_value
|
37
54
|
else
|
@@ -57,6 +74,10 @@ module Syntropy
|
|
57
74
|
@__export_value__ = ref
|
58
75
|
end
|
59
76
|
|
77
|
+
def self.templ(&block)
|
78
|
+
Papercraft.html(&block)
|
79
|
+
end
|
80
|
+
|
60
81
|
def self.__export_value__
|
61
82
|
@__export_value__
|
62
83
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'qeweney'
|
4
|
+
|
5
|
+
module Syntropy
|
6
|
+
module RequestExtensions
|
7
|
+
def ctx
|
8
|
+
@ctx ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_http_method(*accepted)
|
12
|
+
raise Syntropy::Error.method_not_allowed if !accepted.include?(method)
|
13
|
+
end
|
14
|
+
|
15
|
+
def respond_by_http_method(map)
|
16
|
+
value = map[self.method]
|
17
|
+
raise Syntropy::Error.method_not_allowed if !value
|
18
|
+
|
19
|
+
value = value.() if value.is_a?(Proc)
|
20
|
+
(body, headers) = value
|
21
|
+
respond(body, headers)
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond_on_get(body, headers = {})
|
25
|
+
case self.method
|
26
|
+
when 'head'
|
27
|
+
respond(nil, headers)
|
28
|
+
when 'get'
|
29
|
+
respond(body, headers)
|
30
|
+
else
|
31
|
+
raise Syntropy::Error.method_not_allowed
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def respond_on_post(body, headers = {})
|
36
|
+
case self.method
|
37
|
+
when 'head'
|
38
|
+
respond(nil, headers)
|
39
|
+
when 'post'
|
40
|
+
respond(body, headers)
|
41
|
+
else
|
42
|
+
raise Syntropy::Error.method_not_allowed
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_param(name, *clauses)
|
47
|
+
value = query[name]
|
48
|
+
clauses.each do |c|
|
49
|
+
valid = param_is_valid?(value, c)
|
50
|
+
raise(Syntropy::ValidationError, 'Validation error') if !valid
|
51
|
+
|
52
|
+
value = param_convert(value, c)
|
53
|
+
end
|
54
|
+
value
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
BOOL_REGEXP = /^(t|f|true|false|on|off|1|0|yes|no)$/
|
60
|
+
BOOL_TRUE_REGEXP = /^(t|true|on|1|yes)$/
|
61
|
+
INTEGER_REGEXP = /^[+-]?[0-9]+$/
|
62
|
+
FLOAT_REGEXP = /^[+-]?[0-9]+(\.[0-9]+)?$/
|
63
|
+
|
64
|
+
def param_is_valid?(value, cond)
|
65
|
+
return cond.any? { |c| param_is_valid?(value, c) } if cond.is_a?(Array)
|
66
|
+
|
67
|
+
if value
|
68
|
+
if cond == :bool
|
69
|
+
return value =~ BOOL_REGEXP
|
70
|
+
elsif cond == Integer
|
71
|
+
return value =~ INTEGER_REGEXP
|
72
|
+
elsif cond == Float
|
73
|
+
return value =~ FLOAT_REGEXP
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
cond === value
|
78
|
+
end
|
79
|
+
|
80
|
+
def param_convert(value, klass)
|
81
|
+
if klass == :bool
|
82
|
+
value =~ BOOL_TRUE_REGEXP ? true : false
|
83
|
+
elsif klass == Integer
|
84
|
+
value.to_i
|
85
|
+
elsif klass == Float
|
86
|
+
value.to_f
|
87
|
+
elsif klass == Symbol
|
88
|
+
value.to_sym
|
89
|
+
else
|
90
|
+
value
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
Qeweney::Request.include(Syntropy::RequestExtensions)
|