rserve-client 0.1.5 → 0.1.6

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.
Files changed (46) hide show
  1. data.tar.gz.sig +1 -2
  2. data/.gitignore +1 -0
  3. data/History.txt +15 -2
  4. data/Manifest.txt +8 -0
  5. data/README.txt +41 -19
  6. data/lib/rserve.rb +1 -1
  7. data/lib/rserve/connection.rb +152 -69
  8. data/lib/rserve/engine.rb +9 -9
  9. data/lib/rserve/packet.rb +2 -2
  10. data/lib/rserve/protocol.rb +51 -46
  11. data/lib/rserve/protocol/rexpfactory.rb +659 -366
  12. data/lib/rserve/rexp.rb +175 -156
  13. data/lib/rserve/rexp/double.rb +45 -38
  14. data/lib/rserve/rexp/environment.rb +40 -0
  15. data/lib/rserve/rexp/expressionvector.rb +9 -0
  16. data/lib/rserve/rexp/genericvector.rb +5 -3
  17. data/lib/rserve/rexp/integer.rb +38 -38
  18. data/lib/rserve/rexp/language.rb +2 -2
  19. data/lib/rserve/rexp/list.rb +3 -0
  20. data/lib/rserve/rexp/logical.rb +17 -4
  21. data/lib/rserve/rexp/null.rb +20 -14
  22. data/lib/rserve/rexp/raw.rb +19 -0
  23. data/lib/rserve/rexp/reference.rb +42 -0
  24. data/lib/rserve/rexp/s4.rb +10 -0
  25. data/lib/rserve/rexp/string.rb +5 -3
  26. data/lib/rserve/rexp/symbol.rb +3 -0
  27. data/lib/rserve/rexp/vector.rb +30 -15
  28. data/lib/rserve/rexp/wrapper.rb +58 -0
  29. data/lib/rserve/rfactor.rb +10 -10
  30. data/lib/rserve/rlist.rb +129 -100
  31. data/lib/rserve/talk.rb +61 -61
  32. data/spec/rserve_connection_spec.rb +99 -33
  33. data/spec/rserve_double_spec.rb +28 -15
  34. data/spec/rserve_integer_spec.rb +24 -15
  35. data/spec/rserve_logical_spec.rb +21 -12
  36. data/spec/rserve_protocol_spec.rb +7 -7
  37. data/spec/rserve_rexp_spec.rb +3 -3
  38. data/spec/rserve_rexp_wrapper_spec.rb +36 -0
  39. data/spec/rserve_rexpfactory_spec.rb +86 -20
  40. data/spec/rserve_rfactor_spec.rb +2 -2
  41. data/spec/rserve_rlist_spec.rb +53 -0
  42. data/spec/rserve_spec.rb +8 -5
  43. data/spec/rserve_talk_spec.rb +7 -7
  44. data/spec/spec_helper.rb +1 -0
  45. metadata +13 -3
  46. metadata.gz.sig +0 -0
@@ -16,8 +16,8 @@ module Rserve
16
16
  @index_base=index_base
17
17
  end
18
18
  # returns the level of a given case
19
- # * @param i case number
20
- # * @return name. may throw exception if out of range
19
+ # * @param i case number
20
+ # * @return name. may throw exception if out of range
21
21
  def [](i)
22
22
  li = @ids[i]-index_base
23
23
  return (li<0 or li>levels.length) ? nil : levels[li]
@@ -26,16 +26,16 @@ module Rserve
26
26
  li=level_index(li) if li.is_a? String
27
27
  @ids.any? {|v| v==li}
28
28
  end
29
-
30
- # return the index of a given level name or -1 if it doesn't exist
29
+
30
+ # return the index of a given level name or -1 if it doesn't exist
31
31
  def level_index(name)
32
- return nil if name.nil?
33
- levels.length.times {|i|
34
- return i+index_base if !levels[i].nil? and levels[i]==name
35
- }
36
- return nil
32
+ return nil if name.nil?
33
+ levels.length.times {|i|
34
+ return i+index_base if !levels[i].nil? and levels[i]==name
35
+ }
36
+ return nil
37
37
  end
38
- def count(li)
38
+ def count(li)
39
39
  li=level_index(li) if li.is_a? String
40
40
  @ids.inject(0) {|ac,v| ac+ ((v==li) ? 1 : 0 ) }
41
41
  end
@@ -4,118 +4,147 @@ module Rserve
4
4
  attr_reader :names
5
5
  attr_reader :data
6
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]
7
+ @names=nil
8
+ @data=[]
9
+ if data
10
+ @data=case data
11
+ when Array
12
+ data
13
+ when Numeric
14
+ Array.new(data)
34
15
  else
35
- raise ArgumentError,"Should be String or Integer"
16
+ raise ArgumentError
36
17
  end
