rserve-client 0.1.0 → 0.1.3

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.tar.gz.sig CHANGED
Binary file
data/.autotest ADDED
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *~
2
+ doc
3
+ pkg
4
+ coverage
data/History.txt CHANGED
@@ -1,3 +1,19 @@
1
+ === 0.1.3 / 2010-05-24
2
+ * Better README.txt
3
+ * Implemented hash of options on Connection.new
4
+ * Better implementation of errors on connections
5
+ * REXP::Double#as_strings returns values as floats, not Rationals
6
+ * Bug fix on REXP#as_double_matrix.
7
+ * Added REXP#as_matrix, which return a standard library matrix from a R matrix
8
+ * New spec: REXP
9
+
10
+ === 0.1.2 / 2010-05-21
11
+ * List names works now
12
+
13
+ === 0.1.1 / 2010-05-21
14
+
15
+ YANKED
16
+
1
17
  === 0.1.0 / 2010-05-21
2
18
 
3
19
  * First operational version. Can void_eval and eval on vectors. List needs more work
data/Manifest.txt CHANGED
@@ -1,3 +1,5 @@
1
+ .autotest
2
+ .gitignore
1
3
  History.txt
2
4
  Manifest.txt
3
5
  README.txt
@@ -12,10 +14,13 @@ lib/rserve/rexp.rb
12
14
  lib/rserve/rexp/double.rb
13
15
  lib/rserve/rexp/genericvector.rb
14
16
  lib/rserve/rexp/integer.rb
17
+ lib/rserve/rexp/language.rb
15
18
  lib/rserve/rexp/list.rb
16
19
  lib/rserve/rexp/logical.rb
20
+ lib/rserve/rexp/null.rb
17
21
  lib/rserve/rexp/string.rb
18
22
  lib/rserve/rexp/symbol.rb
23
+ lib/rserve/rexp/unknown.rb
19
24
  lib/rserve/rexp/vector.rb
20
25
  lib/rserve/rlist.rb
21
26
  lib/rserve/talk.rb
@@ -24,6 +29,7 @@ spec/rserve_double_spec.rb
24
29
  spec/rserve_integer_spec.rb
25
30
  spec/rserve_packet_spec.rb
26
31
  spec/rserve_protocol_spec.rb
32
+ spec/rserve_rexp_spec.rb
27
33
  spec/rserve_rexpfactory_spec.rb
28
34
  spec/rserve_spec.rb
29
35
  spec/rserve_talk_spec.rb
