em-synchrony 0.3.0.beta.1 → 1.0.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.
@@ -0,0 +1,5 @@
1
+ require 'active_record'
2
+ require 'em-synchrony/mysql2'
3
+
4
+ require 'em-synchrony/active_record/connection_adapters/em_mysql2_adapter'
5
+ require 'em-synchrony/active_record/patches'
@@ -48,7 +48,7 @@ module EventMachine
48
48
  def release(fiber)
49
49
  @available.push(@reserved.delete(fiber.object_id))
50
50
 
51
- if pending = @pending.pop
51
+ if pending = @pending.shift
52
52
  pending.resume
53
53
  end
54
54
  end
@@ -62,11 +62,11 @@ module EventMachine
62
62
  # once it is complete (assumption: fiber will yield until
63
63
  # data is available, or request is complete)
64
64
  #
65
- def method_missing(method, *args)
65
+ def method_missing(method, *args, &blk)
66
66
  async = (method[0,1] == "a")
67
67
 
68
68
  execute(async) do |conn|
69
- df = conn.send(method, *args)
69
+ df = conn.send(method, *args, &blk)
70
70
 
71
71
  if async
72
72
  fiber = Fiber.current
@@ -0,0 +1,24 @@
1
+ begin
2
+ require 'em-hiredis'
3
+ rescue LoadError => error
4
+ raise 'Missing EM-Synchrony dependency: gem install em-hiredis'
5
+ end
6
+
7
+ module EventMachine
8
+ module Hiredis
9
+ class Connection
10
+ attr_reader :connected
11
+
12
+ def self.connect(host = 'localhost', port = 6379)
13
+ conn = new(host, port)
14
+ EM::Synchrony.sync conn
15
+ conn
16
+ end
17
+
18
+ alias :old_method_missing :method_missing
19
+ def method_missing(sym, *args)
20
+ EM::Synchrony.sync old_method_missing(sym, *args)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  begin
2
- require "em-http"
2
+ require "em-http-request"
3
3
  rescue LoadError => error
4
4
  raise "Missing EM-Synchrony dependency: gem install em-http-request"
5
5
  end
@@ -12,11 +12,15 @@ module EventMachine
12
12
  def #{type}(options = {}, &blk)
13
13
  f = Fiber.current
14
14
 
