em-midori 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '081416a49873c3fef7ed675ec2640bc3923284cc'
4
- data.tar.gz: f2ab1575e5b6664572a2167f7dbdbced1b94a45a
3
+ metadata.gz: b72c4ed56a72fbec6c4ad3476c864725ff799cf8
4
+ data.tar.gz: f18ebee6e089fd9e981519c54ac24daa14a015d0
5
5
  SHA512:
6
- metadata.gz: 128bb0d384e2eb10978a03532c68db04d8bf668260a211ce6d94a46d5970bea2fa5e9bad94a9797329843a611e657618858d988313764031c5452cbf37b17458
7
- data.tar.gz: 441c0ee3debb05818d91417533ef0a42f1ab609b3dd28a7b422ee28a9ce84b1b30208d6d20927fb8c6d6ccc90cc54b0e13bb7f881e260f9ace5f26c71d25d3de
6
+ metadata.gz: a800b68b2d413aadff9c1fa63569e28adf8194806012d2ce7d016f280ee3f28c228b4a22a44a481aa412551186887f4235f264e74811e1e78f5a47d81386d125
7
+ data.tar.gz: 1d9d3288d00d8293a18ae4baa99943d25a34adcb0a11c82a97ed3e1e63b0b014762e0a8f359069dcc65399184bbcb70cf4091011885b28eb83cada0b9049ab8a
@@ -1,22 +1,17 @@
1
1
  require 'digest/sha1'
2
2
  require 'stringio'
3
- require 'nio'
4
3
  require 'fiber'
5
4
  require 'logger'
6
5
  require 'http/parser'
7
6
  require 'mustermann'
7
+ require 'murasaki'
8
8
  require 'socket'
9
9
 
10
10
  require_relative 'midori/core_ext/configurable'
11
11
  require_relative 'midori/core_ext/string'
12
- require_relative 'midori/core_ext/event_loop'
13
- require_relative 'midori/core_ext/timer'
14
- require_relative 'midori/core_ext/promise'
15
12
  require_relative 'midori/core_ext/define_class'
16
13
  require_relative 'midori/core_ext/proc'
17
- require_relative 'midori/core_ext/safe_require'
18
14
 
19
- require_relative 'midori/version'
20
15
  require_relative 'midori/const'
21
16
  require_relative 'midori/exception'
22
17
  require_relative 'midori/env'
@@ -339,7 +339,7 @@ class Midori::API
339
339
  # @param [String, Regexp] path path definition
340
340
  # @param [Proc] block process to run when route matched
341
341
  # @return [nil] nil
342
- def add_route(method, path, block)
342
+ private def add_route(method, path, block)
343
343
  # Argument check
344
344
  raise ArgumentError unless path.is_a?String
345
345
 
@@ -380,13 +380,11 @@ class Midori::API
380
380
  end
381
381
  end
382
382
 
383
- def inherited(subclass)
383
+ private def inherited(subclass)
384
384
  subclass.class_initialize
385
385
  end
386
386
  end
387
387
 
388
- private_class_method :add_route, :inherited
389
-
390
388
  # Constants of supported methods in route definition
