em-midori 0.0.9.2 → 0.0.9.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6692e87f5ea4a18142263b9562cb6028ebb2ade
4
- data.tar.gz: f04594483fcfd0d6246b69eb3123ee921c909117
3
+ metadata.gz: 10e7c12fb4c851be82d6b1ebf1a5edbdfa8c3b77
4
+ data.tar.gz: 1e8c9cfe8c4cb0416f8e4f2f84ac2160e1825096
5
5
  SHA512:
6
- metadata.gz: 50c92c8b8e94100969250047ca66cde9a48aa9f66c8d7cdde79027128d617309044f628c9f94f082d4b06409c2a7027fe477af69943bca4025b5b5cdc76e0328
7
- data.tar.gz: c8af60ef0b79b5b3d06b8c8878dbcf9ef907df2a4a5b0ab95a75c1ab9d67a4d4abeeaff79d5e5af5edccccc1839af07034aecb35585359a8bc248a2cdd441e06
6
+ metadata.gz: a97cb0557f0865a4198415d9d72f3abf36b0b1929dda53b5fdcd0470c36d9ca99372da9deb3db60a510100052d36ae5e768456be6674d9ead089b4404d0f1e81
7
+ data.tar.gz: 7041791083c422d4b7929bcbb9fb97a43c37d82890333b61eba09a60fae44c355f2bd79c74c56b4dc44c0839d47e3793181f4e94b0a831a457899661218b35fd
data/lib/em-midori.rb CHANGED
@@ -2,6 +2,7 @@ require 'digest/sha1'
2
2
  require 'stringio'
3
3
  require 'eventmachine'
4
4
  require 'fiber'
5
+ require 'logger'
5
6
 
6
7
  require_relative 'em-midori/version'
7
8
  require_relative 'em-midori/string'
data/lib/em-midori/api.rb CHANGED
@@ -3,165 +3,143 @@
3
3
  class Midori::API
4
4
  class << self
5
5
  # Add GET method as a DSL for route definition
6
- # === Attributes
7
- # * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
8
- # === Returns
9
- # nil
10
- # === Examples
11
- # String as router
6
+ # @param [ String, Regexp ] path Accepts as part of path in route definition
7
+ # @yield what to run when route matched
8
+ # @return [ nil ] nil
9
+ # @example String as router
12
10
  # get '/' do
13
11
  # puts 'Hello World'
14
12
  # end
15
13
  #
16
- # Regex as router
14
+ # @example Regex as router
17
15
  # get /\/hello\/(.*?)/ do
18
16
  # puts 'Hello World'
19
17
  # end
20
18
  def get(path, &block) end
21
19
 
22
20
  # Add POST method as a DSL for route definition
23
- # === Attributes
24
- # * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
25
- # === Returns
26
- # nil
27
- # === Examples
28
- # String as router
21
+ # @param [ String, Regexp ] path Accepts as part of path in route definition
22
+ # @yield what to run when route matched
23
+ # @return [ nil ] nil
24
+ # @example String as router
29
25
  # post '/' do
30
26
  # puts 'Hello World'
31
27
  # end
32
28
  #
33
- # Regex as router
29
+ # @example Regex as router
34
30
  # post /\/hello\/(.*?)/ do
35
31
  # puts 'Hello World'
36
32
  # end
37
33
  def post(path, &block) end
38
34
 
39
35
  # Add PUT method as a DSL for route definition
40
- # === Attributes
41
- # * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
42
- # === Returns
43
- # nil
44
- # === Examples
45
- # String as router
36
+ # @param [ String, Regexp ] path Accepts as part of path in route definition
37
+ # @yield what to run when route matched
38
+ # @return [ nil ] nil
39
+ # @example String as router
46
40
  # put '/' do
47
41
  # puts 'Hello World'
48
42
  # end
49
43
  #
50
- # Regex as router
44
+ # @example Regex as router
51
45
  # put /\/hello\/(.*?)/ do
52
46
  # puts 'Hello World'
53
47
  # end
54
48
  def put(path, &block) end
55
49
 
56
50
  # Add DELETE method as a DSL for route definition
57
- # === Attributes
58
- # * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
59
- # === Returns
60
- # nil
61
- # === Examples
62
- # String as router
51
+ # @param [ String, Regexp ] path Accepts as part of path in route definition
52
+ # @yield what to run when route matched
53
+ # @return [ nil ] nil
54
+ # @example String as router
63
55
  # delete '/' do
