em-synchrony 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ require "lib/em-synchrony"
2
+ require "net/http"
3
+
4
+ $VERBOSE = nil
5
+
6
+ EM.synchrony do
7
+ # monkey patch default Socket code to use EventMachine Sockets instead
8
+ TCPSocket = EventMachine::Synchrony::TCPSocket
9
+
10
+ Net::HTTP.get_print 'www.google.com', '/index.html'
11
+
12
+ EM.stop
13
+ end
data/lib/em-synchrony.rb CHANGED
@@ -8,9 +8,11 @@ rescue LoadError => error
8
8
  raise error unless defined? Fiber
9
9
  end
10
10
 
11
+ require "em-synchrony/thread"
11
12
  require "em-synchrony/em-multi"
13
+ require "em-synchrony/tcpsocket"
12
14
  require "em-synchrony/connection_pool"
13
- # require "em-synchrony/iterator" # iterators are not release in EM yet
15
+ require "em-synchrony/iterator" unless EventMachine::VERSION >= '0.12.10'
14
16
 
15
17
  module EventMachine
16
18
 
@@ -24,4 +26,36 @@ module EventMachine
24
26
  self.run(context, tail)
25
27
  end
26
28
 
29
+ module Synchrony
30
+
31
+ # sync is a close relative to inclineCallbacks from Twisted (Python)
32
+ #
33
+ # Synchrony.sync allows you to write sequential code while using asynchronous
34
+ # or callback-based methods under the hood. Example:
35
+ #
36
+ # result = EM::Synchrony.sync EventMachine::HttpRequest.new(URL).get
37
+ # p result.response
38
+ #
39
+ # As long as the asynchronous function returns a Deferrable object, which
40
+ # has a "callback" and an "errback", the sync methond will automatically
41
+ # yield and automatically resume your code (via Fibers) when the call
42
+ # either succeeds or fails. You do not need to patch or modify the
43
+ # Deferrable object, simply pass it to EM::Synchrony.sync
44
+ #
45
+ def self.sync(df)
46
+ f = Fiber.current
47
+ df.callback { |r| f.resume(r) }
48
+ df.errback { |r| f.resume(r) }
49
+
50
+ Fiber.yield
51
+ end
52
+
53
+ # a Fiber-aware sleep function using an EM timer
54
+ def self.sleep( secs )
55
+ fiber = Fiber.current
56
+ EM::Timer.new(secs) { fiber.resume }
57
+ Fiber.yield
58
+ end
59
+ end
60
+
27
61
  end
@@ -43,7 +43,7 @@ module EventMachine
43
43
  end
44
44
 
45
45
  # Release connection assigned to the supplied fiber and
46
- # resume any other pending connections (which will
46
+ # resume any other pending connections (which will
47
47
  # immediately try to run acquire on the pool)
48
48
  def release(fiber)
49
49
  @available.push(@reserved.delete(fiber.object_id))
@@ -73,7 +73,7 @@ module EventMachine
73
73
  df.callback { release(fiber) }
74
74
  df.errback { release(fiber) }
75
75
  end
76
-
76
+
77
77
  df
78
78
  end
79
79
  end
@@ -1,8 +1,10 @@
1
+ require 'cgi'
2
+
1
3
  begin
2
4
  require "em-synchrony/em-http"
3
5
  require "bitly"
4
6
  rescue LoadError => error
5
- raise "Missing EM-Synchrony dependencies: gem install em-http-request; gem install bitly -v=0.4.0"
7
+ raise "Missing EM-Synchrony dependencies: gem install em-http-request; gem install bitly -v=0.5.0"
6
8
  end
7
9
 
8
10
  module Bitly
@@ -23,4 +25,34 @@ module Bitly
23
25
  end
24
26
  end
25
27
  end
28
+
29
+ module V3
30
+ class Client
31
+ class << self
32
+ def get(method, query)
33
+ query_values=[]
34
+ query[:query].each do |key, value|
35
+ query_values << "#{key}=#{CGI::escape(value.to_s)}"
36
+ end
37
+ query_values=query_values.join('&')
38
+ request=(method[0]=='/' ? "#{base_uri}#{method}" : method)
39
+ request=(request.include?('?') ? "#{request}&#{query_values}" : "#{request}?#{query_values}")
40
+
41
+ http = EventMachine::HttpRequest.new(request).get(:timeout => 100)
42
+ response = if (http.response_header.status == 200)
43
+ Crack::JSON.parse(http.response)
44
+ else
45
+ {'errorMessage' => 'JSON Parse Error(Bit.ly messed up)', 'errorCode' => 69, 'statusCode' => 'ERROR'}
46
+ end
47
+
48
+ if response['status_code'] == 200
49
+ return response
50
+ else
51
+ raise BitlyError.new(response['status_txt'], response['status_code'])
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+ end
26
58
  end
