rserve-client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/History.txt +3 -0
- data/Manifest.txt +31 -0
- data/README.txt +50 -0
- data/Rakefile +14 -0
- data/lib/rserve.rb +14 -0
- data/lib/rserve/connection.rb +138 -0
- data/lib/rserve/engine.rb +40 -0
- data/lib/rserve/packet.rb +31 -0
- data/lib/rserve/protocol.rb +183 -0
- data/lib/rserve/protocol/rexpfactory.rb +360 -0
- data/lib/rserve/rexp.rb +234 -0
- data/lib/rserve/rexp/double.rb +46 -0
- data/lib/rserve/rexp/genericvector.rb +29 -0
- data/lib/rserve/rexp/integer.rb +49 -0
- data/lib/rserve/rexp/list.rb +33 -0
- data/lib/rserve/rexp/logical.rb +48 -0
- data/lib/rserve/rexp/string.rb +33 -0
- data/lib/rserve/rexp/symbol.rb +26 -0
- data/lib/rserve/rexp/vector.rb +23 -0
- data/lib/rserve/rlist.rb +124 -0
- data/lib/rserve/talk.rb +123 -0
- data/spec/rserve_connection_spec.rb +43 -0
- data/spec/rserve_double_spec.rb +55 -0
- data/spec/rserve_integer_spec.rb +55 -0
- data/spec/rserve_packet_spec.rb +19 -0
- data/spec/rserve_protocol_spec.rb +75 -0
- data/spec/rserve_rexpfactory_spec.rb +44 -0
- data/spec/rserve_spec.rb +18 -0
- data/spec/rserve_talk_spec.rb +73 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +4 -0
- metadata +149 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
module Rserve
|
2
|
+
class REXP
|
3
|
+
class Double < REXP::Vector
|
4
|
+
attr_reader :payload
|
5
|
+
NA = 0x7ff00000000007a2;
|
6
|
+
def initialize(data, attrs=nil)
|
7
|
+
@payload=case data
|
8
|
+
when Numeric
|
9
|
+
[data.to_f]
|
10
|
+
when Array
|
11
|
+
data
|
12
|
+
else
|
13
|
+
raise ArgumentError, "Should be Numeric or Array"
|
14
|
+
end
|
15
|
+
super(attrs)
|
16
|
+
end
|
17
|
+
def length
|
18
|
+
payload.length
|
19
|
+
end
|
20
|
+
def integer?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
def numeric?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
def as_integers
|
27
|
+
@payload.map(&:to_i)
|
28
|
+
end
|
29
|
+
def as_doubles
|
30
|
+
@payload
|
31
|
+
end
|
32
|
+
def as_strings
|
33
|
+
@payload.map(&:to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
def na?(value=nil)
|
37
|
+
return value == NA unless value.nil?
|
38
|
+
@payload.map {|v| v==NA}
|
39
|
+
end
|
40
|
+
def to_debug_string
|
41
|
+
t=super
|
42
|
+
t << "{" << @payload.map(&:to_s).join(",") << "}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rserve
|
2
|
+
class REXP
|
3
|
+
class GenericVector < REXP::Vector
|
4
|
+
attr_reader :payload
|
5
|
+
def initialize(list,attr=nil)
|
6
|
+
super(attr)
|
7
|
+
@payload=list.nil? ? Rlist.new() : list
|
8
|
+
|
9
|
+
if (payload.named?)
|
10
|
+
@attr = REXP::List.new(
|
11
|
+
Rlist.new([REXP::String.new(payload.keys())],
|
12
|
+
["names"]));
|
13
|
+
end
|
14
|
+
end
|
15
|
+
def length
|
16
|
+
@payload.size
|
17
|
+
end
|
18
|
+
def list?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
def recursive?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
def as_list
|
25
|
+
@payload
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Rserve
|
2
|
+
class REXP
|
3
|
+
class Integer < REXP::Vector
|
4
|
+
public_class_method :new
|
5
|
+
attr_reader :payload
|
6
|
+
NA = -2147483648;
|
7
|
+
def initialize(data, attrs=nil)
|
8
|
+
@payload=case data
|
9
|
+
when Integer
|
10
|
+
[data]
|
11
|
+
when Numeric
|
12
|
+
[data.to_i]
|
13
|
+
when Array
|
14
|
+
data
|
15
|
+
else
|
16
|
+
raise ArgumentError, "Should be Numeric or Array"
|
17
|
+
end
|
18
|
+
super(attrs)
|
19
|
+
end
|
20
|
+
def length
|
21
|
+
payload.length
|
22
|
+
end
|
23
|
+
def integer?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
def numeric?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
def as_integers
|
30
|
+
@payload
|
31
|
+
end
|
32
|
+
def as_doubles
|
33
|
+
@payload.map(&:to_f)
|
34
|
+
end
|
35
|
+
def as_strings
|
36
|
+
@payload.map(&:to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
def na?(value=nil)
|
40
|
+
return value == NA unless value.nil?
|
41
|
+
@payload.map {|v| v==NA}
|
42
|
+
end
|
43
|
+
def to_debug_string
|
44
|
+
t=super
|
45
|
+
t << "{" << @payload.map(&:to_s).join(",") << "}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rserve
|
2
|
+
class REXP
|
3
|
+
class List < REXP::Vector
|
4
|
+
attr_reader :payload
|
5
|
+
def initialize(list, attrs=nil)
|
6
|
+
@payload=list.nil? ? RList.new : list
|
7
|
+
super(attrs)
|
8
|
+
end
|
9
|
+
def length
|
10
|
+
payload.size
|
11
|
+
end
|
12
|
+
def list?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
def pair_list?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
def recursive?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
def as_list
|
22
|
+
@payload
|
23
|
+
end
|
24
|
+
def to_s
|
25
|
+
super+(as_list.named? ? "named":"")
|
26
|
+
end
|
27
|
+
def to_debug_string
|
28
|
+
t=super
|
29
|
+
t << "{" << @payload.map {|k,v| "#{k}=#{v}"}.join(",\n") << "}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Rserve
|
2
|
+
class REXP
|
3
|
+
class Logical < REXP::Vector
|
4
|
+
attr_reader :payload
|
5
|
+
NA_internal = -2147483648;
|
6
|
+
NA=-128
|
7
|
+
TRUE=1
|
8
|
+
FALSE=0
|
9
|
+
def na?(value=nil)
|
10
|
+
if value
|
11
|
+
value==NA
|
12
|
+
else
|
13
|
+
@payload.map {|v| v==NA}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def initialize(l,attr=nil)
|
17
|
+
super(attr)
|
18
|
+
if l.is_a? Array
|
19
|
+
@payload=l.map {|l| l==1 ? TRUE : FALSE}
|
20
|
+
else
|
21
|
+
@payload = [ l==1 ? TRUE : FALSE ]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
def length
|
25
|
+
@payload.length
|
26
|
+
end
|
27
|
+
def logical?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
def as_integers
|
31
|
+
@payload.map {|v| v==NA ? REXP::Integer::NA : ( v == FALSE ? 0 : 1 )}
|
32
|
+
end
|
33
|
+
def as_doubles
|
34
|
+
@payload.map {|v| v==NA ? REXP::Double::NA : ( v == FALSE ? 0.0 : 1.0 )}
|
35
|
+
end
|
36
|
+
def as_strings
|
37
|
+
@payload.map {|v| v==NA ? REXP::Double::NA : ( v == FALSE ? "FALSE" : "TRUE" )}
|
38
|
+
end
|
39
|
+
def true?
|
40
|
+
@payload.map {|v| v==TRUE}
|
41
|
+
end
|
42
|
+
def false?
|
43
|
+
@payload.map {|v| v==FALSE}
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rserve
|
2
|
+
class REXP
|
3
|
+
class String < REXP::Vector
|
4
|
+
attr_reader :payload
|
5
|
+
def initialize(data, attrs=nil)
|
6
|
+
@payload=case data
|
7
|
+
when Array
|
8
|
+
data.map {|v| v.to_s}
|
9
|
+
else
|
10
|
+
[data.to_s]
|
11
|
+
end
|
12
|
+
super(attrs)
|
13
|
+
end
|
14
|
+
def length
|
15
|
+
payload.length
|
16
|
+
end
|
17
|
+
def string?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
def as_strings
|
21
|
+
@payload
|
22
|
+
end
|
23
|
+
|
24
|
+
def na?
|
25
|
+
@payload.map {|v| v.nil}
|
26
|
+
end
|
27
|
+
def to_debug_string
|
28
|
+
t=super
|
29
|
+
t << "{" << @payload.map(&:to_s).join(",") << "}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Rserve
|
2
|
+
class REXP
|
3
|
+
class Symbol < REXP::Vector
|
4
|
+
attr_reader :name
|
5
|
+
def initialize(name)
|
6
|
+
super()
|
7
|
+
@name== name.nil? ? "":name
|
8
|
+
end
|
9
|
+
def symbol?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
def as_string
|
13
|
+
@name
|
14
|
+
end
|
15
|
+
def as_strings
|
16
|
+
[@name]
|
17
|
+
end
|
18
|
+
def to_s
|
19
|
+
super+"["+name+"]"
|
20
|
+
end
|
21
|
+
def to_debug_string
|
22
|
+
super+"["+name+"]"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rserve
|
2
|
+
class REXP
|
3
|
+
class Vector < REXP
|
4
|
+
# returns the length of the vector (i.e. the number of elements)
|
5
|
+
# @return length of the vector
|
6
|
+
def length;
|
7
|
+
end;
|
8
|
+
def vector?;
|
9
|
+
true;
|
10
|
+
end
|
11
|
+
# returns a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values
|
12
|
+
# @return a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values */
|
13
|
+
def na?
|
14
|
+
end
|
15
|
+
def to_s
|
16
|
+
super+"[#{length}]"
|
17
|
+
end
|
18
|
+
def to_debug_string
|
19
|
+
super+"[#{length}]"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/rserve/rlist.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
module Rserve
|
2
|
+
class Rlist
|
3
|
+
include Enumerable
|
4
|
+
attr_reader :names
|
5
|
+
attr_reader :data
|
6
|
+
def initialize(data=nil,n=nil)
|
7
|
+
@names=nil
|
8
|
+
@data=[]
|
9
|
+
if data
|
10
|
+
@data=case data
|
11
|
+
when Array
|
12
|
+
data
|
13
|
+
when Numeric
|
14
|
+
Array.new(data)
|
15
|
+
else
|
16
|
+
raise ArgumentError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
if n
|
20
|
+
@names=Array.new(@data.size)
|
21
|
+
n.each_index {|i| @names[i]=n[i]} if n.respond_to? :each_index
|
22
|
+
end
|
23
|
+
end
|
24
|
+
def named?
|
25
|
+
!@names.nil?
|
26
|
+
end
|
27
|
+
def [](v)
|
28
|
+
if v.is_a? String
|
29
|
+
return nil if @names.nil?
|
30
|
+
i=@names.index(v)
|
31
|
+
return i.nil? ? nil : @data[i]
|
32
|
+
elsif v.is_a? Integer
|
33
|
+
return @data[i]
|
34
|
+
else
|
35
|
+
raise ArgumentError,"Should be String or Integer"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def at(v)
|
40
|
+
@data[v]
|
41
|
+
end
|
42
|
+
|
43
|
+
def key_at(v)
|
44
|
+
return nil if @names.nil?
|
45
|
+
@names[v]
|
46
|
+
end
|
47
|
+
def size
|
48
|
+
@data.size
|
49
|
+
end
|
50
|
+
|
51
|
+
def each
|
52
|
+
@data.each do |v|
|
53
|
+
yield v
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_key_at(i,value)
|
58
|
+
if @names.nil?
|
59
|
+
@names=Array.new
|
60
|
+
end
|
61
|
+
if @names.size<size
|
62
|
+
(size-@names.size).times {@names.push(nil)}
|
63
|
+
end
|
64
|
+
|
65
|
+
@names[i]=value if i < size
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
def keys
|
70
|
+
@names
|
71
|
+
end
|
72
|
+
|
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
|
82
|
+
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)
|
90
|
+
end
|
91
|
+
@names.push(key)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
def add(a,b=nil)
|
96
|
+
if b.nil?
|
97
|
+
@data.push(a)
|
98
|
+
@names=Array.new(@data.size-1) if @names.nil?
|
99
|
+
@names.push(nil)
|
100
|
+
else
|
101
|
+
@data.insert(a,b)
|
102
|
+
@names.insert(a,nil)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
def clear
|
106
|
+
@data.clear
|
107
|
+
@names=nil
|
108
|
+
end
|
109
|
+
def clone
|
110
|
+
RList.new(@data,@names)
|
111
|
+
end
|
112
|
+
def remove(elem)
|
113
|
+
if elem.is_a? String
|
114
|
+
i=@data.index(elem)
|
115
|
+
elsif elem.is_a? Integer
|
116
|
+
i=elem
|
117
|
+
end
|
118
|
+
return false if i.nil?
|
119
|
+
@data.delete_at(i)
|
120
|
+
@names.delete_at(i) unless @names.nil?
|
121
|
+
@names=nil if size==0
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/rserve/talk.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module Rserve
|
2
|
+
# Class which 'talk' to the server.
|
3
|
+
#
|
4
|
+
# I separated the 'abstract' aspect of the protocol on
|
5
|
+
# Protocol module, for better testing
|
6
|
+
#
|
7
|
+
class Talk
|
8
|
+
include Rserve::Protocol
|
9
|
+
attr_reader :io
|
10
|
+
def initialize(io)
|
11
|
+
@io=io
|
12
|
+
end
|
13
|
+
# sends a request with attached prefix and parameters.
|
14
|
+
# All parameters should be enter on Hash
|
15
|
+
# Both :prefix and :cont can be <code>nil</code>. Effectively <code>request(:cmd=>a,:prefix=>b,:cont=>nil)</code> and <code>request(:cmd=>a,:prefix=>nil,:cont=>b)</code> are equivalent.
|
16
|
+
# @param :cmd command - a special command of -1 prevents request from sending anything
|
17
|
+
# @param :prefix - this content is sent *before* cont. It is provided to save memory copy operations where a small header precedes a large data chunk (usually prefix conatins the parameter header and cont contains the actual data).
|
18
|
+
# @param :cont contents
|
19
|
+
# @param :offset offset in cont where to start sending (if <0 then 0 is assumed, if >cont.length then no cont is sent)
|
20
|
+
# @param :len number of bytes in cont to send (it is clipped to the length of cont if necessary)
|
21
|
+
# @return returned packet or <code>null</code> if something went wrong */
|
22
|
+
def request(params=Hash.new)
|
23
|
+
|
24
|
+
cmd = params.delete :cmd
|
25
|
+
prefix = params.delete :prefix
|
26
|
+
cont = params.delete :cont
|
27
|
+
offset = params.delete :offset
|
28
|
+
len = params.delete :len
|
29
|
+
|
30
|
+
if cont.is_a? String
|
31
|
+
cont=request_string(cont)
|
32
|
+
elsif cont.is_a? Integer
|
33
|
+
cont=request_int(cont)
|
34
|
+
end
|
35
|
+
raise ArgumentError, ":cont should be an Enumerable" if !cont.nil? and !cont.is_a? Enumerable
|
36
|
+
|
37
|
+
if len.nil?
|
38
|
+
len=(cont.nil?) ? 0 : cont.length
|
39
|
+
end
|
40
|
+
|
41
|
+
offset||=0
|
42
|
+
|
43
|
+
|
44
|
+
if (!cont.nil?)
|
45
|
+
if (offset>=cont.length)
|
46
|
+
cont=nil;len=0
|
47
|
+
elsif (len>cont.length-offset)
|
48
|
+
len=cont.length-offset
|
49
|
+
end
|
50
|
+
end
|
51
|
+
offset=0 if offset<0
|
52
|
+
len=0 if len<0
|
53
|
+
contlen=(cont.nil?) ? 0 : len
|
54
|
+
contlen+=prefix.length if (!prefix.nil? and prefix.length>0)
|
55
|
+
|
56
|
+
hdr=Array.new(16)
|
57
|
+
set_int(cmd,hdr,0)
|
58
|
+
set_int(contlen,hdr,4);
|
59
|
+
8.upto(15) {|i| hdr[i]=0}
|
60
|
+
if (cmd!=-1)
|
61
|
+
io.write(hdr.pack("C*"))
|
62
|
+
if (!prefix.nil? && prefix.length>0)
|
63
|
+
io.write(prefix.pack("C*"))
|
64
|
+
puts "SEND PREFIX #{prefix}" if $DEBUG
|
65
|
+
end
|
66
|
+
if (!cont.nil? && cont.length>0)
|
67
|
+
io.write(cont.slice(offset, len).pack("C*"))
|
68
|
+
puts "SEND CONTENT '#{cont.slice(offset, len).pack("C*")}'" if $DEBUG
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
ih=io.recv(16).unpack("C*")
|
73
|
+
|
74
|
+
return nil if (ih.length!=16)
|
75
|
+
|
76
|
+
puts "Answer: #{ih.to_s}" if $DEBUG
|
77
|
+
|
78
|
+
rep=get_int(ih,0);
|
79
|
+
|
80
|
+
rl =get_int(ih,4);
|
81
|
+
|
82
|
+
if $DEBUG
|
83
|
+
puts "rep: #{rep} #{rep.class} #{rep & 0x00FFFFFF}"
|
84
|
+
puts "rl: #{rl} #{rl.class}"
|
85
|
+
end
|
86
|
+
|
87
|
+
if (rl>0)
|
88
|
+
ct=Array.new();
|
89
|
+
n=0;
|
90
|
+
while (n<rl) do
|
91
|
+
data=io.recv(rl-n).unpack("C*")
|
92
|
+
ct+=data
|
93
|
+
rd=data.length
|
94
|
+
n+=rd;
|
95
|
+
end
|
96
|
+
|
97
|
+
return Packet.new(rep,ct);
|
98
|
+
end
|
99
|
+
|
100
|
+
return Packet.new(rep,nil);
|
101
|
+
end
|
102
|
+
def request_string(s)
|
103
|
+
b=s.unpack("C*")
|
104
|
+
sl=b.length+1;
|
105
|
+
sl=(sl&0xfffffc)+4 if ((sl&3)>0) # make sure the length is divisible by 4
|
106
|
+
rq=Array.new(sl+5)
|
107
|
+
|
108
|
+
b.length.times {|i| rq[i+4]=b[i]}
|
109
|
+
((b.length)..sl).each {|i|
|
110
|
+
rq[i+4]=0
|
111
|
+
}
|
112
|
+
set_hdr(DT_STRING,sl,rq,0)
|
113
|
+
rq
|
114
|
+
end
|
115
|
+
def request_int(par)
|
116
|
+
rq=Array.new(8)
|
117
|
+
set_int(par,rq,4)
|
118
|
+
set_hdr(DT_INT,4,rq,0)
|
119
|
+
rq
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|