37
18
  end
38
-
39
- def at(v)
40
- self.[](v)
41
- end
42
-
43
- def key_at(v)
44
- return nil if @names.nil?
45
- @names[v]
19
+ if n
20
+ @names=Array.new(@data.size)
21
+ n.each_index {|i| @names[i]=n[i]} if n.respond_to? :each_index
46
22
  end
47
- def size
48
- @data.size
23
+ end
24
+ def named?
25
+ !@names.nil?
26
+ end
27
+ def ==(o)
28
+ #p "Comparing #{self.inspect} with #{o.inspect} gives #{o.is_a? Rlist and self.data==o.data and self.names==o.names}"
29
+ o.is_a? Rlist and self.data==o.data and self.names==o.names
30
+ end
31
+ # Return element with name x or 0-index x
32
+ def [](v)
33
+ if v.is_a? String
34
+ return nil if @names.nil? or v==""
35
+ i=@names.index(v)
36
+ return i.nil? ? nil : @data[i]
37
+ elsif v.is_a? Integer
38
+ return @data[v]
39
+ else
40
+ raise ArgumentError,"Should be String or Integer"
49
41
  end
50
-
51
- def each
52
- @data.each do |v|
53
- yield v
54
- end
42
+ end
43
+
44
+ def at(v)
45
+ self.[](v)
46
+ end
47
+
48
+ def key_at(v)
49
+ return nil if @names.nil?
50
+ @names[v]
51
+ end
52
+ def size
53
+ @data.size
54
+ end
55
+
56
+ def each
57
+ @data.each do |v|
58
+ yield v
55
59
  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
-
60
+ end
61
+
62
+ def set_key_at(i,value)
63
+ if @names.nil?
64
+ @names=Array.new
67
65
  end
68
-
69
- def keys
70
- @names
66
+ if @names.size<size
67
+ (size-@names.size).times {@names.push(nil)}
71
68
  end
72
-
73
- def put(key,value)
74
- puts "rlist.put(#{key},#{value})" if $DEBUG
75
- if key.nil?
76
- add(value)
77
- return nil
78
- end
79
- if !@names.nil?
80
- p=names.index(key)
81
- if !p.nil?
82
- return @names[p]=value
83
- end
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)
89
- @names.push(key)
90
-
69
+
70
+ @names[i]=value if i < size
71
+
72
+ end
73
+
74
+ def keys
75
+ @names
76
+ end
77
+
78
+ def put(key,value)
79
+ puts "rlist.put(#{key},#{value})" if $DEBUG
80
+ if key.nil?
81
+ add(value)
82
+ return nil
91
83
  end
92
- def add(a,b=nil)
93
- if b.nil?
94
- @data.push(a)
95
- @names=Array.new(@data.size-1) if @names.nil?
96
- @names.push(nil)
97
- else
98
- @data.insert(a,b)
99
- @names.insert(a,nil)
84
+ if !@names.nil?
85
+ p=names.index(key)
86
+ if !p.nil?
87
+ return @names[p]=value
100
88
  end
101
89
  end
102
- def clear
103
- @data.clear
104
- @names=nil
90
+ puts "add key and value" if $DEBUG
91
+ i=size
92
+ @names=Array.new(i) if(@names.nil?)
93
+ @data.push(value)
94
+ @names.push(key)
95
+
96
+ end
97
+ def add(a,b=nil)
98
+ if b.nil?
99
+ @data.push(a)
100
+ @names=Array.new(@data.size-1) if @names.nil?
101
+ @names.push(nil)
102
+ else
103
+ @data.insert(a,b)
104
+ @names.insert(a,nil)
105
105
  end
106
- def clone
107
- RList.new(@data,@names)
106
+ end
107
+ def clear
108
+ @data.clear
109
+ @names=nil
110
+ end
111
+ def clone
112
+ RList.new(@data,@names)
113
+ end
114
+ def remove(elem)
115
+ if elem.is_a? String
116
+ i=@data.index(elem)
117
+ elsif elem.is_a? Integer
118
+ i=elem
108
119
  end
109
- def remove(elem)
110
- if elem.is_a? String
111
- i=@data.index(elem)
112
- elsif elem.is_a? Integer
113
- i=elem
114
- end
115
- return false if i.nil?
116
- @data.delete_at(i)
117
- @names.delete_at(i) unless @names.nil?
118
- @names=nil if size==0
120
+ return false if i.nil?
121
+ @data.delete_at(i)
122
+ @names.delete_at(i) unless @names.nil?
123
+ @names=nil if size==0
124
+ end
125
+ # Returns data with appropiate ruby representation
126
+ def to_a
127
+ @data.map {|d|
128
+ d.to_ruby
129
+ }
130
+ end
131
+ # Returns recursively a hash or array:
132
+ # * Unnamed list: returns an Array
133
+ # * Named List: returns a Hash. Every element without explicit name receive
134
+ # as key the number of element, 1-based
135
+ #
136
+
137
+ def to_ruby
138
+ if names.nil? or names.all? {|v| v.nil?}
139
+ to_a
140
+ else
141
+ h=Hash.new
142
+ names.each_with_index {|name,i|
143
+ key= (name=="" or name.nil?) ? i+1 : name
144
+ h[key]=@data[i].to_ruby
145
+ }
146
+ h
119
147
  end