@@ -0,0 +1,19 @@
1
+ module EventMachine::Protocols::Memcache
2
+ %w[delete get set].each do |type|
3
+ module_eval %[
4
+ alias :a#{type} :#{type}
5
+ def #{type}(*params, &blk)
6
+ f = Fiber.current
7
+ self.a#{type}(*params) { |*cb_params| f.resume(*cb_params) }
8
+
9
+ Fiber.yield
10
+ end
11
+ ]
12
+ end
13
+
14
+ alias :aget_hash :get_hash
15
+ def get_hash(*keys)
16
+ index = 0
17
+ get(*keys).inject({}) { |h,v| h[keys[index]] = v; index += 1; h }
18
+ end
19
+ end
@@ -0,0 +1,46 @@
1
+ begin
2
+ require "em-mongo"
3
+ rescue LoadError => error
4
+ raise "Missing EM-Synchrony dependency: gem install em-mongo"
5
+ end
6
+
7
+ module EM
8
+ module Mongo
9
+
10
+ class Connection
11
+ def initialize(host = DEFAULT_IP, port = DEFAULT_PORT, timeout = nil, opts = {})
12
+ f = Fiber.current
13
+
14
+ @em_connection = EMConnection.connect(host, port, timeout, opts)
15
+ @db = {}
16
+
17
+ # establish connection before returning
18
+ EM.next_tick { f.resume }
19
+ Fiber.yield
20
+ end
21
+ end
22
+
23
+ class Collection
24
+
25
+ alias :afind :find
26
+ def find(selector={}, opts={})
27
+
28
+ f = Fiber.current
29
+ cb = proc { |res| f.resume(res) }
30
+
31
+ skip = opts.delete(:skip) || 0
32
+ limit = opts.delete(:limit) || 0
33
+
34
+ @connection.find(@name, skip, limit, selector, nil, &cb)
35
+ Fiber.yield
36
+ end
37
+
38
+ alias :afirst :first
39
+ def first(selector={}, opts={})
40
+ opts[:limit] = 1
41
+ find(selector, opts).first
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -13,23 +13,27 @@ module EventMachine
13
13
  def add(name, conn)
14
14
  fiber = Fiber.current
15
15
  conn.callback { @responses[:callback][name] = conn; check_progress(fiber) }
16
- conn.errback { @responses[:errback][name] = conn; check_progress(fiber) }
16
+ conn.errback { @responses[:errback][name] = conn; check_progress(fiber) }
17
17
 
18
18
  @requests.push(conn)
19
19
  end
20
20
 
21
+ def finished?
22
+ (@responses[:callback].size + @responses[:errback].size) == @requests.size
23
+ end
24
+
21
25
  def perform
22
- Fiber.yield
26
+ Fiber.yield unless finished?
23
27
  end
24
28
 
25
29
  protected
26
30
 
27
31
  def check_progress(fiber)
28
- if (@responses[:callback].size + @responses[:errback].size) == @requests.size
32
+ if finished?
29
33
  succeed
30
34
 
31
35
  # continue processing
32
- fiber.resume(self)
36
+ fiber.resume(self) if fiber.alive? && fiber != Fiber.current
33
37
  end
34
38
  end
35
39
  end
@@ -1,7 +1,8 @@
1
1
  begin
2
+ require "mysqlplus"
2
3
  require "em-mysqlplus"
3
4
  rescue LoadError => error
4
- raise "Missing EM-Synchrony dependency: gem install em-mysqlplus"
5
+ raise "Missing EM-Synchrony dependency: gem install mysqlplus, gem install em-mysqlplus"
5
6
  end
6
7
 
7
8
  module EventMachine
@@ -17,7 +18,9 @@ module EventMachine
17
18
 
18
19
  @connection.execute(sql, cb, eb)
19
20
 
20
- Fiber.yield
21
+ result = Fiber.yield
22
+ raise result if Mysql::Error == result.class
23
+ result
21
24
  end
22
25
 
23
26
  end