64
56
  # puts 'Hello World'
65
57
  # end
66
58
  #
67
- # Regex as router
59
+ # @example Regex as router
68
60
  # delete /\/hello\/(.*?)/ do
69
61
  # puts 'Hello World'
70
62
  # end
71
63
  def delete(path, &block) end
72
64
 
73
65
  # Add OPTIONS method as a DSL for route definition
74
- # === Attributes
75
- # * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
76
- # === Returns
77
- # nil
78
- # === Examples
79
- # String as router
66
+ # @param [ String, Regexp ] path Accepts as part of path in route definition
67
+ # @return [ nil ] nil
68
+ # @example String as router
80
69
  # options '/' do
81
70
  # puts 'Hello World'
82
71
  # end
83
72
  #
84
- # Regex as router
73
+ # @example Regex as router
85
74
  # options /\/hello\/(.*?)/ do
86
75
  # puts 'Hello World'
87
76
  # end
88
77
  def options(path, &block) end
89
78
 
90
79
  # Add LINK method as a DSL for route definition
91
- # === Attributes
92
- # * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
93
- # === Returns
94
- # nil
95
- # === Examples
96
- # String as router
80
+ # @param [ String, Regexp ] path Accepts as part of path in route definition
81
+ # @yield what to run when route matched
82
+ # @return [ nil ] nil
83
+ # @example String as router
97
84
  # link '/' do
98
85
  # puts 'Hello World'
99
86
  # end
100
87
  #
101
- # Regex as router
88
+ # @example Regex as router
102
89
  # link /\/hello\/(.*?)/ do
103
90
  # puts 'Hello World'
104
91
  # end
105
92
  def link(path, &block) end
106
93
 
107
94
  # Add UNLINK method as a DSL for route definition
108
- # === Attributes
109
- # * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
110
- # === Returns
111
- # nil
112
- # === Examples
113
- # String as router
95
+ # @param [ String, Regexp ] path Accepts as part of path in route definition
96
+ # @yield what to run when route matched
97
+ # @return [ nil ] nil
98
+ # @example String as router
114
99
  # unlink '/' do
115
100
  # puts 'Hello World'
116
101
  # end
117
102
  #
118
- # Regex as router
103
+ # @example Regex as router
119
104
  # unlink /\/hello\/(.*?)/ do
120
105
  # puts 'Hello World'
121
106
  # end
122
107
  def unlink(path, &block) end
123
108
 
124
109
  # Add WEBSOCKET method as a DSL for route definition
125
- # === Attributes
126
- # * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
127
- # === Returns
128
- # nil
129
- # === Examples
130
- # String as router
131
- # unlink '/' do
110
+ # @param [ String, Regexp ] path Accepts as part of path in route definition
111
+ # @yield what to run when route matched
112
+ # @return [ nil ] nil
113
+ # @example String as router
114
+ # websocket '/' do
132
115
  # puts 'Hello World'
133
116
  # end
134
117
  #
135
- # Regex as router
136
- # unlink /\/hello\/(.*?)/ do
118
+ # @example Regex as router
119
+ # websocket /\/hello\/(.*?)/ do
137
120
  # puts 'Hello World'
138
121
  # end
139
122
  def websocket(path, &block) end
140
123
 
141
124
  # Add EVENTSOURCE method as a DSL for route definition
142
- # === Attributes
143
- # * +path+ [+String+, +Regexp+] - Accepts as part of path in route definition.
144
- # === Returns
145
- # nil
146
- # === Examples
147
- # String as router
148
- # unlink '/' do
125
+ # @param [ String, Regexp ] path Accepts as part of path in route definition
126
+ # @return [ nil ] nil
127
+ # @example String as router
128
+ # eventsource '/' do
149
129
  # puts 'Hello World'
150
130
  # end
151
131
  #
152
- # Regex as router
153
- # unlink /\/hello\/(.*?)/ do
132
+ # @example Regex as router
133
+ # eventsource /\/hello\/(.*?)/ do
154
134
  # puts 'Hello World'
155
135
  # end
156
136
  def eventsource(path, &block) end
157
137
 
158
138
  # Implementation of route DSL