15
- conn = setup_request(:#{type}, options, &blk)
16
- conn.callback { f.resume(conn) }
17
- conn.errback { f.resume(conn) }
18
-
19
- Fiber.yield
15
+ conn = setup_request(:#{type}, options, &blk)
16
+ if conn.error.nil?
17
+ conn.callback { f.resume(conn) }
18
+ conn.errback { f.resume(conn) }
19
+
20
+ Fiber.yield
21
+ else
22
+ conn
23
+ end
20
24
  end
21
25
  ]
22
26
  end
@@ -22,24 +22,91 @@ module EM
22
22
 
23
23
  class Collection
24
24
 
25
- alias :afind :find
26
- def find(selector={}, opts={})
25
+ #
26
+ # The upcoming versions of EM-Mongo change Collection#find's interface: it
27
+ # now returns a deferrable cursor YAY. This breaks compatibility with past
28
+ # versions BOO. We'll just choose based on the presence/absence of
29
+ # EM::Mongo::Cursor YAY
30
+ #
27
31
 
28
- f = Fiber.current
29
- cb = proc { |res| f.resume(res) }
32
+ #
33
+ # em-mongo version > 0.3.6
34
+ #
35
+ if defined?(EM::Mongo::Cursor)
30
36
 
31
- skip = opts.delete(:skip) || 0
32
- limit = opts.delete(:limit) || 0
37
+ # afind is the old (async) find
38
+ # afind_one is rewritten to call afind
39
+ # find is sync, using a callback on the cursor
40
+ # find_one is sync, by calling find and taking the first element.
41
+ # first is sync, an alias for find_one
33
42
 
34
- @connection.find(@name, skip, limit, selector, nil, &cb)
35
- Fiber.yield
36
- end
43
+ alias :afind :find
44
+ def find(*args)
45
+ f = Fiber.current
46
+ cursor = afind(*args)
47
+ cursor.to_a.callback{ |res| f.resume(res) }
48
+ Fiber.yield
49
+ end
50
+
51
+ # need to rewrite afind_one manually, as it calls 'find' (reasonably
52
+ # expecting it to be what is now known as 'afind')
53
+
54
+ def afind_one(spec_or_object_id=nil, opts={})
55
+ spec = case spec_or_object_id
56
+ when nil
57
+ {}
58
+ when BSON::ObjectId
59
+ {:_id => spec_or_object_id}
60
+ when Hash
61
+ spec_or_object_id
62
+ else
63
+ raise TypeError, "spec_or_object_id must be an instance of ObjectId or Hash, or nil"
64
+ end
65
+ afind(spec, opts.merge(:limit => -1)).next_document
66
+ end
67
+ alias :afirst :afind_one
68
+
69
+ def find_one(selector={}, opts={})
70
+ opts[:limit] = 1
71
+ find(selector, opts).first
72
+ end
73
+ alias :first :find_one
74
+
75
+ #
76
+ # em-mongo version <= 0.3.6
77
+ #
78
+ else
37
79
 
38
- alias :afirst :first
39
- def first(selector={}, opts={})
40
- opts[:limit] = 1
41
- find(selector, opts).first
80
+ alias :afind :find
81
+ def find(selector={}, opts={})
82
+
83
+ f = Fiber.current
84
+ cb = proc { |res| f.resume(res) }
85
+
86
+ skip = opts.delete(:skip) || 0
87
+ limit = opts.delete(:limit) || 0
88
+ order = opts.delete(:order)
89
+
90
+ @connection.find(@name, skip, limit, order, selector, nil, &cb)
91
+ Fiber.yield
92
+ end
93
+
94
+ # need to rewrite afirst manually, as it calls 'find' (reasonably
95
+ # expecting it to be what is now known as 'afind')
96
+
97
+ def afirst(selector={}, opts={}, &blk)
98
+ opts[:limit] = 1
99
+ afind(selector, opts) do |res|
100
+ yield res.first
101
+ end
102
+ end
103
+
104
+ def first(selector={}, opts={})
105
+ opts[:limit] = 1
106
+ find(selector, opts).first
107
+ end
42
108
  end
109
+
43
110
  end
44
111
 
45
112
  end
@@ -21,13 +21,13 @@ module EventMachine
21
21
 
22
22
  Fiber.yield
23
23
  end
24
-
24
+
25
25
  alias :old_call_command :call_command
26
-
26
+
27
+ SYNC = ['add', 'auth']
27
28
  def call_command(argv, &blk)
28
- # async commands are 'a' prefixed, but do check
29
- # for the 'add' command corner case (ugh)
30
- if argv.first.size > 3 && argv.first[0] == 'a'
29
+ # async commands are 'a' prefixed
30
+ if (argv.first[0] == 'a') && !SYNC.include?(argv.first.to_s)
31
31
  argv[0] = argv[0].to_s.slice(1,argv[0].size)
32
32
  old_call_command(argv, &blk)
33
33
 
@@ -42,6 +42,29 @@ module EventMachine
42
42
  Fiber.yield
43
43
  end
44
44
  end
45
+
46
+ # adapted from em-redis' implementation to use
47
+ # the asynchronous version of mget
48
+ def amapped_mget(*keys)
49
+ self.amget(*keys) do |response|
50
+ result = {}
51
+ response.each do |value|
52
+ key = keys.shift
53
+ result.merge!(key => value) unless value.nil?
54
+ end
55
+ yield result if block_given?
56
+ end
57
+ end
58
+
59
+ def mapped_mget(*keys)
60
+ f = Fiber.current
61
+
62
+ self.amapped_mget(*keys) do |values|
63
+ f.resume(values)
64
+ end
65
+
66
+ Fiber.yield
67
+ end
45
68
  end
46
69
  end
47
- end
70
+ end
@@ -11,14 +11,18 @@ module Memcached
11
11
  Memcached.servers = servers
12
12
 
13
13
  f = Fiber.current
14
+ @w = EM::Timer.new(10.0) { f.resume :error }
14
15
  @t = EM::PeriodicTimer.new(0.01) do
15
16
  if Memcached.usable?
17
+ @w.cancel
16
18
  @t.cancel
17
19
  f.resume(self)
18
20
  end
19
21
  end
20
22
 
21
- Fiber.yield
23
+ r = Fiber.yield
24
+
25
+ (r == :error) ? (raise Exception.new('Cannot connect to memcached server')) : r
22
26
  end
23
27
 
24
28
  %w[add get set delete].each do |type|
@@ -30,42 +34,62 @@ module Memcached
30
34
  cb = Proc.new { |res| df.succeed(res) }
31
35
  operation Request::#{type.capitalize}, contents, &cb
32
36
 
33
- df
37
+ df
34
38
  end
35
39
 
36
40
  def #{type}(contents, &callback)
37
41
  fiber = Fiber.current
42
+ paused = false
43
+
44
+ cb = Proc.new do |res|
45
+ if paused
46
+ fiber.resume(res)
47
+ else
48
+ return res
49
+ end
50
+ end
38
51
 
39
- df = a#{type}(contents, &Proc.new { |res| fiber.resume(res) })
52
+ df = a#{type}(contents, &cb)
40
53
  df.callback &callback
41
54
 
55
+ paused = true
42
56
  Fiber.yield
43
57
  end
44
58
  ]
45
59
  end
46
60
 
