rserve-client 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data.tar.gz.sig +1 -2
  2. data/.gitignore +1 -0
  3. data/History.txt +15 -2
  4. data/Manifest.txt +8 -0
  5. data/README.txt +41 -19
  6. data/lib/rserve.rb +1 -1
  7. data/lib/rserve/connection.rb +152 -69
  8. data/lib/rserve/engine.rb +9 -9
  9. data/lib/rserve/packet.rb +2 -2
  10. data/lib/rserve/protocol.rb +51 -46
  11. data/lib/rserve/protocol/rexpfactory.rb +659 -366
  12. data/lib/rserve/rexp.rb +175 -156
  13. data/lib/rserve/rexp/double.rb +45 -38
  14. data/lib/rserve/rexp/environment.rb +40 -0
  15. data/lib/rserve/rexp/expressionvector.rb +9 -0
  16. data/lib/rserve/rexp/genericvector.rb +5 -3
  17. data/lib/rserve/rexp/integer.rb +38 -38
  18. data/lib/rserve/rexp/language.rb +2 -2
  19. data/lib/rserve/rexp/list.rb +3 -0
  20. data/lib/rserve/rexp/logical.rb +17 -4
  21. data/lib/rserve/rexp/null.rb +20 -14
  22. data/lib/rserve/rexp/raw.rb +19 -0
  23. data/lib/rserve/rexp/reference.rb +42 -0
  24. data/lib/rserve/rexp/s4.rb +10 -0
  25. data/lib/rserve/rexp/string.rb +5 -3
  26. data/lib/rserve/rexp/symbol.rb +3 -0
  27. data/lib/rserve/rexp/vector.rb +30 -15
  28. data/lib/rserve/rexp/wrapper.rb +58 -0
  29. data/lib/rserve/rfactor.rb +10 -10
  30. data/lib/rserve/rlist.rb +129 -100
  31. data/lib/rserve/talk.rb +61 -61
  32. data/spec/rserve_connection_spec.rb +99 -33
  33. data/spec/rserve_double_spec.rb +28 -15
  34. data/spec/rserve_integer_spec.rb +24 -15
  35. data/spec/rserve_logical_spec.rb +21 -12
  36. data/spec/rserve_protocol_spec.rb +7 -7
  37. data/spec/rserve_rexp_spec.rb +3 -3
  38. data/spec/rserve_rexp_wrapper_spec.rb +36 -0
  39. data/spec/rserve_rexpfactory_spec.rb +86 -20
  40. data/spec/rserve_rfactor_spec.rb +2 -2
  41. data/spec/rserve_rlist_spec.rb +53 -0
  42. data/spec/rserve_spec.rb +8 -5
  43. data/spec/rserve_talk_spec.rb +7 -7
  44. data/spec/spec_helper.rb +1 -0
  45. metadata +13 -3
  46. metadata.gz.sig +0 -0
