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 +0 -0
- data/.autotest +23 -0
- data/.gitignore +4 -0
- data/History.txt +16 -0
- data/Manifest.txt +6 -0
- data/README.txt +63 -2
- data/Rakefile +2 -2
- data/lib/rserve.rb +1 -1
- data/lib/rserve/connection.rb +48 -31
- data/lib/rserve/packet.rb +10 -2
- data/lib/rserve/protocol.rb +24 -2
- data/lib/rserve/protocol/rexpfactory.rb +51 -10
- data/lib/rserve/rexp.rb +25 -18
- data/lib/rserve/rexp/double.rb +1 -1
- data/lib/rserve/rexp/language.rb +13 -0
- data/lib/rserve/rexp/list.rb +7 -2
- data/lib/rserve/rexp/null.rb +17 -0
- data/lib/rserve/rexp/symbol.rb +1 -1
- data/lib/rserve/rexp/unknown.rb +14 -0
- data/lib/rserve/rlist.rb +14 -17
- data/spec/rserve_connection_spec.rb +27 -7
- data/spec/rserve_rexp_spec.rb +52 -0
- data/spec/rserve_rexpfactory_spec.rb +4 -2
- data/spec/rserve_spec.rb +20 -9
- data/spec/spec_helper.rb +1 -0
- metadata +9 -5
- metadata.gz.sig +0 -0
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
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
|
-
|
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<-
|
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
|
-
|
8
|
+
self.testlib=:rspec
|
9
9
|
self.version=Rserve::VERSION
|
10
10
|
self.rubyforge_name = 'ruby-statsample' # if different than 'rserve'
|
11
|
-
|
11
|
+
self.developer('Claudio Bustos', 'clbustos_AT_gmail.com')
|
12
12
|
end
|
13
13
|
|
14
14
|
# vim: syntax=ruby
|
data/lib/rserve.rb
CHANGED
data/lib/rserve/connection.rb
CHANGED
@@ -1,12 +1,23 @@
|
|
1
1
|
module Rserve
|
2
2
|
class Connection < Rserve::Engine
|
3
3
|
include Rserve::Protocol
|
4
|
-
|
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
|
-
|
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
|
-
@
|
34
|
-
|
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
|
-
|
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
|
-
|
82
|
-
@
|
83
|
-
|
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="
|
32
|
+
status="error:'#{get_error_description(stat)}'(#{stat})"
|
25
33
|
else
|
26
|
-
status="
|
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
|
data/lib/rserve/protocol.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
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
|
-
|
224
|
-
|
225
|
-
|
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
|
-
|
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
|
-
|
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=
|
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.
|
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.
|
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
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
175
|
-
|
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
|
181
|
-
# Matrix(c.eval("matrix(c(1,2,3,4,5,6),2,3)").
|
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],
|
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
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
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'
|
data/lib/rserve/rexp/double.rb
CHANGED
data/lib/rserve/rexp/list.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/rserve/rexp/symbol.rb
CHANGED
data/lib/rserve/rlist.rb
CHANGED
@@ -37,7 +37,7 @@ module Rserve
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def at(v)
|
40
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
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 "
|
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
|
31
|
-
|
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
|
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',
|
41
|
-
|
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
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
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-
|
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
|