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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +40 -14
  3. data/bin/wabur +103 -0
  4. data/lib/wab/controller.rb +133 -90
  5. data/lib/wab/data.rb +14 -27
  6. data/lib/wab/errors.rb +14 -8
  7. data/lib/wab/impl/bool_expr.rb +25 -0
  8. data/lib/wab/impl/configuration.rb +166 -0
  9. data/lib/wab/impl/data.rb +155 -232
  10. data/lib/wab/impl/expr.rb +26 -0
  11. data/lib/wab/impl/expr_parser.rb +55 -0
  12. data/lib/wab/impl/exprs/and.rb +29 -0
  13. data/lib/wab/impl/exprs/between.rb +41 -0
  14. data/lib/wab/impl/exprs/eq.rb +28 -0
  15. data/lib/wab/impl/exprs/gt.rb +30 -0
  16. data/lib/wab/impl/exprs/gte.rb +30 -0
  17. data/lib/wab/impl/exprs/has.rb +26 -0
  18. data/lib/wab/impl/exprs/in.rb +28 -0
  19. data/lib/wab/impl/exprs/lt.rb +30 -0
  20. data/lib/wab/impl/exprs/lte.rb +30 -0
  21. data/lib/wab/impl/exprs/not.rb +27 -0
  22. data/lib/wab/impl/exprs/or.rb +29 -0
  23. data/lib/wab/impl/exprs/regex.rb +28 -0
  24. data/lib/wab/impl/handler.rb +95 -0
  25. data/lib/wab/impl/model.rb +197 -0
  26. data/lib/wab/impl/path_expr.rb +14 -0
  27. data/lib/wab/impl/shell.rb +92 -7
  28. data/lib/wab/impl/utils.rb +110 -0
  29. data/lib/wab/impl.rb +24 -0
  30. data/lib/wab/io/call.rb +4 -7
  31. data/lib/wab/io/engine.rb +128 -51
  32. data/lib/wab/io/shell.rb +61 -64
  33. data/lib/wab/io.rb +0 -2
  34. data/lib/wab/open_controller.rb +43 -0
  35. data/lib/wab/shell.rb +46 -61
  36. data/lib/wab/shell_logger.rb +13 -0
  37. data/lib/wab/utils.rb +36 -0
  38. data/lib/wab/uuid.rb +3 -6
  39. data/lib/wab/version.rb +2 -2
  40. data/lib/wab.rb +3 -0
  41. data/pages/Plan.md +20 -14
  42. data/test/bench_io_shell.rb +49 -0
  43. data/test/{impl_test.rb → helper.rb} +2 -4
  44. data/test/mirror_controller.rb +16 -0
  45. data/test/test_configuration.rb +38 -0
  46. data/test/test_data.rb +207 -0
  47. data/test/test_expr.rb +35 -0
  48. data/test/test_expr_and.rb +24 -0
  49. data/test/test_expr_between.rb +43 -0
  50. data/test/test_expr_eq.rb +24 -0
  51. data/test/test_expr_gt.rb +24 -0
  52. data/test/test_expr_gte.rb +24 -0
  53. data/test/test_expr_has.rb +19 -0
  54. data/test/test_expr_in.rb +24 -0
  55. data/test/test_expr_lt.rb +24 -0
  56. data/test/test_expr_lte.rb +24 -0
  57. data/test/test_expr_not.rb +22 -0
  58. data/test/test_expr_or.rb +24 -0
  59. data/test/test_expr_regex.rb +30 -0
  60. data/test/test_impl.rb +38 -0
  61. data/test/test_io_shell.rb +189 -0
  62. data/test/test_model.rb +31 -0
  63. data/test/test_runner.rb +177 -0
  64. data/test/tests.rb +3 -8
  65. metadata +91 -18
  66. data/lib/wab/model.rb +0 -136
  67. data/lib/wab/view.rb +0 -21
  68. data/test/data_test.rb +0 -253
  69. 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