120
148
  end
121
- end
149
+ end
150
+ end
@@ -3,7 +3,7 @@ module Rserve
3
3
  #
4
4
  # I separated the 'abstract' aspect of the protocol on
5
5
  # Protocol module, for better testing
6
- #
6
+ #
7
7
  class Talk
8
8
  include Rserve::Protocol
9
9
  attr_reader :io
@@ -20,29 +20,29 @@ module Rserve
20
20
  # @param :len number of bytes in cont to send (it is clipped to the length of cont if necessary)
21
21
  # @return returned packet or <code>null</code> if something went wrong */
22
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)
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
+ if len.nil?
37
+ len=(cont.nil?) ? 0 : cont.length
38
+ end
39
+
40
+ offset||=0
41
+
42
+
43
+ if (!cont.nil?)
44
+ raise ":cont shouldn't contain anything but Fixnum" if cont.any? {|v| !v.is_a? Fixnum}
45
+ if (offset>=cont.length)
46
46
  cont=nil;len=0
47
47
  elsif (len>cont.length-offset)
48
48
  len=cont.length-offset
@@ -51,8 +51,8 @@ module Rserve
51
51
  offset=0 if offset<0
52
52
  len=0 if len<0
53
53
  contlen=(cont.nil?) ? 0 : len
54
- contlen+=prefix.length if (!prefix.nil? and prefix.length>0)
55
-
54
+ contlen+=prefix.length if (!prefix.nil? and prefix.length>0)
55
+
56
56
  hdr=Array.new(16)
57
57
  set_int(cmd,hdr,0)
58
58
  set_int(contlen,hdr,4);
@@ -63,48 +63,48 @@ module Rserve
63
63
  io.write(prefix.pack("C*"))
64
64
  puts "SEND PREFIX #{prefix}" if $DEBUG
65
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
66
+ if (!cont.nil? and cont.length>0)
67
+ puts "SEND CONTENT #{cont.slice(offset, len)} (#{offset},#{len})" if $DEBUG
68
+ io.write(cont.slice(offset, len).pack("C*"))
69
69
  end
70
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);
71
+ puts "Expecting 16 bytes..." if $DEBUG
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;
98
95
  end
99
-
100
- return Packet.new(rep,nil);
96
+
97
+ return Packet.new(rep,ct);
98
+ end
99
+
100
+ return Packet.new(rep,nil);
101
101
  end
102
102
  def request_string(s)
103
103
  b=s.unpack("C*")
104
104
  sl=b.length+1;
105
105
  sl=(sl&0xfffffc)+4 if ((sl&3)>0) # make sure the length is divisible by 4
106
106
  rq=Array.new(sl+5)
107
-
107
+
108
108
  b.length.times {|i| rq[i+4]=b[i]}