47
61
  %w[add get set delete].each do |type|
48
- class_eval %[
49
- def amulti_#{type}(contents, &callback)
50
- df = EventMachine::DefaultDeferrable.new
51
- df.callback &callback
62
+ class_eval %[
63
+ def amulti_#{type}(contents, &callback)
64
+ df = EventMachine::DefaultDeferrable.new
65
+ df.callback &callback
66
+
67
+ cb = Proc.new { |res| df.succeed(res) }
68
+ multi_operation Request::#{type.capitalize}, contents, &cb
52
69
 
53
- cb = Proc.new { |res| df.succeed(res) }
54
- multi_operation Request::#{type.capitalize}, contents, &cb
70
+ df
71
+ end
55
72
 
56
- df
57
- end
73
+ def multi_#{type}(contents, &callback)
74
+ fiber = Fiber.current
75
+ paused = false
58
76
 
59
- def multi_#{type}(contents, &callback)
60
- fiber = Fiber.current
77
+ cb = Proc.new do |res|
78
+ if paused
79
+ fiber.resume(res)
80
+ else
81
+ return res
82
+ end
83
+ end
61
84
 
62
- df = amulti_#{type}(contents, &Proc.new { |res| fiber.resume(res) })
63
- df.callback &callback
85
+ df = amulti_#{type}(contents, &cb)
86
+ df.callback &callback
64
87
 
65
- Fiber.yield
66
- end
67
- ]
68
- end
88
+ paused = true
89
+ Fiber.yield
90
+ end
91
+ ]
92
+ end
69
93
 
70
94
  end
71
95
  end
@@ -0,0 +1,18 @@
1
+ module EventMachine
2
+ module Synchrony
3
+
4
+ class FiberIterator < EM::Synchrony::Iterator
5
+
6
+ # execute each iterator block within its own fiber
7
+ # and auto-advance the iterator after each call
8
+ def each(foreach=nil, after=nil, &blk)
9
+ fe = Proc.new do |obj, iter|
10
+ Fiber.new { (foreach || blk).call(obj); iter.next }.resume
11
+ end
12
+
13
+ super(fe, after)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ module EventMachine
2
+ module Synchrony
3
+ class Keyboard
4
+ attr_reader :current_fiber, :separator
5
+
6
+ def gets
7
+ @current_fiber = Fiber.current
8
+ EM.open_keyboard(EventMachine::Synchrony::KeyboardHandler, self)
9
+
10
+ Fiber.yield
11
+ end
12
+ end
13
+
14
+ class KeyboardHandler < EM::Connection
15
+ include EM::Protocols::LineText2
16
+
17
+ def initialize(keyboard)
18
+ @keyboard = keyboard
19
+ end
20
+
21
+ def receive_line(line)
22
+ # Simulate gets by adding a trailing line feed
23
+ @input = "#{line}#{$/}"
24
+
25
+ close_connection
26
+ end
27
+
28
+ def unbind
29
+ @keyboard.current_fiber.resume @input
30
+ end
31
+ end
32
+ end
33
+ end
@@ -11,4 +11,22 @@ silence_warnings do
11
11
  Mutex = ::EventMachine::Synchrony::Thread::Mutex
12
12
  ConditionVariable = ::EventMachine::Synchrony::Thread::ConditionVariable
13
13
  end
14
- end
14
+
15
+ class Mongo::Pool
16
+ TCPSocket = ::EventMachine::Synchrony::TCPSocket
17
+ Mutex = ::EventMachine::Synchrony::Thread::Mutex
18
+ ConditionVariable = ::EventMachine::Synchrony::Thread::ConditionVariable
19
+ end
20
+
21
+ class EventMachine::Synchrony::MongoTimeoutHandler
22
+ def self.timeout(op_timeout, ex_class, &block)
23
+ f = Fiber.current
24
+ timer = EM::Timer.new(op_timeout) { f.resume(nil) }
25
+ res = block.call
26
+ timer.cancel
27
+ res
28
+ end
29
+ end
30
+
31
+ Mongo::TimeoutHandler = EventMachine::Synchrony::MongoTimeoutHandler
32
+ end
@@ -0,0 +1,25 @@
1
+ begin
2
+ require 'mysql2/em'
3
+ rescue LoadError => error
4
+ raise 'Missing EM-Synchrony dependency: gem install mysql2'
5
+ end
6
+
7
+ module Mysql2
8
+ module EM
9
+ class Client
10
+
11
+ alias :aquery :query
12
+ def query(sql, opts={})
13
+ deferable = aquery(sql, opts)
14
+
15
+ f = Fiber.current
16
+ deferable.callback { |res| f.resume(res) }
17
+ deferable.errback { |err| f.resume(err) }
18
+
19
+ Fiber.yield.tap do |result|
20
+ raise result if result.is_a?(::Exception)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end