159
- # === Attributes
160
- # * +method+ [+String+] - HTTP method
161
- # * +path+ [+String+, +Regexp+] - path definition
162
- # * +block+ [+Proc+] - process to run when route matched
163
- # === Returns
164
- # nil
139
+ # @param [ String ] method HTTP method
140
+ # @param [ String, Regexp ] path path definition
141
+ # @param [ Proc ] block process to run when route matched
142
+ # @return [ nil ] nil
165
143
  def add_route(method, path, block)
166
144
  if path.class == String
167
145
  # Convert String to Regexp to provide performance boost (Precompiled Regexp)
@@ -173,10 +151,11 @@ class Midori::API
173
151
  end
174
152
 
175
153
  # Process after receive data from client
176
- # === Attributes
177
- # * +request+ [+Midori::Request+] - Http Raw Request
178
- # === Returns
179
- # [+Midori::Response+] - Http response
154
+ # @param request [ Midori::Request ] Http Raw Request
155
+ # @param connection [ EM::Connection ] A connection created by EventMachine
156
+ # @yield what to run when route matched
157
+ # @return [ Midori::Response ] Http Response
158
+ # @raise [ Midori::Error::NotFound ] If no route matched
180
159
  def receive(request, connection = nil)
181
160
  @route.each do |route|
182
161
  matched = match(route.method, route.path, request.method, request.path)
@@ -188,8 +167,6 @@ class Midori::API
188
167
  end
189
168
  middlewares.each { |middleware| request = middleware.before(request) }
190
169
  clean_room = Midori::CleanRoom.new(request, middlewares, body_accept)
191
- @helpers ||= []
192
- @helpers.map { |block| clean_room.instance_exec(&block) }
193
170
  if request.websocket?
194
171
  # Send 101 Switching Protocol
195
172
  connection.send_data Midori::Response.new(101, websocket_header(request.header['Sec-WebSocket-Key']), '')
@@ -211,15 +188,13 @@ class Midori::API
211
188
  end
212
189
 
213
190
  # Match route with given definition
214
- # === Attributes
215
- # * +method+ [+String+] - Accepts an HTTP/1.1 method like GET POST PUT ...
216
- # * +path+ [+Regexp+] - Precompiled route definition.
217
- # * +request+ [+String+] - HTTP Request String
218
- # === Returns
219
- # if not matched returns false
220
- #
221
- # else returns an array of parameter string matched
222
- # === Examples
191
+ # @param [ String ] method Accepts an HTTP/1.1 method like GET POST PUT ...
192
+ # @param [ Regexp ] path Precompiled route definition.
193
+ # @param [ String ] request_method HTTP Request Method
194
+ # @param [ String ] request_path HTTP Request Path
195
+ # @return [ Array ] matched parameters
196
+ # @return [ Boolean ] false if not matched
197
+ # @example match a route
223
198
  # match('GET', /^\/user\/(.*?)\/order\/(.*?)$/, '/user/foo/order/bar') # => ['foo', 'bar']
224
199
  def match(method, path, request_method, request_path)
225
200
  if request_method == method
@@ -232,11 +207,9 @@ class Midori::API
232
207
  end
233
208
 
234
209
  # Convert String path to its Regexp equivalent
235
- # === Attributes
236
- # * +path+ [+String+] - String route definition
237
- # === Returns
238
- # Regexp equivalent
239
- # === Examples
210
+ # @param [ String ] path String route definition
211
+ # @return [ Regexp ] Regexp equivalent
212
+ # @example
240
213
  # convert_route('/user/:id/order/:order_id') # => Regexp
241
214
  def convert_route(path)
242
215
  path = '^' + path
@@ -246,13 +219,21 @@ class Midori::API
246
219
  Regexp.new path
247
220
  end
248
221
 
222
+ # Use a middleware in the all routes
223
+ # @param [Class] middleware Inherited from +Midori::Middleware+
224
+ # @return [nil] nil
249
225
  def use(middleware, *args)
250
226
  middleware = middleware.new(*args)
227
+ CleanRoom.class_exec { middleware.helper }
251
228
  @middleware = [] if @middleware.nil?
252
229
  @middleware << middleware
253
230
  @body_accept = middleware.body_accept
231
+ nil
254
232
  end
255
233
 
234
+ # Return websocket header with given key
235
+ # @param [String] key 'Sec-WebSocket-Key' in request header
236
+ # @return [Hash] header
256
237
  def websocket_header(key)
257
238
  {
258
239
  'Upgrade' => 'websocket',
@@ -261,15 +242,17 @@ class Midori::API
261
242
  }
