em-rserve 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/TODO +0 -1
- data/em-rserve.gemspec +1 -1
- data/lib/em-rserve/backend.rb +44 -0
- data/lib/em-rserve/connection.rb +38 -5
- data/lib/em-rserve/fibered_connection.rb +41 -1
- data/lib/em-rserve/pooler.rb +37 -5
- data/lib/em-rserve/protocol/connector.rb +53 -0
- data/lib/em-rserve/protocol/parser.rb +19 -1
- data/lib/em-rserve/protocol/request.rb +5 -0
- data/lib/em-rserve/version.rb +1 -1
- metadata +4 -3
data/TODO
CHANGED
@@ -8,7 +8,6 @@
|
|
8
8
|
* login command
|
9
9
|
support crypt and store salt from connection setup
|
10
10
|
* better attach method
|
11
|
-
* write doc
|
12
11
|
* more spec once the RServe spec is better understood/have enough examples
|
13
12
|
* subclass Request for each commmand instead of current quick'n dirty mechanism
|
14
13
|
* better test requests stacking/unstacking
|
data/em-rserve.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = EM::Rserve::VERSION
|
8
8
|
s.authors = ["crapooze"]
|
9
9
|
s.email = ["crapooze@gmail.com"]
|
10
|
-
s.homepage = ""
|
10
|
+
s.homepage = "https://github.com/crapooze/em-rserve"
|
11
11
|
s.summary = %q{An EventMachine client for RServe}
|
12
12
|
s.description = %q{Do evented stats with EventMachine and RServe}
|
13
13
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
module EM::Rserve
|
3
|
+
# A Backend is a way to specify connection parameters.
|
4
|
+
# The Pooler rely on a Backend to feed him the list of places to connect.
|
5
|
+
class Backend
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# A Server just holds an host and a port.
|
9
|
+
Server = Struct.new(:host, :port)
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
yield self if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def next
|
16
|
+
raise NotImplementedError, "you should use subclasses of Backend"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# The default backend: an RServe running on localhost and default port (6311)
|
21
|
+
class DefaultBackend < Backend
|
22
|
+
def initialize
|
23
|
+
super
|
24
|
+
@server = Server.new('127.0.0.1', 6311).freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
def next
|
28
|
+
@server
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Round-robin looping on servers
|
33
|
+
class RoundRobinBackend < Backend
|
34
|
+
def initialize(servers)
|
35
|
+
super()
|
36
|
+
raise ArgumentError, "need at least one server" if servers.empty?
|
37
|
+
@servers = servers
|
38
|
+
end
|
39
|
+
|
40
|
+
def next
|
41
|
+
@servers.unshift(@servers.pop).first
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/em-rserve/connection.rb
CHANGED
@@ -7,10 +7,18 @@ require "em-rserve/qap1/header"
|
|
7
7
|
require "em-rserve/qap1/message"
|
8
8
|
|
9
9
|
module EM::Rserve
|
10
|
+
# A Connection speaks to RServe using the methods in Protocol::Connector
|
11
|
+
# In addition, a Connection implements helper methods to call RServe commands.
|
10
12
|
class Connection < EM::Connection
|
11
13
|
include Protocol::Connector
|
12
14
|
include QAP1
|
13
15
|
|
16
|
+
# Asks the server to close the connection.
|
17
|
+
# Note that this does not close the TCP connection yet because you will
|
18
|
+
# receive an acknowledgement first.
|
19
|
+
#
|
20
|
+
# Returns and pass to the optional block a new Request instance. See
|
21
|
+
# Protocol::Connector#request.
|
14
22
|
def shutdown!(&blk)
|
15
23
|
header = Header.new(Constants::CMD_shutdown,0,0,0)
|
16
24
|
send_data header.to_bin
|
@@ -18,6 +26,10 @@ module EM::Rserve
|
|
18
26
|
request(&blk)
|
19
27
|
end
|
20
28
|
|
29
|
+
# Evaluates a R-code string in the R session
|
30
|
+
#
|
31
|
+
# Returns and pass to the optional block a new Request instance. See
|
32
|
+
# Protocol::Connector#request.
|
21
33
|
def r_eval(string, void=false,&blk)
|
22
34
|
data = Message.encode_string(string)
|
23
35
|
if void
|
@@ -31,7 +43,11 @@ module EM::Rserve
|
|
31
43
|
request(&blk)
|
32
44
|
end
|
33
45
|
|
34
|
-
|
46
|
+
# Logs-in if the RServe connection asks for a user/password pair
|
47
|
+
#
|
48
|
+
# Returns and pass to the optional block a new Request instance. See
|
49
|
+
# Protocol::Connector#request.
|
50
|
+
def login(user, pwd, crypted=true, &blk)
|
35
51
|
raise NotImplementedError, "will come later"
|
36
52
|
#XXX need to read the salt during connection setup
|
37
53
|
cifer = crypted ? pwd : crypt(pwd, salt)
|
@@ -43,6 +59,11 @@ module EM::Rserve
|
|
43
59
|
request(&blk)
|
44
60
|
end
|
45
61
|
|
62
|
+
# Detaches current session, the response will hold a key to later re-attach
|
63
|
+
# the session.
|
64
|
+
#
|
65
|
+
# Returns and pass to the optional block a new Request instance. See
|
66
|
+
# Protocol::Connector#request.
|
46
67
|
def detach(&blk)
|
47
68
|
header = Header.new(Constants::CMD_detachSession, 0, 0, 0)
|
48
69
|
send_data header.to_bin #port, key of 20 bytes
|
@@ -50,16 +71,28 @@ module EM::Rserve
|
|
50
71
|
request(&blk)
|
51
72
|
end
|
52
73
|
|
74
|
+
# Attaches to the session using the secret key.
|
75
|
+
#
|
76
|
+
# Returns and pass to the optional block a new Request instance. See
|
77
|
+
# Protocol::Connector#request.
|
53
78
|
def attach(key, &blk)
|
54
|
-
#XXX it seems that there is no need to send a Header + Message
|
55
|
-
#
|
56
|
-
#connections
|
57
|
-
raise ArgumentError, "wrong key length, Rserve wants 32bytes" unless key.size == 32
|
79
|
+
#XXX it seems that there is no need to send a Header + Message. We can
|
80
|
+
#just write the key because the RServe code does a read of 32 bytes on newly
|
81
|
+
#accepted connections and tests the key.
|
82
|
+
raise ArgumentError, "wrong key length, Rserve wants 32bytes" unless key.size == 32
|
58
83
|
send_data key
|
59
84
|
|
60
85
|
request(&blk)
|
61
86
|
end
|
62
87
|
|
88
|
+
# Assign an R object (represented by a Ruby instance of Sexp) to a symbol
|
89
|
+
# within the context of the connection. symbol must respond to :to_s, this
|
90
|
+
# value will be the symbol name in R.
|
91
|
+
# If parse_symbol_name is true, RServe will verify whether the R symbol is
|
92
|
+
# a legal symbol.
|
93
|
+
#
|
94
|
+
# Returns and pass to the optional block a new Request instance. See
|
95
|
+
# Protocol::Connector#request.
|
63
96
|
def assign(symbol, sexp_node, parse_symbol_name=true, &blk)
|
64
97
|
data = Message.new([symbol.to_s, sexp_node]).to_bin
|
65
98
|
data << "\xFF" * data.length % 4
|
@@ -3,20 +3,48 @@ require "fiber"
|
|
3
3
|
require "em-rserve/connection"
|
4
4
|
|
5
5
|
module EM::Rserve
|
6
|
+
# A FiberedConnection is a type of EM::RServe::Connection but it handles every
|
7
|
+
# connection in a Ruby Fiber.
|
8
|
+
# This class also implements many high-level methods such that you don't have to understand the in and out of Ruby Fiber.
|
9
|
+
#
|
10
|
+
# It is usually enough to use the connection this way:
|
11
|
+
# EM.run do
|
12
|
+
# Fiber.new do
|
13
|
+
# conn = FiberedConnection.new
|
14
|
+
# conn[:foo] = [1, 2, 3, 4]
|
15
|
+
# conn[:bar] = [1, 2, 3, 4]
|
16
|
+
# puts conn.call('cor(foo, bar)')
|
17
|
+
# end.resume
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# For some context, please read http://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/
|
6
21
|
class FiberedConnection < EM::Rserve::Connection
|
22
|
+
|
23
|
+
# The Fiber holding the context for this connection
|
7
24
|
attr_accessor :fiber
|
8
25
|
|
26
|
+
# Starts a new connection, the first parameter is a Fiber to hold the context
|
27
|
+
# of the connection, defaults to current fiber.
|
28
|
+
# This fiber cannot be the root Fiber.
|
29
|
+
# Remaining parameters are the same than in EM::Rserve::Connection.start
|
9
30
|
def self.start(fiber=Fiber.current, *args)
|
10
31
|
conn = super(*args)
|
11
32
|
conn.fiber = fiber
|
12
33
|
Fiber.yield
|
13
34
|
end
|
14
35
|
|
36
|
+
# Called when ready, resume the Fiber execution
|
15
37
|
def ready
|
16
38
|
super
|
17
39
|
fiber.resume self if fiber
|
18
40
|
end
|
19
41
|
|
42
|
+
# Evaluates a piece of R script and returns:
|
43
|
+
# - script is a string (or an object that we'll transform to a string with :to_s)
|
44
|
+
# - nil if the script doesn't return anything (it often does but not on)
|
45
|
+
# - a Ruby object coming from the translation of a Sexp which represents an R object
|
46
|
+
# - WARNING: current version also return nil on error
|
47
|
+
# Blocks the Fiber but not the event loop.
|
20
48
|
def call(script, *args)
|
21
49
|
r_eval(script.to_s, *args) do |req|
|
22
50
|
req.errback do |err|
|
@@ -40,6 +68,10 @@ class FiberedConnection < EM::Rserve::Connection
|
|
40
68
|
Fiber.yield
|
41
69
|
end
|
42
70
|
|
71
|
+
# Sets a symbol to a val in the R context.
|
72
|
+
# sym will be passed verbatim to EM::Rserve::Connection#assign
|
73
|
+
# val is a Ruby object that will get translated to a Sexp which represents an R object
|
74
|
+
# Blocks the Fiber but not the event loop.
|
43
75
|
def set(sym, val)
|
44
76
|
root = EM::Rserve::R::RubytoR::Translator.ruby_to_r val
|
45
77
|
|
@@ -55,7 +87,15 @@ class FiberedConnection < EM::Rserve::Connection
|
|
55
87
|
Fiber.yield
|
56
88
|
end
|
57
89
|
|
58
|
-
#
|
90
|
+
# Same thing as call, RServe doesn't provide a way to read the value of a
|
91
|
+
# symbol although it provides a way of writing a symbol. Hence, we just
|
92
|
+
# evaluate a script with the name of the symbol.
|
93
|
+
# A sad side effect is the following warning:
|
94
|
+
#
|
95
|
+
# *WARNING* this method is unsafe because it evaluates arbitrary R Code.
|
96
|
+
# Always check that your input parameter looks like an R symbol.
|
97
|
+
#
|
98
|
+
# Blocks the Fiber but not the event loop.
|
59
99
|
def get(sym)
|
60
100
|
call(sym)
|
61
101
|
end
|
data/lib/em-rserve/pooler.rb
CHANGED
@@ -1,13 +1,21 @@
|
|
1
1
|
|
2
2
|
require "em-rserve/fibered_connection"
|
3
|
+
require "em-rserve/backend"
|
3
4
|
|
4
5
|
module EM::Rserve
|
6
|
+
# A Pooler is a pool of already ready FiberedConnections as new RServe
|
7
|
+
# connections are requested, new fibers/connections are added.
|
8
|
+
# There is currently no limitation on the maximum number of establish
|
9
|
+
# connections, but just a limit on the minimum pre-established connections
|
5
10
|
class Pooler
|
6
11
|
class << self
|
7
|
-
|
12
|
+
# Immediately creates and yields a new connection of class klass.
|
13
|
+
# Shorthand method to create one connection wrapped in a Fiber.
|
14
|
+
def r(klass=FiberedConnection, backend=DefaultBackend.new)
|
8
15
|
Fiber.new do
|
9
16
|
begin
|
10
|
-
|
17
|
+
server = backend.next
|
18
|
+
conn = klass.start(Fiber.current, server.host, server.port)
|
11
19
|
yield conn
|
12
20
|
ensure
|
13
21
|
conn.close_connection
|
@@ -16,29 +24,50 @@ module EM::Rserve
|
|
16
24
|
end
|
17
25
|
end
|
18
26
|
|
19
|
-
|
20
|
-
|
27
|
+
# An array of pending connections
|
28
|
+
attr_reader :connections
|
29
|
+
# The minimum number of connections to maintain
|
30
|
+
attr_reader :size
|
31
|
+
# The klass to use when instanciating new connections
|
32
|
+
attr_reader :connection_class
|
33
|
+
# The backend, which says on which host/port to connect to
|
34
|
+
attr_reader :backend
|
35
|
+
|
36
|
+
# Initializes and pre-establish size connections of class klass
|
37
|
+
def initialize(size=10, klass=FiberedConnection, backend=DefaultBackend.new)
|
21
38
|
@connections = []
|
22
39
|
@size = size
|
23
40
|
@connection_class = klass
|
41
|
+
@backend = backend
|
24
42
|
fill size
|
25
43
|
end
|
26
44
|
|
45
|
+
# True if there are no connections left in the pool
|
27
46
|
def empty?
|
28
47
|
connections.empty?
|
29
48
|
end
|
30
49
|
|
50
|
+
# True if there are at least size connections in the pool
|
31
51
|
def full?
|
32
52
|
connections.size >= size
|
33
53
|
end
|
34
54
|
|
55
|
+
# Creates and yields a new connection in a Fiber
|
35
56
|
def connection
|
36
57
|
#XXX duplicated code from Pooler.r to avoid proc-ing the blk
|
37
58
|
Fiber.new do
|
38
|
-
|
59
|
+
server = backend.next
|
60
|
+
yield connection_class.start(Fiber.current, server.host, server.port)
|
39
61
|
end.resume
|
40
62
|
end
|
41
63
|
|
64
|
+
# Pick and yield a new connection from the connections pool.
|
65
|
+
# If the pool, yield a new connection.
|
66
|
+
#
|
67
|
+
# This method also ensure that the TCP connection is closed once the work
|
68
|
+
# is finished.
|
69
|
+
#
|
70
|
+
# It also re-establish new connections, trying to maintain the pool filled.
|
42
71
|
def r
|
43
72
|
conn = connections.shift
|
44
73
|
if conn
|
@@ -63,6 +92,8 @@ module EM::Rserve
|
|
63
92
|
end
|
64
93
|
end
|
65
94
|
|
95
|
+
# Preconnect a connection, once the connection is ready, it is added to the
|
96
|
+
# connections pool.
|
66
97
|
def preconnect!
|
67
98
|
connection do |conn|
|
68
99
|
conn.fiber = nil
|
@@ -70,6 +101,7 @@ module EM::Rserve
|
|
70
101
|
end
|
71
102
|
end
|
72
103
|
|
104
|
+
# Shorthand to preconnect n connections in parallel.
|
73
105
|
def fill(n=size)
|
74
106
|
n.times{preconnect!}
|
75
107
|
end
|
@@ -10,6 +10,7 @@ module EM::Rserve
|
|
10
10
|
end
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
+
# Starts a new TCP connection to the server/port parameters
|
13
14
|
def start(server='127.0.0.1', port=6311)
|
14
15
|
EM.connect(server, port, self)
|
15
16
|
end
|
@@ -17,8 +18,16 @@ module EM::Rserve
|
|
17
18
|
|
18
19
|
extend ClassMethods
|
19
20
|
|
21
|
+
# FIFO for pending requests.
|
22
|
+
#
|
23
|
+
# RServe protocol implements a synchronous request/response scheme over a
|
24
|
+
# single TCP stream. Thus we can hold contexts in a FIFO array of requests
|
25
|
+
# to the same server
|
20
26
|
attr_accessor :request_queue
|
21
27
|
|
28
|
+
# Implements EM::Connection post_init hook:
|
29
|
+
# - sets the parser to parse IDs
|
30
|
+
# - create a request_queue
|
22
31
|
def post_init
|
23
32
|
super
|
24
33
|
#type of parser carries the state, no need to carry it internally and do
|
@@ -28,6 +37,17 @@ module EM::Rserve
|
|
28
37
|
@request_queue = []
|
29
38
|
end
|
30
39
|
|
40
|
+
# Creates and appends a new request to the request_queue
|
41
|
+
# If a block is given, it will be called and the new request will be
|
42
|
+
# passed as argument.
|
43
|
+
# Also returns the newly created request.
|
44
|
+
#
|
45
|
+
# r = connector.request
|
46
|
+
# r.success{ p 'good' }
|
47
|
+
#
|
48
|
+
# connector.request do |r|
|
49
|
+
# r.success{p 'good'}
|
50
|
+
# end
|
31
51
|
def request
|
32
52
|
r = Request.new
|
33
53
|
yield r if block_given?
|
@@ -35,6 +55,16 @@ module EM::Rserve
|
|
35
55
|
r
|
36
56
|
end
|
37
57
|
|
58
|
+
# Replaces current Parser by a new Parser.
|
59
|
+
# The klass parameter gives the class to instanciate.
|
60
|
+
# Instanciation of the new parser will receive self as only parameter.
|
61
|
+
# If the hold parser holds data in its buffer, we may lose this data, hence
|
62
|
+
# we call Parser#replace to correctly hand-over the parsing.
|
63
|
+
# Note that this complication comes from the RServe protocol which has an
|
64
|
+
# initialization phase.
|
65
|
+
# Rather than holding the state and testing whether the connection is
|
66
|
+
# initialized or not each time we receive a byte, once the initialization
|
67
|
+
# phase is done, we just change the parser.
|
38
68
|
def replace_parser!(klass)
|
39
69
|
new_parser = klass.new(self)
|
40
70
|
if @parser
|
@@ -43,10 +73,12 @@ module EM::Rserve
|
|
43
73
|
@parser = new_parser
|
44
74
|
end
|
45
75
|
|
76
|
+
# Implements EM::Connection receive_data hook, just forwarding to the parser.
|
46
77
|
def receive_data(dat)
|
47
78
|
@parser << dat
|
48
79
|
end
|
49
80
|
|
81
|
+
# Implements IDParser's receive_id callback.
|
50
82
|
def receive_id(id)
|
51
83
|
# on last line, the messaging can start
|
52
84
|
if id.last_one?
|
@@ -60,9 +92,16 @@ module EM::Rserve
|
|
60
92
|
|
61
93
|
# HOOKS TO OVERRIDE, PLEASE CALL SUPER
|
62
94
|
|
95
|
+
# This method gets called whenever the connection is started and initialized.
|
96
|
+
# By default, it does nothing, you don't need to call super when
|
97
|
+
# overriding this method.
|
63
98
|
def ready
|
64
99
|
end
|
65
100
|
|
101
|
+
# Implements MessageParser's callback.
|
102
|
+
# This method gets called whenever the server start answering with a
|
103
|
+
# message header.
|
104
|
+
# If you override this method, call super first.
|
66
105
|
def receive_message_header(head)
|
67
106
|
if head.error?
|
68
107
|
receive_error_message_header(head)
|
@@ -73,14 +112,28 @@ module EM::Rserve
|
|
73
112
|
end
|
74
113
|
end
|
75
114
|
|
115
|
+
# This method gets called by receive_message_header if the header is an
|
116
|
+
# error. It dequeues the request_queue FIFO and calls its error method
|
117
|
+
# with the header as only parameter.
|
118
|
+
# If you override this method, call super first.
|
76
119
|
def receive_error_message_header(head)
|
77
120
|
request_queue.shift.error(head)
|
78
121
|
end
|
79
122
|
|
123
|
+
# This method gets called by receive_message_header if the header is an error.
|
124
|
+
# If the header tells us there is no more data to answer this request
|
125
|
+
# (i.e., there will be no body data), dequeues the request_queue FIFO and
|
126
|
+
# calls the success method with "nil" as only parameter.
|
127
|
+
# If you override this method, call super first.
|
80
128
|
def receive_success_message_header(head)
|
81
129
|
request_queue.shift.success(nil) unless head.body?
|
82
130
|
end
|
83
131
|
|
132
|
+
# Implements MessageParser's callback.
|
133
|
+
# This methods gets called whenever a message was completely received.
|
134
|
+
# Dequeues the request_queue FIFO and calls the success method with the
|
135
|
+
# message as only parameter.
|
136
|
+
# If you override this method, call super first.
|
84
137
|
def receive_message(msg)
|
85
138
|
request_queue.shift.success(msg)
|
86
139
|
end
|
@@ -6,24 +6,38 @@ require 'em-rserve/r/sexp'
|
|
6
6
|
|
7
7
|
module EM::Rserve
|
8
8
|
module Protocol
|
9
|
+
# Top class for parsers.
|
9
10
|
class Parser
|
10
|
-
|
11
|
+
# A handler is an object which will receive method calls from parsers on
|
12
|
+
# specific events.
|
13
|
+
attr_reader :handler
|
11
14
|
|
15
|
+
# A buffer holding data.
|
16
|
+
attr_reader :buffer
|
17
|
+
|
18
|
+
# Initializes a new Parser and stores the handler.
|
12
19
|
def initialize(handler)
|
13
20
|
@handler = handler
|
14
21
|
@buffer = ''
|
15
22
|
end
|
16
23
|
|
24
|
+
# Replaces current buffer with other's parser's buffer.
|
25
|
+
# This is useful when handing-over from one type of parsing to another.
|
17
26
|
def replace(other)
|
18
27
|
@buffer.replace(other.buffer)
|
19
28
|
self
|
20
29
|
end
|
21
30
|
|
31
|
+
# Input some data to the parser. As there is new data, will start a
|
32
|
+
# parsing loop by calling parse_loop!
|
22
33
|
def << data
|
23
34
|
buffer << data if data
|
24
35
|
parse_loop!
|
25
36
|
end
|
26
37
|
|
38
|
+
# Infinitely calls the parse! method (to override in subclasses of parsers).
|
39
|
+
# To get out of the loop (e.g., when we realize that there is not enough
|
40
|
+
# data in the buffer to complete a message), one must throw :stop .
|
27
41
|
def parse_loop!
|
28
42
|
catch :stop do
|
29
43
|
loop do
|
@@ -42,6 +56,8 @@ module EM::Rserve
|
|
42
56
|
# This parser is useful only at the beginning.
|
43
57
|
# Instead of carrying its dynamic all the time (e.g., keeping a state).
|
44
58
|
# We pop-it out as another parser.
|
59
|
+
#
|
60
|
+
# This parser's handler must respond_to :receive_id
|
45
61
|
class IDParser < Parser
|
46
62
|
def parse!
|
47
63
|
if buffer.size >= 4
|
@@ -55,6 +71,8 @@ module EM::Rserve
|
|
55
71
|
end
|
56
72
|
|
57
73
|
# This message parser will parse qap1 headers and associated qap1 data.
|
74
|
+
#
|
75
|
+
# This parser's handler must respond_to :receive_message and :receive_message_header
|
58
76
|
class MessageParser < Parser
|
59
77
|
def initialize(handler)
|
60
78
|
super(handler)
|
@@ -1,19 +1,24 @@
|
|
1
1
|
|
2
2
|
module EM::Rserve
|
3
3
|
module Protocol
|
4
|
+
# Simple Request structure holding a references to a callback and an errback
|
4
5
|
Request = Struct.new(:callback_blk, :errback_blk) do
|
6
|
+
# sets the async callback with a block argument
|
5
7
|
def callback(&blk)
|
6
8
|
self.callback_blk = blk
|
7
9
|
end
|
8
10
|
|
11
|
+
# sets the async errback with a block argument
|
9
12
|
def errback(&blk)
|
10
13
|
self.errback_blk = blk
|
11
14
|
end
|
12
15
|
|
16
|
+
# calls the errback
|
13
17
|
def error(val)
|
14
18
|
errback_blk.call(val) if errback_blk
|
15
19
|
end
|
16
20
|
|
21
|
+
# calls the callback
|
17
22
|
def success(val)
|
18
23
|
callback_blk.call(val) if callback_blk
|
19
24
|
end
|
data/lib/em-rserve/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-rserve
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-22 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: Do evented stats with EventMachine and RServe
|
15
15
|
email:
|
@@ -25,6 +25,7 @@ files:
|
|
25
25
|
- TODO
|
26
26
|
- em-rserve.gemspec
|
27
27
|
- lib/em-rserve.rb
|
28
|
+
- lib/em-rserve/backend.rb
|
28
29
|
- lib/em-rserve/connection.rb
|
29
30
|
- lib/em-rserve/fibered_connection.rb
|
30
31
|
- lib/em-rserve/pooler.rb
|
@@ -52,7 +53,7 @@ files:
|
|
52
53
|
- specs/parser_spec.rb
|
53
54
|
- specs/ruby_to_r_translator_spec.rb
|
54
55
|
- specs/spec_helper.rb
|
55
|
-
homepage:
|
56
|
+
homepage: https://github.com/crapooze/em-rserve
|
56
57
|
licenses: []
|
57
58
|
post_install_message:
|
58
59
|
rdoc_options: []
|