wabur 0.2.0d1 → 0.4.0d1

Sign up to get free protection for your applications and to get access to all the features.
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