391
389
  METHODS = %w( delete
392
390
  get
@@ -22,7 +22,7 @@ class Midori::APIEngine
22
22
  end
23
23
 
24
24
  # Merge all routes with a Depth-first search
25
- def merge(prefix, root_api, middlewares)
25
+ private def merge(prefix, root_api, middlewares)
26
26
  root_api.routes[:MOUNT].each do |mount|
27
27
  root_api.routes.merge!(merge(mount[0], mount[1], root_api.scope_middlewares)) do |_key, old_val, new_val|
28
28
  old_val + new_val
@@ -48,7 +48,6 @@ class Midori::APIEngine
48
48
  params = route.path.params(request.path)
49
49
  next unless params # Skip if not matched
50
50
  request.params = params
51
- route.middlewares.each { |middleware| request = middleware.before(request) }
52
51
  clean_room = Midori::CleanRoom.new(request)
53
52
  if request.websocket?
54
53
  # Send 101 Switching Protocol
@@ -66,10 +65,12 @@ class Midori::APIEngine
66
65
  Midori::Sandbox.run(clean_room, route.function, connection.eventsource)
67
66
  return Midori::Response.new
68
67
  else
68
+ request = middleware_exec(route.middlewares, clean_room, request)
69
+ return request if request.is_a? Midori::Response # Early stop
69
70
  result = Midori::Sandbox.run(clean_room, route.function)
70
- clean_room.body = result unless result.nil?
71
- response = (result.is_a?Midori::Response) ? result : clean_room.raw_response
72
- route.middlewares.reverse_each { |middleware| response = middleware.after(request, response) }
71
+ clean_room.body = result
72
+ response = result.is_a?(Midori::Response) ? result : clean_room.raw_response
73
+ response = middleware_exec(route.middlewares, clean_room, request, response)
73
74
  return response
74
75
  end
75
76
  end
@@ -85,5 +86,24 @@ class Midori::APIEngine
85
86
  header
86
87
  end
87
88
 
88
- private :merge
89
+ # Exec middlewares
90
+ private def middleware_exec(middlewares, clean_room, request, response=nil)
91
+ result = response.nil? ? request : response
92
+ middlewares.each do |middleware|
93
+ if response.nil?
94
+ result = Midori::Sandbox.run(
95
+ clean_room,
96
+ proc { |req| middleware.before(req) },
97
+ result)
98
+ else
99
+ result = Midori::Sandbox.run(
100
+ clean_room,
101
+ proc { |req, resp| middleware.after(req, resp) },
102
+ request,
103
+ result)
104
+ end
105
+ return result if response.nil? && result.is_a?(Midori::Response) # Early stop
106
+ end
107
+ result
108
+ end
89
109
  end
@@ -20,13 +20,11 @@ module Configurable
20
20
  self
21
21
  end
22
22
 
23
- private
24
-
25
23
  # Dynamically defines a method on settings.
26
24
  # @param [String] name method name
27
25
  # @param [Proc] content method content
28
26
  # @return [nil] nil
29
- def define_singleton(name, content = Proc.new)
27
+ private def define_singleton(name, content = Proc.new)
30
28
  singleton_class.class_eval do
31
29
  undef_method(name) if method_defined? name
32
30
  String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
@@ -1,5 +1,5 @@
1
1
  # Midori Module
2
2
  module Midori
3
3
  # Current Version Code
4
- VERSION = '0.3.0'.freeze
4
+ VERSION = '0.4.0'.freeze
5
5
  end
@@ -6,38 +6,32 @@ Open up a command line prompt. Any commands prefaced with a dollar sign `$` sh
6
6
 
7
7
  ```
8
8
  $ ruby -v
9
- ruby 2.4.0p0
9
+ ruby 2.4.1p111
10
10
  ```
11
11
 
12
12
  Generally, midori supports the following ruby interpreters:
13
13
 
14
- - Ruby (MRI) **>= 2.1.0**
15
- - JRuby >= **9.0.4.0**
14
+ - Ruby (MRI) **>= 2.2.6**
16
15
 
17
16
  For every version released, it would be tested and **officially ensured** in the following environments:
18
17
 
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
18
+ - Ruby (MRI)
19
+ - 2.2.7
20
+ - 2.3.4
21
+ - 2.4.1
28
22
 
29
23
  **Note: **
30
24
 
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.**
25
+ - **For JRuby users, due to some C extensions used in midori, it is still unable to run on the current version of JRuby.**
26
+ - **For macOS users, you may meet performance problem due to the issue of [nio4r](https://github.com/socketry/nio4r/issues/125). Very few people would use macOS in production, so this issue may not affect much. We would still be working hard on fixing it, but the issue wouldn't be a high priority one.**
33
27
 
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.
28
+ It's hard to say that if it is possible for running on other ruby implementations like Rubinius or RubyMotion, 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
29
 
36
30
  ## Install with RubyGems
37
31
 
38
32
  ```
39
33
  $ gem install em-midori
40
- Successfully installed em-midori-0.1.12
34
+ Successfully installed em-midori-0.3.0
41
35
  1 gem installed
42
36
  ```
43
37
 
@@ -50,7 +44,7 @@ $ ruby -r "midori" -e "class A < Midori::API;end;Midori::Runner.new(A).start"
50
44
  If you see the following message, then everything now works fine.
51
45
 
52
46
  ```
53
- Midori 0.1.12 is now running on 127.0.0.1:8080
47
+ Midori 0.3.0 is now running on 127.0.0.1:8080
54
48
  ```
55
49
 
56
50
  ## Use Bundler
@@ -60,7 +54,7 @@ Example `Gemfile` of basic usage as following:
60
54
  ```ruby
61
55
  source 'https://rubygems.org'
62
56
  gem 'bundler', '~> 1.0'
63
- gem 'em-midori', '~> 0.1', require: 'midori'
57
+ gem 'em-midori', '~> 0.3', require: 'midori'
64
58
  ```
65
59
 
66
60
  and then running:
@@ -83,7 +77,7 @@ To include built-in extensions of midori you could make your `Gemfile` like:
83
77
  ```ruby
84
78
  source 'https://rubygems.org'
85
79
  gem 'bundler', '~> 1.0'
86
- gem 'em-midori', '~> 0.1', require: %w'midori midori/extension/sequel'
80
+ gem 'em-midori', '~> 0.3', require: %w'midori midori/extension/sequel'
87
81
  ```
88
82
 
89
83
  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).
@@ -49,7 +49,7 @@ class ExampleAPI < Midori::API
49
49
  end
50
50
 
51
51
  get '/case_2' do
52
- header['Example-Header'] = 'Example-Value'
52
+ @header['Example-Header'] = 'Example-Value'
53
53
  'Hello'
54
54
  # HTTP/1.1 200 OK
55
55
  # Server: Midori/1.0
@@ -60,11 +60,10 @@ class ExampleAPI < Midori::API
60
60
 
61
61
  get '/case_3' do
62
62
  @status = 202
63
- header['Example-Header'] = 'Example-Value'
63
+ @header['Example-Header'] = 'Example-Value'
64
64
  Midori::Response.new(status: 200, header: {}, body: 'Hello') # Overrides everything else
65
65
  # HTTP/1.1 200 OK
66
66
  # Server: Midori/1.0
67
- # Example-Header: Example-Value
68
67
  #
69
68
  # Hello
70
69
  end
@@ -40,7 +40,7 @@ end
40
40
 
41
41
  Routes are matched in the order they are defined. The first route that matches the request is invoked.
42
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.
43
+ Midori not only supports the methods above, it supports almost 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
44
 
45
45
  ## Params
46
46
 
@@ -111,7 +111,7 @@ class ExampleAPI < Midori::API
111
111
  end
112
112
  ```
113
113
 
114
- midori also supports `EventSource ` connection as part of your route.
114
+ midori also supports `EventSource` connection as part of your route.
115
115
 
116
116
  Here's a chatroom example using eventsource in midori:
117
117
 
@@ -0,0 +1,59 @@
1
+ # Runner
2
+
3
+ ## Introdution
4
+
5
+ `Runner` is the container of midori server. You could create, start, stop midori instance by `Runner`.
6
+
7
+ `Runner` use `Midori::Configure` as its configuration by default.
8
+
9
+ ## Examples
10
+
11
+ Here're some examples for common usages
12
+
13
+ ### Port Binding
14
+
15
+ Start midori instance with port `4567` instead of the default `8080`.
16
+
17
+ ```ruby
18
+ require 'midori'
19
+ class API < Midori::API
20
+ get '/' do
21
+ 'Hello World'
22
+ end
23
+ end
24
+
25
+ Midori::Configure.set :port, 4567
26
+ Midori::Runner.new(API).start
27
+ ```
28
+
29
+ ### Address Binding
30
+
31
+ Start midori instance listening to all IP addresses.
32
+
33
+ ```ruby
34
+ require 'midori'
35
+ class API < Midori::API
36
+ get '/' do
37
+ 'Hello World'
38
+ end
39
+ end
40
+
41
+ Midori::Configure.set :bind, '0.0.0.0'
42
+ Midori::Runner.new(API).start
43
+ ```
44
+
45
+ ### Stop Midori
46
+
47
+ Stop midori instance when specified route been called.
48
+
49
+ ```ruby
50
+ require 'midori'
51
+ $runner = nil
52
+ class API < Midori::API
53
+ get '/stop' do
54
+ $runner.stop
55
+ end
56
+ end
57
+
58
+ $runner = Midori::Runner.new(API).start
59
+ ```
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-midori
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
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-06-08 00:00:00.000000000 Z
11
+ date: 2017-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: nio4r
14
+ name: murasaki
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '0.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '0.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: mustermann
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -71,20 +71,11 @@ files:
71
71
  - lib/midori/const.rb
72
72
  - lib/midori/core_ext/configurable.rb
73
73
  - lib/midori/core_ext/define_class.rb
74
- - lib/midori/core_ext/event_loop.rb
75
74
  - lib/midori/core_ext/proc.rb
76
- - lib/midori/core_ext/promise.rb
77
- - lib/midori/core_ext/safe_require.rb
78
75
  - lib/midori/core_ext/string.rb
79
- - lib/midori/core_ext/timer.rb
80
76
  - lib/midori/env.rb
81
77
  - lib/midori/eventsource.rb
82
78
  - lib/midori/exception.rb
83
- - lib/midori/extension/file.rb
84
- - lib/midori/extension/net.rb
85
- - lib/midori/extension/redic.rb
86
- - lib/midori/extension/sequel/mysql2.rb
87
- - lib/midori/extension/sequel/postgres.rb
88
79
  - lib/midori/logger.rb
89
80
  - lib/midori/middleware.rb
90
81
  - lib/midori/request.rb
@@ -1,118 +0,0 @@
1
- ##
2
- # EventLoop Module, providing main loop for events
3
- module EventLoop
4
- class << self
5
- # Config EvnetLoop, call by default if any other methods called
6
- # @param [NIO::Selector] selector an event selector
7
- def config(selector = NIO::Selector.new)
8
- # Raw NIO Selector
9
- @selector = selector
10
- # Array of active timers
11
- @timers = []
12
- # Hash of io and its callback
13
- @ios = Hash.new
14
- # IO queue
15
- @queue = Hash.new
16
- end
17
-
18
- # Add timer in event loop
19
- # @param [EventLoop::Timer] timer timer to insert
20
- # @return [nil] nil
21
- def add_timer(timer)
22
- config if @selector.nil?
23
- timer.start_time = Time.now.to_f + timer.time
24
- @timers << timer
25
- nil
26
- end
27
-
28
- # Register I/O event with queue protection
29
- # @param [IO] io io to register
30
- # @param [Symbol] interest :r for read only, :w for write only, and :rw for both
31
- # @yield what to run when io callbacks
32
- # @return [nil] nil
33
- def register(io, interest=(:rw), &callback)
34
- config if @selector.nil?
35
- if @queue[io.to_i].nil?
36
- @queue[io.to_i] = Array.new
37
- register_raw(io, interest, callback)
38
- else
39
- @queue[io.to_i] << [io, interest, callback]
40
- end
41
- nil
42
- end
43
-
44
- # Register I/O event directly, without any queue protection
45
- # @param [IO] io io to register
46
- # @param [Symbol] interest :r for read only, :w for write only, and :rw for both
47
- # @param [Proc] callback what to run when io callbacks
48
- # @return [nil] nil
49
- def register_raw(io, interest=(:rw), callback)
50
- config if @selector.nil?
51
- @selector.register(io, interest)
52
- @ios[io] = { callback: callback }
53
- nil
54
- end
55
-
56
- # Deregister I/O event
57
- # @param [IO] io io to deregister
58
- # @return [nil] nil
59
- def deregister(io)
60
- fd = io.to_i
61
- @selector.deregister(io)
62
- @ios.delete(io)
63
- next_register = @queue[fd].shift
64
- next_register.nil? ? @queue.delete(fd) : register_raw(*next_register)
65
- nil
66
- end
67
-
68
- # Run I/O selector once
69
- # @return [nil] nil
70
- def run_once
71
- config if @selector.nil?
72
- @selector.select(0.2) do |monitor| # Timeout for 0.2 secs
73
- @ios[monitor.io][:callback].call(monitor)
74
- end
75
- timer_once
76
- nil
77
- end
78
-
79
- # Run timer once
80
- # @return [nil] nil
81
- def timer_once
82
- config if @selector.nil?
83
- now_time = Time.now.to_f
84
- @timers.delete_if do |timer|
85
- if timer.start_time < now_time
86
- timer.callback.call
87
- true
88
- end
89
- end
90
- nil
91
- end
92
-
93
- # Start the event loop
94
- # @return [nil] nil
95
- def start
96
- return if running?
97
- @stop = false
98
- until @stop
99
- run_once
100
- end
101
- @stop = nil
102
- end
103
-
104
- # Set the stop flag
105
- # @return [nil] nil
106
- def stop
107
- @stop = true
108
- nil
109
- end
110
-
111
- # Detect the stop flag
112
- # @return [Boolean] return if eventloop is set to be stopped
113
- def running?
114
- @stop = true if @stop.nil?
115
- !@stop
116
- end
117
- end
118
- end
@@ -1,70 +0,0 @@
1
- ##
2
- # Meta-programming String for Syntactic Sugars
3
- # Referenced from {Qiita}[http://qiita.com/south37/items/99a60345b22ef395d424]
4
- class Promise
5
- # Init a Promise
6
- # @param [Proc] callback an async method
7
- def initialize(&callback)
8
- @callback = callback
9
- end
10
-
11
- # Define what to do after a method callbacks
12
- # @param [Proc] resolve what on callback
13
- # @return [nil] nil
14
- def then(&resolve)
15
- @callback.call(resolve)
16
- end
17
- end
18
-
19
- module Kernel
20
- # Make fiber as async chain
21
- # @param [Fiber] fiber root of async chain
22
- def async_fiber(fiber)
23
- chain = proc do |result|
24
- next unless result.is_a? Promise
25
- result.then do |val|
26
- chain.call(fiber.resume(val))
27
- end
28
- end
29
- chain.call(fiber.resume)
30
- end
31
-
32
- # Define an async method
33
- # @param [Symbol] method method name
34
- # @yield async method
35
- # @example
36
- # async :hello do
37
- # puts 'Hello'
38
- # end
39
- def async(method)
40
- define_singleton_method method do |*args|
41
- async_fiber(Fiber.new {yield(*args)})
42
- end
43
- end
44
-
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
- result = Fiber.yield promise
51
- if result.is_a? PromiseException
52
- raise result.payload
53
- end
54
- result
55
- end
56
- end
57
-
58
-
59
- ##
60
- # Exceptions for Promises
61
- # @!attribute [r] payload
62
- # @return [Exception] raw execption
63
- class PromiseException < Exception
64
- attr_reader :payload
65
- # Init PromiseException with existed Exception
66
- # @param [Exception] payload raw execption
67
- def initialize(payload)
68
- @payload = payload
69
- end
70
- end
@@ -1,13 +0,0 @@
1
- module Kernel
2
- # Raise error if Load Failed
3
- # @param [String] file file to Load
4
- # @param [String] prompt To prompt what when load error
5
- # @return [Boolean] whether it has been required already
6
- def safe_require(file, prompt)
7
- begin
8
- require file
9
- rescue LoadError => _e
10
- raise prompt
11
- end
12
- end
13
- end
@@ -1,22 +0,0 @@
1
- ##
2
- # Timer Object in EventLoop
3
- # @!attribute [r] time
4
- # @return [Float] timeout length
5
- # @!attribute [r] callback
6
- # @return [Proc] proc to call when callbacks
7
- # @!attribute start_time
8
- # @return [Float] when timer should callbacks
9
- class EventLoop::Timer
10
-
11
- attr_reader :time, :callback
12
- attr_accessor :start_time
13
-
14
- # Init a timer with a time period and callback
15
- # @param [Float] time timeout length
16
- # @yield proc to call when callbacks
17
- def initialize(time, &callback)
18
- @time = time
19
- @callback = callback
20
- @start_time = Float::INFINITY
21
- end
22
- end
@@ -1,51 +0,0 @@
1
- ##
2
- # Midori Extension of File reading and writing
3
- class Midori::File
4
- # Init File object
5
- # @param [Array] args same args like File.new
6
- def initialize(*args)
7
- @file = File.new(*args)
8
- end
9
-
10
- # read file
11
- # @return [String] string readed
12
- def read
13
- await(Promise.new do |resolve|
14
- data = ''
15
- EventLoop.register(@file, :r) do
16
- if @file.eof?
17
- EventLoop.deregister(@file)
18
- resolve.call(data)
19
- else
20
- data << @file.read_nonblock(16384)
21
- end
22
- end
23
- end)
24
- end
25
-
26
- # write file
27
- # @param [String] data string to be written
28
- def write(data)
29
- await(Promise.new do |resolve|
30
- written = 0
31
- EventLoop.register(@file, :w) do
32
- written += @file.write_nonblock(data[written..-1])
33
- if written == data.size
34
- EventLoop.deregister(@file)
35
- resolve.call(written)
36
- end
37
- end
38
- end)
39
- end
40
-
41
- # raw file object
42
- # @return [File] file
43
- def raw
44
- @file
45
- end
46
-
47
- # Close the file
48
- def close
49
- @file.close
50
- end
51
- end
@@ -1,42 +0,0 @@
1
- require 'net/protocol'
2
-
3
- ##
4
- # Meta programming Net class for async HTTP and FTP connection
5
- class Net::BufferedIO
6
- # Wait till io finishes
7
- # @param [Symbol] interest
8
- def wait_io(interest)
9
- await(Promise.new do |resolve|
10
- io = @io.to_io
11
- EventLoop.register(io, interest) do
12
- EventLoop.deregister(io)
13
- resolve.call(self)
14
- end
15
- end)
16
- end
17
-
18
- # Fill until the operation finishes
19
- def rbuf_fill
20
- loop do
21
- case rv = @io.read_nonblock(BUFSIZE, exception: false)
22
- when String
23
- return @rbuf << rv
24
- when :wait_readable
25
- wait_io(:r)
26
- # @io.to_io.wait_readable(@read_timeout) or raise Net::ReadTimeout
27
- # continue looping
28
- when :wait_writable
29
- # OpenSSL::Buffering#read_nonblock may fail with IO::WaitWritable.
30
- # http://www.openssl.org/support/faq.html#PROG10
31
- # :nocov:
32
- wait_io(:w)
33
- # @io.to_io.wait_writable(@read_timeout) or raise Net::ReadTimeout
34
- # continue looping
35
- when nil
36
- # callers do not care about backtrace, so avoid allocating for it
37
- raise EOFError, 'end of file reached', []
38
- # :nocov:
39
- end
40
- end
41
- end
42
- end
@@ -1,99 +0,0 @@
1
- ##
2
- # Meta-programming hiredis for redis async extension
3
- module Hiredis
4
- require 'hiredis/connection'
5
- # Overwrite with ruby implementation to hook IO
6
- require 'hiredis/ruby/connection'
7
- require 'hiredis/ruby/reader'
8
- # Redis Connection
9
- Connection = Ruby::Connection
10
- # Redis Result Reader
11
- Reader = Ruby::Reader
12
-
13
- ##
14
- # Meta-programming hiredis for redis async extension
15
- module Ruby
16
- ##
17
- # Meta-programming hiredis for redis async extension
18
- class Connection
19
- # Do redis query
20
- # @param [Array] args equal to Hiredis write args
21
- def query(args)
22
- await(Promise.new do |resolve|
23
- read_flag = false
24
- data = pre_write(args)
25
- written = 0
26
- EventLoop.register(@sock, :rw) do |monitor|
27
- if read_flag && monitor.readable?
28
- # Reading
29
- _read(resolve, @sock)
30
- end
31
- if !read_flag && monitor.writable?
32
- # Writing
33
- written += @sock.write_nonblock(data[written..-1])
34
- read_flag = true if written == string_size(data)
35
- end
36
- end
37
- end)
38
- end
39
-
40
- private
41
- def pre_write(args)
42
- command = []
43
- command << "*#{args.size}"
44
- args.each do |arg|
45
- arg = arg.to_s
46
- command << "$#{string_size arg}"
47
- command << arg
48
- end
49
- data = command.join(COMMAND_DELIMITER) + COMMAND_DELIMITER
50
- data.force_encoding('binary') if data.respond_to?(:force_encoding)
51
- data
52
- end
53
-
54
- def _read(resolve, sock)
55
- @reader.feed @sock.read_nonblock(1024)
56
- reply = @reader.gets
57
- if reply
58
- EventLoop.deregister(sock)
59
- resolve.call(reply)
60
- end
61
- end
62
- end
63
- end
64
- end
65
-
66
- require 'redic'
67
-
68
- ##
69
- # Meta-programming Redic for redis async extension
70
- class Redic
71
- # Meta-programming Redic for redis async extension
72
- class Client
73
- # Connect redis, yield optional
74
- def connect
75
- establish_connection unless connected?
76
- if block_given?
77
- # Redic default yield
78
- # :nocov:
79
- @semaphore.synchronize do
80
- yield
81
- end
82
- # :nocov:
83
- end
84
- end
85
-
86
- # Call without thread lock
87
- # @param [Array] args same params as Redic
88
- def call(*args)
89
- @connection.query(*args)
90
- end
91
- end
92
-
93
- # Call without thread lock
94
- # @param [Array] args same params as Redic
95
- def call(*args)
96
- @client.connect
97
- @client.call(args)
98
- end
99
- end
@@ -1,111 +0,0 @@
1
- safe_require 'sequel', 'gem install sequel'
2
- require 'sequel/adapters/mysql2'
3
-
4
- # Management of MySQL Sockets
5
- MYSQL_SOCKETS = {}
6
-
7
- ##
8
- # Meta-programming Sequel for async extensions
9
- module Sequel
10
- # Midori Extension of sequel MySQL through meta programming
11
- module Mysql2
12
- # Midori Extension of sequel MySQL through meta programming
13
- class Database
14
- # Execute the given SQL on the given connection. If the :type
15
- # option is :select, yield the result of the query, otherwise
16
- # yield the connection if a block is given.
17
- # @param [Mysql2::Client] conn connection to database
18
- # @param [String] sql sql query
19
- # @param [Hash] opts optional options
20
- # @return [Mysql2::Result] MySQL results
21
- def _execute(conn, sql, opts) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
22
- begin
23
- # :nocov:
24
- stream = opts[:stream]
25
- if NativePreparedStatements
26
- if (args = opts[:arguments])
27
- args = args.map{|arg| bound_variable_value(arg)}
28
- end
29
-
30
- case sql
31
- when ::Mysql2::Statement
32
- stmt = sql
33
- when Dataset
34
- sql = sql.sql
35
- close_stmt = true
36
- stmt = conn.prepare(sql)
37
- end
38
- end
39
-
40
- r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn, args) do
41
- if stmt
42
- conn.query_options.merge!(cache_rows: true,
43
- database_timezone: timezone,
44
- application_timezone: Sequel.application_timezone,
45
- stream: stream,
46
- cast_booleans: convert_tinyint_to_bool)
47
- stmt.execute(*args)
48
- # :nocov:
49
- else
50
- if MYSQL_SOCKETS[conn.socket].nil?
51
- MYSQL_SOCKETS[conn.socket] = IO::open(conn.socket)
52
- end
53
- socket = MYSQL_SOCKETS[conn.socket]
54
- await(Promise.new do |resolve|
55
- count = 0
56
- EventLoop.register(socket, :rw) do
57
- if (count == 0)
58
- # Writable
59
- count += 1
60
- conn.query(sql,
61
- database_timezone: timezone,
62
- application_timezone: Sequel.application_timezone,
63
- stream: stream,
64
- async: true)
65
- else
66
- # Readable
67
- begin
68
- EventLoop.deregister(socket)
69
- resolve.call(conn.async_result)
70
- rescue ::Mysql2::Error => e
71
- resolve.call(PromiseException.new(e))
72
- next
73
- end
74
- end
75
- end
76
- end)
77
- end
78
- end
79
-
80
- # :nocov:
81
- if opts[:type] == :select
82
- if r
83
- if stream
84
- begin
85
- r2 = yield r
86
- ensure
87
- # If r2 is nil, it means the block did not exit normally,
88
- # so the rest of the results must be drained to prevent
89
- # "commands out of sync" errors.
90
- r.each{} unless r2
91
- end
92
- else
93
- yield r
94
- end
95
- end
96
- elsif block_given?
97
- yield conn
98
- end
99
- rescue ::Mysql2::Error => e
100
- raise_error(e)
101
- ensure
102
- if stmt
103
- conn.query_options.replace(conn.instance_variable_get(:@sequel_default_query_options))
104
- stmt.close if close_stmt
105
- end
106
- # :nocov:
107
- end
108
- end
109
- end
110
- end
111
- end
@@ -1,45 +0,0 @@
1
- safe_require 'sequel', 'gem install sequel'
2
- require 'sequel/adapters/postgres'
3
-
4
- # Management of Postgres Sockets
5
- POSTGRES_SOCKETS = {}
6
-
7
- ##
8
- # Midori Extension of sequel postgres through meta programming
9
- class Sequel::Postgres::Adapter
10
- # Call a sql request asynchronously
11
- # @param [String] sql sql request
12
- # @param [Array] args args to send
13
- # @return [Array] sql query result
14
- def execute_query(sql, args)
15
- @db.log_connection_yield(sql, self, args) do
16
- if POSTGRES_SOCKETS[self].nil?
17
- POSTGRES_SOCKETS[self] = IO::open(socket)
18
- end
19
- socket_object = POSTGRES_SOCKETS[self]
20
- await(Promise.new do |resolve|
21
- count = 0
22
- EventLoop.register(socket_object, :rw) do
23
- begin
24
- if (count == 0)
25
- # Writable
26
- unless is_busy
27
- send_query(sql)
28
- count += 1
29
- end
30
- else
31
- # Readable
32
- EventLoop.deregister(socket_object)
33
- resolve.call(get_result)
34
- end
35
- # For extra errors
36
- # :nocov:
37
- rescue => e
38
- resolve.call(PromiseException.new(e))
39
- # :nocov:
40
- end
41
- end
42
- end)
43
- end
44
- end
45
- end