data.tar.gz.sig CHANGED
@@ -1,2 +1 @@
1
- A.���CTzEc��-�ˣmt��r�I���Y���>w��������:��_ܭA�Z��y�a >�e����xX���_A8�}��כ���5~~��C�,�p��r>��n� _���˂�P���t��`F��Ʉ'���'���f��# ��M/��t�|�*�}F��U��\�{�� ������а�
2
- �| �5`[2�W���ڀl����!o�E!�s�O5�G�nB�x<Y̬�\5w 蛩�ɨ��
1
+ `�~�i��N �hDNr�ZJ)C�-*ؔtt�|��ŽkR�����OHn�B��uy%@��O�=o��u���I�������� #Ͱp�����f�O����{���l)j?c�%{p}ް�s���T3Ww��(*�@����^:kx�]yꋭ�EXvb/%��{������w%[�� �^�e���"��s#����q�A����A]�4F���2�{q�%��#�%oݤJ>�b{o�%b���t ��
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ coverage.info
1
2
  *~
2
3
  doc
3
4
  pkg
@@ -1,11 +1,24 @@
1
+ === 0.1.6 / 2010-05-27
2
+ * Assign complete for all commons REXPs
3
+ * Added Connection#shutdown
4
+ * Added arch dependent for Double NA
5
+ * Bug fix: list with booleans and NA raise an error
6
+ * Added support for all types of REXP.
7
+ * REXP::Wrapper provides a method to convert ruby types to R types.
8
+ * Added spec for Rlist and REXP::Wrapper
9
+ * Added generic to_ruby method. Ex.: Rlist#to_ruby returns an array
10
+ * README.txt updated
11
+
12
+
1
13
  === 0.1.5 / 2010-05-24
2
14
  * Bug fix: Incorrect NA for Logical, String and Double Vector
3
15
  * Factors implemented
16
+ * Correct Manifest.txt
4
17
 
5
18
  === 0.1.3 / 2010-05-24
6
19
  * Better README.txt
7
- * Implemented hash of options on Connection.new
8
- * Better implementation of errors on connections
20
+ * Implemented options hash on Connection.new
21
+ * Better implementation of errors on connection
9
22
  * REXP::Double#as_strings returns values as floats, not Rationals
10
23
  * Bug fix on REXP#as_double_matrix.
11
24
  * Added REXP#as_matrix, which return a standard library matrix from a R matrix
@@ -12,6 +12,8 @@ lib/rserve/protocol.rb
12
12
  lib/rserve/protocol/rexpfactory.rb
13
13
  lib/rserve/rexp.rb
14
14
  lib/rserve/rexp/double.rb
15
+ lib/rserve/rexp/environment.rb
16
+ lib/rserve/rexp/expressionvector.rb
15
17
  lib/rserve/rexp/factor.rb
16
18
  lib/rserve/rexp/genericvector.rb
17
19
  lib/rserve/rexp/integer.rb
@@ -19,10 +21,14 @@ lib/rserve/rexp/language.rb
19
21
  lib/rserve/rexp/list.rb
20
22
  lib/rserve/rexp/logical.rb
21
23
  lib/rserve/rexp/null.rb
24
+ lib/rserve/rexp/raw.rb
25
+ lib/rserve/rexp/reference.rb
26
+ lib/rserve/rexp/s4.rb
22
27
  lib/rserve/rexp/string.rb
23
28
  lib/rserve/rexp/symbol.rb
24
29
  lib/rserve/rexp/unknown.rb
25
30
  lib/rserve/rexp/vector.rb
31
+ lib/rserve/rexp/wrapper.rb
26
32
  lib/rserve/rfactor.rb
27
33
  lib/rserve/rlist.rb
28
34
  lib/rserve/talk.rb
@@ -33,8 +39,10 @@ spec/rserve_logical_spec.rb
33
39
  spec/rserve_packet_spec.rb
34
40
  spec/rserve_protocol_spec.rb
35
41
  spec/rserve_rexp_spec.rb
42
+ spec/rserve_rexp_wrapper_spec.rb
36
43
  spec/rserve_rexpfactory_spec.rb
37
44
  spec/rserve_rfactor_spec.rb
45
+ spec/rserve_rlist_spec.rb
38
46
  spec/rserve_spec.rb
39
47
  spec/rserve_talk_spec.rb
40
48
  spec/spec.opts
data/README.txt CHANGED
@@ -15,13 +15,17 @@ Follows closely the new Java client API, but maintains all Ruby conventions when
15
15
  * Uses TCP/IP sockets to interchange data and commands
16
16
  * Requires Rserve installed on the server machine. On debian / ubuntu, you should use <tt>sudo apt-get install r-cran-rserve</tt>
17
17
  Pros:
18
- * Work with Ruby 1.8, 1.9 and JRuby 1.5
18
+ * Work with Ruby 1.8, 1.9 and JRuby 1.5.
19
+ * Should work on Windows (not tested yet)
19
20
  * Implements almost completely R's datatypes: integer, doubles, chars, logical vectors, lists and raw data.
20
- * Follows closely the Java API, so any change on the server could be adopted without much problem
21
+ * Follows closely the Java API, so any change on the server API could be adopted without much problem
21
22
  * Fast
23
+ * Easy management of differences between R and Ruby, or "You can have your cake and eat it, too!"
24
+ * From R side: The evaluation of expression retrieves REXP object, with a lot of information from original variables on R. You can construct your REXP objects and <tt>assign</tt> them to variables on R fast using binary TCP/IP port or send complex expression without lost of time using <tt>void_eval</tt>
25
+ * Between R and Ruby: Every REXP object implements methods to convert to specific Ruby type: as_integers, as_doubles, as_strings
26
+ * From Ruby side: Every REXP objects has a <tt>to_ruby</tt> method, which automagicly converts every R type on equivalent Ruby type. So, a vector of size 1 is converted to an integer or double, a vector of size>1 returns an array, a named list returns a hash and so on. If you need to create a complex expression, you could always use method <tt>eval</tt> without problem
22
27
  Cons:
23
28
  * Requires Rserve
24
- * No seamless integration with Ruby. You obtain data with an interface closer to R than Ruby.
25
29
 
26
30
  == RELATED LIBRARIES (Ruby / R)
27
31
 
@@ -51,13 +55,6 @@ Cons:
51
55
 
52
56
  Implements
53
57
 
54
- * REXPs
55
- * Enviroment
56
- * ExpressionVector
57
- * Raw
58
- * Reference
59
- * S4
60
- * Wrapper
61
58
  * Sessions
62
59
  * Authentification
63
60
  * Original test
@@ -70,16 +67,41 @@ Spec
70
67
 
71
68
  == SYNOPSIS:
72
69
 
73
- require 'rserve'
74
- con=Rserve::Connection.new
75
- con.eval("x<-rnorm(1)")
76
- => #<Rserve::REXP::Double:0x000000011a13c8
77
- @payload=[(5339785585931699/2251799813685248)],
78
- @attr=nil>
79
- con.eval("list(name='Fred')").as_list
80
-
81
- => #<Rserve::Rlist:0x00000001bf82a8 @names=["name"], @data=[#<Rserve::REXP::String:0x00000001bf8548 @payload=["Fred"], @attr=nil>]>
70
+ require 'rserve'
71
+ con=Rserve::Connection.new
72
+
73
+ # Evaluation retrieves a <tt>Rserve::REXP</tt> object
74
+
75
+ x=con.eval('x<-rnorm(1)')
76
+ => #<Rserve::REXP::Double:0x000000010a81f0 @payload=[(4807469545488851/9007199254740992)], @attr=nil>
82
77
 
78
+ # You could use specific methods to retrieve ruby objects
79
+ x.as_doubles => [0.533736337958596]
80
+ x.as_strings => ["0.533736337958596"]
81
+
82
+ # Every Rserve::REXP could be converted to Ruby objects using
83
+ # method <tt>to_ruby</tt>
84
+ x.to_ruby => (4807469545488851/9007199254740992)
85
+
86
+ # The API could manage complex recursive list
87
+
88
+ x=con.eval('list(l1=list(c(2,3)),l2=c(1,2,3))').to_ruby
89
+ => {"l1"=>[[(2/1), (3/1)]], "l2"=>[(1/1), (2/1), (3/1)]}
90
+
91
+ # You could assign a REXP to R variables
92
+
93
+ con.assign("x", Rserve::REXP::Double.new([1.5,2.3,5]))
94
+ => #<Rserve::Packet:0x0000000136b068 @cmd=65537, @cont=nil>
95
+ con.eval("x")
96
+ => #<Rserve::REXP::Double:0x0000000134e770 @payload=[(3/2), (2589569785738035/1125899906842624), (5/1)], @attr=nil>
97
+
98
+ # Rserve::REXP::Wrapper.wrap allows you to transform Ruby object to
99
+ # REXP, could be assigned to R variables
100
+
101
+ Rserve::REXP::Wrapper.wrap(["a","b",["c","d"]])
102
+
103
+ => #<Rserve::REXP::GenericVector:0x000000010c81d0 @attr=nil, @payload=#<Rserve::Rlist:0x000000010c8278 @names=nil, @data=[#<Rserve::REXP::String:0x000000010c86d8 @payload=["a"], @attr=nil>, #<Rserve::REXP::String:0x000000010c85c0 @payload=["b"], @attr=nil>, #<Rserve::REXP::String:0x000000010c82e8 @payload=["c", "d"], @attr=nil>]>>
104
+
83
105
  == REQUIREMENTS:
84
106
 
85
107
  * R
@@ -1,7 +1,7 @@
1
1
  require 'socket'
2
2
 
3
3
  module Rserve
4
- VERSION = '0.1.5'
4
+ VERSION = '0.1.6'
5
5
  end
6
6
 
7
7
 
@@ -1,7 +1,7 @@
1
1
  module Rserve
2
2
  class Connection < Rserve::Engine
3
3
  include Rserve::Protocol
4
-
4
+
5
5
  # :section: Exceptions
6
6
  RserveNotStarted=Class.new(Exception)
7
7
  ServerNotAvailable=Class.new(Exception)
@@ -18,7 +18,7 @@ module Rserve
18
18
  end
19
19
  attr_reader :hostname
20
20
  attr_reader :port_number
21
- attr_reader :protocol
21
+ attr_reader :protocol
22
22
  attr_reader :last_error
23
23
  attr_reader :connected
24
24
  attr_reader :auth_req
@@ -29,9 +29,10 @@ module Rserve
29
29
  attr_reader :port
30
30
  attr_writer :transfer_charset
31
31
  attr_reader :rsrv_version
32
+ attr_writer :persistent
32
33
  AT_plain=0
33
34
  AT_crypt=1
34
-
35
+
35
36
  def initialize(opts=Hash.new)
36
37
  @auth_req = opts.delete(:auth_req) || false
37
38
  @transfer_charset = opts.delete(:transfer_charset) || "UTF-8"
@@ -39,52 +40,53 @@ module Rserve
39
40
  @hostname = opts.delete(:hostname) || "127.0.0.1"
40
41
  @port_number = opts.delete(:port_number) || 6311
41
42
  @max_tries = opts.delete(:max_tries) || 5
42
-
43
43
  @tries = 0
44
44
  @connected=false
45
-
46
-
47
- begin
45
+
46
+
47
+ begin
48
48
  #puts "Tryin to connect..."
49
49
  connect
50
50
  rescue Errno::ECONNREFUSED
51
51
  if @tries<@max_tries
52
- @tries+=1
53
- # Rserve is available?
54
- if system "killall -s 0 Rserve"
55
- # Rserve is available. Incorrect host and/or portname
56
- raise ServerNotAvailable, "Rserve started, but not available on #{hostname}:#{port_number}"
57
- # Rserve not available. We should instanciate it first
58
- else
59
- if system "R CMD Rserve"
60
- #puts "Ok"
61
- retry
62
- else
63
- raise RserveNotStarted, "Can't start Rserve"
64
- end
65
- end
52
+ @tries+=1
53
+ # Rserve is available?
54
+ if system "killall -s 0 Rserve"
55
+ # Rserve is available. Incorrect host and/or portname
56
+ raise ServerNotAvailable, "Rserve started, but not available on #{hostname}:#{port_number}"
57
+ # Rserve not available. We should instanciate it first
58
+ else
59
+ if system "R CMD Rserve"
60
+ # Wait a moment please
61
+ sleep(0.25)
62
+ retry
63
+ else
64
+ raise RserveNotStarted, "Can't start Rserve"
65
+ end
66
+ end
66
67
  #puts "Init RServe"
67
-
68
+
68
69
  else
69
70
  raise
70
71
  end
71
72
  end
72
73
  end
73
74
  def connect
74
- close if @connected
75
- @s = TCPSocket::new(@hostname, @port_number)
76
- @rt=Rserve::Talk.new(@s)
77
- #puts "Connected"
78
- # Accept first input
79
- input=@s.recv(32).unpack("a4a4a4a20")
80
- raise IncorrectServer,"Handshake failed: Rsrv signature expected, but received [#{input[0]}]" unless input[0]=="Rsrv"
81
- @rsrv_version=input[1].to_i
82
- raise IncorrectServerVersion, "Handshake failed: The server uses more recent protocol than this client." if @rsrv_version>103
83
- @protocol=input[2]
84
- raise IncorrectProtocol, "Handshake failed: unsupported transfer protocol #{@protocol}, I talk only QAP1." if @protocol!="QAP1"
85
- @extra=input[4]
86
- @connected=true
87
- @last_error="OK"
75
+
76
+ close if @connected
77
+ @s = TCPSocket::new(@hostname, @port_number)
78
+ @rt=Rserve::Talk.new(@s)
79
+ #puts "Connected"
80
+ # Accept first input
81
+ input=@s.recv(32).unpack("a4a4a4a20")
82
+ raise IncorrectServer,"Handshake failed: Rsrv signature expected, but received [#{input[0]}]" unless input[0]=="Rsrv"
83
+ @rsrv_version=input[1].to_i
84
+ raise IncorrectServerVersion, "Handshake failed: The server uses more recent protocol than this client." if @rsrv_version>103
85
+ @protocol=input[2]
86
+ raise IncorrectProtocol, "Handshake failed: unsupported transfer protocol #{@protocol}, I talk only QAP1." if @protocol!="QAP1"
87
+ @extra=input[4]
88
+ @connected=true
89
+ @last_error="OK"
88
90
 
89
91
  end
90
92
  def connected?
@@ -102,10 +104,10 @@ module Rserve
102
104
  def get_server_version
103
105
  @rsrv_version
104
106
  end
105
-
107
+
106
108
  # evaluates the given command, but does not fetch the result (useful for assignment operations)
107
109
  # * @param cmd command/expression string */
108
- def void_eval(cmd)
110
+ def void_eval(cmd)
109
111
  raise NotConnected if !connected? or rt.nil?
110
112
  rp=rt.request(:cmd=>Rserve::Protocol::CMD_voidEval, :cont=>cmd+"\n")
111
113
  if !rp.nil? and rp.ok?
@@ -113,43 +115,124 @@ module Rserve
113
115
  else
114
116
  raise EvalError.new(rp), "voidEval failed: #{rp.to_s}"
115
117
  end
116
-
117
- end
118
-
119
-
120
- # evaluates the given command and retrieves the result
121
- # * @param cmd command/expression string
122
- # * @return R-xpression or <code>null</code> if an error occured */
123
- def eval(cmd)
124
- raise NotConnected if !connected? or rt.nil?
125
- rp=rt.request(:cmd=>Rserve::Protocol::CMD_eval, :cont=>cmd+"\n")
126
- if !rp.nil? and rp.ok?
127
- parse_eval_response(rp)
128
- else
129
- raise EvalError.new(rp), "voidEval failed: #{rp.to_s}"
118
+
130
119
  end
131
- end
132
- def parse_eval_response(rp)
133
- rxo=0
134
- pc=rp.cont
135
- if (rsrv_version>100) # /* since 0101 eval responds correctly by using DT_SEXP type/len header which is 4 bytes long */
136
- rxo=4
137
- # we should check parameter type (should be DT_SEXP) and fail if it's not
138
- if (pc[0]!=Rserve::Protocol::DT_SEXP && pc[0]!=(Rserve::Protocol::DT_SEXP|Rserve::Protocol::DT_LARGE))
139
- raise "Error while processing eval output: SEXP (type "+Rserve::Protocol::DT_SEXP+") expected but found result type "+pc[0].to_s+"."
140
- end
141
- if (pc[0]==(Rserve::Protocol::DT_SEXP|Rserve::Protocol::DT_LARGE))
142
- rxo=8; # large data need skip of 8 bytes
120
+
121
+
122
+ # evaluates the given command and retrieves the result
123
+ # * @param cmd command/expression string
124
+ # * @return R-xpression or <code>null</code> if an error occured */
125
+ def eval(cmd)
126
+ raise NotConnected if !connected? or rt.nil?
127
+ rp=rt.request(:cmd=>Rserve::Protocol::CMD_eval, :cont=>cmd+"\n")
128
+ if !rp.nil? and rp.ok?
129
+ parse_eval_response(rp)
130
+ else
131
+ raise EvalError.new(rp), "voidEval failed: #{rp.to_s}"
143
132
  end
144
- # warning: we are not checking or using the length - we assume that only the one SEXP is returned. This is true for the current CMD_eval implementation, but may not be in the future. */
145
133
  end
146
- if pc.length>rxo
134
+
135
+ # NOT TESTED
136
+ def parse_eval_response(rp)
137
+ rxo=0
138
+ pc=rp.cont
139
+ if (rsrv_version>100) # /* since 0101 eval responds correctly by using DT_SEXP type/len header which is 4 bytes long */
140
+ rxo=4
141
+ # we should check parameter type (should be DT_SEXP) and fail if it's not
142
+ if (pc[0]!=Rserve::Protocol::DT_SEXP && pc[0]!=(Rserve::Protocol::DT_SEXP|Rserve::Protocol::DT_LARGE))
143
+ raise "Error while processing eval output: SEXP (type "+Rserve::Protocol::DT_SEXP+") expected but found result type "+pc[0].to_s+"."
144
+ end
145
+ if (pc[0]==(Rserve::Protocol::DT_SEXP|Rserve::Protocol::DT_LARGE))
146
+ rxo=8; # large data need skip of 8 bytes
147
+ end
148
+ # warning: we are not checking or using the length - we assume that only the one SEXP is returned. This is true for the current CMD_eval implementation, but may not be in the future. */
149
+ end
150
+ if pc.length>rxo
147
151
  rx=REXPFactory.new;
148
- rx.parse_REXP(pc, rxo);
149
- return rx.get_REXP();
152
+ rx.parse_REXP(pc, rxo);
153
+ return rx.get_REXP();
150
154
  else
151
155
  return nil
156
+ end
157
+ end
158
+
159
+ #assign a string value to a symbol in R. The symbol is created if it doesn't exist already.
160
+ # @param sym symbol name. Currently assign uses CMD_setSEXP command of Rserve, i.e. the symbol value is NOT parsed. It is the responsibility of the user to make sure that the symbol name is valid in R (recall the difference between a symbol and an expression!). In fact R will always create the symbol, but it may not be accessible (examples: "bar\nfoo" or "bar$foo").
161
+ # @param ct contents
162
+ def assign(sym, ct)
163
+ raise NotConnected if !connected? or rt.nil?
164
+ case ct
165
+ when String
166
+ assign_string(sym,ct)
167
+ when REXP
168
+ assign_rexp(sym,ct)
169
+ else
170
+ raise "Should be String or REXP"
171
+ end
172
+ end
173
+ def assign_string(sym,ct)
174
+ symn = sym.unpack("C*")
175
+ ctn = ct.unpack("C*")
176
+ sl=symn.length+1
177
+ cl=ctn.length+1
178
+ sl=(sl&0xfffffc)+4 if ((sl&3)>0) # make sure the symbol length is divisible by 4
179
+ cl=(cl&0xfffffc)+4 if ((cl&3)>0) # make sure the content length is divisible by 4
180
+ rq=Array.new(sl+4+cl+4)
181
+ symn.length.times {|i| rq[i+4]=symn[i]}
182
+ ic=symn.length
183
+ while (ic<sl)
184
+ rq[ic+4]=0
185
+ ic+=1
186
+ end
187
+ ctn.length.times {|i| rq[i+sl+8]=ctn[i]}
188
+ ic=ctn.length
189
+ while (ic<cl)
190
+ rq[ic+sl+8]=0
191
+ ic+=1
192
+ end
193
+ set_hdr(Rserve::Protocol::DT_STRING,sl,rq,0)
194
+ set_hdr(Rserve::Protocol::DT_STRING,cl,rq,sl+4)
195
+ rp=rt.request(:cmd=>Rserve::Protocol::CMD_setSEXP,:cont=>rq)
196
+ if (!rp.nil? and rp.ok?)
197
+ rp
198
+ else
199
+ raise "Assign Failed"
200
+ end
201
+ end
202
+ def assign_rexp(sym, rexp)
203
+ r = REXPFactory.new(rexp);
204
+ rl=r.get_binary_length();
205
+ symn=sym.unpack("C*");
206
+ sl=symn.length+1;
207
+ sl=(sl&0xfffffc)+4 if ((sl&3)>0) # make sure the symbol length is divisible by 4
208
+ rq=Array.new(sl+rl+((rl>0xfffff0) ? 12 : 8));
209
+ symn.length.times {|i| rq[i+4]=symn[i]}
210
+ ic=symn.length
211
+ while(ic<sl)
212
+ rq[ic+4]=0;
213
+ ic+=1;
214
+ end # pad with 0
215
+
216
+ set_hdr(Rserve::Protocol::DT_STRING,sl,rq,0)
217
+ set_hdr(Rserve::Protocol::DT_SEXP,rl,rq,sl+4);
218
+ r.get_binary_representation(rq, sl+((rl>0xfffff0) ? 12 : 8));
219
+ # puts "ASSIGN RQ: #{rq}" if $DEBUG
220
+ rp=rt.request(:cmd=>Rserve::Protocol::CMD_setSEXP, :cont=>rq)
221
+ if (!rp.nil? and rp.ok?)
222
+ rp
223
+ else
224
+ raise "Assign Failed"
225
+ end
226
+ end
227
+ def shutdown
228
+ raise NotConnected if !connected? or rt.nil?
229
+ rp=rt.request(:cmd=>Rserve::Protocol::CMD_shutdown)
230
+ if !rp.nil? and rp.ok?
231
+ true
232
+ else
233
+ raise "Shutdown failed"
234
+ end
152
235
  end
236
+
153
237
  end
154
238
  end
155
- end