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 +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
|