wabur 0.2.0d1 → 0.4.0d1
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/README.md +40 -14
- data/bin/wabur +103 -0
- data/lib/wab/controller.rb +133 -90
- data/lib/wab/data.rb +14 -27
- data/lib/wab/errors.rb +14 -8
- data/lib/wab/impl/bool_expr.rb +25 -0
- data/lib/wab/impl/configuration.rb +166 -0
- data/lib/wab/impl/data.rb +155 -232
- data/lib/wab/impl/expr.rb +26 -0
- data/lib/wab/impl/expr_parser.rb +55 -0
- data/lib/wab/impl/exprs/and.rb +29 -0
- data/lib/wab/impl/exprs/between.rb +41 -0
- data/lib/wab/impl/exprs/eq.rb +28 -0
- data/lib/wab/impl/exprs/gt.rb +30 -0
- data/lib/wab/impl/exprs/gte.rb +30 -0
- data/lib/wab/impl/exprs/has.rb +26 -0
- data/lib/wab/impl/exprs/in.rb +28 -0
- data/lib/wab/impl/exprs/lt.rb +30 -0
- data/lib/wab/impl/exprs/lte.rb +30 -0
- data/lib/wab/impl/exprs/not.rb +27 -0
- data/lib/wab/impl/exprs/or.rb +29 -0
- data/lib/wab/impl/exprs/regex.rb +28 -0
- data/lib/wab/impl/handler.rb +95 -0
- data/lib/wab/impl/model.rb +197 -0
- data/lib/wab/impl/path_expr.rb +14 -0
- data/lib/wab/impl/shell.rb +92 -7
- data/lib/wab/impl/utils.rb +110 -0
- data/lib/wab/impl.rb +24 -0
- data/lib/wab/io/call.rb +4 -7
- data/lib/wab/io/engine.rb +128 -51
- data/lib/wab/io/shell.rb +61 -64
- data/lib/wab/io.rb +0 -2
- data/lib/wab/open_controller.rb +43 -0
- data/lib/wab/shell.rb +46 -61
- data/lib/wab/shell_logger.rb +13 -0
- data/lib/wab/utils.rb +36 -0
- data/lib/wab/uuid.rb +3 -6
- data/lib/wab/version.rb +2 -2
- data/lib/wab.rb +3 -0
- data/pages/Plan.md +20 -14
- data/test/bench_io_shell.rb +49 -0
- data/test/{impl_test.rb → helper.rb} +2 -4
- data/test/mirror_controller.rb +16 -0
- data/test/test_configuration.rb +38 -0
- data/test/test_data.rb +207 -0
- data/test/test_expr.rb +35 -0
- data/test/test_expr_and.rb +24 -0
- data/test/test_expr_between.rb +43 -0
- data/test/test_expr_eq.rb +24 -0
- data/test/test_expr_gt.rb +24 -0
- data/test/test_expr_gte.rb +24 -0
- data/test/test_expr_has.rb +19 -0
- data/test/test_expr_in.rb +24 -0
- data/test/test_expr_lt.rb +24 -0
- data/test/test_expr_lte.rb +24 -0
- data/test/test_expr_not.rb +22 -0
- data/test/test_expr_or.rb +24 -0
- data/test/test_expr_regex.rb +30 -0
- data/test/test_impl.rb +38 -0
- data/test/test_io_shell.rb +189 -0
- data/test/test_model.rb +31 -0
- data/test/test_runner.rb +177 -0
- data/test/tests.rb +3 -8
- metadata +91 -18
- data/lib/wab/model.rb +0 -136
- data/lib/wab/view.rb +0 -21
- data/test/data_test.rb +0 -253
- data/test/ioshell_test.rb +0 -461
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
class ExprParser
|
5
|
+
XMAP = {
|
6
|
+
between: Between,
|
7
|
+
eq: Eq,
|
8
|
+
gt: Gt,
|
9
|
+
gte: Gte,
|
10
|
+
in: In,
|
11
|
+
has: Has,
|
12
|
+
lt: Lt,
|
13
|
+
lte: Lte,
|
14
|
+
regex: Regex,
|
15
|
+
and: And,
|
16
|
+
or: Or,
|
17
|
+
not: Not,
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Parses an Array into a set of Expr subsclasses.
|
22
|
+
#
|
23
|
+
# native:: native Ruby representation of the expression.
|
24
|
+
def parse(native)
|
25
|
+
raise WAB::TypeError.new('Invalid expression. Must be an Array.') unless native.is_a?(Array)
|
26
|
+
|
27
|
+
op = native[0]
|
28
|
+
op = op.downcase.to_sym unless op.is_a?(Symbol)
|
29
|
+
|
30
|
+
xclass = XMAP[op]
|
31
|
+
raise WAB::Error.new("#{op} is not a valid expression function.") if xclass.nil?
|
32
|
+
|
33
|
+
args = []
|
34
|
+
native[1..-1].each { |n|
|
35
|
+
args << if n.is_a?(Array)
|
36
|
+
parse(n)
|
37
|
+
elsif n.is_a?(String)
|
38
|
+
unless n.empty?
|
39
|
+
if "'" == n[0]
|
40
|
+
n[1..-1]
|
41
|
+
else
|
42
|
+
WAB::Impl::Data.detect_string(n)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
else
|
46
|
+
n
|
47
|
+
end
|
48
|
+
}
|
49
|
+
xclass.new(*args)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end # ExprParser
|
54
|
+
end # Impl
|
55
|
+
end # WAB
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# A logical AND expression.
|
6
|
+
class And < BoolExpr
|
7
|
+
|
8
|
+
# Create an AND expression with the provided arguments which must be
|
9
|
+
# instances of subclasses of the Expr class.
|
10
|
+
#
|
11
|
+
# args:: argument to the AND expression
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def eval(data)
|
17
|
+
@args.each { |a|
|
18
|
+
return false unless a.eval(data)
|
19
|
+
}
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def native()
|
24
|
+
@args.map(&:native).unshift('AND')
|
25
|
+
end
|
26
|
+
|
27
|
+
end # And
|
28
|
+
end # Impl
|
29
|
+
end # WAB
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Matches numeric nodes between a minimum and maximum value. By default
|
6
|
+
# the matches are inclusive of the minimum and maximum. The optional
|
7
|
+
# arguments allow the inclusivity to be changed to exclusive separately
|
8
|
+
# for the minimum and maximum. By default the min_incl and max_incl are
|
9
|
+
# true. If set to false the corresponding limit becomes exclusive. An
|
10
|
+
# error is returned if used with non-numeric minimum or maximum.
|
11
|
+
class Between < PathExpr
|
12
|
+
|
13
|
+
# Creates a new instance with the provided parameters.
|
14
|
+
#
|
15
|
+
# path:: path to the value to compare
|
16
|
+
# min:: minimum
|
17
|
+
# max:: maximum
|
18
|
+
# min_incl:: minimum inclusive flag
|
19
|
+
# max_incl:: maximum inclusive flag
|
20
|
+
def initialize(path, min, max, min_incl=true, max_incl=true)
|
21
|
+
super(path)
|
22
|
+
@min = min
|
23
|
+
@max = max
|
24
|
+
@min_incl = min_incl
|
25
|
+
@max_incl = max_incl
|
26
|
+
end
|
27
|
+
|
28
|
+
def eval(data)
|
29
|
+
value = data.get(@path)
|
30
|
+
return false if (@min_incl ? value < @min : value <= @min)
|
31
|
+
return false if (@max_incl ? @max < value : @max <= value)
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def native()
|
36
|
+
['BETWEEN', @path, @min, @max, @min_incl, @max_incl]
|
37
|
+
end
|
38
|
+
|
39
|
+
end # Between
|
40
|
+
end # Impl
|
41
|
+
end # WAB
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Matches a node that has the same value as provided at the end of the
|
6
|
+
# path provided. Any type is acceptable.#
|
7
|
+
class Eq < PathExpr
|
8
|
+
|
9
|
+
# Creates a new instance with the provided parameters.
|
10
|
+
#
|
11
|
+
# path:: path to the value to compare
|
12
|
+
# value:: value to compare against
|
13
|
+
def initialize(path, value)
|
14
|
+
super(path)
|
15
|
+
@value = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def eval(data)
|
19
|
+
data.get(@path) == @value
|
20
|
+
end
|
21
|
+
|
22
|
+
def native()
|
23
|
+
['EQ', @path, @value]
|
24
|
+
end
|
25
|
+
|
26
|
+
end # Eq
|
27
|
+
end # Impl
|
28
|
+
end # WAB
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Matches a node that has a value greater than the provided value. If a
|
6
|
+
# integer or float is provided then both integer and floats are
|
7
|
+
# checked. If the value provided is a time then only time nodes are
|
8
|
+
# checked. Any other type results in an error.
|
9
|
+
class Gt < PathExpr
|
10
|
+
|
11
|
+
# Creates a new instance with the provided parameters.
|
12
|
+
#
|
13
|
+
# path:: path to the value to compare
|
14
|
+
# value:: value to compare against
|
15
|
+
def initialize(path, value)
|
16
|
+
super(path)
|
17
|
+
@value = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def eval(data)
|
21
|
+
data.get(@path) > @value
|
22
|
+
end
|
23
|
+
|
24
|
+
def native()
|
25
|
+
['GT', @path, @value]
|
26
|
+
end
|
27
|
+
|
28
|
+
end # Gt
|
29
|
+
end # Impl
|
30
|
+
end # WAB
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Matches a node that has a value greater than or equals to the provided
|
6
|
+
# value. If a integer or float is provided then both integer and floats
|
7
|
+
# are checked. If the value provided is a time then only time nodes are
|
8
|
+
# checked. Any other type results in an error.
|
9
|
+
class Gte < PathExpr
|
10
|
+
|
11
|
+
# Creates a new instance with the provided parameters.
|
12
|
+
#
|
13
|
+
# path:: path to the value to compare
|
14
|
+
# value:: value to compare against
|
15
|
+
def initialize(path, value)
|
16
|
+
super(path)
|
17
|
+
@value = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def eval(data)
|
21
|
+
data.get(@path) >= @value
|
22
|
+
end
|
23
|
+
|
24
|
+
def native()
|
25
|
+
['GTE', @path, @value]
|
26
|
+
end
|
27
|
+
|
28
|
+
end # Gte
|
29
|
+
end # Impl
|
30
|
+
end # WAB
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Used to filters out all nodes that do not have a node at the end of the
|
6
|
+
# provided path.
|
7
|
+
class Has < PathExpr
|
8
|
+
|
9
|
+
# Creates a new instance with the provided parameters.
|
10
|
+
#
|
11
|
+
# path:: path to the value to check
|
12
|
+
def initialize(path)
|
13
|
+
super(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def eval(data)
|
17
|
+
data.has?(@path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def native()
|
21
|
+
['HAS', @path]
|
22
|
+
end
|
23
|
+
|
24
|
+
end # Has
|
25
|
+
end # Impl
|
26
|
+
end # WAB
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Match any node that has a value equal to one of the values provided in
|
6
|
+
# the argument list after the path. This can be used with any type.
|
7
|
+
class In < PathExpr
|
8
|
+
|
9
|
+
# Creates a new instance with the provided parameters.
|
10
|
+
#
|
11
|
+
# path:: path to the value to compare
|
12
|
+
# values:: values to compare to
|
13
|
+
def initialize(path, *values)
|
14
|
+
super(path)
|
15
|
+
@values = values
|
16
|
+
end
|
17
|
+
|
18
|
+
def eval(data)
|
19
|
+
@values.include?(data.get(@path))
|
20
|
+
end
|
21
|
+
|
22
|
+
def native()
|
23
|
+
['IN', @path].concat(@values)
|
24
|
+
end
|
25
|
+
|
26
|
+
end # In
|
27
|
+
end # Impl
|
28
|
+
end # WAB
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Matches a node that has a value less than the provided value. If a
|
6
|
+
# integer or float is provided then both integer and floats are
|
7
|
+
# checked. If the value provided is a time then only time nodes are
|
8
|
+
# checked. Any other type results in an error.
|
9
|
+
class Lt < PathExpr
|
10
|
+
|
11
|
+
# Creates a new instance with the provided parameters.
|
12
|
+
#
|
13
|
+
# path:: path to the value to compare
|
14
|
+
# value:: value to compare against
|
15
|
+
def initialize(path, value)
|
16
|
+
super(path)
|
17
|
+
@value = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def eval(data)
|
21
|
+
data.get(@path) < @value
|
22
|
+
end
|
23
|
+
|
24
|
+
def native()
|
25
|
+
['LT', @path, @value]
|
26
|
+
end
|
27
|
+
|
28
|
+
end # Lt
|
29
|
+
end # Impl
|
30
|
+
end # WAB
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Matches a node that has a value less than or equal to the provided
|
6
|
+
# value. If a integer or float is provided then both integer and floats
|
7
|
+
# are checked. If the value provided is a time then only time nodes are
|
8
|
+
# checked. Any other type results in an error.
|
9
|
+
class Lte < PathExpr
|
10
|
+
|
11
|
+
# Creates a new instance with the provided parameters.
|
12
|
+
#
|
13
|
+
# path:: path to the value to compare
|
14
|
+
# value:: value to compare against
|
15
|
+
def initialize(path, value)
|
16
|
+
super(path)
|
17
|
+
@value = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def eval(data)
|
21
|
+
data.get(@path) <= @value
|
22
|
+
end
|
23
|
+
|
24
|
+
def native()
|
25
|
+
['LTE', @path, @value]
|
26
|
+
end
|
27
|
+
|
28
|
+
end # Lte
|
29
|
+
end # Impl
|
30
|
+
end # WAB
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Negates and expression.
|
6
|
+
class Not < Expr
|
7
|
+
|
8
|
+
# Create an NOT expression with the provided argument which must be an
|
9
|
+
# instance of a subclass of the Expr class.
|
10
|
+
#
|
11
|
+
# arg:: argument to the NOT expression
|
12
|
+
def initialize(arg)
|
13
|
+
super()
|
14
|
+
@arg = arg
|
15
|
+
end
|
16
|
+
|
17
|
+
def eval(data)
|
18
|
+
!@arg.eval(data)
|
19
|
+
end
|
20
|
+
|
21
|
+
def native()
|
22
|
+
['NOT', @arg.native]
|
23
|
+
end
|
24
|
+
|
25
|
+
end # Not
|
26
|
+
end # Impl
|
27
|
+
end # WAB
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# A logical OR expression.
|
6
|
+
class Or < BoolExpr
|
7
|
+
|
8
|
+
# Create an OR expression with the provided arguments which must be
|
9
|
+
# instances of subclasses of the Expr class.
|
10
|
+
#
|
11
|
+
# args:: argument to the OR expression
|
12
|
+
def initialize(*args)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def eval(data)
|
17
|
+
@args.each { |a|
|
18
|
+
return true if a.eval(data)
|
19
|
+
}
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def native()
|
24
|
+
@args.map(&:native).unshift('OR')
|
25
|
+
end
|
26
|
+
|
27
|
+
end # Or
|
28
|
+
end # Impl
|
29
|
+
end # WAB
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
class Regex < PathExpr
|
6
|
+
|
7
|
+
# Creates a new instance with the provided parameters.
|
8
|
+
#
|
9
|
+
# path:: path to the value to compare
|
10
|
+
# rx:: regexp to match against a string value from the path lookup
|
11
|
+
def initialize(path, rx)
|
12
|
+
super(path)
|
13
|
+
@rx = rx.is_a?(Regexp) ? rx : Regexp.new(rx.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def eval(data)
|
17
|
+
value = data.get(@path)
|
18
|
+
return !@rx.match(value).nil? if value.is_a?(String)
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def native()
|
23
|
+
['REGEX', @path, @rx.source]
|
24
|
+
end
|
25
|
+
|
26
|
+
end # Regex
|
27
|
+
end # Impl
|
28
|
+
end # WAB
|
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
require 'webrick'
|
3
|
+
|
4
|
+
module WAB
|
5
|
+
module Impl
|
6
|
+
|
7
|
+
# Handler for requests that fall under the path assigned to the
|
8
|
+
# Controller. This is used only with the WAB::Impl::Shell.
|
9
|
+
class Handler < WEBrick::HTTPServlet::AbstractServlet
|
10
|
+
|
11
|
+
def initialize(server, shell)
|
12
|
+
super(server)
|
13
|
+
@shell = shell
|
14
|
+
end
|
15
|
+
|
16
|
+
def do_GET(req, res)
|
17
|
+
controller, path, query = extract_req(req)
|
18
|
+
log_response('controller.read', path, query) if @shell.logger.info?
|
19
|
+
send_result(controller.read(path, query), res)
|
20
|
+
rescue StandardError => e
|
21
|
+
send_error(e, res)
|
22
|
+
end
|
23
|
+
|
24
|
+
def do_PUT(req, res)
|
25
|
+
controller, path, query, body = extract_req(req)
|
26
|
+
log_response_with_body('controller.create', path, query, body) if @shell.logger.info?
|
27
|
+
send_result(controller.create(path, query, body), res)
|
28
|
+
rescue StandardError => e
|
29
|
+
send_error(e, res)
|
30
|
+
end
|
31
|
+
|
32
|
+
def do_POST(req, res)
|
33
|
+
controller, path, query, body = extract_req(req)
|
34
|
+
log_response_with_body('controller.update', path, query, body) if @shell.logger.info?
|
35
|
+
send_result(controller.update(path, query, body), res)
|
36
|
+
rescue StandardError => e
|
37
|
+
send_error(e, res)
|
38
|
+
end
|
39
|
+
|
40
|
+
def do_DELETE(req, res)
|
41
|
+
controller, path, query = extract_req(req)
|
42
|
+
log_response('controller.delete', path, query) if @shell.logger.info?
|
43
|
+
send_result(controller.delete(path, query), res)
|
44
|
+
rescue StandardError => e
|
45
|
+
send_error(e, res)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def log_response(caller, path, query)
|
51
|
+
@shell.logger.info("#{caller}(#{path.join('/')}#{query})")
|
52
|
+
end
|
53
|
+
|
54
|
+
def log_response_with_body(caller, path, query, body)
|
55
|
+
@shell.logger.info("#{caller}(#{path.join('/')}#{query}, #{body.json})")
|
56
|
+
end
|
57
|
+
|
58
|
+
# Pulls and converts the request path, query, and body. Also returns the
|
59
|
+
# controller.
|
60
|
+
def extract_req(req)
|
61
|
+
path = req.path.split('/')[1..-1]
|
62
|
+
query = {}
|
63
|
+
req.query.each { |k,v| query[k.to_sym] = v }
|
64
|
+
if req.body.nil?
|
65
|
+
body = nil
|
66
|
+
else
|
67
|
+
body = Oj.strict_load(req.body, symbol_keys: true)
|
68
|
+
body = Data.new(body, false)
|
69
|
+
body.detect
|
70
|
+
end
|
71
|
+
[@shell.path_controller(path), path, query, body]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Sends the results from a controller request.
|
75
|
+
def send_result(result, res)
|
76
|
+
result = @shell.data(result) unless result.is_a?(WAB::Data)
|
77
|
+
res.status = 200
|
78
|
+
res['Content-Type'] = 'application/json'
|
79
|
+
@shell.logger.debug("Reply: #{result.json}") if @shell.logger.debug?
|
80
|
+
res.body = result.json
|
81
|
+
end
|
82
|
+
|
83
|
+
# Sends an error from a rescued call.
|
84
|
+
def send_error(e, res)
|
85
|
+
res.status = 500
|
86
|
+
res['Content-Type'] = 'application/json'
|
87
|
+
body = { code: -1, error: "#{e.class}: #{e.message}" }
|
88
|
+
body[:backtrace] = e.backtrace
|
89
|
+
res.body = @shell.data(body).json
|
90
|
+
@shell.logger.warn(%|*-*-* #{e.class}: #{e.message}\n #{e.backtrace.join("\n ")}|)
|
91
|
+
end
|
92
|
+
|
93
|
+
end # Handler
|
94
|
+
end # Impl
|
95
|
+
end # WAB
|