em-synchrony 0.1.5 → 0.2.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,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