data/README.txt CHANGED
@@ -9,16 +9,77 @@ Ruby client for Rserve, a Binary R server (http://www.rforge.net/Rserve/).
9
9
 
10
10
  Follows closely the new Java client API, but maintains all Ruby conventions when possible.
11
11
 
12
- Connection to Rserve not yet developed, but between June-July 2010 the system should be operational.
12
+ == FEATURES / LIMITATIONS
13
+
14
+ * 100% ruby
15
+ * Uses TCP/IP sockets to interchange data and commands
16
+ * Requires Rserve installed on the server machine. On debian / ubuntu, you should use <tt>sudo apt-get install r-cran-rserve</tt>
17
+ Pros:
18
+ * Work with Ruby 1.8, 1.9 and JRuby 1.5
19
+ * 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
+ * Fast
22
+ Cons:
23
+ * Requires Rserve
24
+ * No seamless integration with Ruby. You obtain data with an interface closer to R than Ruby.
25
+
26
+ == RELATED LIBRARIES (Ruby / R)
27
+
28
+ * Rinruby [http://rinruby.ddahl.org/]
29
+ * 100% ruby
30
+ * Uses pipes to send commands and evals
31
+ * Uses TCP/IP Sockets to send and retrieve data
32
+ * Pros:
33
+ * Doesn't requires anything but R
34
+ * Work with Ruby 1.8, 1.9 and JRuby 1.5
35
+ * All API tested
36
+ * Cons:
37
+ * VERY SLOW
38
+ * Very limited datatypes: Only vector and Matrix
39
+ * RSRuby
40
+ * C Extension for Ruby, linked to R's shared library
41
+ * Pros:
42
+ * Very fast data access
43
+ * Seamless integration with ruby. Every method and object is treated like a Ruby one
44
+ * Cons:
45
+ * Transformation between R and Ruby types aren't trivial
46
+ * Dependent of operating system, Ruby implementation and R version
47
+ * Not available for alternative implementations of Ruby (JRuby, IronRuby and Rubinius)
48
+
49
+
50
+ == TODO
51
+
52
+ Implements
53
+
54
+ * REXPs
55
+ * Enviroment
56
+ * ExpressionVector
57
+ * Factor
58
+ * Raw
59
+ * Reference
60
+ * S4
61
+ * Wrapper
62
+ * Sessions
63
+ * Authentification
64
+ * Original test
65
+
66
+ Spec
67
+
68
+ * Test suite on Rserve Java new API
69
+ * First tutorial on R
70
+
13
71
 
14
72
  == SYNOPSIS:
15
73
 
16
74
  require 'rserve'
17
75
  con=Rserve::Connection.new
18
- con.eval("x<-renorm(1)")
76
+ con.eval("x<-rnorm(1)")
19
77
  => #<Rserve::REXP::Double:0x000000011a13c8
20
78
  @payload=[(5339785585931699/2251799813685248)],
21
79
  @attr=nil>
80
+ con.eval("list(name='Fred')").as_list
81
+
82
+ => #<Rserve::Rlist:0x00000001bf82a8 @names=["name"], @data=[#<Rserve::REXP::String:0x00000001bf8548 @payload=["Fred"], @attr=nil>]>
22
83
 
23
84
  == REQUIREMENTS:
24
85
 
data/Rakefile CHANGED
@@ -5,10 +5,10 @@ require 'hoe'
5
5
  require 'rserve'
6
6
  Hoe.plugin :git
7
7
  Hoe.spec 'rserve-client' do
8
- self.testlib=:rspec
8
+ self.testlib=:rspec
9
9
  self.version=Rserve::VERSION
10
10
  self.rubyforge_name = 'ruby-statsample' # if different than 'rserve'
11
- self.developer('Claudio Bustos', 'clbustos_AT_gmail.com')
11
+ self.developer('Claudio Bustos', 'clbustos_AT_gmail.com')
12
12
  end
13
13
 
14
14
  # vim: syntax=ruby
data/lib/rserve.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'socket'
2
2
 
3
3
  module Rserve
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.3'
5
5
  end
6
6
 
7
7
 
@@ -1,12 +1,23 @@
1
1
  module Rserve
2
2
  class Connection < Rserve::Engine
3
3
  include Rserve::Protocol
4
- ServerNotInstalled=Class.new(Exception)
4
+
5
+ # :section: Exceptions
6
+ RserveNotStarted=Class.new(Exception)
7
+ ServerNotAvailable=Class.new(Exception)
5
8
  IncorrectServer=Class.new(Exception)
6
9
  IncorrectServerVersion=Class.new(Exception)
7
10
  IncorrectProtocol=Class.new(Exception)
8
11
  NotConnected=Class.new(Exception)
9
- EvalError=Class.new(Exception)
12
+ # Eval error
13
+ class EvalError < RuntimeError
14
+ attr_accessor :request_packet
15
+ def initialize(rp)
16
+ @request_packet=rp
17
+ end
18
+ end
19
+ attr_reader :hostname
20
+ attr_reader :port_number
10
21
  attr_reader :protocol
11
22
  attr_reader :last_error
12
23
  attr_reader :connected
@@ -21,41 +32,43 @@ module Rserve
21
32
  AT_plain=0
22
33
  AT_crypt=1
23
34
 
24
- def host
25
- @hostname
26
- end
27
35
  def initialize(opts=Hash.new)
28
- @auth_req=false
29
- @transfer_charset="UTF-8"
30
- @auth_type=AT_plain
31
- @hostname="127.0.0.1"
32
- @port_number=6311
33
- @tries=0
34
- @max_tries=5
36
+ @auth_req = opts.delete(:auth_req) || false
37
+ @transfer_charset = opts.delete(:transfer_charset) || "UTF-8"
38
+ @auth_type = opts.delete(:auth_type) || AT_plain
39
+ @hostname = opts.delete(:hostname) || "127.0.0.1"
40
+ @port_number = opts.delete(:port_number) || 6311
41
+ @max_tries = opts.delete(:max_tries) || 5
42
+
43
+ @tries = 0
35
44
  @connected=false
45
+
46
+
36
47
  begin
37
48
  #puts "Tryin to connect..."
38
49
  connect
39
50
  rescue Errno::ECONNREFUSED
40
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
41
66
  #puts "Init RServe"
42
- if system "R CMD Rserve"
43
- #puts "Ok"
44
- retry
45
- else
46
- raise ServerNotInstalled, "Rserve not installed"
47
- end
67
+
48
68
  else
49
69
  raise
50
70
  end
51
71
  end
52
-
53
- end
54
- def is
55
- @s
56
- end
57
- def os
58
- @s
59
72
  end
60
73
  def connect
61
74
  close if @connected
@@ -64,7 +77,7 @@ module Rserve
64
77
  #puts "Connected"
65
78
  # Accept first input
66
79
  input=@s.recv(32).unpack("a4a4a4a20")
67
- raise IncorrectServer,"Handshake failed: Rsrv signature expected, but received #{input[0]}" unless input[0]=="Rsrv"
80
+ raise IncorrectServer,"Handshake failed: Rsrv signature expected, but received [#{input[0]}]" unless input[0]=="Rsrv"
68
81
  @rsrv_version=input[1].to_i
69
82
  raise IncorrectServerVersion, "Handshake failed: The server uses more recent protocol than this client." if @rsrv_version>103
70
83
  @protocol=input[2]
@@ -78,9 +91,13 @@ module Rserve
78
91
  @connected
79
92
  end
80
93
  def close
81
- @s.shutdown if !@s.nil? and !@s.closed?
82
- @connected=false
83
- return true
94
+ if !@s.nil? and !@s.closed?
95
+ @s.close_write
96
+ @s.close_read
97
+ end
98
+ raise "Can't close socket" unless @s.closed?
99
+ @connected=false
100
+ true
84
101
  end
85
102
  def get_server_version
86
103
  @rsrv_version
@@ -94,7 +111,7 @@ module Rserve
94
111
  if !rp.nil? and rp.ok?
95
112
  true
96
113
  else
97
- raise EvalError, "voidEval failed: #{rp.to_s}"
114
+ raise EvalError.new(rp), "voidEval failed: #{rp.to_s}"
98
115
  end
99
116
 
100
117
  end
@@ -109,7 +126,7 @@ module Rserve
109
126
  if !rp.nil? and rp.ok?
110
127
  parse_eval_response(rp)
111
128
  else
112
- raise EvalError, "voidEval failed: #{rp.to_s}"
129
+ raise EvalError.new(rp), "voidEval failed: #{rp.to_s}"
113
130
  end
114
131
  end
115
132
  def parse_eval_response(rp)
data/lib/rserve/packet.rb CHANGED
@@ -2,6 +2,11 @@ module Rserve
2
2
  class Packet
3
3
  attr_reader :cont
4
4
  attr_reader :cmd
5
+
6
+ ERROR_DESCRIPTIONS={
7
+ 2=>'Invalid expression',
8
+ 127=>'Unknown variable/method'}
9
+
5
10
  def initialize(cmd, cont)
6
11
  raise "cont [#{cont.class} - #{cont.to_s}] should respond to :length" if !cont.nil? and !cont.respond_to? :length
7
12
  @cmd=cmd
@@ -19,11 +24,14 @@ module Rserve
19
24
  def stat
20
25
  (@cmd>>24)&127
21
26
  end
27
+ def get_error_description(stat)
28
+ ERROR_DESCRIPTIONS[stat]
29
+ end
22
30
  def to_s
23
31
  if error?
24
- status="status=error - #{stat}"
32
+ status="error:'#{get_error_description(stat)}'(#{stat})"
25
33
  else
26
- status="status=ok"
34
+ status="ok"
27
35
  end
28
36
  "Packet[cmd=#{@cmd},len="+((cont.nil?)?"<nil>":(""+cont.length.to_s))+", con='"+(cont.nil? ? "<nil>" : cont.pack("C*"))+"', status=#{status}]"
29
37
  end
@@ -11,13 +11,15 @@ module Rserve
11
11
  CMD_RESP=0x010000 # all responses have this flag set
12
12
  RESP_OK=(CMD_RESP|0x0001) # command succeeded; returned parameters depend on the command issued
13
13
  RESP_ERR=(CMD_RESP|0x0002) # command failed, check stats code attached string may describe the error
14
-
15
14
  ERR_auth_failed=0x41 # auth.failed or auth.requested but no login came. in case of authentification failure due to name/pwd mismatch, server may send CMD_accessDenied instead
16
15
  ERR_conn_broken=0x42 # connection closed or broken packet killed it */
16
+
17
17
  ERR_inv_cmd=0x43 # unsupported/invalid command */
18
18
  ERR_inv_par=0x44 # some parameters are invalid */
19
19
  ERR_Rerror=0x45 # R-error occured, usually followed by connection shutdown */
20
20
  ERR_IOerror=0x46 # I/O error */
21
+
22
+
21
23
  ERR_notOpen=0x47 # attempt to perform fileRead/Write on closed file */
22
24
  ERR_accessDenied=0x48 # this answer is also valid on CMD_login; otherwise it's sent if the server deosn;t allow the user to issue the specified command. (e.g. some server admins may block file I/O operations for some users)
23
25
  ERR_unsupportedCmd=0x49 # unsupported command */
@@ -27,7 +29,7 @@ module Rserve
27
29
  ERR_out_of_mem=0x4d # out of memory. the connection is usually closed after this error was sent
28
30
  ERR_ctrl_closed=0x4e # control pipe to the master process is closed or broken
29
31
  ERR_session_busy=0x50 # session is still busy */
30
- ERR_detach_failed=0x51 # unable to detach seesion (cannot determine peer IP or problems creating a listening socket for resume) */
32
+ ERR_detach_failed=0x51 # unable to detach session (cannot determine peer IP or problems creating a listening socket for resume) */
31
33
 
32
34
 
33
35
  CMD_login=0x001 # "name\npwd" : - */
@@ -89,6 +91,26 @@ module Rserve
89
91
  DT_SEXP=10 # encoded SEXP */
90
92
  DT_ARRAY=11 # array of objects (i.e. first 4 bytes specify how many subsequent objects are part of the array; 0 is legitimate) */
91
93
  DT_LARGE=64 # new in 0102: if this flag is set then the length of the object is coded as 56-bit integer enlarging the header by 4 bytes */
94
+
95
+ ERROR_DESCRIPTIONS={
96
+ ERR_auth_failed=>'auth.failed or auth.requested but no login came',
97
+ ERR_conn_broken=>'connection closed or broken packet killed it',
98
+ ERR_inv_cmd=>"unsupported/invalid command",
99
+ ERR_inv_par=>"some parameters are invalid",
100
+ ERR_Rerror=>"R-error",
101
+ ERR_IOerror=>"I/O error",
102
+ ERR_notOpen=>"attempt to perform fileRead/Write on closed file",
103
+ ERR_accessDenied=>"Access denied",
104
+ ERR_unsupportedCmd=>"unsupported command",
105
+ ERR_unknownCmd=>"unknown command",
106
+ ERR_data_overflow=>"data overflow",
107
+ ERR_object_too_big=>"requested object is too big",
108
+ ERR_out_of_mem=>"out of memory",
109
+ ERR_ctrl_closed=>"control pipe to the master process is closed or broken",
110
+ ERR_session_busy=>"session still busy",
111
+ ERR_detach_failed=>"unable to detach seesion"
112
+ } # error descriptions
113
+
92
114
 
93
115
  # writes bit-wise int to a byte buffer at specified position in Intel-endian form
94
116
  # Internal: byte buffer will be the result of unpack("CCCC") an integer.
@@ -75,6 +75,9 @@ module Rserve
75
75
  def get_attr
76
76
  attr.nil? ? nil : attr.cont
77
77
  end
78
+ def sexp_mismatch(type)
79
+ STDERR.puts("Warning: #{type} SEXP size mismatch")
80
+ end
78
81
  def initialize(*args)
79
82
  if args.size==0
80
83
 
@@ -129,7 +132,7 @@ module Rserve
129
132
  o=attr.parse_REXP(buf,o) if has_at
130
133
 
131
134
  if xt==XT_NULL
132
- @cont=REXP::Null(get_attr)
135
+ @cont=REXP::Null.new(get_attr)
133
136
  return o
134
137
  end
135
138
  if xt==XT_DOUBLE
@@ -137,7 +140,7 @@ module Rserve
137
140
  d=[longBitsToDouble(lr)]
138
141
  o+=8
139
142
  if(o!=eox)
140
- $STDERR.puts("Warning: double SEXP size mismatch\n");
143
+ sexp_mismatch("double")
141
144
  o=eox
142
145
  end
143
146
  @cont=REXP::Double.new(d,get_attr)
@@ -153,7 +156,7 @@ module Rserve
153
156
  i+=1
154
157
  end
155
158
  if(o!=eox)
156
- $STDERR.puts("Warning: double SEXP size mismatch\n");
159
+ sexp_mismatch("double")
157
160
  o=eox
158
161
  end
159
162
  @cont=REXP::Double.new(d,get_attr)
@@ -220,13 +223,29 @@ module Rserve
220
223
  $STDERR.puts "int SEXP size mismatch"
221
224
  o=eox
222
225
  end
223
- # hack for list. Not implemented yet!
224
- @cont=nil
225
- @cont=REXP::Integer(d,get_attr)
226
+
227
+ # hack for factors
228
+ if (!get_attr.nil?)
229
+ ca = get_attr().as_list().at("class");
230
+ ls = get_attr().as_list().at("levels");
231
+ if (!ca.nil? and !ls.nil? and ca.as_string=="factor")
232
+ # R uses 1-based index, Java (and Ruby) uses 0-based one
233
+ @cont = REXP::Factor.new(d, ls.as_strings(), get_attr)
234
+ xt = XT_FACTOR;
235
+ end
236
+ end
237
+
238
+
239
+
240
+ if @cont.nil?
241
+ @cont=REXP::Integer.new(d,get_attr)
242
+ end
226
243
  return o
227
244
  end
228
245
 
229
246
  # RAW not implemented yet
247
+
248
+
230
249
  if xt==XT_LIST_NOTAG or xt==XT_LIST_TAG or xt==XT_LANG_NOTAG or xt==XT_LANG_TAG
231
250
  lc=REXPFactory.new
232
251
  nf=REXPFactory.new
@@ -236,9 +255,11 @@ module Rserve
236
255
  o=lc.parse_REXP(buf,o)
237
256
  if(xt==XT_LIST_TAG or xt==XT_LANG_TAG)
238
257
  o=nf.parse_REXP(buf,o)
239
- name=nf.cont.as_strings if(nf.cont.symbol? or nf.cont.string?)
258
+
259
+ name=nf.cont.as_string if(nf.cont.symbol? or nf.cont.string?)
240
260
  end
241
261
  if name.nil?
262
+
242
263
  l.add(lc.cont)
243
264
  else
244
265
  l.put(name,lc.cont)
@@ -268,7 +289,7 @@ module Rserve
268
289
  v.push(xx.cont);
269
290
  end
270
291
  if (o!=eox)
271
- $STDERR.puts("Warning: int vector SEXP size mismatch\n");
292
+ sexp_mismatch("int")
272
293
  o=eox;
273
294
  end
274
295
  # fixup for lists since they're stored as attributes of vectors
@@ -282,7 +303,7 @@ module Rserve
282
303
  names=Array.new(aa.length)
283
304
  aa.length.times {|i| names[i]=aa[i].as_string}
284
305
  end
285
- l=RList.new(v,names)
306
+ l=Rlist.new(v,names)
286
307
  @cont=(xt==XT_VECTOR_EXP) ? REXP::ExpressionVector.new(l,get_attr) : REXP::GenericVector.new(l,get_attr)
287
308
  else
288
309
 
@@ -341,6 +362,7 @@ module Rserve
341
362
  end
342
363
  if (xt==XT_STR)
343
364
  @cont = REXP::String.new(buf[o,i-o].pack("C*"), get_attr);
365
+
344
366
  else
345
367
  @cont = REXP::Symbol.new(buf[o,i-o].pack("C*"))
346
368
  end
@@ -348,8 +370,27 @@ module Rserve
348
370
  o = eox;
349
371
  return o;
350
372
  end
373
+
374
+ #not implemented XT_SYM,
375
+
376
+
377
+ if (xt==XT_CLOS)
378
+ o=eox;
379
+ return o;
380
+ end
381
+
382
+ if (xt==XT_UNKNOWN)
383
+ @cont = REXP::Unknown.new(get_int(buf,o), get_attr)
384
+ o=eox;
385
+ return o;
386
+ end
387
+
388
+ if (xt==XT_S4)
389
+ @cont = new REXP::S4(get_attr)
390
+ o=eox
391
+ return o;
392
+ end
351
393
 
352
- #not implemented XT_SYM, XT_CLOSS, XT_UNKNOWN, XT_S4
353
394
  @cont=nil
354
395
  o=eox
355
396
  raise "Unhandled type:#{xt}"
data/lib/rserve/rexp.rb CHANGED
@@ -121,7 +121,7 @@ module Rserve
121
121
  # * @return attribute value or <code>null</code> if the attribute does not exist */
122
122
 
123
123
  def get_attribute(name)
124
- nil if @attr.nil? or !@attr.is_list?
124
+ nil if @attr.nil? or !@attr.list?
125
125
  @attr.as_list[name]
126
126
  end
127
127
 
@@ -129,7 +129,7 @@ module Rserve
129
129
  # * @param name attribute name
130
130
  # * @return <code>true</code> if the attribute exists, <code>false</code> otherwise */
131
131
  def has_attribute? (name)
132
- return (!@attr.nil? and @attr.is_list and !@attr.as_list[name].nil?);
132
+ return (!@attr.nil? and @attr.list? and !@attr.as_list[name].nil?);
133
133
  end
134
134
 
135
135
 
@@ -165,37 +165,39 @@ module Rserve
165
165
 
166
166
  # returns a string description of the object
167
167
  # @return string describing the object - it can be of an arbitrary form and used only for debugging (do not confuse with {@link #asString()} for accessing string REXPs) */
168
- #def to_s
169
- #return super+((!@attr.nil?) ? "+" : "");
170
- # end
168
+ def to_s
169
+ return (!@attr.nil?) ? "+" : ""
170
+ end
171
171
 
172
172
  # returns representation that it useful for debugging (e.g. it includes attributes and may include vector values -- see {@link #maxDebugItems})
173
173
  # @return extended description of the obejct -- it may include vector values
174
- #def inspect {
175
- # (!@attr.nil?) ? (("<"+@attr.inspect()+">")+to_s()) : to_s;
176
- #}
177
-
174
+ def to_debug_string
175
+ (!@attr.nil?) ? (("<"+@attr.to_debug_string()+">")+to_s()) : to_s
176
+ end
177
+
178
+
179
+
178
180
  #//======= complex convenience methods
179
181
  # returns the content of the REXP as a ruby matrix of doubles (2D-array: m[rows][cols]). You could use Matrix.rows(result) to create
180
- # a ruby dfeault library matrix.
181
- # Matrix(c.eval("matrix(c(1,2,3,4,5,6),2,3)").asDoubleMatrix());</code>
182
+ # a ruby matrix.
183
+ # Matrix(c.eval("matrix(c(1,2,3,4,5,6),2,3)").as_double_matrix());</code>
184
+ #
182
185
  # @return 2D array of doubles in the form double[rows][cols] or <code>null</code> if the contents is no 2-dimensional matrix of doubles */
183
186
  def as_double_matrix()
184
187
  ct = as_doubles()
185
- dim =get_attribute "dim"
188
+ dim = get_attribute "dim"
186
189
  raise MismatchException, "matrix (dim attribute missing)" if dim.nil?
187
190
  ds = dim.as_integers
188
191
  raise MismatchException, "matrix (wrong dimensionality)" if (ds.length!=2)
189
- m = ds[0], n = ds[1]
192
+ m,n = ds[0], ds[1]
190
193
  # R stores matrices as matrix(c(1,2,3,4),2,2) = col1:(1,2), col2:(3,4)
191
194
  # we need to copy everything, since we create 2d array from 1d array
192
195
  r=m.times.map {|i| n.times.map {|j| ct[j*n+i]}}
193
196
  end
194
- def to_s
195
- (@attr.nil? ? "" : "+")
196
- end
197
- def to_debug_string
198
- (@attr.nil? ? "" : "<"+@attr.to_debug_string+">")
197
+ # Returns a standard library's matrix
198
+ def as_matrix
199
+ require 'matrix'
200
+ Matrix.rows(as_double_matrix)
199
201
  end
200
202
 
201
203
  # :section: tools
@@ -224,10 +226,15 @@ module Rserve
224
226
  end
225
227
 
226
228
  require 'rserve/rexp/vector'
229
+ require 'rserve/rexp/null'
230
+
227
231
  require 'rserve/rexp/genericvector'
228
232
  require 'rserve/rexp/integer'
229
233
  require 'rserve/rexp/double'
230
234
  require 'rserve/rexp/list'
235
+ require 'rserve/rexp/language'
236
+ require 'rserve/rexp/unknown'
237
+
231
238
  require 'rserve/rexp/logical'
232
239
  require 'rserve/rexp/string'
233
240
  require 'rserve/rexp/symbol'
@@ -30,7 +30,7 @@ module Rserve
30
30
  @payload
31
31
  end
32
32
  def as_strings
33
- @payload.map(&:to_s)
33
+ @payload.map {|v| v.to_f.to_s}
34
34
  end
35
35
 
36
36
  def na?(value=nil)
@@ -0,0 +1,13 @@
1
+ module Rserve
2
+ class REXP
3
+ class Language < REXP::List
4
+ def initialize(list, attr=nil)
5
+ super(list,attr)
6
+
7
+ end
8
+ def language?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
@@ -25,8 +25,13 @@ module Rserve
25
25
  super+(as_list.named? ? "named":"")
26
26
  end
27
27
  def to_debug_string
28
- t=super
29
- t << "{" << @payload.map {|k,v| "#{k}=#{v}"}.join(",\n") << "}"
28
+ t=super+(as_list.named? ? "named":"")
29
+ if @payload.named?
30
+ inner="{"+@payload.size.times.map {|i| "#{@payload.names[i]}=#{@payload.data[i].to_debug_string}"}.join(",")+"}"
31
+ else
32
+ inner="{"+@payload.size.times.map {|i| "#{@payload.data[i].to_debug_string}"}.join(",")+"}"
33
+ end
34
+ t+inner
30
35
  end
31
36
  end
32
37
  end
@@ -0,0 +1,17 @@
1
+ module Rserve
2
+ class REXP
3
+ # represents a NULL object in R.
4
+ # Note: there is a slight asymmetry - in R NULL is represented by a zero-length pairlist. For this reason <code>REXPNull</code> returns <code>true</code> for {@link #isList()} and {@link #asList()} will return an empty list. Nonetheless <code>REXPList</code> of the length 0 will NOT return <code>true</code> in {@link #isNull()} (currently), becasue it is considered a different object in Java. These nuances are still subject to change, because it's not clear how it should be treated. At any rate use <code>REXPNull</code> instead of empty <code>REXPList</code> if NULL is the intended value.
5
+ class Null < REXP
6
+ def null?
7
+ true
8
+ end
9
+ def list?
10
+ true
11
+ end
12
+ def as_list
13
+ Rlist.new
14
+ end
15
+ end
16
+ end
17
+ end
@@ -4,7 +4,7 @@ module Rserve
4
4
  attr_reader :name
5
5
  def initialize(name)
6
6
  super()
7
- @name== name.nil? ? "":name
7
+ @name= (name.nil?) ? "" : name
8
8
  end
9
9
  def symbol?
10
10
  true
@@ -0,0 +1,14 @@
1
+ module Rserve
2
+ class REXP
3
+ class Unknown < REXP
4
+ attr_reader :type
5
+ def initialize(type,attr=nil)
6
+ @type=type
7
+ super(attr)
8
+ end
9
+ def to_s
10
+ super()+"[#{@type}]"
11
+ end
12
+ end
13
+ end
14
+ end
data/lib/rserve/rlist.rb CHANGED
@@ -37,7 +37,7 @@ module Rserve
37
37
  end
38
38
 
39
39
  def at(v)
40
- @data[v]
40
+ self.[](v)
41
41
  end
42
42
 
43
43
  def key_at(v)
@@ -71,25 +71,22 @@ module Rserve
71
71
  end
72
72
 
73
73
  def put(key,value)
74
- if key.nil?
75
- add(value)
76
- return nil
77
- end
78
- if !names.nil?
79
- p=names.index(key)
80
- if !p.nil?
81
- return @names[p]=value
74
+ puts "rlist.put(#{key},#{value})" if $DEBUG
75
+ if key.nil?
76
+ add(value)
77
+ return nil
82
78
  end
83
- end
84
- i=size
85
- add(value)
86
- if(@names.nil?)
87
- @names=Array.new(i+1)
88
- while(@names.size<i) do
89
- @names.push(nil)
79
+ if !@names.nil?
80
+ p=names.index(key)
81
+ if !p.nil?
82
+ return @names[p]=value
83
+ end
90
84
  end
85
+ puts "add key and value" if $DEBUG
86
+ i=size
87
+ @names=Array.new(i) if(@names.nil?)
88
+ @data.push(value)
91
89
  @names.push(key)
92
- end
93
90
 
94
91
  end
95
92
  def add(a,b=nil)
@@ -1,20 +1,28 @@
1
1
  require File.dirname(__FILE__)+"/spec_helper.rb"
2
2
  describe Rserve::Connection do
3
3
  describe "opening and closing" do
4
- it "should be open a connection and receive ID-String" do
4
+ before do
5
5
  @r=Rserve::Connection.new()
6
+ end
7
+ it "should be open a connection and receive ID-String" do
6
8
  @r.get_server_version.should==103
7
9
  @r.protocol.should=="QAP1"
8
10
  @r.last_error.should=="OK"
9
11
  @r.rt.should be_instance_of(Rserve::Talk)
10
12
  end
13
+ it "should raise ServerNotAvailable if started another instance on another port" do
14
+ lambda {Rserve::Connection.new(:port_number=>6700)}.should raise_exception(Rserve::Connection::ServerNotAvailable)
15
+ end
11
16
  it "should quit correctly" do
12
- @r=Rserve::Connection.new
13
17
  @r.should be_connected
14
18
  @r.close.should be_true
15
19
  @r.should_not be_connected
16
20
  @r.close.should be_true
17
21
  end
22
+ it "raise an error if eval is clased after closed" do
23
+ @r.close
24
+ lambda {@r.eval("TRUE")}.should raise_exception(Rserve::Connection::NotConnected)
25
+ end
18
26
  after do
19
27
  @r.close if @r.connected?
20
28
  end
@@ -24,20 +32,32 @@ describe Rserve::Connection do
24
32
  before do
25
33
  @r=Rserve::Connection.new
26
34
  end
27
- it "should eval_void correctly" do
35
+ it "method eval_void should return true with correct expression" do
28
36
  @r.void_eval("x<-1").should be_true
29
37
  end
30
- it "should eval correctly" do
31
- la=@r.eval("c(TRUE,TRUE,FALSE,FALSE)")
38
+ it "method eval_void should raise an error with an incorrect expression" do
39
+ lambda {@r.void_eval("x<-")}.should raise_exception(Rserve::Connection::EvalError) {|e| e.request_packet.stat.should==2}
40
+ lambda {@r.void_eval("as.stt(c(1))")}.should raise_exception(Rserve::Connection::EvalError) {|e|
41
+ e.request_packet.stat.should==127}
42
+ end
43
+
44
+ it "method eval should return a simple object" do
45
+ la=@r.eval("TRUE")
32
46
  la.should be_instance_of(Rserve::REXP::Logical)
33
- la.true?.should==[true,true,false,false]
47
+ la.true?.should==[true]
48
+ end
49
+
50
+ it "method eval should raise an error with an incorrect expression" do
51
+ lambda {@r.eval("x<-")}.should raise_exception(Rserve::Connection::EvalError) {|e| e.request_packet.stat.should==2}
52
+ lambda {@r.eval("as.stt(c(1))")}.should raise_exception(Rserve::Connection::EvalError) {|e|
53
+ e.request_packet.stat.should==127}
34
54
  end
55
+
35
56
  it "should eval_void and eval correctly" do
36
57
  @r.void_eval("x<-c(TRUE,FALSE)").should be_true
37
58
  la=@r.eval("x")
38
59
  la.should be_instance_of(Rserve::REXP::Logical)
39
60
  la.true?.should==[true,false]
40
-
41
61
  end
42
62
  end
43
63
  end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__)+"/spec_helper.rb"
2
+
3
+ describe Rserve::REXP do
4
+ before do
5
+ @r=Rserve::Connection.new
6
+ end
7
+ describe "with matrix" do
8
+ before {@m=@r.eval('matrix(c(1,2,3,4,5,6,7,8,9), 3,3)') }
9
+ it "method as_integers should return correct values for original vector" do
10
+ @m.as_integers.should==[1,2,3,4,5,6,7,8,9] # verification
11
+ end
12
+ it "method dim should return correct dimensions" do
13
+ @m.dim.should==[3,3]
14
+ end
15
+ it "method as_double_matrix returns a valid double matrix" do
16
+ @m.as_double_matrix.should==[[1,4,7],[2,5,8],[3,6,9]]
17
+ end
18
+ it "method as_matrix returns a valid standard library matrix" do
19
+ @m=@r.eval('matrix(c(1,2,3,4,5,6,7,8,9), 3,3)')
20
+ @m.as_matrix.should==Matrix[[1,4,7],[2,5,8],[3,6,9]]
21
+ end
22
+ end
23
+ describe "common methods" do
24
+ before do
25
+ @v=@r.eval("c(1.5,2.5,3.5)")
26
+ @l=@r.eval("list(at='val')")
27
+ end
28
+ it "method as_integer should return first value as integer" do
29
+ @v.as_integer.should==1
30
+ end
31
+ it "method as_double should return first value as float" do
32
+ @v.as_double.should==1.5
33
+ end
34
+ it "method as_string should return first value as string (float representation)" do
35
+ @v.as_string.should=="1.5"
36
+ end
37
+ it "method has_attribute? should return false for non-lists" do
38
+ @v.has_attribute?('randomattribute').should be_false
39
+ end
40
+ it "method has_attribute? should return true for existing value" do
41
+ @l.has_attribute?('names').should be_true
42
+ end
43
+ it "method has_attribute? should return false for non existing value" do
44
+ @l.has_attribute?('at2').should be_false
45
+ end
46
+ it "method get_attrivute should return correct value for attribute" do
47
+ @l.get_attribute('names').as_strings.should==['at']
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -37,8 +37,10 @@ describe Rserve::Protocol::REXPFactory do
37
37
  end
38
38
  it "should process list" do
39
39
  require 'pp'
40
- la=@r.eval("list(name='Fred', wife='Mary', no.children=3, child.ages=c(4,7,9))")
41
- pp la
40
+ la=@r.eval("list(name='Fred',age=30,10,20,kids=c(1,2,3))")
41
+ la.should be_list
42
+ la.should be_recursive
43
+ la.as_list.names.should==['name','age','','','kids']
42
44
  end
43
45
 
44
46
  end
data/spec/rserve_spec.rb CHANGED
@@ -4,15 +4,26 @@ describe Rserve do
4
4
  before do
5
5
  @r=Rserve::Connection.new()
6
6
  end
7
- it "should receive a valid response to valid expression" do
8
- #response=@r.send(Rserve::CMD_eval, @r.string("1:5"))
9
- #p response
10
- #response[:response].should==Rserve::RESP_OK
11
- end
12
- it "should receive error on invalid expression" do
13
- #response=@r.send(Rserve::CMD_eval, @r.string("a|sdsds<-||#@r3"))
14
- #p response
15
- #response[:response].should==Rserve::RESP_ERR
7
+ it "should calcule a basic LR without hazzle" do
8
+ script=<<-EOF
9
+ ## Disease severity as a function of temperature
10
+
11
+ # Response variable, disease severity
12
+ diseasesev<-c(1.9,3.1,3.3,4.8,5.3,6.1,6.4,7.6,9.8,12.4)
13
+
14
+ # Predictor variable, (Centigrade)
15
+ temperature<-c(2,1,5,5,20,20,23,10,30,25)
16
+ ## For convenience, the data may be formatted into a dataframe
17
+ severity <- as.data.frame(cbind(diseasesev,temperature))
18
+
19
+ ## Fit a linear model for the data and summarize the output from function lm()
20
+ severity.lm <- lm(diseasesev~temperature,data=severity)
21
+ EOF
22
+ @r.void_eval(script)
23
+ @r.eval('severity.lm').should be_true
24
+ @r.eval('summary(severity.lm)').should be_true
25
+
26
+
16
27
  end
17
28
 
18
29
  end
data/spec/spec_helper.rb CHANGED
@@ -2,3 +2,4 @@ $:.unshift(File.dirname(__FILE__)+"/../lib")
2
2
  require 'spec'
3
3
  require 'spec/autorun'
4
4
  require 'rserve'
5
+ require 'matrix'
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 0
9
- version: 0.1.0
8
+ - 3
9
+ version: 0.1.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Claudio Bustos
@@ -35,7 +35,7 @@ cert_chain:
35
35
  rpP0jjs0
36
36
  -----END CERTIFICATE-----
37
37
 
38
- date: 2010-05-21 00:00:00 -04:00
38
+ date: 2010-05-24 00:00:00 -04:00
39
39
  default_executable:
40
40
  dependencies:
41
41
  - !ruby/object:Gem::Dependency
@@ -70,8 +70,6 @@ description: |-
70
70
  Ruby client for Rserve, a Binary R server (http://www.rforge.net/Rserve/).
71
71
 
72
72
  Follows closely the new Java client API, but maintains all Ruby conventions when possible.
73
-
74
- Connection to Rserve not yet developed, but between June-July 2010 the system should be operational.
75
73
  email:
76
74
  - clbustos_AT_gmail.com
77
75
  executables: []
@@ -83,6 +81,8 @@ extra_rdoc_files:
83
81
  - Manifest.txt
84
82
  - README.txt
85
83
  files:
84
+ - .autotest
85
+ - .gitignore
86
86
  - History.txt
87
87
  - Manifest.txt
88
88
  - README.txt
@@ -97,10 +97,13 @@ files:
97
97
  - lib/rserve/rexp/double.rb
98
98
  - lib/rserve/rexp/genericvector.rb
99
99
  - lib/rserve/rexp/integer.rb
100
+ - lib/rserve/rexp/language.rb
100
101
  - lib/rserve/rexp/list.rb
101
102
  - lib/rserve/rexp/logical.rb
103
+ - lib/rserve/rexp/null.rb
102
104
  - lib/rserve/rexp/string.rb
103
105
  - lib/rserve/rexp/symbol.rb
106
+ - lib/rserve/rexp/unknown.rb
104
107
  - lib/rserve/rexp/vector.rb
105
108
  - lib/rserve/rlist.rb
106
109
  - lib/rserve/talk.rb
@@ -109,6 +112,7 @@ files:
109
112
  - spec/rserve_integer_spec.rb
110
113
  - spec/rserve_packet_spec.rb
111
114
  - spec/rserve_protocol_spec.rb
115
+ - spec/rserve_rexp_spec.rb
112
116
  - spec/rserve_rexpfactory_spec.rb
113
117
  - spec/rserve_spec.rb
114
118
  - spec/rserve_talk_spec.rb
metadata.gz.sig CHANGED
Binary file