wabur 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +106 -15
  3. data/bin/wabur +6 -0
  4. data/export/assets/js/ui.js +18 -0
  5. data/lib/wab.rb +7 -1
  6. data/lib/wab/client.rb +145 -0
  7. data/lib/wab/controller.rb +1 -1
  8. data/lib/wab/errors.rb +6 -0
  9. data/lib/wab/impl.rb +1 -1
  10. data/lib/wab/impl/agoo.rb +18 -0
  11. data/lib/wab/impl/agoo/export_proxy.rb +55 -0
  12. data/lib/wab/impl/agoo/handler.rb +51 -0
  13. data/lib/wab/impl/agoo/sender.rb +50 -0
  14. data/lib/wab/impl/agoo/server.rb +59 -0
  15. data/lib/wab/impl/agoo/tql_handler.rb +35 -0
  16. data/lib/wab/impl/model.rb +8 -1
  17. data/lib/wab/impl/rack_error.rb +27 -0
  18. data/lib/wab/impl/rack_handler.rb +69 -0
  19. data/lib/wab/impl/shell.rb +56 -51
  20. data/lib/wab/impl/sinatra.rb +18 -0
  21. data/lib/wab/impl/sinatra/export_proxy.rb +57 -0
  22. data/lib/wab/impl/sinatra/handler.rb +50 -0
  23. data/lib/wab/impl/sinatra/sender.rb +53 -0
  24. data/lib/wab/impl/sinatra/server.rb +66 -0
  25. data/lib/wab/impl/sinatra/tql_handler.rb +35 -0
  26. data/lib/wab/impl/templates/wabur.conf.template +1 -1
  27. data/lib/wab/impl/webrick.rb +18 -0
  28. data/lib/wab/impl/webrick/export_proxy.rb +41 -0
  29. data/lib/wab/impl/webrick/handler.rb +116 -0
  30. data/lib/wab/impl/webrick/sender.rb +34 -0
  31. data/lib/wab/impl/webrick/server.rb +39 -0
  32. data/lib/wab/impl/webrick/tql_handler.rb +58 -0
  33. data/lib/wab/racker.rb +25 -0
  34. data/lib/wab/version.rb +1 -1
  35. data/pages/Architecture.md +15 -6
  36. data/test/test_client.rb +282 -0
  37. data/test/test_impl.rb +2 -0
  38. data/test/test_runner.rb +267 -91
  39. metadata +27 -5
  40. data/lib/wab/impl/export_proxy.rb +0 -39
  41. data/lib/wab/impl/handler.rb +0 -98