109
109
  ((b.length)..sl).each {|i|
110
110
  rq[i+4]=0
@@ -114,8 +114,8 @@ module Rserve
114
114
  end
115
115
  def request_int(par)
116
116
  rq=Array.new(8)
117
- set_int(par,rq,4)
118
- set_hdr(DT_INT,4,rq,0)
117
+ set_int(par,rq,4)
118
+ set_hdr(DT_INT,4,rq,0)
119
119
  rq
120
120
  end
121
121
  end
@@ -1,34 +1,34 @@
1
1
  require File.dirname(__FILE__)+"/spec_helper.rb"
2
2
  describe Rserve::Connection do
3
- describe "opening and closing" do
4
- before do
5
- @r=Rserve::Connection.new()
6
- end
7
- it "should be open a connection and receive ID-String" do
8
- @r.get_server_version.should==103
9
- @r.protocol.should=="QAP1"
10
- @r.last_error.should=="OK"
11
- @r.rt.should be_instance_of(Rserve::Talk)
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
16
- it "should quit correctly" do
17
- @r.should be_connected
18
- @r.close.should be_true
19
- @r.should_not be_connected
20
- @r.close.should be_true
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
26
- after do
27
- @r.close if @r.connected?
28
- end
29
-
30
- end
31
- describe "basic eval methods" do
3
+ describe "opening and closing" do
4
+ before do
5
+ @r=Rserve::Connection.new()
6
+ end
7
+ it "should be open a connection and receive ID-String" do
8
+ @r.get_server_version.should==103
9
+ @r.protocol.should=="QAP1"
10
+ @r.last_error.should=="OK"
11
+ @r.rt.should be_instance_of(Rserve::Talk)
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
16
+ it "should quit correctly" do
17
+ @r.should be_connected
18
+ @r.close.should be_true
19
+ @r.should_not be_connected
20
+ @r.close.should be_true
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
26
+ after do
27
+ @r.close if @r.connected?
28
+ end
29
+
30
+ end
31
+ describe "basic eval methods" do
32
32
  before do
33
33
  @r=Rserve::Connection.new
34
34
  end
@@ -40,24 +40,90 @@ describe Rserve::Connection do
40
40
  lambda {@r.void_eval("as.stt(c(1))")}.should raise_exception(Rserve::Connection::EvalError) {|e|
41
41
  e.request_packet.stat.should==127}
42
42
  end
43
-
43
+
44
44
  it "method eval should return a simple object" do
45
45
  la=@r.eval("TRUE")
46
46
  la.should be_instance_of(Rserve::REXP::Logical)
47
47
  la.true?.should==[true]
48
48
  end
49
-
49
+
50
50
  it "method eval should raise an error with an incorrect expression" do
51
51
  lambda {@r.eval("x<-")}.should raise_exception(Rserve::Connection::EvalError) {|e| e.request_packet.stat.should==2}
52
52
  lambda {@r.eval("as.stt(c(1))")}.should raise_exception(Rserve::Connection::EvalError) {|e|
53
53
  e.request_packet.stat.should==127}
54
54
  end
55
-
55
+
56
56
  it "should eval_void and eval correctly" do
57
57
  @r.void_eval("x<-c(TRUE,FALSE)").should be_true
58
58
  la=@r.eval("x")
59
59
  la.should be_instance_of(Rserve::REXP::Logical)
60
- la.true?.should==[true,false]
60
+ la.true?.should==[true,false]
61
+ end
62
+ it "should assign a string" do
63
+ @r.assign("x","a string")
64
+ @r.eval("x").to_ruby.should=="a string"
65
+ end
66
+
67
+ end
68
+ describe "assign using REXPs" do
69
+ before do
70
+ @r=Rserve::Connection.new
71
+
72
+ end
73
+ it "should assign double" do
74
+ rexp=Rserve::REXP::Double.new([1.5,-1.5])
75
+ @r.assign("x", rexp)
76
+ @r.eval("x").should==rexp
77
+ end
78
+
79
+ it "should assign a null" do
80
+ rexp=Rserve::REXP::Null.new
81
+ @r.assign("x", rexp)
82
+ @r.eval("x").should==rexp
83
+ end
84
+ it "should assign a int vector" do
85
+ rexp=Rserve::REXP::Integer.new([1,2,3])
86
+ @r.assign("x", rexp)
87
+ @r.eval("x").should==rexp
88
+ end
89
+ it "should assign a bool vector" do
90
+ rexp=Rserve::REXP::Logical.new([1,0,1])
91
+ @r.assign("x", rexp)
92
+ @r.eval("x").should==rexp
93
+ end
94
+ it "should assign a string vector" do
95
+ rexp=Rserve::REXP::String.new(%w{a b c})
96
+ @r.assign("x", rexp)
97
+ @r.eval("x").should==rexp
98
+ end
99
+ it "should assign a list without names" do
100
+ rexp=Rserve::REXP::GenericVector.new(Rserve::Rlist.new([Rserve::REXP::String.new("a")]))
101
+ @r.assign("x", rexp)
102
+ @r.eval("x").should==rexp
103
+ end
104
+ it "should assign a list without names and nulls" do
105
+ rexp=Rserve::REXP::GenericVector.new(Rserve::Rlist.new([Rserve::REXP::Null.new]))
106
+ @r.assign("x", rexp)
107
+ @r.eval("x").should==rexp
108
+ end
109
+ it "should assign a list with names" do
110
+ x=@r.eval("list(a=TRUE)")
111
+ @r.assign("x",x)
112
+ @r.eval("x").should==x
113
+ end
114
+ it "should assign a data.frame" do
115
+ x=@r.eval("data.frame(a=rnorm(100),b=rnorm(100))")
116
+ @r.assign("x",x)
117
+ @r.eval("x").should==x
118
+ end
119
+ it "should assign a matrix" do
120
+ x=@r.eval("matrix(1:9,3,3)")
121
+ @r.assign("x",x)
122
+ @r.eval("x").should==x
123
+
124
+ end
125
+ after do
126
+ @r.void_eval("rm(x)")
61
127
  end
62
128
  end
63
129
  end