em-midori 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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