em-midori 0.1.7.1 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/midori/api.rb +33 -60
- data/lib/midori/api_engine.rb +9 -36
- data/lib/midori/const.rb +30 -0
- data/lib/midori/core_ext/proc.rb +4 -4
- data/lib/midori/core_ext/promise.rb +10 -10
- data/lib/midori/core_ext/promise_exception.rb +3 -3
- data/lib/midori/core_ext/safe_require.rb +2 -2
- data/lib/midori/extension/file.rb +2 -2
- data/lib/midori/extension/ohm.rb +1 -1
- data/lib/midori/extension/postgres.rb +12 -12
- data/lib/midori/extension/redis.rb +4 -4
- data/lib/midori/extension/sequel.rb +3 -3
- data/lib/midori/middleware.rb +1 -1
- data/lib/midori/request.rb +2 -2
- data/lib/midori/runner.rb +4 -4
- data/lib/midori/sandbox.rb +8 -8
- data/lib/midori/server.rb +4 -4
- data/lib/midori/version.rb +1 -1
- data/lib/midori/websocket.rb +14 -14
- data/tutorial/README.md +11 -0
- data/tutorial/SUMMARY.md +28 -0
- data/tutorial/advanced/custom_extensions.md +0 -0
- data/tutorial/advanced/deploying_for_production.md +0 -0
- data/tutorial/advanced/error_handling.md +0 -0
- data/tutorial/advanced/rerl.md +0 -0
- data/tutorial/advanced/scaling_project.md +0 -0
- data/tutorial/advanced/unit_testing.md +0 -0
- data/tutorial/essentials/extensions.md +0 -0
- data/tutorial/essentials/getting_started.md +27 -0
- data/tutorial/essentials/installation.md +98 -0
- data/tutorial/essentials/middlewares.md +0 -0
- data/tutorial/essentials/request_handling.md +71 -0
- data/tutorial/essentials/routing.md +136 -0
- data/tutorial/essentials/runner.md +0 -0
- data/tutorial/meta/comparison_with_other_frameworks.md +0 -0
- data/tutorial/meta/join_the_midori_community.md +0 -0
- data/tutorial/meta/next_steps.md +0 -0
- metadata +29 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bded68c0e7831eb9e36ee2b49dec6c61c760a815
|
4
|
+
data.tar.gz: de5f760feffc996aa88443e3db67eab589d179d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ffe68ce957c773adf24da6a5e0c4c94deca32d0a617e29ee9ac10eacf4883e5e1d6cf0c94b0698fa30e9bde26599f4425bd081b38d945d0baa6b97a3d4a620e
|
7
|
+
data.tar.gz: 220b8c5f8ec1af61be67526d24975ccd1387384afd41c7014f7176a4bf2468c4fd061475eb147fd9bc8655e5f06b5b2677f382774bc3d8d1f954ff4b98a8b5b8
|
data/lib/midori/api.rb
CHANGED
@@ -9,38 +9,11 @@ class Midori::API
|
|
9
9
|
attr_accessor :routes, :scope_middlewares
|
10
10
|
|
11
11
|
# Init private variables of class
|
12
|
-
# @return [
|
12
|
+
# @return [nil] nil
|
13
13
|
def class_initialize
|
14
|
-
@routes = {
|
15
|
-
|
16
|
-
|
17
|
-
HEAD: [],
|
18
|
-
POST: [],
|
19
|
-
PUT: [],
|
20
|
-
CONNECT: [],
|
21
|
-
OPTIONS: [],
|
22
|
-
TRACE: [],
|
23
|
-
COPY: [],
|
24
|
-
LOCK: [],
|
25
|
-
MKCOL: [],
|
26
|
-
MOVE: [],
|
27
|
-
PROPFIND: [],
|
28
|
-
PROPPATCH: [],
|
29
|
-
UNLOCK: [],
|
30
|
-
REPORT: [],
|
31
|
-
MKACTIVITY: [],
|
32
|
-
CHECKOUT: [],
|
33
|
-
MERGE: [],
|
34
|
-
:'M-SEARCH' => [],
|
35
|
-
NOTIFY: [],
|
36
|
-
SUBSCRIBE: [],
|
37
|
-
UNSUBSCRIBE: [],
|
38
|
-
PATCH: [],
|
39
|
-
PURGE: [],
|
40
|
-
WEBSOCKET: [],
|
41
|
-
EVENTSOURCE: [],
|
42
|
-
MOUNT: []
|
43
|
-
}
|
14
|
+
@routes = {}
|
15
|
+
Midori::Const::ROUTE_METHODS.map {|method| @routes[method] = []}
|
16
|
+
@routes[:MOUNT] = []
|
44
17
|
@scope_middlewares = []
|
45
18
|
@temp_middlewares = []
|
46
19
|
nil
|
@@ -57,9 +30,9 @@ class Midori::API
|
|
57
30
|
def delete(path, &block) end
|
58
31
|
|
59
32
|
# Add GET method as a DSL for route definition
|
60
|
-
# @param [
|
33
|
+
# @param [String] path Accepts as part of path in route definition
|
61
34
|
# @yield what to run when route matched
|
62
|
-
# @return [
|
35
|
+
# @return [nil] nil
|
63
36
|
# @example String as router
|
64
37
|
# get '/' do
|
65
38
|
# puts 'Hello World'
|
@@ -77,9 +50,9 @@ class Midori::API
|
|
77
50
|
def head(path, &block) end
|
78
51
|
|
79
52
|
# Add POST method as a DSL for route definition
|
80
|
-
# @param [
|
53
|
+
# @param [String] path Accepts as part of path in route definition
|
81
54
|
# @yield what to run when route matched
|
82
|
-
# @return [
|
55
|
+
# @return [nil] nil
|
83
56
|
# @example String as router
|
84
57
|
# post '/' do
|
85
58
|
# puts 'Hello World'
|
@@ -87,9 +60,9 @@ class Midori::API
|
|
87
60
|
def post(path, &block) end
|
88
61
|
|
89
62
|
# Add PUT method as a DSL for route definition
|
90
|
-
# @param [
|
63
|
+
# @param [String] path Accepts as part of path in route definition
|
91
64
|
# @yield what to run when route matched
|
92
|
-
# @return [
|
65
|
+
# @return [nil] nil
|
93
66
|
# @example String as router
|
94
67
|
# put '/' do
|
95
68
|
# puts 'Hello World'
|
@@ -97,9 +70,9 @@ class Midori::API
|
|
97
70
|
def put(path, &block) end
|
98
71
|
|
99
72
|
# Add CONNECT method as a DSL for route definition
|
100
|
-
# @param [
|
73
|
+
# @param [String] path Accepts as part of path in route definition
|
101
74
|
# @yield what to run when route matched
|
102
|
-
# @return [
|
75
|
+
# @return [nil] nil
|
103
76
|
# @example String as router
|
104
77
|
# connect '/' do
|
105
78
|
# puts 'Hello World'
|
@@ -107,8 +80,8 @@ class Midori::API
|
|
107
80
|
def connect(path, &block) end
|
108
81
|
|
109
82
|
# Add OPTIONS method as a DSL for route definition
|
110
|
-
# @param [
|
111
|
-
# @return [
|
83
|
+
# @param [String] path Accepts as part of path in route definition
|
84
|
+
# @return [nil] nil
|
112
85
|
# @example String as router
|
113
86
|
# options '/' do
|
114
87
|
# puts 'Hello World'
|
@@ -174,7 +147,7 @@ class Midori::API
|
|
174
147
|
# propfind '/' do
|
175
148
|
# puts 'Hello World'
|
176
149
|
# end
|
177
|
-
def
|
150
|
+
def propfind(path, &block) end
|
178
151
|
|
179
152
|
# Add PROPPATCH method as a DSL for route definition
|
180
153
|
# @param [ String ] path Accepts as part of path in route definition
|
@@ -266,7 +239,7 @@ class Midori::API
|
|
266
239
|
# subscribe '/' do
|
267
240
|
# puts 'Hello World'
|
268
241
|
# end
|
269
|
-
def
|
242
|
+
def subscribe(path, &block) end
|
270
243
|
|
271
244
|
# Add UNSUBSCRIBE method as a DSL for route definition
|
272
245
|
# @param [ String ] path Accepts as part of path in route definition
|
@@ -299,9 +272,9 @@ class Midori::API
|
|
299
272
|
def purge(path, &block) end
|
300
273
|
|
301
274
|
# Add LINK method as a DSL for route definition
|
302
|
-
# @param [
|
275
|
+
# @param [String] path Accepts as part of path in route definition
|
303
276
|
# @yield what to run when route matched
|
304
|
-
# @return [
|
277
|
+
# @return [nil] nil
|
305
278
|
# @example String as router
|
306
279
|
# link '/' do
|
307
280
|
# puts 'Hello World'
|
@@ -309,9 +282,9 @@ class Midori::API
|
|
309
282
|
def link(path, &block) end
|
310
283
|
|
311
284
|
# Add UNLINK method as a DSL for route definition
|
312
|
-
# @param [
|
285
|
+
# @param [String] path Accepts as part of path in route definition
|
313
286
|
# @yield what to run when route matched
|
314
|
-
# @return [
|
287
|
+
# @return [nil] nil
|
315
288
|
# @example String as router
|
316
289
|
# unlink '/' do
|
317
290
|
# puts 'Hello World'
|
@@ -319,9 +292,9 @@ class Midori::API
|
|
319
292
|
def unlink(path, &block) end
|
320
293
|
|
321
294
|
# Add WEBSOCKET method as a DSL for route definition
|
322
|
-
# @param [
|
295
|
+
# @param [String] path Accepts as part of path in route definition
|
323
296
|
# @yield what to run when route matched
|
324
|
-
# @return [
|
297
|
+
# @return [nil] nil
|
325
298
|
# @example String as router
|
326
299
|
# websocket '/' do
|
327
300
|
# puts 'Hello World'
|
@@ -329,8 +302,8 @@ class Midori::API
|
|
329
302
|
def websocket(path, &block) end
|
330
303
|
|
331
304
|
# Add EVENTSOURCE method as a DSL for route definition
|
332
|
-
# @param [
|
333
|
-
# @return [
|
305
|
+
# @param [String] path Accepts as part of path in route definition
|
306
|
+
# @return [nil] nil
|
334
307
|
# @example String as router
|
335
308
|
# eventsource '/' do
|
336
309
|
# puts 'Hello World'
|
@@ -338,9 +311,9 @@ class Midori::API
|
|
338
311
|
def eventsource(path, &block) end
|
339
312
|
|
340
313
|
# Mount a route prefix with another API defined
|
341
|
-
# @param [
|
342
|
-
# @param [
|
343
|
-
# @return [
|
314
|
+
# @param [String] prefix prefix of the route String
|
315
|
+
# @param [Class] api inherited from Midori::API
|
316
|
+
# @return [nil] nil
|
344
317
|
def mount(prefix, api)
|
345
318
|
raise ArgumentError if prefix == '/' # Cannot mount route API
|
346
319
|
@routes[:MOUNT] << [prefix, api]
|
@@ -348,9 +321,9 @@ class Midori::API
|
|
348
321
|
end
|
349
322
|
|
350
323
|
# Definitions for global error handler
|
351
|
-
# @param [
|
324
|
+
# @param [Class] error Error class, must be inherited form StandardError
|
352
325
|
# @yield what to do to deal with error
|
353
|
-
# @yieldparam [
|
326
|
+
# @yieldparam [StandardError] e the detailed error
|
354
327
|
# @example Basic Usage
|
355
328
|
# capture Midori::InternalError do |e|
|
356
329
|
# Midori::Response(500, {}, e.backtrace)
|
@@ -361,10 +334,10 @@ class Midori::API
|
|
361
334
|
end
|
362
335
|
|
363
336
|
# Implementation of route DSL
|
364
|
-
# @param [
|
365
|
-
# @param [
|
366
|
-
# @param [
|
367
|
-
# @return [
|
337
|
+
# @param [String] method HTTP method
|
338
|
+
# @param [String, Regexp] path path definition
|
339
|
+
# @param [Proc] block process to run when route matched
|
340
|
+
# @return [nil] nil
|
368
341
|
def add_route(method, path, block)
|
369
342
|
# Argument check
|
370
343
|
raise ArgumentError unless path.is_a?String
|
data/lib/midori/api_engine.rb
CHANGED
@@ -1,42 +1,15 @@
|
|
1
1
|
##
|
2
2
|
# Merge and manage all APIs.
|
3
|
-
# @attr [
|
3
|
+
# @attr [Hash] routes A hash of all routes merged
|
4
4
|
class Midori::APIEngine
|
5
5
|
attr_accessor :routes
|
6
6
|
|
7
7
|
# Init an API Engine
|
8
|
-
# @param [
|
9
|
-
# @param [
|
8
|
+
# @param [Class] root_api API inherited from [Midori::API]
|
9
|
+
# @param [Symbol] type type mustermann support
|
10
10
|
def initialize(root_api, type = :sinatra)
|
11
|
-
@routes = {
|
12
|
-
|
13
|
-
GET: [],
|
14
|
-
HEAD: [],
|
15
|
-
POST: [],
|
16
|
-
PUT: [],
|
17
|
-
CONNECT: [],
|
18
|
-
OPTIONS: [],
|
19
|
-
TRACE: [],
|
20
|
-
COPY: [],
|
21
|
-
LOCK: [],
|
22
|
-
MKCOL: [],
|
23
|
-
MOVE: [],
|
24
|
-
PROPFIND: [],
|
25
|
-
PROPPATCH: [],
|
26
|
-
UNLOCK: [],
|
27
|
-
REPORT: [],
|
28
|
-
MKACTIVITY: [],
|
29
|
-
CHECKOUT: [],
|
30
|
-
MERGE: [],
|
31
|
-
:'M-SEARCH' => [],
|
32
|
-
NOTIFY: [],
|
33
|
-
SUBSCRIBE: [],
|
34
|
-
UNSUBSCRIBE: [],
|
35
|
-
PATCH: [],
|
36
|
-
PURGE: [],
|
37
|
-
WEBSOCKET: [],
|
38
|
-
EVENTSOURCE: []
|
39
|
-
}
|
11
|
+
@routes = {}
|
12
|
+
Midori::Const::ROUTE_METHODS.map {|method| @routes[method] = []}
|
40
13
|
@root_api = root_api
|
41
14
|
@type = type
|
42
15
|
@routes = merge('', root_api, [])
|
@@ -66,10 +39,10 @@ class Midori::APIEngine
|
|
66
39
|
end
|
67
40
|
|
68
41
|
# Process after receive data from client
|
69
|
-
# @param request [
|
70
|
-
# @param connection [
|
71
|
-
# @return [
|
72
|
-
# @raise [
|
42
|
+
# @param request [Midori::Request] Http Raw Request
|
43
|
+
# @param connection [EM::Connection] A connection created by EventMachine
|
44
|
+
# @return [Midori::Response] Http Response
|
45
|
+
# @raise [Midori::Error::NotFound] If no route matched
|
73
46
|
def receive(request, connection = nil)
|
74
47
|
@routes[request.method].each do |route|
|
75
48
|
params = route.path.params(request.path)
|
data/lib/midori/const.rb
CHANGED
@@ -84,4 +84,34 @@ module Midori::Const
|
|
84
84
|
'Upgrade' => 'websocket',
|
85
85
|
'Connection' => 'Upgrade'
|
86
86
|
}
|
87
|
+
|
88
|
+
ROUTE_METHODS = %i(
|
89
|
+
DELETE
|
90
|
+
GET
|
91
|
+
HEAD
|
92
|
+
POST
|
93
|
+
PUT
|
94
|
+
CONNECT
|
95
|
+
OPTIONS
|
96
|
+
TRACE
|
97
|
+
COPY
|
98
|
+
LOCK
|
99
|
+
MKCOL
|
100
|
+
MOVE
|
101
|
+
PROPFIND
|
102
|
+
PROPPATCH
|
103
|
+
UNLOCK
|
104
|
+
REPORT
|
105
|
+
MKACTIVITY
|
106
|
+
CHECKOUT
|
107
|
+
MERGE
|
108
|
+
M-SEARCH
|
109
|
+
NOTIFY
|
110
|
+
SUBSCRIBE
|
111
|
+
UNSUBSCRIBE
|
112
|
+
PATCH
|
113
|
+
PURGE
|
114
|
+
WEBSOCKET
|
115
|
+
EVENTSOURCE
|
116
|
+
)
|
87
117
|
end
|
data/lib/midori/core_ext/proc.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
##
|
2
2
|
# Meta-programming Proc for Syntactic Sugars
|
3
3
|
class Proc
|
4
|
-
# Convert [
|
5
|
-
# @param [
|
6
|
-
# @return [
|
4
|
+
# Convert [Proc] to [Lambda]
|
5
|
+
# @param [Object] instance the context
|
6
|
+
# @return [Lambda] Lambda converted
|
7
7
|
# @note Converting [Proc] to [Lambda] may have incorrect behaviours on corner cases.
|
8
8
|
# @note See {Ruby Language Issues}[https://bugs.ruby-lang.org/issues/7314] for more details.
|
9
|
-
def to_lambda
|
9
|
+
def to_lambda(instance = Object.new)
|
10
10
|
instance.define_singleton_method(:_, &self)
|
11
11
|
instance.method(:_).to_proc
|
12
12
|
end
|
@@ -3,15 +3,15 @@
|
|
3
3
|
# Referenced from {Qiita}[http://qiita.com/south37/items/99a60345b22ef395d424]
|
4
4
|
class Promise
|
5
5
|
# Init a Promise
|
6
|
-
# @param [
|
6
|
+
# @param [Proc] callback an async method
|
7
7
|
def initialize(callback)
|
8
8
|
@callback = callback
|
9
9
|
end
|
10
10
|
|
11
11
|
# Define what to do after a method callbacks
|
12
|
-
# @param [
|
13
|
-
# @param [
|
14
|
-
# @return [
|
12
|
+
# @param [Proc] resolve what on callback
|
13
|
+
# @param [Proc] reject what on callback failed
|
14
|
+
# @return [nil] nil
|
15
15
|
def then(resolve = ->() {}, reject = ->() {})
|
16
16
|
@callback.call(resolve, reject)
|
17
17
|
end
|
@@ -20,7 +20,7 @@ end
|
|
20
20
|
# A Syntactic Sugar for Promise
|
21
21
|
class DeferPromise < Promise
|
22
22
|
# A Syntactic Sugar for Promise
|
23
|
-
# @param [
|
23
|
+
# @param [Proc] deffered To do what asynchronous
|
24
24
|
def initialize(deffered)
|
25
25
|
super(->(resolve, _reject){
|
26
26
|
EventMachine.defer(proc {
|
@@ -36,7 +36,7 @@ end
|
|
36
36
|
|
37
37
|
module Kernel
|
38
38
|
# Logic dealing of async method
|
39
|
-
# @param [
|
39
|
+
# @param [Fiber] fiber a fiber to call
|
40
40
|
def async_internal(fiber)
|
41
41
|
chain = lambda do |result|
|
42
42
|
return unless result.is_a?Promise
|
@@ -48,7 +48,7 @@ module Kernel
|
|
48
48
|
end
|
49
49
|
|
50
50
|
# Define an async method
|
51
|
-
# @param [
|
51
|
+
# @param [Symbol] method_name method name
|
52
52
|
# @yield async method
|
53
53
|
# @example
|
54
54
|
# async :hello do
|
@@ -60,15 +60,15 @@ module Kernel
|
|
60
60
|
}
|
61
61
|
end
|
62
62
|
|
63
|
-
# Shortcut to init [
|
63
|
+
# Shortcut to init [DeferPromise]
|
64
64
|
# @yield To do what asynchronous
|
65
|
-
# @return [
|
65
|
+
# @return [DerferPromise] instance
|
66
66
|
def defer(&block)
|
67
67
|
DeferPromise.new(block)
|
68
68
|
end
|
69
69
|
|
70
70
|
# Block the I/O to wait for async method response
|
71
|
-
# @param [
|
71
|
+
# @param [Promise] promise promise method
|
72
72
|
# @example
|
73
73
|
# result = await SQL.query('SELECT * FROM hello')
|
74
74
|
def await(promise)
|
@@ -1,11 +1,11 @@
|
|
1
1
|
##
|
2
|
-
# A special error as containers of errors inside [
|
3
|
-
# @attr [
|
2
|
+
# A special error as containers of errors inside [Promise]
|
3
|
+
# @attr [StandardError] raw_exception raw execption raised
|
4
4
|
class PromiseException < StandardError
|
5
5
|
attr_reader :raw_exception
|
6
6
|
|
7
7
|
# Init PromiseException
|
8
|
-
# @param [
|
8
|
+
# @param [StandardError] raw_exception raw execption raised
|
9
9
|
def initialize(raw_exception)
|
10
10
|
super(nil)
|
11
11
|
@raw_exception = raw_exception
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Kernel
|
2
2
|
# Raise error if Load Failed
|
3
|
-
# @param [
|
4
|
-
# @param [
|
3
|
+
# @param [String] file file to Load
|
4
|
+
# @param [String] prompt To prompt what when load error
|
5
5
|
def safe_require(file, prompt)
|
6
6
|
begin
|
7
7
|
require file
|
@@ -3,13 +3,13 @@
|
|
3
3
|
class Midori::File
|
4
4
|
class << self
|
5
5
|
# Same APIs as File.read
|
6
|
-
# @param [
|
6
|
+
# @param [Array] args args of File.read
|
7
7
|
def read(*args)
|
8
8
|
await(defer{File.read(*args)})
|
9
9
|
end
|
10
10
|
|
11
11
|
# Same APIs as File.write
|
12
|
-
# @param [
|
12
|
+
# @param [Array] args args of File.write
|
13
13
|
def write(*args)
|
14
14
|
await(defer{File.write(*args)})
|
15
15
|
end
|
data/lib/midori/extension/ohm.rb
CHANGED
@@ -4,7 +4,7 @@ safe_require 'ohm', 'gem install ohm'
|
|
4
4
|
# Midori Extension of ohm through meta programming Redic
|
5
5
|
class Redic
|
6
6
|
# Call a redis request asynchronously
|
7
|
-
# @param [
|
7
|
+
# @param [Array] args args of Redic.call
|
8
8
|
def call(*args)
|
9
9
|
await(defer{
|
10
10
|
@client.connect do
|
@@ -2,22 +2,22 @@ safe_require 'postgres-pr/message', 'gem install postgres-pr'
|
|
2
2
|
|
3
3
|
##
|
4
4
|
# Midori Extension for Postgres Driver
|
5
|
-
# @attr [
|
5
|
+
# @attr [Integer] connected Connection Status
|
6
6
|
class Midori::Postgres
|
7
7
|
attr_reader :connected
|
8
8
|
|
9
9
|
# Init a Postgres Connection
|
10
|
-
# @param [
|
10
|
+
# @param [Array] args args of EM.connect
|
11
11
|
def initialize(*args)
|
12
12
|
@connected = false
|
13
13
|
@db = EM.connect(*args, EM::P::Postgres3)
|
14
14
|
end
|
15
15
|
|
16
16
|
# Connect the Postgres server
|
17
|
-
# @param [
|
18
|
-
# @param [
|
19
|
-
# @param [
|
20
|
-
# @return [
|
17
|
+
# @param [String] db_name database name
|
18
|
+
# @param [String] username username
|
19
|
+
# @param [password] password password
|
20
|
+
# @return [nil] nil
|
21
21
|
def connect(db_name, username, password)
|
22
22
|
await(Promise.new(->(resolve, _reject) {
|
23
23
|
@db.connect(db_name, username, password).callback do |status|
|
@@ -28,8 +28,8 @@ class Midori::Postgres
|
|
28
28
|
end
|
29
29
|
|
30
30
|
# Make SQL query
|
31
|
-
# @param [
|
32
|
-
# @return [
|
31
|
+
# @param [String] sql sql query
|
32
|
+
# @return [Midori::Postgres::Result] query result
|
33
33
|
def query(sql)
|
34
34
|
await(Promise.new(->(resolve, _reject) {
|
35
35
|
begin
|
@@ -44,14 +44,14 @@ end
|
|
44
44
|
|
45
45
|
##
|
46
46
|
# Postgres Result for Midori Postgres Driver Extension
|
47
|
-
# @attr [
|
48
|
-
# @attr [
|
47
|
+
# @attr [Array] result result if success
|
48
|
+
# @attr [Array] errors exceptions met
|
49
49
|
class Midori::Postgres::Result
|
50
50
|
attr_reader :result, :errors
|
51
51
|
|
52
52
|
# Init a Postgres Result
|
53
|
-
# @param [
|
54
|
-
# @param [
|
53
|
+
# @param [Array] result result if success
|
54
|
+
# @param [Array] errors exceptions met
|
55
55
|
def initialize(result, errors)
|
56
56
|
@result = result
|
57
57
|
@errors = errors
|
@@ -5,15 +5,15 @@ safe_require 'em-hiredis', 'gem install em-hiredis'
|
|
5
5
|
class Midori::Redis
|
6
6
|
|
7
7
|
# Init a Redis Connection
|
8
|
-
# @param [
|
8
|
+
# @param [Array] args args EM::Hiredis.connect
|
9
9
|
def initialize(*args)
|
10
10
|
@connection = EM::Hiredis.connect(*args)
|
11
11
|
@connection
|
12
12
|
end
|
13
13
|
|
14
14
|
# Call a redis request asynchronously
|
15
|
-
# @param [
|
16
|
-
# @param [
|
15
|
+
# @param [String] sys method name
|
16
|
+
# @param [Array] args args of the method calling
|
17
17
|
def method_missing(sys, *args)
|
18
18
|
await(Promise.new(->(resolve, _reject) {
|
19
19
|
@connection.send(sys, *args).callback do |*ret_args|
|
@@ -23,7 +23,7 @@ class Midori::Redis
|
|
23
23
|
end
|
24
24
|
|
25
25
|
# Return raw pubsub mode
|
26
|
-
# @return [
|
26
|
+
# @return [EM::Hiredis::Pubsub] raw pubsub
|
27
27
|
def pubsub
|
28
28
|
@connection.pubsub
|
29
29
|
end
|
@@ -5,9 +5,9 @@ require 'sequel/adapters/postgres'
|
|
5
5
|
# Midori Extension of sequel postgres through meta programming
|
6
6
|
class Sequel::Postgres::Adapter
|
7
7
|
# Call a sql request asynchronously
|
8
|
-
# @param [
|
9
|
-
# @param [
|
10
|
-
# @return [
|
8
|
+
# @param [String] sql sql request
|
9
|
+
# @param [Array] args args to send
|
10
|
+
# @return [Array] sql query result
|
11
11
|
def execute_query(sql, args)
|
12
12
|
@db.log_connection_yield(sql, self, args) do
|
13
13
|
args ? await(defer{async_exec(sql, args)}) : await(defer{async_exec(sql)})
|
data/lib/midori/middleware.rb
CHANGED
@@ -21,7 +21,7 @@ class Midori::Middleware
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Dynamically generate a method to use inside router
|
24
|
-
# @param [
|
24
|
+
# @param [Symbol] name name of the method
|
25
25
|
# @yield the block to run
|
26
26
|
def self.helper(name, &block)
|
27
27
|
Midori::CleanRoom.class_exec do
|
data/lib/midori/request.rb
CHANGED
data/lib/midori/runner.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
##
|
2
2
|
# Abstract runner class to control instance of Midori Server
|
3
|
-
# @attr [
|
4
|
-
# @attr [
|
3
|
+
# @attr [String] bind the address to bind
|
4
|
+
# @attr [Integer] port the port to bind
|
5
5
|
class Midori::Runner
|
6
6
|
attr_reader :bind, :port
|
7
7
|
|
8
8
|
# Define status of a runner
|
9
|
-
# @param [
|
10
|
-
# @param [
|
9
|
+
# @param [Class] api inherited from [Midori::API]
|
10
|
+
# @param [Class] configure inherited from [Midori::Configure]
|
11
11
|
def initialize(api, configure = Midori::Configure)
|
12
12
|
@logger = configure.logger
|
13
13
|
@bind = configure.bind
|
data/lib/midori/sandbox.rb
CHANGED
@@ -9,17 +9,17 @@ class Midori::Sandbox
|
|
9
9
|
end
|
10
10
|
|
11
11
|
# Add a rule to Sandbox
|
12
|
-
# @param [
|
13
|
-
# @param [
|
14
|
-
# @return [
|
12
|
+
# @param [Class] class_name the class to capture
|
13
|
+
# @param [Proc] block what to do when captured
|
14
|
+
# @return [nil] nil
|
15
15
|
def add_rule(class_name, block)
|
16
16
|
@handlers[class_name] = block
|
17
17
|
nil
|
18
18
|
end
|
19
19
|
|
20
20
|
# Detect what to run with given error
|
21
|
-
# @param [
|
22
|
-
# @return [
|
21
|
+
# @param [StandardError] error the error captured
|
22
|
+
# @return [nil] nil
|
23
23
|
def capture(error)
|
24
24
|
if @handlers[error.class].nil?
|
25
25
|
@handlers[Midori::Exception::InternalError].call(error)
|
@@ -29,9 +29,9 @@ class Midori::Sandbox
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# Run sandbox inside given clean room
|
32
|
-
# @param [
|
33
|
-
# @param [
|
34
|
-
# @return [
|
32
|
+
# @param [Midori::CleanRoom] clean_room Clean room to run
|
33
|
+
# @param [Proc] function the block to run
|
34
|
+
# @return [nil] nil
|
35
35
|
def run(clean_room, function, *args)
|
36
36
|
begin
|
37
37
|
function.to_lambda(clean_room).call(*args)
|
data/lib/midori/server.rb
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
# Logic to EventMachine TCP Server, running inside +EM::Connection+
|
3
3
|
module Midori::Server
|
4
4
|
# @!attribute request
|
5
|
-
# @return [
|
5
|
+
# @return [Midori::Request] raw request
|
6
6
|
# @!attribute api
|
7
|
-
# @return [
|
7
|
+
# @return [Class] inherited from Midori::API
|
8
8
|
# @!attribute websocket
|
9
|
-
# @return [
|
9
|
+
# @return [Midori::WebSocket] defined websocket instance
|
10
10
|
# @!attribute eventsource
|
11
|
-
# @return [
|
11
|
+
# @return [Midori::EventSource] defined eventsource instance
|
12
12
|
attr_accessor :request, :api, :websocket, :eventsource
|
13
13
|
|
14
14
|
# Define server behaviour
|
data/lib/midori/version.rb
CHANGED
data/lib/midori/websocket.rb
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
##
|
2
2
|
# This class provides methods for WebSocket connection instance.
|
3
|
-
# @attr [
|
4
|
-
# @attr [
|
5
|
-
# @attr [
|
6
|
-
# @attr [
|
7
|
-
# @attr [
|
3
|
+
# @attr [Array<Integer>, String] msg message send from client
|
4
|
+
# @attr [Integer] opcode operation code of WebSocket
|
5
|
+
# @attr [Hash] events response for different event
|
6
|
+
# @attr [EM::Connection] connection raw EventMachine connection
|
7
|
+
# @attr [Midori::Request] request raw request
|
8
8
|
class Midori::WebSocket
|
9
9
|
attr_accessor :msg, :opcode, :events, :connection, :request
|
10
10
|
|
11
11
|
# Init a WebSocket instance with a connection
|
12
|
-
# @param [
|
12
|
+
# @param [EM::Connection] connection raw EventMachine connection
|
13
13
|
def initialize(connection)
|
14
14
|
@events = {}
|
15
15
|
@connection = connection
|
16
16
|
end
|
17
17
|
|
18
18
|
# Decode raw data send from client
|
19
|
-
# @param [
|
19
|
+
# @param [StringIO] data raw data
|
20
20
|
def decode(data)
|
21
21
|
# Fin and Opcode
|
22
22
|
byte_tmp = data.getbyte
|
@@ -31,7 +31,7 @@ class Midori::WebSocket
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# Decode masked message send from client
|
34
|
-
# @param [
|
34
|
+
# @param [StringIO] data raw data
|
35
35
|
def decode_mask(data)
|
36
36
|
# Mask
|
37
37
|
byte_tmp = data.getbyte
|
@@ -50,7 +50,7 @@ class Midori::WebSocket
|
|
50
50
|
end
|
51
51
|
|
52
52
|
# API definition for events
|
53
|
-
# @param [
|
53
|
+
# @param [Symbol] event event name(open, message, close, ping, pong)
|
54
54
|
# @yield what to do after event matched
|
55
55
|
# @example
|
56
56
|
# websocket '/websocket' do |ws|
|
@@ -63,7 +63,7 @@ class Midori::WebSocket
|
|
63
63
|
end
|
64
64
|
|
65
65
|
# Send data
|
66
|
-
# @param [
|
66
|
+
# @param [Array<Integer>, String] msg data to send
|
67
67
|
def send(msg)
|
68
68
|
output = []
|
69
69
|
if msg.is_a?String
|
@@ -79,20 +79,20 @@ class Midori::WebSocket
|
|
79
79
|
end
|
80
80
|
|
81
81
|
# Send a Ping request
|
82
|
-
# @param [
|
82
|
+
# @param [String] str string to send
|
83
83
|
def ping(str)
|
84
84
|
heartbeat(0b10001001, str)
|
85
85
|
end
|
86
86
|
|
87
87
|
# Send a Pong request
|
88
|
-
# @param [
|
88
|
+
# @param [String] str string to send
|
89
89
|
def pong(str)
|
90
90
|
heartbeat(0b10001010, str)
|
91
91
|
end
|
92
92
|
|
93
93
|
# Ancestor of ping pong
|
94
|
-
# @param [
|
95
|
-
# @param [
|
94
|
+
# @param [Integer] method opcode
|
95
|
+
# @param [String] str string to send
|
96
96
|
def heartbeat(method, str)
|
97
97
|
raise Midori::Exception::PingPongSizeTooLarge if str.size > 125
|
98
98
|
@connection.send_data [method, str.size, str].pack("CCA#{str.size}")
|
data/tutorial/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Introduction
|
2
|
+
|
3
|
+
## What is midori?
|
4
|
+
|
5
|
+
Midori (pronounced /miːdɒliː/) is a **Ruby web framework** for building APIs, web pages and realtime web services. Midori is designed to provide **non-blocking** I/O operations without **callback hells** on web development. The core library is focused on network I/Os, and gives a very easy DSL to pick up and converting existed projects on. On the other hand, midori also officially provides extension libraries that could deal with file, database, cache, network requests and other I/O operations without blocking.
|
6
|
+
|
7
|
+
If you want to know how midori compares to other libraries/frameworks, checkout out the [Compatison with Other Frameworks](meta/comparison_with_other_frameworks.md)
|
8
|
+
|
9
|
+
## Note
|
10
|
+
|
11
|
+
The official guide assumes intermediate level knowledge of Ruby and backend development. If you are totally new , it might not be the best idea to jump right into a framework as your first step - grasp the basics then come back! Prior experience with other frameworks like Rails helps, but is not required.
|
data/tutorial/SUMMARY.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Summary
|
2
|
+
|
3
|
+
## Essentials
|
4
|
+
|
5
|
+
* [Introduction](README.md)
|
6
|
+
* [Installation](essentials/installation.md)
|
7
|
+
* [Getting Started](essentials/getting_started.md)
|
8
|
+
* [Routing](essentials/routing.md)
|
9
|
+
* [Request Handling](essentials/request_handling.md)
|
10
|
+
* [Runner](essentials/runner.md)
|
11
|
+
* [Middlewares](essentials/middlewares.md)
|
12
|
+
* [Extensions](essentials/extensions.md)
|
13
|
+
|
14
|
+
|
15
|
+
## Advanced
|
16
|
+
|
17
|
+
- [Error Handling](advanced/error_handling.md)
|
18
|
+
- [Request-Eval-Response Loop](advanced/rerl.md)
|
19
|
+
- [Custom Extensions](advanced/custom_extensions.md)
|
20
|
+
- [Scaling Project](advanced/scaling_project.md)
|
21
|
+
- [Unit Testing](advanced/unit_testing.md)
|
22
|
+
- [Deploying for Production](advanced/deploying_for_production.md)
|
23
|
+
|
24
|
+
## Meta
|
25
|
+
|
26
|
+
- [Comparison with Other Frameworks](meta/comparison_with_other_frameworks.md)
|
27
|
+
- [Next Steps](meta/next_steps.md)
|
28
|
+
- [Join the Midori Community](meta/join_the_midori_community.md)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Getting Started
|
2
|
+
|
3
|
+
## Hello Midori
|
4
|
+
|
5
|
+
midori is a [DSL](https://en.wikipedia.org/wiki/Domain-specific_language) for web and API development in Ruby with minimal effort:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
# hello_midori.rb
|
9
|
+
require 'midori'
|
10
|
+
|
11
|
+
class HelloWorldAPI < Midori::API
|
12
|
+
get '/' do
|
13
|
+
'Ohayou Midori'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Midori::Runner.new(HelloWorldAPI).start
|
18
|
+
```
|
19
|
+
|
20
|
+
Run with
|
21
|
+
|
22
|
+
```
|
23
|
+
$ ruby hello_midori.rb
|
24
|
+
```
|
25
|
+
|
26
|
+
View at: http://127.0.0.1:8080 with your browser, if you see a page showing: **Ohayou Midori**. You've made your first application with midori.
|
27
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Installation
|
2
|
+
|
3
|
+
## Requirements
|
4
|
+
|
5
|
+
Open up a command line prompt. Any commands prefaced with a dollar sign `$` should be run in the command line. Verify that you have a current version of Ruby installed:
|
6
|
+
|
7
|
+
```
|
8
|
+
$ ruby -v
|
9
|
+
ruby 2.4.0p0
|
10
|
+
```
|
11
|
+
|
12
|
+
Generally, midori supports the following ruby interpreters:
|
13
|
+
|
14
|
+
- Ruby (MRI) **>= 2.1.0**
|
15
|
+
- JRuby >= **9.0.4.0**
|
16
|
+
|
17
|
+
For every version released, it would be tested and **officially ensured** in the following environments:
|
18
|
+
|
19
|
+
- Ruby
|
20
|
+
- 2.1.0
|
21
|
+
- 2.2.6
|
22
|
+
- 2.3.3
|
23
|
+
- 2.4.0
|
24
|
+
- JRuby
|
25
|
+
- 9.0.4.0, OpenJDK 7
|
26
|
+
- 9.0.4.0, OracleJDK 7
|
27
|
+
- 9.0.4.0, OracleJDK 8
|
28
|
+
|
29
|
+
**Note: **
|
30
|
+
|
31
|
+
- **For JRuby users, you may meet performance problem due to the issues of [Fiber implementation](https://github.com/jruby/jruby/wiki/DifferencesBetweenMriAndJruby#continuations-and-fibers) with JVM, which may [possibly improved](https://github.com/jruby/jruby/wiki/PerformanceTuning#enable-coroutine-based-fibers) in the future JRuby versions with JDK 9.**
|
32
|
+
- **For macOS users, you may meet performance problem due to the issues of [EventMachine](https://github.com/heckpsi-lab/em-midori/issues/15). Very few people would use macOS in production, so this issue may not affect much, but we are still working hard on fixing it.**
|
33
|
+
|
34
|
+
It's hard to say that if it is possible for running on other ruby implementations like Rubinius, if you're in favor of supporting more ruby implementations, you could open a ticket [here](https://github.com/heckpsi-lab/em-midori/issues), and we are glad to discuss it.
|
35
|
+
|
36
|
+
## Install with RubyGems
|
37
|
+
|
38
|
+
```
|
39
|
+
$ gem install em-midori
|
40
|
+
Successfully installed em-midori-0.1.7
|
41
|
+
1 gem installed
|
42
|
+
```
|
43
|
+
|
44
|
+
To test whether it has installed properly, run:
|
45
|
+
|
46
|
+
```
|
47
|
+
$ ruby -r "midori" -e "class A < Midori::API;end;Midori::Runner.new(A).start"
|
48
|
+
```
|
49
|
+
|
50
|
+
If you see the following message, then everything now works fine.
|
51
|
+
|
52
|
+
```
|
53
|
+
Midori 0.1.7 is now running on 127.0.0.1:8080
|
54
|
+
```
|
55
|
+
|
56
|
+
## Use Bundler
|
57
|
+
|
58
|
+
Example `Gemfile` of basic usage as following:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
source 'https://rubygems.org'
|
62
|
+
gem 'bundler', '~> 1.0'
|
63
|
+
gem 'em-midori', '~> 0.1', require: 'midori'
|
64
|
+
```
|
65
|
+
|
66
|
+
and then running:
|
67
|
+
|
68
|
+
```
|
69
|
+
$ bundle install
|
70
|
+
```
|
71
|
+
|
72
|
+
You could use
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
require 'bundler'
|
76
|
+
Bundler.require
|
77
|
+
```
|
78
|
+
|
79
|
+
in your entrance ruby file.
|
80
|
+
|
81
|
+
To include built-in extensions of midori you could make your `Gemfile` like:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
source 'https://rubygems.org'
|
85
|
+
gem 'bundler', '~> 1.0'
|
86
|
+
gem 'em-midori', '~> 0.1', require: %w'midori midori/extension/sequel'
|
87
|
+
```
|
88
|
+
|
89
|
+
Using bunlder could make dependency management much easier, which helps a lot in scaling project. To learn more about bundler, you could see docs [here](http://bundler.io/docs.html).
|
90
|
+
|
91
|
+
## For Developers in China
|
92
|
+
|
93
|
+
You may probably meet problems with rubygems due to unstable overseas internet connection issues in China. The most popular way to solve it is to use mirror provided by [RubyChina](https://gems.ruby-china.org/) or [TUNA](https://mirror.tuna.tsinghua.edu.cn/help/rubygems/) as your gem source. This may have some side effects in development, because there's a few minutes' delay in receiving gem updates.
|
94
|
+
|
95
|
+
Alternatively, you could use proxy to connect the main repository directly to avoid the delay problem. But using proxy is a little too complex in production environment.
|
96
|
+
|
97
|
+
Choose the solution better fits your requirements. Mixing the solutions like using proxy in development and using mirror in production is also a good choice.
|
98
|
+
|
File without changes
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Request Handling
|
2
|
+
|
3
|
+
## Accessing the request object
|
4
|
+
|
5
|
+
The incoming request object can be accessed from request level (filter, routes, error handlers) through the `request` method:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class ExampleAPI < Midori::API
|
9
|
+
get '/' do
|
10
|
+
request.ip # '127.0.0.1' client ip address
|
11
|
+
request.port # '8080' client port
|
12
|
+
request.method # 'GET'
|
13
|
+
request.path # '/'
|
14
|
+
request.query_string # ''
|
15
|
+
request.header # {} request header
|
16
|
+
request.body # request body sent by the client
|
17
|
+
request.params # {} params matched in router
|
18
|
+
end
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
**Note (for sinatra users): **The `request.body` in modori is a `String` object but not a `StringIO` object.
|
23
|
+
|
24
|
+
## Construct the response object
|
25
|
+
|
26
|
+
Midori accepts the return value of the block as the response body by default.
|
27
|
+
|
28
|
+
You could edit variable `status` and `header` to construct things other than body.
|
29
|
+
|
30
|
+
You could also return a `Midori::Response` object as your response, which could override everything.
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class ExampleAPI < Midori::API
|
34
|
+
get '/case_0' do
|
35
|
+
'Hello'
|
36
|
+
# HTTP/1.1 200 OK
|
37
|
+
# Server: Midori/1.0
|
38
|
+
#
|
39
|
+
# Hello
|
40
|
+
end
|
41
|
+
|
42
|
+
get '/case_1' do
|
43
|
+
status = 418
|
44
|
+
"I\'m a teapot"
|
45
|
+
# HTTP/1.1 418 I'm a teapot
|
46
|
+
# Server: Midori/1.0
|
47
|
+
#
|
48
|
+
# I'm a teapot
|
49
|
+
end
|
50
|
+
|
51
|
+
get '/case_2' do
|
52
|
+
header['Example-Header'] = 'Example-Value'
|
53
|
+
'Hello'
|
54
|
+
end
|
55
|
+
# HTTP/1.1 200 OK
|
56
|
+
# Server: Midori/1.0
|
57
|
+
# Example-Header: Example-Value
|
58
|
+
#
|
59
|
+
# Hello
|
60
|
+
|
61
|
+
get '/case_3' do
|
62
|
+
status = 202
|
63
|
+
header['Example-Header'] = 'Example-Value'
|
64
|
+
Midori::Response.new(200, {}, 'Hello')
|
65
|
+
end
|
66
|
+
# HTTP/1.1 200 OK
|
67
|
+
#
|
68
|
+
# Hello
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# Routing
|
2
|
+
|
3
|
+
## Basic Usage
|
4
|
+
|
5
|
+
Routes should be defined inside a class inherited from `Midori::API`. Midori doesn't support defining routes globally like sinatra to avoid scope pollution, which affects a lot in scaling project.
|
6
|
+
|
7
|
+
In midori, a route is an HTTP method with a URL-matching pattern. Each route is associated with a block:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class ExampleAPI < Midori::API
|
11
|
+
get '/' do
|
12
|
+
#.. show something ..
|
13
|
+
end
|
14
|
+
|
15
|
+
post '/' do
|
16
|
+
#.. create something ..
|
17
|
+
end
|
18
|
+
|
19
|
+
put '/' do
|
20
|
+
#.. replace something ..
|
21
|
+
end
|
22
|
+
|
23
|
+
delete '/' do
|
24
|
+
#.. annihilate something ..
|
25
|
+
end
|
26
|
+
|
27
|
+
options '/' do
|
28
|
+
#.. appease something ..
|
29
|
+
end
|
30
|
+
|
31
|
+
link '/' do
|
32
|
+
#.. affiliate something ..
|
33
|
+
end
|
34
|
+
|
35
|
+
unlink '/' do
|
36
|
+
#.. separate something ..
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
Routes are matched in the order they are defined. The first route that matches the request is invoked.
|
42
|
+
|
43
|
+
Midori not only supports the methods above, it supports almostly every method provided in RFC standards. You could look it up in [API doc](http://www.rubydoc.info/gems/em-midori/Midori/API) for more details.
|
44
|
+
|
45
|
+
## Params
|
46
|
+
|
47
|
+
Routes patterns may include named parameters, accessible via the `request.params` hash:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
class ExampleAPI < Midori::API
|
51
|
+
get '/hello/:name' do
|
52
|
+
"Ohayou #{request.params['name']}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
Route patterns may also include splat (or wildcard) parameters, accessible via the `request.params['splat']` array:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
class ExampleAPI < Midori::API
|
61
|
+
get '/say/*/to/*' do
|
62
|
+
# matches /say/hello/to/world
|
63
|
+
request.params['splat'] # => ["hello", "world"]
|
64
|
+
end
|
65
|
+
|
66
|
+
get '/download/*.*' do
|
67
|
+
# matches /download/path/to/file.xml
|
68
|
+
request.params['splat'] # => ["path/to/file", "xml"]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
Routes may also utilize query string:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
class ExampleAPI < Midori::API
|
77
|
+
get '/posts' do
|
78
|
+
# matches "GET /posts?title=foo&author=bar"
|
79
|
+
request.query_string # => title=foo&author=bar
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
## WebSocket & EventSource
|
85
|
+
|
86
|
+
`WebSocket` connection uses `GET` method in HTTP protocol, but indeed, it behaves totally different from `GET` requests. You don't need to care about the protocol details. In midori, you could easily manage websocket connections easily.
|
87
|
+
|
88
|
+
Here's a chatroom example using websocket in midori:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
CONNECTION_POOL = []
|
92
|
+
|
93
|
+
class ExampleAPI < Midori::API
|
94
|
+
websocket '/' do |ws|
|
95
|
+
ws.on :open do
|
96
|
+
ws.send 'Ohayo Midori'
|
97
|
+
CONNECTION_POOL << ws
|
98
|
+
end
|
99
|
+
|
100
|
+
ws.on :message do |msg|
|
101
|
+
CONNECTION_POOL.map do |client|
|
102
|
+
client.send msg
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
ws.on :close do
|
107
|
+
CONNECTION_POOL.delete(ws)
|
108
|
+
puts 'Oyasumi midori'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
midori also supports `EventSource ` connection as part of your route.
|
115
|
+
|
116
|
+
Here's a chatroom example using eventsource in midori:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
CONNECTION_POOL = []
|
120
|
+
|
121
|
+
class ExampleAPI < Midori::API
|
122
|
+
post '/pub' do
|
123
|
+
clients = CONNECTION_POOL.clone
|
124
|
+
CONNECTION_POOL.clear
|
125
|
+
# EventSource connection disconnects every time message sent, DO NOT reuse connection pool
|
126
|
+
clients.map do |client|
|
127
|
+
client.send request.body
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
eventsource '/sub' do |es|
|
132
|
+
CONNECTION_POOL << es
|
133
|
+
end
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-midori
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HeckPsi Lab
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
@@ -52,8 +52,8 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.6.1
|
55
|
-
description:
|
56
|
-
|
55
|
+
description: Midori is a Ruby Web Framework, providing high performance and proper
|
56
|
+
abstraction.
|
57
57
|
email: business@heckpsi.com
|
58
58
|
executables: []
|
59
59
|
extensions: []
|
@@ -91,12 +91,35 @@ files:
|
|
91
91
|
- lib/midori/server.rb
|
92
92
|
- lib/midori/version.rb
|
93
93
|
- lib/midori/websocket.rb
|
94
|
+
- tutorial/README.md
|
95
|
+
- tutorial/SUMMARY.md
|
96
|
+
- tutorial/advanced/custom_extensions.md
|
97
|
+
- tutorial/advanced/deploying_for_production.md
|
98
|
+
- tutorial/advanced/error_handling.md
|
99
|
+
- tutorial/advanced/rerl.md
|
100
|
+
- tutorial/advanced/scaling_project.md
|
101
|
+
- tutorial/advanced/unit_testing.md
|
102
|
+
- tutorial/essentials/extensions.md
|
103
|
+
- tutorial/essentials/getting_started.md
|
104
|
+
- tutorial/essentials/installation.md
|
105
|
+
- tutorial/essentials/middlewares.md
|
106
|
+
- tutorial/essentials/request_handling.md
|
107
|
+
- tutorial/essentials/routing.md
|
108
|
+
- tutorial/essentials/runner.md
|
109
|
+
- tutorial/meta/comparison_with_other_frameworks.md
|
110
|
+
- tutorial/meta/join_the_midori_community.md
|
111
|
+
- tutorial/meta/next_steps.md
|
94
112
|
homepage: https://github.com/heckpsi-lab/em-midori
|
95
113
|
licenses:
|
96
114
|
- MIT
|
97
115
|
metadata:
|
98
116
|
issue_tracker: https://github.com/heckpsi-lab/em-midori/issues
|
99
|
-
post_install_message:
|
117
|
+
post_install_message: "\n _____ _ \n| _ | | \n|
|
118
|
+
| | | |__ __ _ _ _ ___ \n| | | | '_ \\ / _` | | | |/ _ \\ \n\\ \\_/ / | |
|
119
|
+
| (_| | |_| | (_) |\n \\___/|_| |_|\\__,_|\\__, |\\___/ \n __/
|
120
|
+
| \n |___/ \n _ _ _ \n (_)
|
121
|
+
\ | | (_)\n _ __ ___ _ __| | ___ _ __ _ \n| '_ ` _ \\| |/ _` |/ _ \\|
|
122
|
+
'__| |\n| | | | | | | (_| | (_) | | | |\n|_| |_| |_|_|\\__,_|\\___/|_| |_|\n\n"
|
100
123
|
rdoc_options: []
|
101
124
|
require_paths:
|
102
125
|
- lib
|
@@ -115,5 +138,5 @@ rubyforge_project:
|
|
115
138
|
rubygems_version: 2.6.8
|
116
139
|
signing_key:
|
117
140
|
specification_version: 4
|
118
|
-
summary:
|
141
|
+
summary: High performance ruby web framework
|
119
142
|
test_files: []
|