em-rserve 0.1.1 → 0.1.2
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.
- 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: []
|