rserve-client 0.1.0 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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