@@ -0,0 +1,34 @@
1
+
2
+ require 'webrick'
3
+
4
+ module WAB
5
+ module Impl
6
+ module WEBrick
7
+
8
+ # The Sender module adds support for sending results and errors.
9
+ module Sender
10
+
11
+ # Sends the results from a controller request.
12
+ def send_result(result, res, path, query)
13
+ result = @shell.data(result) unless result.is_a?(WAB::Data)
14
+ response_body = result.json(@shell.indent)
15
+ res.status = 200
16
+ res['Content-Type'] = 'application/json'
17
+ @shell.logger.debug("reply to #{path.join('/')}#{query}: #{response_body}") if @shell.logger.debug?
18
+ res.body = response_body
19
+ end
20
+
21
+ # Sends an error from a rescued call.
22
+ def send_error(e, res)
23
+ res.status = 500
24
+ res['Content-Type'] = 'application/json'
25
+ body = { code: -1, error: "#{e.class}: #{e.message}" }
26
+ body[:backtrace] = e.backtrace
27
+ res.body = @shell.data(body).json(@shell.indent)
28
+ @shell.logger.warn(Impl.format_error(e))
29
+ end
30
+
31
+ end # Sender
32
+ end # WEBrick
33
+ end # Impl
34
+ end # WAB
@@ -0,0 +1,39 @@
1
+
2
+ require 'webrick'
3
+
4
+ module WAB
5
+ module Impl
6
+ module WEBrick
7
+
8
+ # The Server module provides a server start method.
9
+ module Server
10
+
11
+ # Start the WEBrick server and set the mount points.
12
+ def self.start(shell)
13
+ mime_types = ::WEBrick::HTTPUtils::DefaultMimeTypes
14
+ mime_types['es6'] = 'application/javascript'
15
+ server = ::WEBrick::HTTPServer.new(Port: shell.http_port,
16
+ DocumentRoot: shell.http_dir,
17
+ MimeTypes: mime_types)
18
+ server.logger.level = 5 - shell.logger.level unless shell.logger.nil?
19
+
20
+ shell.mounts.each { |hh|
21
+ if hh.has_key?(:type)
22
+ server.mount("#{shell.pre_path}/#{hh[:type]}", WAB::Impl::WEBrick::Handler, shell, shell.create_controller(hh[:handler]), false)
23
+ elsif hh.has_key?(:path)
24
+ server.mount(hh[:path], WAB::Impl::WEBrick::Handler, shell, shell.create_controller(hh[:handler]), true)
25
+ else
26
+ raise WAB::Error.new("Invalid handle configuration. Missing path or type.")
27
+ end
28
+ }
29
+ server.mount(shell.tql_path, TqlHandler, shell) unless (shell.tql_path.nil? || shell.tql_path.empty?)
30
+ server.mount('/', ExportProxy, shell.http_dir) if shell.export_proxy
31
+
32
+ trap 'INT' do server.shutdown end
33
+ server.start
34
+ end
35
+
36
+ end # Server
37
+ end # WEBrick
38
+ end # Impl
39
+ end # WAB
@@ -0,0 +1,58 @@
1
+
2
+ require 'webrick'
3
+
4
+ module WAB
5
+ module Impl
6
+ module WEBrick
7
+
8
+ # Handler for requests that fall under the path assigned to the
9
+ # Controller. This is used only with the WAB::Impl::Shell.
10
+ class TqlHandler < ::WEBrick::HTTPServlet::AbstractServlet
11
+
12
+ def initialize(server, shell)
13
+ super(server)
14
+ @shell = shell
15
+ end
16
+
17
+ def do_POST(req, res)
18
+ path = req.path.split('/')[1..-1]
19
+ query = {}
20
+ req.query.each { |k,v| query[k.to_sym] = v }
21
+ tql = Oj.load(req.body, mode: :wab)
22
+ log_request_with_body('TQL', path, query, tql) if @shell.logger.info?
23
+ send_result(@shell.query(tql), res, path, query)
24
+ rescue StandardError => e
25
+ send_error(e, res)
26
+ end
27
+
28
+ private
29
+
30
+ def log_request_with_body(caller, path, query, body)
31
+ body = Data.new(body) unless body.is_a?(WAB::Data)
32
+ @shell.logger.info("#{caller} #{path.join('/')}#{query}\n#{body.json(@shell.indent)}")
33
+ end
34
+
35
+ # Sends the results from a controller request.
36
+ def send_result(result, res, path, query)
37
+ result = @shell.data(result) unless result.is_a?(WAB::Data)
38
+ response_body = result.json(@shell.indent)
39
+ res.status = 200
40
+ res['Content-Type'] = 'application/json'
41
+ @shell.logger.debug("reply to #{path.join('/')}#{query}: #{response_body}") if @shell.logger.debug?
42
+ res.body = response_body
43
+ end
44
+
45
+ # Sends an error from a rescued call.
46
+ def send_error(e, res)
47
+ res.status = 500
48
+ res['Content-Type'] = 'application/json'
49
+ body = { code: -1, error: "#{e.class}: #{e.message}" }
50
+ body[:backtrace] = e.backtrace
51
+ res.body = @shell.data(body).json(@shell.indent)
52
+ @shell.logger.warn(Impl.format_error(e))
53
+ end
54
+
55
+ end # TqlHandler
56
+ end # WEBrick
57
+ end # Impl
58
+ end # WAB
@@ -0,0 +1,25 @@
1
+ module WAB
2
+
3
+ # A Racker or a duck-typed alternative should be created and registered with
4
+ # a Shell for paths that expected to follow the Ruby rack API. The shell is
5
+ # included so that queries can be made when responding to requests.
6
+ class Racker # :doc: all
7
+ attr_accessor :shell
8
+
9
+ # Create a instance.
10
+ def initialize(shell)
11
+ @shell = shell
12
+ end
13
+
14
+ # Rack handler for processing rack requests. Implemenation should follow
15
+ # the rack API described at https://rack.github.io.
16
+ #
17
+ # env:: data to be processed
18
+ #
19
+ # return:: a rack compliant response.
20
+ def call(env)
21
+ [200, {}, ['A WABuR Rack Application']]
22
+ end
23
+
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
 
