wabur 0.6.2 → 0.7.0
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 +5 -5
- data/README.md +106 -15
- data/bin/wabur +6 -0
- data/export/assets/js/ui.js +18 -0
- data/lib/wab.rb +7 -1
- data/lib/wab/client.rb +145 -0
- data/lib/wab/controller.rb +1 -1
- data/lib/wab/errors.rb +6 -0
- data/lib/wab/impl.rb +1 -1
- data/lib/wab/impl/agoo.rb +18 -0
- data/lib/wab/impl/agoo/export_proxy.rb +55 -0
- data/lib/wab/impl/agoo/handler.rb +51 -0
- data/lib/wab/impl/agoo/sender.rb +50 -0
- data/lib/wab/impl/agoo/server.rb +59 -0
- data/lib/wab/impl/agoo/tql_handler.rb +35 -0
- data/lib/wab/impl/model.rb +8 -1
- data/lib/wab/impl/rack_error.rb +27 -0
- data/lib/wab/impl/rack_handler.rb +69 -0
- data/lib/wab/impl/shell.rb +56 -51
- data/lib/wab/impl/sinatra.rb +18 -0
- data/lib/wab/impl/sinatra/export_proxy.rb +57 -0
- data/lib/wab/impl/sinatra/handler.rb +50 -0
- data/lib/wab/impl/sinatra/sender.rb +53 -0
- data/lib/wab/impl/sinatra/server.rb +66 -0
- data/lib/wab/impl/sinatra/tql_handler.rb +35 -0
- data/lib/wab/impl/templates/wabur.conf.template +1 -1
- data/lib/wab/impl/webrick.rb +18 -0
- data/lib/wab/impl/webrick/export_proxy.rb +41 -0
- data/lib/wab/impl/webrick/handler.rb +116 -0
- data/lib/wab/impl/webrick/sender.rb +34 -0
- data/lib/wab/impl/webrick/server.rb +39 -0
- data/lib/wab/impl/webrick/tql_handler.rb +58 -0
- data/lib/wab/racker.rb +25 -0
- data/lib/wab/version.rb +1 -1
- data/pages/Architecture.md +15 -6
- data/test/test_client.rb +282 -0
- data/test/test_impl.rb +2 -0
- data/test/test_runner.rb +267 -91
- metadata +27 -5
- data/lib/wab/impl/export_proxy.rb +0 -39
- data/lib/wab/impl/handler.rb +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dbf0b84d397a6216caa0d5190e69b2d024a2c3274ddd18137ccc54154cca80ab
|
4
|
+
data.tar.gz: ee17aea00b5048cf3f9cfddc562ec6bbf98b13f48482343e8805dd6e8c03db55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcd2a2c8e493a38300942c9f76ad38b4ccaac4503637bc7d0929774371e55bc855441bebb9b49de76e49a619f86deb438f2b2f5d3a0e1ee9ba69ac1e9a1534a0
|
7
|
+
data.tar.gz: a04cadc7cd14f6ecc69a842334a41e57a2d7c6737590ca453c63bbd542211bdc05a0110c0948287a0057ab5bb0176edfac4f1ef376bf2d514c5b2386cdf2b74b
|
data/README.md
CHANGED
@@ -5,27 +5,115 @@
|
|
5
5
|
[](https://rubygems.org/gems/wabur)
|
6
6
|
[](https://rubygems.org/gems/wabur)
|
7
7
|
|
8
|
-
WABuR is a Web Application Builder using Ruby. It
|
9
|
-
|
10
|
-
|
8
|
+
WABuR is a Web Application Builder using Ruby. It is easy to use, taking just
|
9
|
+
a minute to create a `Hello World` web application and it is _FAST_, hitting
|
10
|
+
over 200,000 fetches a second with a Ruby core! It employs a modern NoSQL JSON
|
11
|
+
data store and a single-page UI using JavaScript.
|
11
12
|
|
12
13
|
It is pluggable and extendable in many ways to allow new additions,
|
13
14
|
alternative databases, and any number of UIs.
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
## Quick Start
|
17
|
+
|
18
|
+
With WABuR you are up and running in minutes with only one file to modify to
|
19
|
+
specify attributes. It doesn't get any simpler. Heres how.
|
20
|
+
|
21
|
+
Install the wabur gem.
|
22
|
+
|
23
|
+
```
|
24
|
+
$ gem install wabur
|
25
|
+
```
|
26
|
+
|
27
|
+
Create a new project and `cd` into the directory.
|
19
28
|
|
20
|
-
|
29
|
+
```
|
30
|
+
$ wabur new --base blog Entry
|
31
|
+
$ cd blog
|
32
|
+
```
|
21
33
|
|
22
|
-
|
34
|
+
Define attributes for the data elements along with the attributes that will be
|
35
|
+
displayed in a list view. Open `lib/ui_controller.rb` and modify by adding two
|
36
|
+
lines and changing one.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require 'wab/ui'
|
40
|
+
|
41
|
+
class UIController < WAB::UI::MultiFlow
|
42
|
+
|
43
|
+
def initialize(shell)
|
44
|
+
super
|
45
|
+
add_flow(WAB::UI::RestFlow.new(shell,
|
46
|
+
{
|
47
|
+
kind: 'Entry',
|
48
|
+
title: '',
|
49
|
+
content: "\n\n\n\n",
|
50
|
+
}, ['$ref', 'title']))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
Just run the `wabur` command from inside the project directory. Open
|
56
|
+
`localhost:6363` in a browser.
|
57
|
+
|
58
|
+
```
|
59
|
+
$ wabur
|
60
|
+
```
|
23
61
|
|
24
62
|
Want to know more? A tutorial is available in the [tutorial](tutorial/README.md)
|
25
|
-
directory.
|
63
|
+
directory. It takes a couple of minutes to go through the first lesson.
|
64
|
+
|
65
|
+
## WABuR Benchmarks
|
66
|
+
|
67
|
+
<img src="pages/setup.svg" width="400">
|
68
|
+
<table>
|
69
|
+
<tr><td><img src="pages/throughput.svg" width="400"></td><td><img src="pages/latency.svg" width="400"></td></tr>
|
70
|
+
</table>
|
71
|
+
|
72
|
+
| Runner | Read Throughput | Read Latency | Create Throughput | Create Latency | Setup time |
|
73
|
+
| ------------- | --------------- | ------------ | ----------------- | -------------- | ---------- |
|
74
|
+
| WAB Pure Ruby | 2.8K Reads/sec | 1.4 msecs | 2.2K Creates/sec | 1.8 msecs | 1 minute |
|
75
|
+
| OpO-Rub | 212K Reads/sec | 0.09 msecs | 134K Creates/sec | 0.15 msecs | 1 minute |
|
76
|
+
| OpO Direct | 364K Reads/sec | 0.05 msecs | 157K Creates/sec | 0.13 msecs | 1 minute |
|
77
|
+
| Ruby on Rails | 123 Reads/sec | 175 msecs | ---- Creates/sec | ---- msecs | 20 minutes |
|
78
|
+
| Sinatra | 1.5K Reads/sec | 13 msecs | ---- Creates/sec | ---- msecs | 60 minutes |
|
26
79
|
|
27
80
|
More interested in the benchmarks? Then take a look at the [benchmarks page](benchmarks/README.md).
|
28
81
|
|
82
|
+
## Where to use WABuR
|
83
|
+
|
84
|
+
WABuR takes a different approach which opens up new possibilities for uses of
|
85
|
+
Ruby. Some examples that WABuR is suitable for are:
|
86
|
+
|
87
|
+
* Race Results - With the ability to handle massive traffic WABuR can keep up
|
88
|
+
with the load imposed by hundreds of thousands of users requesting results
|
89
|
+
during the race and with the ability to have multiple UIs realtime displays
|
90
|
+
can be different than those used to display results after the race.
|
91
|
+
|
92
|
+
* Voting Systems - Voting systems collect and forward results to central
|
93
|
+
servers. Loads are high during peak periods but well within the limits that
|
94
|
+
WABuR is able to handle. The ease extending WABuR makes it easy to
|
95
|
+
implement the migration of data to a central server or multiple servers.
|
96
|
+
|
97
|
+
* Operations Monitoring - Log using JSON and WABuR can be used to monitor and
|
98
|
+
query logs.
|
99
|
+
|
100
|
+
* Market Data - With the high throughput and low latency WABuR is a good
|
101
|
+
candidate for systems that need to display market data and processing of
|
102
|
+
market data.
|
103
|
+
|
104
|
+
* Mapping - Systems that display data on a map can use WABuRs flexibility
|
105
|
+
with regard to the UI.
|
106
|
+
|
107
|
+
* Directory - As an example of a quick and easy build, an directory or
|
108
|
+
address book can be started in minutes.
|
109
|
+
|
110
|
+
## Rails
|
111
|
+
|
112
|
+
A natural question is *"What about Rails?"*. Rails is well established and has
|
113
|
+
a huge user base. WABuR is not a replacement for Rails. It is an alternative
|
114
|
+
for those who want to explore using JSON databases with a single-page dynamic
|
115
|
+
JavaScript UI.
|
116
|
+
|
29
117
|
## Participate and Contribute
|
30
118
|
|
31
119
|
If you like the idea and want to help out, or become a core developer on the
|
@@ -36,20 +124,23 @@ and lets make something awesome together.
|
|
36
124
|
|
37
125
|
These are the simple guidelines for contributing.
|
38
126
|
|
39
|
-
1.
|
127
|
+
1. Take a look at the [architecture page](pages/Architecture.md) and the source code.
|
128
|
+
|
129
|
+
2. Coordinate with me first before getting started to avoid duplication of
|
40
130
|
effort or implementing something in conflict with the plans.
|
41
131
|
|
42
|
-
|
132
|
+
3. Branch off the `develop` branch and submit a PR.
|
43
133
|
|
44
|
-
|
134
|
+
4. Write unit tests.
|
45
135
|
|
46
|
-
|
136
|
+
5. Write straight forward, clean, and simple code. No magic stuff, no monkey
|
47
137
|
patching Ruby core classes, and no inheriting from core classes.
|
48
138
|
|
49
139
|
## References and Links
|
50
140
|
|
141
|
+
- [Agoo](https://github.com/ohler55/agoo) Web Server GemBuR.
|
51
142
|
- [Oj](https://github.com/ohler55/oj) JSON parser used in WABuR.
|
52
143
|
- [OpO](http://opo.technology) home of the Opo-Rub runner.
|
53
144
|
- [Sass](http://sass-lang.com) used to build the reference implementation UI CSS.
|
54
145
|
- [SystemJS Babel Plugin](https://github.com/systemjs/plugin-babel) also used to transpile JavaScript.
|
55
|
-
- [SystemJS](https://github.com/systemjs/systemjs) used to convert JavaScript ES6 to ES5 in the browser.
|
146
|
+
- [SystemJS](https://github.com/systemjs/systemjs) used to convert JavaScript ES6 to ES5 in the browser.
|
data/bin/wabur
CHANGED
@@ -107,6 +107,12 @@ options = {
|
|
107
107
|
doc: 'HTTP Port to listen on.',
|
108
108
|
arg: 'PORT',
|
109
109
|
},
|
110
|
+
server: {
|
111
|
+
val: 'WEBrick',
|
112
|
+
type: String,
|
113
|
+
doc: 'Web server to use. Can be Agoo, Sinatra, or WEBRick.',
|
114
|
+
arg: 'GEM',
|
115
|
+
},
|
110
116
|
},
|
111
117
|
indent: {
|
112
118
|
val: 0,
|
data/export/assets/js/ui.js
CHANGED
@@ -205,6 +205,24 @@ export class ObjectDisplay extends Display {
|
|
205
205
|
value = obj[key];
|
206
206
|
if (value instanceof Object) {
|
207
207
|
this._setFields(`${path}.${key}`, value);
|
208
|
+
} else if (undefined != element.alt) {
|
209
|
+
switch (element.getAttribute('alt')) {
|
210
|
+
case 'local-time':
|
211
|
+
element.value = new Date(obj[key]).toLocaleString();
|
212
|
+
break;
|
213
|
+
case 'zulu-time':
|
214
|
+
element.value = new Date(obj[key]).toUTCString();
|
215
|
+
break;
|
216
|
+
case 'long-time':
|
217
|
+
element.value = new Date(obj[key]).toString();
|
218
|
+
break;
|
219
|
+
case 'date':
|
220
|
+
element.value = new Date(obj[key]).toDateString();
|
221
|
+
break;
|
222
|
+
default:
|
223
|
+
element.value = obj[key];
|
224
|
+
break;
|
225
|
+
}
|
208
226
|
} else {
|
209
227
|
element.value = obj[key];
|
210
228
|
}
|
data/lib/wab.rb
CHANGED
@@ -11,12 +11,17 @@ module WAB
|
|
11
11
|
end
|
12
12
|
raise ForbiddenError.new(path) if path.include?('..')
|
13
13
|
path = File.expand_path("#{__dir__}/../export#{path}")
|
14
|
-
|
14
|
+
begin
|
15
|
+
File.open(path) { |f| f.read() }
|
16
|
+
rescue Exception
|
17
|
+
nil
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
end
|
18
22
|
|
19
23
|
require 'wab/controller'
|
24
|
+
require 'wab/racker'
|
20
25
|
require 'wab/data'
|
21
26
|
require 'wab/errors'
|
22
27
|
require 'wab/open_controller'
|
@@ -25,3 +30,4 @@ require 'wab/shell_logger'
|
|
25
30
|
require 'wab/utils'
|
26
31
|
require 'wab/uuid'
|
27
32
|
require 'wab/version'
|
33
|
+
require 'wab/client'
|
data/lib/wab/client.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
|
2
|
+
require 'net/http'
|
3
|
+
require 'oj'
|
4
|
+
|
5
|
+
module WAB
|
6
|
+
|
7
|
+
# A client for a WAB server. It is not specific to any particular
|
8
|
+
# runner. The client allows direct access to the server data. It is just
|
9
|
+
# another view implementation.
|
10
|
+
class Client
|
11
|
+
# Address of the WAB server.
|
12
|
+
attr_accessor :server_address
|
13
|
+
# The port the WAB serer is listening on.
|
14
|
+
attr_accessor :server_port
|
15
|
+
# Prefix to add to the URL path.
|
16
|
+
attr_accessor :path_prefix
|
17
|
+
# The URL path to execute TQL,
|
18
|
+
attr_accessor :tql_path
|
19
|
+
# The key for the type. The default is 'kind'.
|
20
|
+
attr_accessor :type_key
|
21
|
+
# If true the connection to the server is Keep-Alive.
|
22
|
+
attr_accessor :keep_alive
|
23
|
+
|
24
|
+
# Create a new Client for the server at +addr+ and +port+. The provided
|
25
|
+
# options should match the attribute names and types.
|
26
|
+
def initialize(addr, port, options={})
|
27
|
+
@server_address = addr
|
28
|
+
@server_port = port
|
29
|
+
@path_prefix = options.fetch(:path_prefix, '/v1/')
|
30
|
+
@tql_path = options.fetch(:tql_path, '/tql')
|
31
|
+
@type_key = options.fetch(:type_key, 'kind').to_sym
|
32
|
+
@keep_alive = !!options.fetch(:keep_alive, true)
|
33
|
+
@http = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create a new data object. If a query is provided it is treated as a
|
37
|
+
# check against an existing object with the same key/value pairs.
|
38
|
+
#
|
39
|
+
# On error an Exception will be raised.
|
40
|
+
#
|
41
|
+
# data:: the data to use as a new object.
|
42
|
+
# kind:: the kind of the data. If nil the kind is taken from the data
|
43
|
+
# query:: query parameters to match against existing instances. A match fails the insert.
|
44
|
+
def create(data, kind=nil, query=nil)
|
45
|
+
kind ||= data[@type_key] || data[@type_key.to_s]
|
46
|
+
send_request('PUT', kind, query, data)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return the objects according to the kind and query arguments. The
|
50
|
+
# following patterns supported:
|
51
|
+
#
|
52
|
+
# * query is a ref [12345] looks for MyType with reference ID of 12345
|
53
|
+
# * query is a Hash {name:fred,age:63} looks for all MyTypes with a name
|
54
|
+
# of 'fred' and an age of 63.
|
55
|
+
#
|
56
|
+
# kind:: the data type
|
57
|
+
# query:: query parameters as a Hash.
|
58
|
+
def read(kind, query=nil)
|
59
|
+
send_request('GET', kind, query, nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Replaces the object data for the identified object. The query can be a
|
63
|
+
# reference to a specific object or a set of parameters to match.
|
64
|
+
#
|
65
|
+
# The return should be the identifiers for the object updated.
|
66
|
+
#
|
67
|
+
# On error an Exception should be raised.
|
68
|
+
#
|
69
|
+
# kind:: the data type
|
70
|
+
# data:: the data to use as a new object.
|
71
|
+
# query:: query parameters.
|
72
|
+
def update(kind, data, query)
|
73
|
+
raise ArgError.new('data') if data.nil?
|
74
|
+
raise ArgError.new('query') if query.nil?
|
75
|
+
send_request('POST', kind, query, data)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Deletes the data for the identified object(s). The query can be a
|
79
|
+
# reference to a specific object or a set of parameters to match.
|
80
|
+
#
|
81
|
+
# The return is the identifiers for the object updated.
|
82
|
+
#
|
83
|
+
# On error an Exception should be raised.
|
84
|
+
#
|
85
|
+
# kind:: the data type
|
86
|
+
# query:: query parameters.
|
87
|
+
def delete(kind, query=nil)
|
88
|
+
send_request('DELETE', kind, query, nil)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Request the server evaluate a TQL query.
|
92
|
+
#
|
93
|
+
# tql:: query to evaluate.
|
94
|
+
def find(tql)
|
95
|
+
raise ArgError.new('tql') if tql.nil?
|
96
|
+
send_request('POST', 'tql', nil, tql)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def connect
|
102
|
+
@http ||= Net::HTTP.new(@server_address, @server_port)
|
103
|
+
end
|
104
|
+
|
105
|
+
def form_path(kind, query)
|
106
|
+
return @tql_path if kind == 'tql'
|
107
|
+
path = "#{@path_prefix}#{kind}"
|
108
|
+
if query.is_a?(Hash)
|
109
|
+
first = true
|
110
|
+
query.each_pair { |k,v|
|
111
|
+
if first
|
112
|
+
path << '?'
|
113
|
+
first = false
|
114
|
+
else
|
115
|
+
path << '&'
|
116
|
+
end
|
117
|
+
path << "#{k}=#{v}"
|
118
|
+
}
|
119
|
+
elsif !query.nil?
|
120
|
+
path << '/'
|
121
|
+
path << query.to_s
|
122
|
+
end
|
123
|
+
path
|
124
|
+
end
|
125
|
+
|
126
|
+
def send_request(method, kind, query, content)
|
127
|
+
connect
|
128
|
+
header = {'Connection' => (@keep_alive ? 'Keep-Alive' : 'Close') }
|
129
|
+
unless content.nil?
|
130
|
+
if content.is_a?(Data)
|
131
|
+
content = content.json
|
132
|
+
else
|
133
|
+
content = Oj.dump(content, mode: :wab, indent: 0)
|
134
|
+
end
|
135
|
+
header['Content-Type'] = 'application/json'
|
136
|
+
resp = @http.send_request(method, form_path(kind, query), content, header)
|
137
|
+
else
|
138
|
+
resp = @http.send_request(method, form_path(kind, query))
|
139
|
+
end
|
140
|
+
raise Error.new(resp.body) unless resp.is_a?(Net::HTTPOK)
|
141
|
+
Oj.load(resp.body, mode: :wab)
|
142
|
+
end
|
143
|
+
|
144
|
+
end # Client
|
145
|
+
end # WAB
|
data/lib/wab/controller.rb
CHANGED
@@ -21,7 +21,7 @@ module WAB
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Handler for paths that do not match the REST pattern or for unregistered
|
24
|
-
# types.
|
24
|
+
# types.
|
25
25
|
#
|
26
26
|
# Processing result are passed back to the view which forward the result
|
27
27
|
# on to the requester. The result, if not nil, should be a Data instance.
|
data/lib/wab/errors.rb
CHANGED
data/lib/wab/impl.rb
CHANGED
@@ -26,9 +26,9 @@ require 'wab/impl/expr'
|
|
26
26
|
require 'wab/impl/path_expr'
|
27
27
|
require 'wab/impl/bool_expr'
|
28
28
|
require 'wab/impl/shell'
|
29
|
-
require 'wab/impl/export_proxy'
|
30
29
|
require 'wab/impl/utils'
|
31
30
|
require 'wab/impl/init'
|
31
|
+
require 'wab/impl/rack_error'
|
32
32
|
|
33
33
|
# Require the concrete Expr subclasses so a mapping table can be created for
|
34
34
|
# the parser.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
require 'wab'
|
3
|
+
|
4
|
+
module WAB
|
5
|
+
module Impl
|
6
|
+
|
7
|
+
# The Agoo module contains handlers for the Agoo web server.
|
8
|
+
module Agoo
|
9
|
+
|
10
|
+
end # Agoo
|
11
|
+
end # Impl
|
12
|
+
end # WAB
|
13
|
+
|
14
|
+
require 'wab/impl/agoo/sender'
|
15
|
+
require 'wab/impl/agoo/handler'
|
16
|
+
require 'wab/impl/agoo/tql_handler'
|
17
|
+
require 'wab/impl/agoo/export_proxy'
|
18
|
+
require 'wab/impl/agoo/server'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
module Impl
|
4
|
+
module Agoo
|
5
|
+
|
6
|
+
# A handler that provides missing files in an assets directory where the
|
7
|
+
# files are the wab and wab UI files.
|
8
|
+
class ExportProxy
|
9
|
+
include Sender
|
10
|
+
|
11
|
+
def initialize(shell)
|
12
|
+
@shell = shell
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_request(req, res)
|
16
|
+
path = (req.script_name + req.path_info)
|
17
|
+
query = parse_query(req.query_string)
|
18
|
+
path = '/index.html' if '/' == path
|
19
|
+
mime = nil
|
20
|
+
index = path.rindex('.')
|
21
|
+
unless index.nil?
|
22
|
+
mime = {
|
23
|
+
'css' => 'text/css',
|
24
|
+
'eot' => 'application/vnd.ms-fontobject',
|
25
|
+
'es5' => 'application/javascript',
|
26
|
+
'es6' => 'application/javascript',
|
27
|
+
'gif' => 'image/gif',
|
28
|
+
'html' => 'text/html',
|
29
|
+
'ico' => 'image/x-icon',
|
30
|
+
'jpeg' => 'image/jpeg',
|
31
|
+
'jpg' => 'image/jpeg',
|
32
|
+
'js' => 'application/javascript',
|
33
|
+
'json' => 'application/json',
|
34
|
+
'png' => 'image/png',
|
35
|
+
'sse' => 'text/plain',
|
36
|
+
'svg' => 'image/svg+xml',
|
37
|
+
'ttf' => 'application/font-sfnt',
|
38
|
+
'txt' => 'text/plain',
|
39
|
+
'woff' => 'application/font-woff',
|
40
|
+
'woff2' => 'font/woff2',
|
41
|
+
}[path[index + 1..-1].downcase]
|
42
|
+
end
|
43
|
+
mime = 'text/plain' if mime.nil?
|
44
|
+
content = WAB.get_export(path)
|
45
|
+
res.code = 200
|
46
|
+
res['Content-Type'] = mime
|
47
|
+
res.body = content
|
48
|
+
rescue Exception => e
|
49
|
+
send_error(e, res)
|
50
|
+
end
|
51
|
+
|
52
|
+
end # ExportProxy
|
53
|
+
end # Agoo
|
54
|
+
end # Impl
|
55
|
+
end # WAB
|