262
243
  end
263
244
 
245
+ # Helper block for defining methods in APIs
246
+ # @yield define what to run in CleanRoom
264
247
  def helper(&block)
265
- @helpers = [] if @helpers.nil?
266
- @helpers << block
248
+ Midori::CleanRoom.class_exec(&block)
267
249
  end
268
250
  end
269
251
 
270
252
  private_class_method :add_route
271
253
 
272
- METHODS = %w(get post put delete options link unlink websocket eventsource).freeze # :nodoc:
254
+ # Constants of supported methods in route definition
255
+ METHODS = %w(get post put delete options link unlink websocket eventsource).freeze
273
256
 
274
257
  # Magics to fill DSL methods through dynamically class method definition
275
258
  METHODS.each do |method|
@@ -1,5 +1,14 @@
1
+ ##
2
+ # This class is used to be sandbox of requests processing.
3
+ # @attr [Fixnum] code HTTP response code
4
+ # @attr [Hash] header HTTP response header
5
+ # @attr [Object] body HTTP response body. String could is accepted by default, but could leave for further process with +Midori::Midlleware+
6
+ # @attr [Midori::Request] request HTTP request
1
7
  class Midori::CleanRoom
2
8
  attr_accessor :code, :header, :body, :request
9
+ # @param [Midori::Request] request HTTP request
10
+ # @param [Array<Midori::Middleware>] middleware middlewares to run
11
+ # @param [Array<Class>] body_accept what class for body could last middleware accept by default
3
12
  def initialize(request, middleware = [], body_accept = [String])
4
13
  @status = 200
5
14
  @header = Midori::Const::DEFAULT_HEADER.clone
@@ -9,15 +18,23 @@ class Midori::CleanRoom
9
18
  @body_accept = body_accept
10
19
  end
11
20
 
21
+ # Genenrate response from variables inside +Midori::CleanRoom+
22
+ # @return [Midori::Response] midori response
12
23
  def raw_response
13
24
  Midori::Response.new(@status, @header, @body)
14
25
  end
15
26
 
27
+ # Add a middleware to a specific route
28
+ # @param [Class] middleware inherited form +Midori::Middleware+ class
29
+ # @param [Array<Object>] args for middleware initialize
30
+ # @return [nil] nil
16
31
  def use(middleware, *args)
17
32
  middleware = middleware.new(*args)
33
+ middleware.helper
18
34
  @middleware = [] if @middleware.nil?
19
35
  @middleware << middleware
20
36
  @body_accept.replace middleware.body_accept
21
37
  @request = middleware.before(request)
38
+ nil
22
39
  end
23
40
  end
@@ -1,4 +1,7 @@
1
+ ##
2
+ # Module for store Midori Consts
1
3
  module Midori::Const