@@ -0,0 +1,45 @@
1
+ begin
2
+ require 'em-redis'
3
+ rescue LoadError => error
4
+ raise 'Missing EM-Synchrony dependency: gem install em-redis'
5
+ end
6
+
7
+ module EventMachine
8
+ module Protocols
9
+ module Redis
10
+ attr_reader :connected
11
+
12
+ class << self
13
+ alias :aconnect :connect
14
+ end
15
+
16
+ def self.connect(*args)
17
+ f = Fiber.current
18
+
19
+ conn = self.aconnect(*args)
20
+ conn.callback { f.resume(conn) }
21
+
22
+ Fiber.yield
23
+ end
24
+
25
+ def call_command(argv, &blk)
26
+ # async commands are 'a' prefixed, but do check
27
+ # for the 'add' command corner case (ugh)
28
+ if argv.first.size > 3 && argv.first[0] == 'a'
29
+ argv[0] = argv[0].to_s.slice(1,argv[0].size)
30
+ callback { raw_call_command(argv, &blk) }
31
+
32
+ else
33
+ # wrap response blocks into fiber callbacks
34
+ # to emulate the sync api
35
+ f = Fiber.current
36
+ blk = proc { |v| v } if !block_given?
37
+ clb = proc { |v| f.resume(blk.call(v)) }
38
+
39
+ callback { raw_call_command(argv, &clb) }
40
+ Fiber.yield
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ begin
2
+ require "mongo/connection"
3
+ rescue LoadError => error
4
+ raise "Missing EM-Synchrony dependency: gem install mongo"
5
+ end
6
+
7
+ # monkey-patch Mongo to use em-synchrony's socket and thread classs
8
+ silence_warnings do
9
+ class Mongo::Connection
10
+ TCPSocket = ::EventMachine::Synchrony::TCPSocket
11
+ Mutex = ::EventMachine::Synchrony::Thread::Mutex
12
+ ConditionVariable = ::EventMachine::Synchrony::Thread::ConditionVariable
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ require "em-synchrony/mongo"
2
+
3
+ # disable mongoid connection initializer
4
+ if defined? Rails
5
+ module Rails
6
+ module Mongoid
7
+ class Railtie < Rails::Railtie
8
+ initializers.delete_if { |i| i.name == 'verify that mongoid is configured' }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,110 @@
1
+ module EventMachine
2
+ module Synchrony
3
+ class TCPSocket < Connection
4
+ class << self
5
+ alias_method :_old_new, :new
6
+ def new(*args)
7
+ if args.size == 1
8
+ _old_new *args
9
+ else
10
+ socket = EventMachine::connect( *args[0..1], self )
11
+ raise SocketError unless socket.sync(:in) # wait for connection
12
+ socket
13
+ end
14
+ end
15
+ alias :open :new
16
+ end
17
+
18
+ def post_init
19
+ @in_buff, @out_buff = '', ''
20
+ @in_req = @out_req = nil
21
+ end
22
+
23
+ def closed?
24
+ @in_req.nil? && @out_req.nil?
25
+ end
26
+
27
+ # direction must be one of :in or :out
28
+ def sync(direction)
29
+ req = self.instance_variable_set "@#{direction.to_s}_req", EventMachine::DefaultDeferrable.new
30
+ EventMachine::Synchrony.sync req
31
+ end
32
+
33
+ # TCPSocket interface
34
+ def setsockopt(level, name, value); end
35
+
36
+ def send(msg, flags = 0)
37
+ raise "Unknown flags in send(): #{flags}" if flags.nonzero?
38
+ len = msg.bytesize
39
+ write_data(msg) or sync(:out) or raise(IOError)
40
+ len
41
+ end
42
+ alias_method :write, :send
43
+
44
+ def read(num_bytes = 16*1024, dest = nil)
45
+ read_data(num_bytes, dest) or sync(:in) or raise(IOError)
46
+ end
47
+ alias_method :read_nonblock, :read
48
+ alias_method :recv, :read
49
+
50
+ def close
51
+ close_connection true
52
+ @in_req = @out_req = nil
53
+ end
54
+
55
+ # EventMachine interface
56
+ def connection_completed
57
+ @in_req.succeed self
58
+ end
59
+
60
+ def unbind
61
+ @in_req.fail nil if @in_req
62
+ @out_req.fail nil if @out_req
63
+ end
64
+
65
+ def receive_data(data)
66
+ @in_buff << data
67
+ if @in_req && (data = read_data)
68
+ @in_req.succeed data
69
+ end
70
+ end
71
+
72
+ protected
73
+ def read_data(num_bytes = nil, dest = nil)
74
+ @read_bytes = num_bytes if num_bytes
75
+ @read_dest = dest if dest
76
+ if @in_buff.size > 0
77
+ data = @in_buff.slice!(0, @read_bytes)
78
+ @read_bytes = 0
79
+
80
+ if @read_dest
81
+ @read_dest.replace data
82
+ @read_dest = nil
83
+ end
84
+ data
85
+ else
86
+ nil
87
+ end
88
+ end
89
+
90
+ def write_data(data = nil)
91
+ @out_buff += data if data
92
+
93
+ loop do
94
+ if @out_buff.empty?
95
+ @out_req.succeed true if @out_req
96
+ return true
97
+ end
98
+
99
+ if self.get_outbound_data_size > EventMachine::FileStreamer::BackpressureLevel
100
+ EventMachine::next_tick { write_data }
101
+ return false
102
+ else
103
+ len = [@out_buff.bytesize, EventMachine::FileStreamer::ChunkSize].min
104
+ self.send_data @out_buff.slice!( 0, len )
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,26 @@
1
+ module EventMachine
2
+ module Synchrony
3
+ module Thread
4
+
5
+ # Fiber-aware drop-in replacements for thread objects
6
+ class Mutex
7
+ def synchronize( &blk )
8
+ blk.call
9
+ end
10
+ end
11
+
12
+ class ConditionVariable
13
+ def wait( mutex )
14
+ @deferrable = EventMachine::DefaultDeferrable.new
15
+ EventMachine::Synchrony.sync @deferrable
16
+ @deferrable = nil
17
+ end
18
+
19
+ def signal
20
+ @deferrable and @deferrable.succeed
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end