rserve-client 0.1.5 → 0.1.6

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