2
2
  module WAB
3
3
  # Current version of the module.
4
- VERSION = '0.6.2'
4
+ VERSION = '0.7.0'
5
5
  end
@@ -158,12 +158,21 @@ API. From the view perspective a REST over HTTP is used.
158
158
 
159
159
  #### Just Data
160
160
 
161
- - just data and data helpers (get, set, inspect, to_s)
162
- - nothing like to_json
163
- - reasoning it that different uses require different behavior
164
- - different stores, view, processing, etc
165
- - use of delgate is encouraged
166
-
161
+ WABuR keeps data and behavior separate. The data portion of a system is
162
+ represented by `WAB::Data` class. Behavior is implemented by support
163
+ classes. This alows extensions to be added without conflict as the `Data`
164
+ class is not modified by adding new features. This avoids the conflicts
165
+ encountered with Rails when it monkey patches core classes to add `to_json`
166
+ which conflicts with the JSON gem and locks out other better performing
167
+ extensions.
168
+
169
+ The reasoning behind the separation of data and behavior to improve
170
+ extendability is that different features often need similar but different
171
+ features. As an example data might need to be stored in multiple data stores
172
+ or be encoded for a view, external processing, or writing to a file. If the
173
+ behavior is encoded in the data object then care must be taken to avoid using
174
+ the same method names which is difficult if separate authors are contributing
175
+ or open source gems are used.
167
176
 
168
177
  ### View/Controller
169
178
 