4
+ # Hash table for converting numbers to HTTP/1.1 status code line
2
5
  STATUS_CODE = {
3
6
  100 => '100 Continue',
4
7
  101 => '101 Switching Protocols',
@@ -32,6 +35,7 @@ module Midori::Const
32
35
  415 => '415 Unsupported Media Type',
33
36
  416 => '416 Requested range not satisfiable',
34
37
  417 => '417 Expectation Failed',
38
+ 451 => '451 ',
35
39
  500 => '500 Internal Server Error',
36
40
  501 => '501 Not Implemented',
37
41
  502 => '502 Bad Gateway',
@@ -42,10 +46,12 @@ module Midori::Const
42
46
  STATUS_CODE.default = '500 Internal Server Error'
43
47
  STATUS_CODE.freeze
44
48
 
49
+ # Default header for Basic HTTP response
45
50
  DEFAULT_HEADER = {
46
51
  'Server' => "Midori/#{Midori::VERSION}"
47
52
  }
48
53
 
54
+ # Default header for Evenrsource response
49
55
  EVENTSOURCE_HEADER = {
50
56
  'Content-Type' => 'text-event-stream',
51
57
  'Cache-Control' => 'no-cache',
@@ -1,16 +1,29 @@
1
- module Kernel #:nodoc:
1
+ ##
2
+ # Meta-programming Kernel for Syntactic Sugars
3
+ module Kernel
2
4
  # This method is implemented to dynamically generate class with given name and template.
3
5
  # Referenced from {Ruby China}[https://ruby-china.org/topics/17382]
6
+ # @param [String] name name of class
7
+ # @param [Class] ancestor class to be inherited
8
+ # @yield inner block to be inserted into class
9
+ # @return [Class] the class defined
4
10
  def define_class(name, ancestor = Object)
5
11
  Object.const_set(name, Class.new(ancestor))
6
12
  Object.const_get(name).class_eval(&Proc.new) if block_given?
7
13
  Object.const_get(name)
8
14
  end
9
15
 
16
+ # Define a batch of error handler with given name
17
+ # @param [Array<Symbol>] args names to be defined
18
+ # @return [nil] nil
19
+ # @example
20
+ # define_error(:foo_error, :bar_error)
21
+ # => nil, FooError < StandardError and BarError < StandardError would be defined
10
22
  def define_error(*args)
11
23
  args.each do |arg|
12
24
  class_name = arg.to_s.split('_').collect(&:capitalize).join
13
25
  define_class(class_name, StandardError)
14
26
  end
27
+ nil
15
28
  end
16
29
  end
@@ -1,18 +1,27 @@
1
- require 'logger'
2
-
1
+ ##
2
+ # The main module of Midori
3
3
  module Midori
4
- @logger = ::Logger.new(StringIO.new)
5
-
6
- def self.run(api = Midori::API, ip = nil, port = nil, logger = ::Logger.new(STDOUT))
7
- ip ||= '127.0.0.1'
8
- port ||= 8081
4
+ @logger = ::Logger.new(STDOUT)
5
+ # Start Midori Server instance
6
+ # @note This is an async method, but no callback
7
+ # @param [Class] api Inherit from +Midori::API+
8
+ # @param [String] ip The ip address to bind
9
+ # @param [Fixnum] port Port number
10
+ # @param [Logger] logger Ruby logger
11
+ # @return [nil] nil
12
+ def self.run(api = Midori::API, ip = '127.0.0.1', port = 8081, logger = ::Logger.new(STDOUT))
9
13
  @logger = logger
10
14
  EventMachine.run do
11
15
  @logger.info "Midori #{Midori::VERSION} is now running on #{ip}:#{port}".blue
12
16
  @midori_server = EventMachine.start_server ip, port, Midori::Server, api, logger
13
17
  end
18
+ nil
14
19
  end
15
20
 
21
+ # Stop Midori Server instance
22
+ # @note This is an async method, but no callback
23
+ # @return [Boolean] [true] stop successfully
24
+ # @return [Boolean] [false] nothing to stop
16
25
  def self.stop
17
26
  if @midori_server.nil?
18
27
  @logger.error 'Midori Server has NOT been started'.red
@@ -1,10 +1,20 @@
1
+ ##
2
+ # This module store errors to be handled in Midori
1
3
  module Midori::Error
4
+ # No route matched
2
5
  class NotFound < StandardError; end
6
+ # Midori doesn't support continuous frame of WebSockets yet
3
7
  class ContinuousFrame < StandardError; end
8
+ # WebSocket OpCode not defined in RFC standards
4
9
  class OpCodeError < StandardError; end
10
+ # Websocket request not masked
5
11
  class NotMasked < StandardError; end
12
+ # Websocket frame has ended
6
13
  class FrameEnd < StandardError; end
14
+ # Websocket Ping Pong size too large
7
15
  class PingPongSizeTooLarge < StandardError; end
16
+ # Not sending String in EventSource
8
17
  class EventSourceTypeError < StandardError; end
18
+ # Insert a not middleware class to middleware list
9
19
  class MiddlewareError < StandardError; end
10
20
  end
@@ -1,10 +1,16 @@
1
+ ##
2
+ # This class provides methods for EventSource connection instance.
3
+ # @attr [EM::Connection] connection the connection instance of EventMachine
1
4
  class Midori::EventSource
2
5
  attr_accessor :connection
3
6
 
7
+ # @param [EM::Connection] connection the connection instance of EventMachine
4
8
  def initialize(connection)
5
9
  @connection = connection
6
10
  end
7
11
 
12
+ # Send data and close the connection
13
+ # @param [String] data data to be sent
8
14
  def send(data)
9
15
  raise Midori::Error::EventSourceTypeError unless data.is_a?String
10
16
  @connection.send_data(data.split("\n").map {|str| "data: #{str}\n"}.join + "\n")
@@ -1,15 +1,32 @@
1
+ ##
2
+ # Ancestor of all middlewares
1
3
  class Midori::Middleware
4
+ # Init a middleware
2
5
  def initialize
3
6
  end
4
7
 
8
+ # run before processing a request
9
+ # @param [Midori::Request] request raw request
10
+ # @return [Midori::Request] request to be further processed
5
11
  def before(request)
6
12
  request
7
13
  end
8
14
 
15
+ # run after processing a request
16
+ # @param [Midori::Request] _request raw request
17
+ # @param [Midori::Response] response raw response
18
+ # @return [Midori::Response] response to be further processed
9
19
  def after(_request, response)
10
20
  response
11
21
  end
12
22
 
23
+ # code to be inserted inside CleanRoom
24
+ # @return [nil] nil
25
+ def helper
26
+ end
27
+
28
+ # Acceptable body
29
+ # @return [Array<Class>] array of acceptable type's class
13
30
  def body_accept
14
31
  [String]
15
32
  end
@@ -1,30 +1,52 @@
1
+ ##
2
+ # Meta-programming String for Syntactic Sugars
1
3
  # Referenced from {Qiita}[http://qiita.com/south37/items/99a60345b22ef395d424]
2
4
  class Promise
5
+ # @param [Proc] callback an async method
3
6
  def initialize(callback)
4
7
  @callback = callback
5
8
  end
6
9
 
10
+ # Define what to do after a method callbacked
11
+ # @param [Proc] resolve what if callbacked
12
+ # @param [Proc] reject what if callback failed
13
+ # @return [nil] nil
7
14
  def then(resolve = ->() {}, reject = ->() {})
8
15
  @callback.call(resolve, reject)
9
16
  end
10
17
  end
11
18
 
12
- def async_internal(fiber)
13
- chain = ->(result) {
14
- return if result.class != Promise
15
- result.then(->(val) {
16
- chain.call(fiber.resume(val))
17
- })
18
- }
19
- chain.call(fiber.resume)
20
- end
19
+ module Kernel
20
+ # Logic dealing of async method
21
+ # @param [Fiber] fiber a fiber to call
22
+ def async_internal(fiber)
23
+ chain = lambda do |result|
24
+ return if result.class != Promise
25
+ result.then(lambda do |val|
26
+ chain.call(fiber.resume(val))
27
+ end)
28
+ end
29
+ chain.call(fiber.resume)
30
+ end
21
31
 
22
- def async(method_name, &block)
23
- define_singleton_method method_name, ->(*args) {
24
- async_internal(Fiber.new {block.call(*args)})
25
- }
26
- end
32
+ # Define an async method
33
+ # @param [Symbol] method_name method name
34
+ # @yield async method
35
+ # @example
36
+ # async :hello do
37
+ # puts 'Hello'
38
+ # end
39
+ def async(method_name)
40
+ define_singleton_method method_name, ->(*args) {
41
+ async_internal(Fiber.new { yield(*args) })
42
+ }
43
+ end
27
44
 
28
- def await(promise)
29
- Fiber.yield promise
45
+ # Block the I/O to wait for async method response
46
+ # @param [Promise] promise promise method
47
+ # @example
48
+ # result = await SQL.query('SELECT * FROM hello')
49
+ def await(promise)
50
+ Fiber.yield promise
51
+ end
30
52
  end
@@ -1,8 +1,19 @@
1
+ ##
2
+ # Request class for midori
3
+ # @attr [String] ip client ip address
4
+ # @attr [Fixnum] port client port
5
+ # @attr [String] protocol protocol version of HTTP request
6
+ # @attr [String] path request path
7
+ # @attr [String] query_string request query string
8
+ # @attr [Hash] header request header
9
+ # @attr [String] body request body
10
+ # @attr [Boolean] parsed whether the request parsed
1
11
  class Midori::Request
2
12
  attr_accessor :ip, :port,
3
13
  :protocol, :method, :path, :query_string,
4
14
  :header, :body, :parsed
5
15
 
16
+ # Init Request
6
17
  def initialize
7
18
  @parsed = false
8
19
  @is_websocket = false
@@ -10,8 +21,7 @@ class Midori::Request
10
21
  end
11
22
 
12
23
  # Init an request with StringIO data
13
- # === Attributes
14
- # * +data+ [+StringIO+] - Request data
24
+ # @param [StringIO+] data Request data
15
25
  def parse(data)
16
26
  @header = {}
17
27
 
@@ -46,14 +56,20 @@ class Midori::Request
46
56
  @parsed = true
47
57
  end
48
58
 
59
+ # Syntatic sugur for whether a request is parsed
60
+ # @return [Boolean] parsed or not
49
61
  def parsed?
50
62
  @parsed
51
63
  end
52
64
 
65
+ # Syntatic sugur for whether a request is a websocket request
66
+ # @return [Boolean] websocket or not
53
67
  def websocket?
54
68
  @is_websocket
55
69
  end
56
70
 
71
+ # Syntatic sugur for whether a request is an eventsource request
72
+ # @return [Boolean] eventsource or not
57
73
  def eventsource?
58
74
  @is_eventsource
59
75
  end
@@ -1,18 +1,30 @@
1
+ ##
2
+ # Class for midori response
3
+ # @attr [String] HTTP response status
4
+ # @attr [Hash] HTTP response header
5
+ # @attr [String] HTTP response body
1
6
  class Midori::Response
2
7
  attr_accessor :status, :header, :body
3
8
 
9
+ # @param [Fixnum] code HTTP response code
10
+ # @param [Hash] header HTTP response header
11
+ # @param [String] body HTTP response body
4
12
  def initialize(code = 200, header = Midori::Const::DEFAULT_HEADER.clone, body = '')
5
13
  @status = Midori::Const::STATUS_CODE[code]
6
14
  @header = header
7
15
  @body = body
8
16
  end
9
17
 
18
+ # Generate header string from hash
19
+ # @return [String] generated string
10
20
  def generate_header
11
21
  @header.map do |key, value|
12
22
  "#{key}: #{value}\r\n"
13
23
  end.join
14
24
  end
15
25
 
26
+ # Convert response to raw string
27
+ # @return [String] generated string
16
28
  def to_s
17
29
  "HTTP/1.1 #{@status}\r\n#{generate_header}\r\n#{@body}"
18
30
  end
@@ -1,5 +1,14 @@
1
+ ##
2
+ # Class for Midori route
3
+ # @attr [String] method HTTP method
4
+ # @attr [Regexp] path regex to match
5
+ # @attr [Proc] function what to do after matched
1
6
  class Midori::Route
2
7
  attr_accessor :method, :path, :function
8
+
9
+ # @param [String] method HTTP method
10
+ # @param [Regexp] path regex to match
11
+ # @param [Proc] function what to do after matched
3
12
  def initialize(method, path, function)
4
13
  @method = method
5
14
  @path = path
@@ -1,6 +1,14 @@
1
+ ##
2
+ # Logics to EventMachine TCP Server, running inside +EM::Connection+
3
+ # @attr [Midori::Request] request
4
+ # @attr [Class] api inherited from Midori::API
5
+ # @attr [Midori::WebSocket] websocket websocket instance
6
+ # @attr [Midori::EventSource] eventsource eventsource instance
1
7
  module Midori::Server
2
8
  attr_accessor :request, :api, :websocket, :eventsource
3
9
 
10
+ # @param [Class] api inherited from Midori::API
11
+ # @param [Logger] logger global logger
4
12
  def initialize(api, logger)
5
13
  @api = api
6
14
  @logger = logger
@@ -9,6 +17,8 @@ module Midori::Server
9
17
  @eventsource = Midori::EventSource.new(self)
10
18
  end
11
19
 
20
+ # Logics of receiving data
21
+ # @param [String] data raw data
12
22
  def receive_data(data)
13
23
  ->() { async_internal(Fiber.new do
14
24
  start_time = Time.now
@@ -26,6 +36,8 @@ module Midori::Server
26
36
  end) }.call
27
37
  end
28
38
 
39
+ # Logics of receiving new request
40
+ # @param [String] data raw data
29
41
  def receive_new_request(data)
30
42
  begin
31
43
  @request.parse(data)
@@ -44,6 +56,8 @@ module Midori::Server
44
56
  end
45
57
  end
46
58
 
59
+ # Logics of receiving WebSocket request
60
+ # @param [String] data raw data
47
61
  def websocket_request(data)
48
62
  @websocket.decode(data)
49
63
  case @websocket.opcode
@@ -69,6 +83,9 @@ module Midori::Server
69
83
  close_connection_after_writing
70
84
  end
71
85
 
86
+ # To call a websocket event if it exist
87
+ # @param [Symbol] event event name
88
+ # @param [Array] args arg list
72
89
  def call_event(event, args = [])
73
90
  (-> { @websocket.instance_exec(*args, &@websocket.events[event]) }.call) unless @websocket.events[event].nil?
74
91
  end
@@ -1,20 +1,28 @@
1
+ ##
2
+ # Meta-programming String for Syntactic Sugars
1
3
  class String
4
+ # @param [Fixnum] color_code ANSI color code
5
+ # @return [String] colored string
2
6
  def colorize(color_code)
3
7
  "\e[#{color_code}m#{self}\e[0m"
4
8
  end
5
9
 
10
+ # color the string with red color
6
11
  def red
7
12
  colorize(31)
8
13
  end
9
14
 
15
+ # color the string with green color
10
16
  def green
11
17
  colorize(32)
12
18
  end
13
-
19
+
20
+ # color the string with yellow color
14
21
  def yellow
15
22
  colorize(33)
16
23
  end
17
24
 
25
+ # color the string with blue color
18
26
  def blue
19
27
  colorize(34)
20
28
  end
@@ -1,3 +1,4 @@
1
1
  module Midori
2
- VERSION = '0.0.9.2'.freeze
2
+ # Current Version Code
3
+ VERSION = '0.0.9.3'.freeze
3
4
  end
@@ -1,13 +1,20 @@
1
1
  ##
2
2
  # This class provides methods for WebSocket connection instance.
3
+ # @attr [Array<Fixnum>, String] msg message send from client
4
+ # @attr [Fixnum] opcode operation code of WebSocket
5
+ # @attr [Hash] events response for different event
6
+ # @attr [EM::Connection] connection raw EventMachine connection
3
7
  class Midori::WebSocket
4
8
  attr_accessor :msg, :opcode, :events, :connection
5
9
 
10
+ # @param [EM::Connection] connection raw EventMachine connection
6
11
  def initialize(connection)
7
12
  @events = {}
8
13
  @connection = connection
9
14
  end
10
15
 
16
+ # Decode raw data send from client
17
+ # @param [String] data raw data
11
18
  def decode(data)
12
19
  # Fin and Opcode
13
20
  byte_tmp = data.getbyte
@@ -21,6 +28,8 @@ class Midori::WebSocket
21
28
  decode_mask(data)
22
29
  end
23
30
 
31
+ # Decode masked message send from client
32
+ # @param [String] data raw data
24
33
  def decode_mask(data)
25
34
  # Mask
26
35
  byte_tmp = data.getbyte
@@ -38,10 +47,21 @@ class Midori::WebSocket
38
47
  # data.bytes {|byte| puts byte.to_s(16)}
39
48
  end
40
49
 
50
+ # API definition for events
51
+ # @param [Symbol] event event name(open, message, close, ping, pong)
52
+ # @yield what to do after event matched
53
+ # @example
54
+ # websocket '/websocket' do |ws|
55
+ # ws.on :message do |msg|
56
+ # puts msg
57
+ # end
58
+ # end
41
59
  def on(event, &block) # open, message, close, ping, pong
42
60
  @events[event] = block
43
61
  end
44
62
 
63
+ # Send data
64
+ # @param [Array<Fixnum>, String] msg data to send
45
65
  def send(msg)
46
66
  output = []
47
67
  if msg.is_a?String
@@ -56,19 +76,27 @@ class Midori::WebSocket
56
76
  end
57
77
  end
58
78
 
79
+ # Send a Ping request
80
+ # @param [String] str string to send
59
81
  def ping(str)
60
82
  heartbeat(0b10001001, str)
61
83
  end
62
84
 
85
+ # Send a Pong request
86
+ # @param [String] str string to send
63
87
  def pong(str)
64
88
  heartbeat(0b10001010, str)
65
89
  end
66
90
 
91
+ # Ancestor of ping pong
92
+ # @param [Fixnum] method opcode
93
+ # @param [String] str string to send
67
94
  def heartbeat(method, str)
68
95
  raise Midori::Error::PingPongSizeTooLarge if str.size > 125
69
96
  @connection.send_data [method, str.size, str].pack("CCA#{str.size}")
70
97
  end
71
98
 
99
+ # Close a websocket connection
72
100
  def close
73
101
  raise Midori::Error::FrameEnd
74
102
  end
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.0.9.2
4
+ version: 0.0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - HeckPsi Lab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-21 00:00:00.000000000 Z
11
+ date: 2016-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eventmachine