@@ -0,0 +1,282 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << __dir__
4
+ $LOAD_PATH << File.expand_path('../lib', __dir__)
5
+
6
+ require 'minitest'
7
+ require 'minitest/autorun'
8
+ require 'fileutils'
9
+ require 'open3'
10
+ require 'uri'
11
+
12
+ require 'wab'
13
+
14
+ class TestClient < Minitest::Test
15
+
16
+ # All tests must leave the DB state of the server unchanged.
17
+
18
+ def run_wabur(port)
19
+ store_dir = "#{__dir__}/client_test/data#{port}"
20
+ FileUtils.remove_dir(store_dir, true)
21
+ _, @out, wt = Open3.popen2e("ruby", "#{__dir__}/../bin/wabur", '-I', "#{__dir__}/../lib", '-c', "#{__dir__}/client_test/wabur.conf", '--http.port', port.to_s, '--store.dir', store_dir)
22
+ 20.times {
23
+ begin
24
+ Net::HTTP.get_response(URI("http://localhost:#{port}"))
25
+ break
26
+ rescue Exception => e
27
+ sleep(0.1)
28
+ end
29
+ }
30
+ wt.pid
31
+ end
32
+
33
+ def shutdown_wabur(pid)
34
+ Process.kill('HUP', pid)
35
+ # Uncomment to get verbose output from the server.
36
+ #puts @out.read
37
+ Process.wait
38
+ rescue
39
+ # ignore
40
+ end
41
+
42
+ def test_simple
43
+ port = 6371
44
+ pid = run_wabur(port)
45
+ client = WAB::Client.new('localhost', port)
46
+ result = client.create({kind: 'Test', value: 123})
47
+ assert_equal(0, result[:code], 'create result code mismatch')
48
+ ref = result[:ref]
49
+
50
+ result = client.read('Test', ref)
51
+ assert_equal(0, result[:code], 'read result code mismatch')
52
+ assert_equal(1, result[:results].length, 'read result wrong length')
53
+ rec = result[:results][0]
54
+ assert_equal('Test', rec[:data][:kind], 'read kind mismatch')
55
+ assert_equal(123, rec[:data][:value], 'read value mismatch')
56
+
57
+ result = client.delete('Test', ref)
58
+ assert_equal(0, result[:code], 'delete result code mismatch')
59
+ assert_equal([ref], result[:deleted], 'delete list mismatch')
60
+
61
+ result = client.read('Test', ref)
62
+ assert_equal(0, result[:code], 'read after delete result code mismatch')
63
+ assert_equal(0, result[:results].length, 'read after delete result wrong length')
64
+
65
+ ensure
66
+ shutdown_wabur(pid)
67
+ end
68
+
69
+ def test_types
70
+ port = 6372
71
+ pid = run_wabur(port)
72
+ client = WAB::Client.new('localhost', port)
73
+ t = Time.now
74
+ uuid = WAB::UUID.new('b0ca922d-372e-41f4-8fea-47d880188ba3')
75
+ uri = URI("http://wab.systems/sample")
76
+ obj = {
77
+ kind: 'Test',
78
+ num: 123,
79
+ bool: true,
80
+ null: nil,
81
+ str: "string",
82
+ dub: 1.23,
83
+ time: t,
84
+ uuid: uuid,
85
+ uri: uri
86
+ }
87
+ result = client.create(obj)
88
+ assert_equal(0, result[:code], 'create result code mismatch')
89
+ ref = result[:ref]
90
+
91
+ result = client.read('Test', ref)
92
+ assert_equal(0, result[:code], 'read result code mismatch')
93
+ assert_equal(1, result[:results].length, 'read result wrong length')
94
+ rec = result[:results][0]
95
+ assert_equal('Test', rec[:data][:kind], 'read kind mismatch')
96
+ assert_equal(obj, rec[:data], 'read data mismatch')
97
+
98
+ result = client.delete('Test', ref)
99
+ assert_equal(0, result[:code], 'delete result code mismatch')
100
+ assert_equal([ref], result[:deleted], 'delete list mismatch')
101
+
102
+ ensure
103
+ shutdown_wabur(pid)
104
+ end
105
+
106
+ def test_create_query
107
+ port = 6373
108
+ pid = run_wabur(port)
109
+ client = WAB::Client.new('localhost', port)
110
+
111
+ result = client.create({kind: 'Test', value: 123}, 'Test', {value: 123})
112
+ assert_equal(0, result[:code], 'create result code mismatch')
113
+ ref = result[:ref]
114
+
115
+ result = client.read('Test', ref)
116
+ assert_equal(0, result[:code], 'read result code mismatch')
117
+ assert_equal(1, result[:results].length, 'read result wrong length')
118
+
119
+ # Same again should fail.
120
+ assert_raises('should fail to create a duplicate') {
121
+ result = client.create({kind: 'Test', value: 123}, 'Test', {value: 123})
122
+ }
123
+
124
+ result = client.delete('Test', ref)
125
+ assert_equal(0, result[:code], 'delete result code mismatch')
126
+ assert_equal([ref], result[:deleted], 'delete list mismatch')
127
+
128
+ ensure
129
+ shutdown_wabur(pid)
130
+ end
131
+
132
+ def test_delete_all
133
+ port = 6374
134
+ pid = run_wabur(port)
135
+ client = WAB::Client.new('localhost', port)
136
+ refs = []
137
+ 3.times { |i|
138
+ result = client.create({kind: 'Test', value: i})
139
+ assert_equal(0, result[:code], "create #{i} result code mismatch")
140
+ refs << result[:ref]
141
+ }
142
+
143
+ result = client.read('Test')
144
+ assert_equal(0, result[:code], 'read result code mismatch')
145
+ assert_equal(3, result[:results].length, 'read result wrong length')
146
+
147
+ result = client.delete('Test')
148
+ assert_equal(0, result[:code], 'delete result code mismatch')
149
+ assert_equal(3, result[:deleted].length, 'delete list length mismatch')
150
+
151
+ ensure
152
+ shutdown_wabur(pid)
153
+ end
154
+
155
+ def test_update_ref
156
+ port = 6375
157
+ pid = run_wabur(port)
158
+ client = WAB::Client.new('localhost', port)
159
+
160
+ result = client.create({kind: 'Test', value: 123})
161
+ assert_equal(0, result[:code], 'create result code mismatch')
162
+ ref = result[:ref]
163
+
164
+ result = client.update('Test', {kind: 'Test', value: 321}, ref)
165
+ assert_equal(0, result[:code], 'create result code mismatch')
166
+
167
+ result = client.read('Test', ref)
168
+ assert_equal(0, result[:code], 'read result code mismatch')
169
+ assert_equal(1, result[:results].length, 'read result wrong length')
170
+ rec = result[:results][0]
171
+ assert_equal('Test', rec[:data][:kind], 'read kind mismatch')
172
+ assert_equal(321, rec[:data][:value], 'read value mismatch')
173
+
174
+ result = client.delete('Test', ref)
175
+ assert_equal(0, result[:code], 'delete result code mismatch')
176
+ assert_equal([ref], result[:deleted], 'delete list mismatch')
177
+
178
+ ensure
179
+ shutdown_wabur(pid)
180
+ end
181
+
182
+ def test_update_query
183
+ port = 6376
184
+ pid = run_wabur(port)
185
+ client = WAB::Client.new('localhost', port)
186
+ refs = []
187
+ 3.times { |i|
188
+ result = client.create({kind: 'Test', value: i})
189
+ assert_equal(0, result[:code], "create #{i} result code mismatch")
190
+ refs << result[:ref]
191
+ }
192
+
193
+ result = client.update('Test', {kind: 'Test', value: 111}, {value: 1})
194
+ assert_equal(0, result[:code], 'create result code mismatch')
195
+ assert_equal(1, result[:updated].length, 'updated wrong length')
196
+
197
+ result = client.read('Test', refs[1])
198
+ assert_equal(0, result[:code], 'read result code mismatch')
199
+ assert_equal(1, result[:results].length, 'read result wrong length')
200
+ rec = result[:results][0]
201
+ assert_equal('Test', rec[:data][:kind], 'read kind mismatch')
202
+ assert_equal(111, rec[:data][:value], 'read value mismatch')
203
+
204
+ result = client.delete('Test')
205
+ assert_equal(0, result[:code], 'delete result code mismatch')
206
+
207
+ ensure
208
+ shutdown_wabur(pid)
209
+ end
210
+
211
+ def test_delete_query
212
+ port = 6377
213
+ pid = run_wabur(port)
214
+ client = WAB::Client.new('localhost', port)
215
+ refs = []
216
+ 3.times { |i|
217
+ result = client.create({kind: 'Test', value: i})
218
+ assert_equal(0, result[:code], "create #{i} result code mismatch")
219
+ refs << result[:ref]
220
+ }
221
+
222
+ result = client.delete('Test', {value: 1})
223
+ assert_equal(0, result[:code], 'create result code mismatch')
224
+ assert_equal(1, result[:deleted].length, 'updated wrong length')
225
+
226
+ result = client.read('Test')
227
+ assert_equal(0, result[:code], 'read result code mismatch')
228
+ assert_equal(2, result[:results].length, 'read result wrong length')
229
+
230
+ result = client.delete('Test')
231
+ assert_equal(0, result[:code], 'delete result code mismatch')
232
+
233
+ ensure
234
+ shutdown_wabur(pid)
235
+ end
236
+
237
+ def test_read_query
238
+ port = 6378
239
+ pid = run_wabur(port)
240
+ client = WAB::Client.new('localhost', port)
241
+ 3.times { |i|
242
+ result = client.create({kind: 'Test', value: i})
243
+ assert_equal(0, result[:code], "create #{i} result code mismatch")
244
+ }
245
+
246
+ result = client.read('Test', {value: 1})
247
+ assert_equal(0, result[:code], 'read result code mismatch')
248
+ assert_equal(1, result[:results].length, 'read wrong length')
249
+
250
+ result = client.delete('Test')
251
+ assert_equal(0, result[:code], 'delete result code mismatch')
252
+
253
+ ensure
254
+ shutdown_wabur(pid)
255
+ end
256
+
257
+ def test_tql
258
+ port = 6379
259
+ pid = run_wabur(port)
260
+ client = WAB::Client.new('localhost', port)
261
+ 3.times { |i|
262
+ result = client.create({kind: 'Test', value: i})
263
+ assert_equal(0, result[:code], "create #{i} result code mismatch")
264
+ }
265
+
266
+ result = client.find({
267
+ where: ['LT', 'value', 2],
268
+ select: '$ref'
269
+ })
270
+
271
+ assert_equal(0, result[:code], 'read result code mismatch')
272
+ assert_equal(2, result[:results].length, 'results wrong length')
273
+
274
+ result = client.delete('Test')
275
+ assert_equal(0, result[:code], 'delete result code mismatch')
276
+
277
+ ensure
278
+ shutdown_wabur(pid)
279
+ end
280
+
281
+ end
282
+