rserve-client 0.1.8 → 0.1.9
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/History.txt +17 -0
- data/{Examples.txt → Introduction.txt} +5 -6
- data/Manifest.txt +5 -1
- data/data/gettysburg.txt +25 -0
- data/examples/gettysburg.rb +24 -0
- data/examples/lowless.rb +2 -2
- data/examples/regression.rb +44 -0
- data/lib/rserve.rb +1 -1
- data/lib/rserve/connection.rb +1 -1
- data/lib/rserve/protocol.rb +6 -3
- data/lib/rserve/rexp.rb +179 -63
- data/lib/rserve/rexp/double.rb +7 -3
- data/lib/rserve/rexp/environment.rb +0 -3
- data/lib/rserve/rexp/factor.rb +3 -0
- data/lib/rserve/rexp/genericvector.rb +1 -1
- data/lib/rserve/rexp/integer.rb +12 -0
- data/lib/rserve/rexp/list.rb +1 -1
- data/lib/rserve/rexp/logical.rb +1 -1
- data/lib/rserve/rexp/unknown.rb +3 -0
- data/lib/rserve/rexp/vector.rb +5 -5
- data/lib/rserve/rfactor.rb +1 -1
- data/lib/rserve/withattributes.rb +3 -0
- data/lib/rserve/withnames.rb +42 -3
- data/spec/rserve_rexp_spec.rb +17 -1
- data/spec/rserve_rexp_to_ruby_spec.rb +43 -4
- data/spec/rserve_rexpfactory_spec.rb +11 -1
- data/spec/rserve_withnames_spec.rb +11 -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/History.txt
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
=== 0.1.9 / 2010-06-04
|
2
|
+
|
3
|
+
* Documentation:
|
4
|
+
* Added commentaries on examples/regression.txt
|
5
|
+
* Examples.txt on main directory renamed to Introduction.txt
|
6
|
+
* Added examples extracted from RinRuby documentation, adapted to rserve-client. Modified Connection#assign to allow ruby objects
|
7
|
+
* Modified lowless example, omiting use of Rserve::REXP::Wrapper
|
8
|
+
* Bug fixes:
|
9
|
+
* WithNames#put changes names, not values.
|
10
|
+
* Negative integer conversion works on i686 and x86_64
|
11
|
+
* New features
|
12
|
+
* Added WithNames[]= method for key assign.
|
13
|
+
* Correct handling of data.frames with to_ruby : row.names returned as a array of (1..x) value if [NA,-x] returned from Rserve
|
14
|
+
* REXP#to_ruby returns object extended with WithAttributes and WithNames when applicable
|
15
|
+
* REXP could process nested arrays properly
|
16
|
+
|
17
|
+
|
1
18
|
=== 0.1.8 / 2010-06-01
|
2
19
|
|
3
20
|
* Bug fix: attributes for generic vectors (data.frames) stored correctly. A Double vector without attribute dim raises an error on #to_ruby
|
@@ -1,5 +1,4 @@
|
|
1
|
-
=
|
2
|
-
|
1
|
+
= Introduction to Ruby Rserve Client
|
3
2
|
<em>Based on original Java version [http://www.rforge.net/Rserve/example.html]</em>
|
4
3
|
|
5
4
|
Rserve itself is provided as a regular R package and can be installed as such. The actual use is not performed by the library command, but by starting the Rserve executable (Windows) or typing R CMD Rserve on the command line (all others). By default Rserve runs in local mode with no enforced authentication. Once the Rserve is running any applications can use its services.
|
@@ -16,15 +15,15 @@ The code has the same effect as typing R.version.string in R. In the first line
|
|
16
15
|
|
17
16
|
The following code fragment illustrates the use of slightly more complex native Ruby types:
|
18
17
|
|
19
|
-
d = c.eval("rnorm(100)").
|
18
|
+
d = c.eval("rnorm(100)").as_floats
|
20
19
|
|
21
20
|
|
22
|
-
The single line in Ruby provides an array of 100
|
21
|
+
The single line in Ruby provides an array of 100 ruby floats (double precision, on C) representing random numbers from the standard normal distribution. The numeric vector in R is converted into an array of floats. In cases where no native Ruby type exists, Rserve Ruby client defines its own classes such as Rlist or Logical (Ruby's boolean type has no support for NA missing values, therefore it cannot be used to directly represent logical type in R). This approach makes the use of Rserve very easy. As a first more practical example we want to calculate a Lowess smoother through a given set of points. The Ruby application lets the user specify the data allowing interactive changes of the points, displays a regular scatter plot and needs coordinates of the smoother to be obtained from R. One way of obtaining such a result would be to construct a long string command of the form lowes(c(0.2,0.4,...), c(2.5,4.8,...)) and using the eval method to obtain the result. This is somewhat clumsy, because the points usually already exist in an array in the Ruby application and the command string must be constructed from these. An alternative involves constructing objects in R directly. The following code shows the full Lowess example:
|
23
22
|
|
24
23
|
require 'rserve'
|
25
24
|
|
26
|
-
data_x=
|
27
|
-
data_y=
|
25
|
+
data_x=10.times.map{|i| rand(i)}
|
26
|
+
data_y=10.times.map{|i| rand(i)}
|
28
27
|
c = Rserve::Connection.new();
|
29
28
|
c.assign("x", data_x);
|
30
29
|
c.assign("y", data_y);
|
data/Manifest.txt
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
|
1
|
+
.autotest
|
2
2
|
History.txt
|
3
|
+
Introduction.txt
|
3
4
|
Manifest.txt
|
4
5
|
README.txt
|
5
6
|
Rakefile
|
7
|
+
data/gettysburg.txt
|
8
|
+
examples/gettysburg.rb
|
6
9
|
examples/hello_world.rb
|
7
10
|
examples/lowless.rb
|
11
|
+
examples/regression.rb
|
8
12
|
lib/rserve.rb
|
9
13
|
lib/rserve/connection.rb
|
10
14
|
lib/rserve/engine.rb
|
data/data/gettysburg.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
THE GETTYSBURG ADDRESS:
|
2
|
+
|
3
|
+
Four score and seven years ago our fathers brought forth on this
|
4
|
+
continent a new nation, conceived in liberty and dedicated to the
|
5
|
+
proposition that all men are created equal. Now we are engaged in
|
6
|
+
a great civil war, testing whether that nation or any nation so
|
7
|
+
conceived and so dedicated can long endure. We are met on a great
|
8
|
+
battlefield of that war. We have come to dedicate a portion of
|
9
|
+
that field as a final resting-place for those who here gave their
|
10
|
+
lives that that nation might live. It is altogether fitting and
|
11
|
+
proper that we should do this. But in a larger sense, we cannot
|
12
|
+
dedicate, we cannot consecrate, we cannot hallow this ground.
|
13
|
+
The brave men, living and dead who struggled here have consecrated
|
14
|
+
it far above our poor power to add or detract. The world will
|
15
|
+
little note nor long remember what we say here, but it can never
|
16
|
+
forget what they did here. It is for us the living rather to be
|
17
|
+
dedicated here to the unfinished work which they who fought here
|
18
|
+
have thus far so nobly advanced. It is rather for us to be here
|
19
|
+
dedicated to the great task remaining before us--that from these
|
20
|
+
honored dead we take increased devotion to that cause for which
|
21
|
+
they gave the last full measure of devotion--that we here highly
|
22
|
+
resolve that these dead shall not have died in vain, that this
|
23
|
+
nation under God shall have a new birth of freedom, and that
|
24
|
+
government of the people, by the people, for the people shall
|
25
|
+
not perish from the earth.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# The code and output below demonstrates some of the Ruby Rserve client, using a example from RinRuby. Ruby code counts the number of occurences of each word in Lincoln's Gettysburg Address and filters out those occurring less than three times or shorter than four letters. R code -- through the Rserve library -- produces a bar plot of the most frequent words and computes the correlation between the length of a word and the usage frequency. Finally, the computed correlation is printed by Ruby
|
2
|
+
|
3
|
+
require "rserve"
|
4
|
+
r=Rserve::Connection.new
|
5
|
+
|
6
|
+
tally = Hash.new(0)
|
7
|
+
File.open(File.dirname(__FILE__)+'/../data/gettysburg.txt').each_line do |line|
|
8
|
+
line.downcase.split(/\W+/).each { |w| tally[w] += 1 }
|
9
|
+
end
|
10
|
+
total = tally.values.inject { |sum,count| sum + count }
|
11
|
+
tally.delete_if { |key,count| count < 3 || key.length < 4 }
|
12
|
+
|
13
|
+
r.assign("keys",tally.keys)
|
14
|
+
r.assign("counts", tally.values)
|
15
|
+
|
16
|
+
r.void_eval <<-EOF
|
17
|
+
names(counts) <- keys
|
18
|
+
barplot(rev(sort(counts)),main="Frequency of Non-Trivial Words",las=2)
|
19
|
+
mtext("Among the #{total} words in the Gettysburg Address",3,0.45)
|
20
|
+
rho <- round(cor(nchar(keys),counts),4)
|
21
|
+
EOF
|
22
|
+
puts "Press enter when finished"
|
23
|
+
STDIN.gets
|
24
|
+
puts "The correlation between word length and frequency is #{r.eval("rho").as_float}."
|
data/examples/lowless.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'rserve'
|
2
2
|
|
3
|
-
data_x=
|
4
|
-
data_y=
|
3
|
+
data_x=20.times.map{|i| rand(i)}
|
4
|
+
data_y=20.times.map{|i| rand(i)}
|
5
5
|
c = Rserve::Connection.new();
|
6
6
|
c.assign("x", data_x);
|
7
7
|
c.assign("y", data_y);
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# As another example of Ruby Rserve client, extracted from RinRuby documentation, consider the usage of Ruby Rserve client for simple linear regression below. The simulation parameters are defined in Ruby, computations are performed in R, and Ruby reports the results. In a more eloborate application, the simulation parameter might come from input from a graphical user interface, the statistical analysis might be more involved, and the results might be an HTML page or PDF report.
|
2
|
+
|
3
|
+
require 'rserve'
|
4
|
+
r=Rserve::Connection.new
|
5
|
+
n = 10
|
6
|
+
beta_0 = 1
|
7
|
+
beta_1 = 0.25
|
8
|
+
alpha = 0.05
|
9
|
+
seed = 23423
|
10
|
+
|
11
|
+
# You could assign a ruby object to a R variable automaticly. You could see
|
12
|
+
# the rules of translation on Rserve::REXP::Wrapper module
|
13
|
+
r.assign "x", (1..n).entries
|
14
|
+
|
15
|
+
# Rserve::Connection.void_eval allows you to send complex R commands
|
16
|
+
# without wasting time retrieving the final result
|
17
|
+
|
18
|
+
r.void_eval <<EOF
|
19
|
+
set.seed(#{seed})
|
20
|
+
y <- #{beta_0} + #{beta_1}*x + rnorm(#{n})
|
21
|
+
fit <- lm( y ~ x )
|
22
|
+
est <- round(coef(fit),3)
|
23
|
+
pvalue <- summary(fit)$coefficients[2,4]
|
24
|
+
EOF
|
25
|
+
|
26
|
+
# Rserve::Connecttion.eval retrieve a Rserve::REXP object, which could
|
27
|
+
# be translated to an appropiate Ruby object using Rserve::REXP.to_ruby method
|
28
|
+
#
|
29
|
+
est=r.eval("est").to_ruby
|
30
|
+
|
31
|
+
# fit$coef is a named doubles vector, so the respective Ruby object is an
|
32
|
+
# array of rational extended with module WithNames. That allows us
|
33
|
+
# to retrieve every element of the vector by its name
|
34
|
+
|
35
|
+
puts "E(y|x) ~= #{est['(Intercept)'].to_f} + #{est['x'].to_f} * x"
|
36
|
+
|
37
|
+
# fit$pvalue is a vector of size 1. So, the method #to_ruby
|
38
|
+
# retrieves a rational or a Fixnum, according to original representation
|
39
|
+
|
40
|
+
if r.eval("pvalue").to_ruby < alpha
|
41
|
+
puts "Reject the null hypothesis and conclude that x and y are related."
|
42
|
+
else
|
43
|
+
puts "There is insufficient evidence to conclude that x and y are related."
|
44
|
+
end
|
data/lib/rserve.rb
CHANGED
data/lib/rserve/connection.rb
CHANGED
data/lib/rserve/protocol.rb
CHANGED
@@ -110,7 +110,9 @@ module Rserve
|
|
110
110
|
ERR_session_busy=>"session still busy",
|
111
111
|
ERR_detach_failed=>"unable to detach seesion"
|
112
112
|
} # error descriptions
|
113
|
-
|
113
|
+
# I have to use to support different archs
|
114
|
+
MAX_LONG_SIGNED=2**31
|
115
|
+
MAX_LONG_UNSIGNED=2**32
|
114
116
|
|
115
117
|
# writes bit-wise int to a byte buffer at specified position in Intel-endian form
|
116
118
|
# Internal: byte buffer will be the result of unpack("CCCC") an integer.
|
@@ -160,8 +162,9 @@ module Rserve
|
|
160
162
|
# make sure that the buffer is big enough
|
161
163
|
|
162
164
|
def get_int(buf, o)
|
163
|
-
#return buf.slice(o,4).pack("C*").unpack("
|
164
|
-
|
165
|
+
#return buf.slice(o,4).pack("C*").unpack("l")[0]
|
166
|
+
v=((buf[o]&255)|((buf[o+1]&255)<<8)|((buf[o+2]&255)<<16)|((buf[o+3]&255)<<24))
|
167
|
+
v >= MAX_LONG_SIGNED ? v-MAX_LONG_UNSIGNED : v
|
165
168
|
end
|
166
169
|
|
167
170
|
# converts bit-wise stored length from a header. "long" format is supported up to 32-bit
|
data/lib/rserve/rexp.rb
CHANGED
@@ -11,9 +11,6 @@ module Rserve
|
|
11
11
|
def initialize(attr=nil)
|
12
12
|
# Sorry for this, but I think is necessary to maintain sanity of attributes
|
13
13
|
raise ArgumentError, "Attribute should be a REXP::List, #{attr.class} provided" unless attr.nil? or attr.is_a? REXP::List
|
14
|
-
|
15
|
-
|
16
|
-
|
17
14
|
@attr=attr
|
18
15
|
end
|
19
16
|
# specifies how many items of a vector or list will be displayed in {@link #toDebugString}
|
@@ -22,111 +19,164 @@ module Rserve
|
|
22
19
|
# check whether the <code>REXP</code> object is a character vector (string)
|
23
20
|
# @return <code>true</code> if the receiver is a character vector, <code>false</code> otherwise
|
24
21
|
|
25
|
-
def string
|
22
|
+
def string?
|
23
|
+
false
|
24
|
+
end
|
26
25
|
|
27
26
|
# # check whether the <code>REXP</code> object is a numeric vector
|
28
27
|
# @return <code>true</code> if the receiver is a numeric vector, <code>false</code> otherwise
|
29
28
|
|
30
|
-
def numeric
|
29
|
+
def numeric?
|
30
|
+
false
|
31
|
+
end
|
31
32
|
# check whether the <code>REXP</code> object is an integer vector
|
32
33
|
# @return <code>true</code> if the receiver is an integer vector, <code>false</code> otherwise
|
33
|
-
def integer
|
34
|
+
def integer?
|
35
|
+
false
|
36
|
+
end
|
34
37
|
# check whether the <code>REXP</code> object is NULL
|
35
38
|
# @return <code>true</code> if the receiver is NULL, <code>false</code> otherwise
|
36
|
-
def null
|
39
|
+
def null?
|
40
|
+
false
|
41
|
+
end
|
37
42
|
# check whether the <code>REXP</code> object is a factor
|
38
43
|
# @return <code>true</code> if the receiver is a factor, <code>false</code> otherwise
|
39
|
-
def factor
|
44
|
+
def factor?
|
45
|
+
false
|
46
|
+
end
|
40
47
|
# check whether the <code>REXP</code> object is a list (either generic vector or a pairlist - i.e. {@link #asList()} will succeed)
|
41
48
|
# @return <code>true</code> if the receiver is a generic vector or a pair-list, <code>false</code> otherwise
|
42
|
-
def list
|
49
|
+
def list?
|
50
|
+
false
|
51
|
+
end
|
43
52
|
# check whether the <code>REXP</code> object is a pair-list
|
44
53
|
# @return <code>true</code> if the receiver is a pair-list, <code>false</code> otherwise
|
45
|
-
def pair_list
|
54
|
+
def pair_list?
|
55
|
+
false
|
56
|
+
end
|
46
57
|
# check whether the <code>REXP</code> object is a logical vector
|
47
58
|
# @return <code>true</code> if the receiver is a logical vector, <code>false</code> otherwise */
|
48
|
-
def logical
|
59
|
+
def logical?
|
60
|
+
false
|
61
|
+
end
|
49
62
|
# check whether the <code>REXP</code> object is an environment
|
50
63
|
# @return <code>true</code> if the receiver is an environment, <code>false</code> otherwise
|
51
|
-
def environment
|
64
|
+
def environment?
|
65
|
+
false
|
66
|
+
end
|
52
67
|
# check whether the <code>REXP</code> object is a language object
|
53
68
|
# @return <code>true</code> if the receiver is a language object, <code>false</code> otherwise
|
54
|
-
def language
|
69
|
+
def language?
|
70
|
+
false
|
71
|
+
end
|
55
72
|
# check whether the <code>REXP</code> object is an expression vector
|
56
73
|
# @return <code>true</code> if the receiver is an expression vector, <code>false</code> otherwise
|
57
|
-
def expression
|
74
|
+
def expression?
|
75
|
+
false
|
76
|
+
end
|
58
77
|
# check whether the <code>REXP</code> object is a symbol
|
59
78
|
# @return <code>true</code> if the receiver is a symbol, <code>false</code> otherwise
|
60
|
-
def symbol
|
79
|
+
def symbol?
|
80
|
+
false
|
81
|
+
end
|
61
82
|
# check whether the <code>REXP</code> object is a vector
|
62
83
|
# @return <code>true</code> if the receiver is a vector, <code>false</code> otherwise
|
63
|
-
def vector
|
84
|
+
def vector?
|
85
|
+
false
|
86
|
+
end
|
64
87
|
# check whether the <code>REXP</code> object is a raw vector
|
65
88
|
# @return <code>true</code> if the receiver is a raw vector, <code>false</code> otherwise
|
66
|
-
def raw
|
89
|
+
def raw?
|
90
|
+
false
|
91
|
+
end
|
67
92
|
# check whether the <code>REXP</code> object is a complex vector
|
68
93
|
# @return <code>true</code> if the receiver is a complex vector, <code>false</code> otherwise
|
69
|
-
def complex
|
94
|
+
def complex?
|
95
|
+
false
|
96
|
+
end
|
70
97
|
# check whether the <code>REXP</code> object is a recursive obejct
|
71
98
|
# @return <code>true</code> if the receiver is a recursive object, <code>false</code> otherwise
|
72
|
-
def recursive
|
99
|
+
def recursive?
|
100
|
+
false
|
101
|
+
end
|
73
102
|
# check whether the <code>REXP</code> object is a reference to an R object
|
74
103
|
# @return <code>true</code> if the receiver is a reference, <code>false</code> otherwise
|
75
|
-
def reference
|
104
|
+
def reference?
|
105
|
+
false
|
106
|
+
end
|
76
107
|
|
77
108
|
# :section: basic accessor methods
|
78
109
|
# returns the contents as an array of Strings (if supported by the represented object)
|
79
|
-
def as_strings
|
110
|
+
def as_strings
|
111
|
+
raise MismatchException, "String"
|
112
|
+
end
|
80
113
|
# returns the contents as an array of integers (if supported by the represented object)
|
81
114
|
|
82
|
-
def as_integers
|
115
|
+
def as_integers
|
116
|
+
raise MismatchException, "int"
|
117
|
+
end
|
118
|
+
|
83
119
|
# returns the contents as an array of doubles (if supported by the represented object)
|
84
|
-
def as_doubles
|
85
|
-
|
120
|
+
def as_doubles
|
121
|
+
raise MismatchException,"double"
|
122
|
+
end
|
123
|
+
|
86
124
|
# On Ruby, Float are stored in double precision
|
87
125
|
def as_floats
|
88
126
|
as_doubles
|
89
127
|
end
|
90
128
|
|
91
129
|
# returns the contents as an array of bytes (if supported by the represented object)
|
92
|
-
def as_bytes
|
130
|
+
def as_bytes
|
131
|
+
raise MismatchException , "byte"
|
132
|
+
end
|
93
133
|
# returns the contents as a (named) list (if supported by the represented object)
|
94
|
-
def as_list
|
134
|
+
def as_list
|
135
|
+
raise MismatchException,"list"
|
136
|
+
end
|
95
137
|
# returns the contents as a factor (if supported by the represented object)
|
96
|
-
def as_factor
|
138
|
+
def as_factor
|
139
|
+
raise MismatchException,"factor"
|
140
|
+
end
|
97
141
|
|
98
142
|
# returns the length of a vector object. Note that we use R semantics here, i.e. a matrix will have a length of <i>m * n</i> since it is represented by a single vector (see {@link #dim} for retrieving matrix and multidimentional-array dimensions).
|
99
143
|
# * @return length (number of elements) in a vector object
|
100
144
|
# * @throws MismatchException if this is not a vector object
|
101
|
-
def length
|
102
|
-
|
145
|
+
def length
|
146
|
+
raise MismatchException, "vector"
|
103
147
|
end
|
104
148
|
|
105
149
|
# 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
|
106
150
|
# * @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
|
107
151
|
# * @throws MismatchException if this is not a vector object
|
108
152
|
def na?
|
109
|
-
|
153
|
+
raise MismatchException, "vector"
|
110
154
|
end
|
111
155
|
|
112
156
|
# :section: convenience accessor methods
|
113
157
|
# convenience method corresponding to <code>as_integer()[0]</code>
|
114
158
|
# @return first entry returned by {@link #as_integer}
|
115
159
|
def as_integer
|
116
|
-
|
160
|
+
as_integers[0]
|
161
|
+
end
|
162
|
+
def to_i
|
163
|
+
as_integers[0]
|
117
164
|
end
|
118
165
|
# convenience method corresponding to <code>asDoubles()[0]</code>
|
119
166
|
# @return first entry returned by {@link #asDoubles}
|
120
167
|
def as_double
|
121
|
-
|
168
|
+
as_doubles[0]
|
122
169
|
end
|
123
170
|
def as_float
|
124
171
|
as_double
|
125
172
|
end
|
173
|
+
def to_f
|
174
|
+
as_double
|
175
|
+
end
|
126
176
|
# convenience method corresponding to <code>asStrings()[0]</code>
|
127
177
|
# @return first entry returned by {@link #asStrings}
|
128
178
|
def as_string
|
129
|
-
|
179
|
+
as_strings[0]
|
130
180
|
end
|
131
181
|
# // methods common to all REXPs
|
132
182
|
|
@@ -135,15 +185,14 @@ module Rserve
|
|
135
185
|
# * @return attribute value or <code>null</code> if the attribute does not exist
|
136
186
|
|
137
187
|
def get_attribute(name)
|
138
|
-
|
139
|
-
@attr.as_list[name]
|
188
|
+
has_attribute?(name) ? @attr.as_list[name] : nil
|
140
189
|
end
|
141
190
|
|
142
191
|
# checks whether this obejct has a given attribute
|
143
192
|
# * @param name attribute name
|
144
193
|
# * @return <code>true</code> if the attribute exists, <code>false</code> otherwise
|
145
194
|
def has_attribute? (name)
|
146
|
-
|
195
|
+
!@attr.nil? and @attr.list? and !@attr.as_list[name].nil?
|
147
196
|
end
|
148
197
|
|
149
198
|
|
@@ -165,14 +214,14 @@ module Rserve
|
|
165
214
|
# @return <code>true</code> if this object is of the class <code>klass</code>, <code>false</code> otherwise
|
166
215
|
def inherits?(klass)
|
167
216
|
return false if (!has_attribute? "class")
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
217
|
+
begin
|
218
|
+
c = get_attribute("class").as_strings;
|
219
|
+
if (!c.nil?)
|
220
|
+
return c.any? {|v| v.equals klass}
|
221
|
+
end
|
222
|
+
rescue MismatchException
|
223
|
+
end
|
224
|
+
false
|
176
225
|
end
|
177
226
|
|
178
227
|
|
@@ -180,7 +229,7 @@ module Rserve
|
|
180
229
|
# returns representation that it useful for debugging (e.g. it includes attributes and may include vector values -- see {@link #maxDebugItems})
|
181
230
|
# @return extended description of the obejct -- it may include vector values
|
182
231
|
def to_debug_string
|
183
|
-
|
232
|
+
(!@attr.nil?) ? (("<"+@attr.to_debug_string()+">")+to_s()) : to_s
|
184
233
|
end
|
185
234
|
|
186
235
|
|
@@ -196,16 +245,54 @@ module Rserve
|
|
196
245
|
raise MismatchException, "matrix (dim attribute missing)" if dim.nil?
|
197
246
|
ds = dim.as_integers
|
198
247
|
raise MismatchException, "matrix (wrong dimensionality)" if (ds.length!=2)
|
199
|
-
|
248
|
+
as_nested_array
|
249
|
+
|
250
|
+
#m,n = ds[0], ds[1]
|
200
251
|
# R stores matrices as matrix(c(1,2,3,4),2,2) = col1:(1,2), col2:(3,4)
|
201
252
|
# we need to copy everything, since we create 2d array from 1d array
|
202
|
-
r=m.times.map {|i| n.times.map {|j| ct[j*n+i]}}
|
253
|
+
#r=m.times.map {|i| n.times.map {|j| ct[j*n+i]}}
|
203
254
|
end
|
204
255
|
# Returns a standard library's matrix
|
205
256
|
def as_matrix
|
206
257
|
require 'matrix'
|
207
258
|
Matrix.rows(as_double_matrix)
|
208
259
|
end
|
260
|
+
# Returns the content of the REXP as a serie of nested arrays of X dimensions
|
261
|
+
def as_nested_array
|
262
|
+
ct=as_doubles
|
263
|
+
dim = get_attribute "dim"
|
264
|
+
raise MismatchException, "array (dim attribute missing" if dim.nil?
|
265
|
+
ds = dim.as_integers.reverse
|
266
|
+
|
267
|
+
split_array(ct,ds)
|
268
|
+
end
|
269
|
+
|
270
|
+
def split_array(ar, dims)
|
271
|
+
# puts "#{ar} - #{dims}"
|
272
|
+
if dims.size==1
|
273
|
+
raise "Improper size ar:#{ar} , dims=#{dims[0]}" if ar.size!=dims[0]
|
274
|
+
return ar
|
275
|
+
elsif dims.size==2
|
276
|
+
# should rearrange values as R do
|
277
|
+
out=[]
|
278
|
+
ar.each_with_index {|v,i|
|
279
|
+
r=(i/dims[1]).to_i;
|
280
|
+
c=i%dims[1];
|
281
|
+
# p "#{r} : #{c}";
|
282
|
+
out[c*dims[0]+r]=v
|
283
|
+
}
|
284
|
+
raise "out size should equal to ar size" if ar.size!=out.size
|
285
|
+
ar=out
|
286
|
+
end
|
287
|
+
dims_c=dims.dup
|
288
|
+
current_dim=dims_c.shift
|
289
|
+
current_size=ar.size/current_dim
|
290
|
+
#puts "dims: #{dims_c} cs:#{current_size}, cd:#{current_dim}"
|
291
|
+
parts=current_dim.times.map do |i|
|
292
|
+
split_array(ar[i*current_size, current_size], dims_c)
|
293
|
+
end
|
294
|
+
parts
|
295
|
+
end
|
209
296
|
|
210
297
|
# :section: tools
|
211
298
|
|
@@ -213,25 +300,54 @@ module Rserve
|
|
213
300
|
# * @param l a (named) list of vectors ({@link REXPVector} subclasses), each element corresponds to a column and all elements must have the same length
|
214
301
|
# * @return a data frame object
|
215
302
|
# * @throws MismatchException if the list is empty or any of the elements is not a vector
|
216
|
-
def create_data_frame(l)
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
303
|
+
def self.create_data_frame(l)
|
304
|
+
raise(MismatchException, "data frame (must have dim>0)") if l.nil? or l.size<1
|
305
|
+
raise MismatchException, "data frame (contents must be vectors)" if (!(l[0].is_a? REXP::Vector))
|
306
|
+
fe = l[0]
|
307
|
+
return REXP::GenericVector.new(l,
|
308
|
+
REXP::List.new(
|
309
|
+
Rlist.new(
|
310
|
+
[
|
311
|
+
REXP::String.new("data.frame"),
|
312
|
+
REXP::String.new(l.keys()),
|
313
|
+
REXP::Integer.new([REXP::Integer::NA, -fe.length()])
|
314
|
+
],
|
315
|
+
["class", "names", "row.names" ])
|
316
|
+
)
|
317
|
+
)
|
231
318
|
end
|
232
319
|
# Retrieves the best Ruby representation of data
|
320
|
+
#
|
321
|
+
# If R object has attributes, the Ruby object is extended with Rserve::WithAttributes.
|
322
|
+
# If R object is names, the Ruby object is extended with Rserve::WithNames and
|
323
|
+
# their elements can be accessed with [] using numbers and literals.
|
324
|
+
#
|
233
325
|
def to_ruby
|
234
|
-
|
326
|
+
#pp self
|
327
|
+
v=to_ruby_internal
|
328
|
+
#p v
|
329
|
+
if !v.nil? and !v.is_a? Fixnum and !v.is_a? TrueClass and !v.is_a? FalseClass
|
330
|
+
v.extend Rserve::WithAttributes
|
331
|
+
v.attributes=attr.to_ruby unless attr.nil?
|
332
|
+
if !v.attributes.nil? and v.attributes.has_name? 'names'
|
333
|
+
v.attributes['names']=[v.attributes['names']] unless v.attributes['names'].is_a? Array or v.attributes['names'].nil?
|
334
|
+
|
335
|
+
v.extend Rserve::WithNames
|
336
|
+
|
337
|
+
v.names=v.attributes['names']
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
# Hack: change attribute row.names according to spec
|
342
|
+
if !attr.nil? and attr.as_list.has_name? 'class' and attr.as_list['class'].as_string=='data.frame' and attr.as_list['row.names'].as_integers[0]==REXP::Integer::NA
|
343
|
+
v.attributes['row.names']=(1..(-attr.as_list['row.names'].as_integers[1])).to_a
|
344
|
+
end
|
345
|
+
|
346
|
+
|
347
|
+
v
|
348
|
+
end
|
349
|
+
def to_ruby_internal
|
350
|
+
raise "You should implement to_ruby_internal for #{self.class}"
|
235
351
|
end
|
236
352
|
end
|
237
353
|
end
|
data/lib/rserve/rexp/double.rb
CHANGED
@@ -49,9 +49,13 @@ module Rserve
|
|
49
49
|
t=super
|
50
50
|
t << "{" << @payload.map(&:to_s).join(",") << "}"
|
51
51
|
end
|
52
|
-
def
|
53
|
-
if
|
54
|
-
|
52
|
+
def to_ruby_internal
|
53
|
+
if dim
|
54
|
+
if dim.size==2
|
55
|
+
as_matrix
|
56
|
+
else
|
57
|
+
as_nested_array
|
58
|
+
end
|
55
59
|
else
|
56
60
|
super
|
57
61
|
end
|
data/lib/rserve/rexp/factor.rb
CHANGED
data/lib/rserve/rexp/integer.rb
CHANGED
data/lib/rserve/rexp/list.rb
CHANGED
data/lib/rserve/rexp/logical.rb
CHANGED
data/lib/rserve/rexp/unknown.rb
CHANGED
data/lib/rserve/rexp/vector.rb
CHANGED
@@ -9,10 +9,10 @@ module Rserve
|
|
9
9
|
|
10
10
|
# returns the length of the vector (i.e. the number of elements)
|
11
11
|
# @return length of the vector
|
12
|
-
def length
|
13
|
-
end
|
14
|
-
def vector
|
15
|
-
true
|
12
|
+
def length
|
13
|
+
end
|
14
|
+
def vector?
|
15
|
+
true
|
16
16
|
end
|
17
17
|
# 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
|
18
18
|
# @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 */
|
@@ -24,7 +24,7 @@ module Rserve
|
|
24
24
|
def to_a
|
25
25
|
@payload.map {|v| na?(v) ? nil : v }
|
26
26
|
end
|
27
|
-
def
|
27
|
+
def to_ruby_internal
|
28
28
|
if @payload.nil? or @payload.size==0
|
29
29
|
nil
|
30
30
|
elsif @payload.size==1
|
data/lib/rserve/rfactor.rb
CHANGED
data/lib/rserve/withnames.rb
CHANGED
@@ -3,7 +3,7 @@ module Rserve
|
|
3
3
|
module WithNames
|
4
4
|
attr_reader :names
|
5
5
|
def names=(v)
|
6
|
-
raise ArgumentError, "#size should be equal to object size" if !v.nil? and v.size!=self.size
|
6
|
+
raise ArgumentError, "#{self}: names size #{v.size} should be equal to object size #{self.size}" if !v.nil? and v.size!=self.size
|
7
7
|
raise ArgumentError, "element must be String or nil" unless v.nil? or v.all? {|v1| v1.nil? or v1.is_a? String}
|
8
8
|
@names=v
|
9
9
|
end
|
@@ -13,8 +13,34 @@ module Rserve
|
|
13
13
|
@names.push(name)
|
14
14
|
super(v)
|
15
15
|
end
|
16
|
+
def pretty_print(q)
|
17
|
+
q.group(1,'[|WN|',']') {
|
18
|
+
q.seplist(self,nil,:each_with_index) {|v,i|
|
19
|
+
if (@names.nil? or @names[i].nil?)
|
20
|
+
q.pp v
|
21
|
+
else
|
22
|
+
q.group {
|
23
|
+
q.pp @names[i]
|
24
|
+
q.text '='
|
25
|
+
q.group(1) { q.pp v }
|
26
|
+
}
|
27
|
+
end
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
def to_s
|
32
|
+
out=[]
|
33
|
+
self.each_with_index { |v,i|
|
34
|
+
if !@names.nil? and !@names[i].nil?
|
35
|
+
out.push("#{@names[i]}=#{v}")
|
36
|
+
else
|
37
|
+
out.push("#{v}")
|
38
|
+
end
|
39
|
+
}
|
40
|
+
"[#{out.join(", ")}]"
|
41
|
+
end
|
16
42
|
def inspect
|
17
|
-
"#<#{self.class}:#{self.object_id} #{
|
43
|
+
"#<#{self.class}:#{self.object_id} #{to_s}>"
|
18
44
|
end
|
19
45
|
def clear
|
20
46
|
@names=nil
|
@@ -53,6 +79,9 @@ module Rserve
|
|
53
79
|
end
|
54
80
|
sliced
|
55
81
|
end
|
82
|
+
def has_name?(v)
|
83
|
+
named? and @names.include? v
|
84
|
+
end
|
56
85
|
def named?
|
57
86
|
!@names.nil?
|
58
87
|
end
|
@@ -74,7 +103,7 @@ module Rserve
|
|
74
103
|
if !@names.nil?
|
75
104
|
pos=@names.index(key)
|
76
105
|
if !pos.nil?
|
77
|
-
return
|
106
|
+
return self[pos]=value
|
78
107
|
end
|
79
108
|
end
|
80
109
|
push(value,key)
|
@@ -89,6 +118,16 @@ module Rserve
|
|
89
118
|
@names.insert(a,nil)
|
90
119
|
end
|
91
120
|
end
|
121
|
+
def []=(i,v)
|
122
|
+
case i
|
123
|
+
when Integer
|
124
|
+
super(i,v)
|
125
|
+
when String
|
126
|
+
put(i,v)
|
127
|
+
else
|
128
|
+
raise "Should be Integer or String"
|
129
|
+
end
|
130
|
+
end
|
92
131
|
def [](v)
|
93
132
|
case v
|
94
133
|
when Integer
|
data/spec/rserve_rexp_spec.rb
CHANGED
@@ -19,6 +19,14 @@ describe Rserve::REXP do
|
|
19
19
|
@m=@r.eval('matrix(c(1,2,3,4,5,6,7,8,9), 3,3)')
|
20
20
|
@m.as_matrix.should==Matrix[[1,4,7],[2,5,8],[3,6,9]]
|
21
21
|
end
|
22
|
+
it "method split_array returns a valid splitted array" do
|
23
|
+
@m.as_double_matrix.should==@m.as_nested_array
|
24
|
+
@r.void_eval("a=1:16; attr(a,'dim')<-c(2,2,2,2)")
|
25
|
+
a=@r.eval("a")
|
26
|
+
a.as_nested_array.should==[[[[1.0, 3.0], [2.0, 4.0]], [[5.0, 7.0], [6.0, 8.0]]],
|
27
|
+
[[[9.0, 11.0], [10.0, 12.0]], [[13.0, 15.0], [14.0, 16.0]]]]
|
28
|
+
|
29
|
+
end
|
22
30
|
end
|
23
31
|
describe "common methods" do
|
24
32
|
before do
|
@@ -46,7 +54,15 @@ describe Rserve::REXP do
|
|
46
54
|
it "method get_attribute should return correct value for attribute" do
|
47
55
|
@l.get_attribute('names').as_strings.should==['at']
|
48
56
|
end
|
49
|
-
|
57
|
+
it "method create_data_frame should create a valid data frame" do
|
58
|
+
l=Rserve::Rlist.new([Rserve::REXP::Integer.new([1,2,3,4,5,6,7,8,9,10])],['a'])
|
59
|
+
@r.assign "b", Rserve::REXP.create_data_frame(l)
|
60
|
+
b=@r.eval("b")
|
61
|
+
b.attr.as_list['class'].as_string.should=='data.frame'
|
62
|
+
b.attr.as_list['row.names'].as_integers.should==[Rserve::REXP::Integer::NA, -10]
|
63
|
+
b.attr.as_list['names'].as_strings.should==['a']
|
64
|
+
|
65
|
+
end
|
50
66
|
end
|
51
67
|
|
52
68
|
end
|
@@ -11,20 +11,49 @@ describe "Rserve::REXP#to_ruby" do
|
|
11
11
|
it "should return an array of Fixnum and nils with vector with two or more elements" do
|
12
12
|
@r.eval("c(1,2,3,NA)").to_ruby.should==[1,2,3,nil]
|
13
13
|
end
|
14
|
+
|
15
|
+
it "should return an array of Fixnum with an enumeration" do
|
16
|
+
@r.eval("1:10").to_ruby.should==[1,2,3,4,5,6,7,8,9,10]
|
17
|
+
end
|
18
|
+
it "should return an array of String with a factor" do
|
19
|
+
@r.eval("factor(c(NA,'a','b','b','c'))").to_ruby.should==[nil]+%w{a b b c}
|
20
|
+
end
|
21
|
+
it "should return an array of String and nils with a factor with NA" do
|
22
|
+
@r.eval("factor(c('a','a','b','b','c'))").to_ruby.should==%w{a a b b c}
|
23
|
+
end
|
24
|
+
|
14
25
|
it "should return a rational with vector with one element" do
|
15
26
|
@r.eval("c(0.5)").to_ruby.should==1.quo(2)
|
16
27
|
end
|
17
28
|
it "should return an array of rationals with vector with more than one elements" do
|
18
29
|
@r.eval("c(0.5,0.5,NA)").to_ruby.should==[1.quo(2), 1.quo(2),nil]
|
19
30
|
end
|
31
|
+
it "should return an object with module WithAttributes included, when attributes are set" do
|
32
|
+
@r.void_eval("a<-c(1,2,3);attr(a,'names')<-c('a','b','c');")
|
33
|
+
a=@r.eval("a").to_ruby
|
34
|
+
a.attributes.names.should==['names']
|
35
|
+
a.attributes['names'].should==%w{a b c}
|
36
|
+
end
|
20
37
|
it "should return a Ruby Matrix with R matrix" do
|
21
38
|
@r.eval("matrix(c(1,2,3,4),2,2)").to_ruby.should==Matrix[[1,3],[2,4]]
|
22
39
|
end
|
23
|
-
it "should return a nested array of Ruby Matrixes with vector with more than tree dimensions"
|
40
|
+
it "should return a nested array of Ruby Matrixes with vector with more than tree dimensions" do
|
41
|
+
@r.void_eval("a<-1:16; attr(a,'dim')<-c(2,2,2,2)")
|
42
|
+
@r.eval("a").to_ruby.should==[[[[1.0, 3.0], [2.0, 4.0]], [[5.0, 7.0], [6.0, 8.0]]],
|
43
|
+
[[[9.0, 11.0], [10.0, 12.0]], [[13.0, 15.0], [14.0, 16.0]]]]
|
44
|
+
|
45
|
+
end
|
24
46
|
it "should return a boolean with a logical with one element" do
|
25
47
|
@r.eval("TRUE").to_ruby.should be_true
|
26
48
|
end
|
27
|
-
|
49
|
+
it "should return an array extended with Rserve::WithNames if vector is named" do
|
50
|
+
@r.void_eval("a<-c(1,2,3);names(a)<-c('a','b','c')")
|
51
|
+
v=@r.eval('a').to_ruby
|
52
|
+
v.names.should==['a','b','c']
|
53
|
+
v[0].should==1
|
54
|
+
v['a'].should==1
|
55
|
+
|
56
|
+
end
|
28
57
|
it "should return an array of booleans with a logical with two or more elements" do
|
29
58
|
@r.eval("c(TRUE,FALSE,NA)").to_ruby.should==[true,false,nil]
|
30
59
|
end
|
@@ -35,10 +64,20 @@ describe "Rserve::REXP#to_ruby" do
|
|
35
64
|
it "should return an array of strings with a vector with two or more strings" do
|
36
65
|
@r.eval("c('a','b',NA)").to_ruby.should==['a','b',nil]
|
37
66
|
end
|
38
|
-
it "should return an array extended with Rserve::WithNames for a list" do
|
67
|
+
it "should return an array extended with Rserve::WithNames and Rserve::WithAttributes for a list" do
|
39
68
|
expected=[1,2,3].extend Rserve::WithNames
|
40
69
|
expected.names=%w{a b c}
|
41
|
-
|
70
|
+
list=@r.eval('list(a=1,b=2,c=3)').to_ruby
|
71
|
+
list.names.should==expected.names
|
72
|
+
list.attributes['names'].should==%w{a b c}
|
73
|
+
end
|
74
|
+
it "should return a data.frame as an array with Rserve::WithNames and Rserve::WithAttributes" do
|
75
|
+
df=@r.eval('data.frame(a=1:10)').to_ruby
|
76
|
+
df['a'].should==[1,2,3,4,5,6,7,8,9,10]
|
77
|
+
df.attributes['names'].should==['a']
|
78
|
+
df.attributes['row.names'].should==[1,2,3,4,5,6,7,8,9,10]
|
79
|
+
df.attributes['class'].should=='data.frame'
|
80
|
+
|
42
81
|
end
|
43
82
|
end
|
44
83
|
end
|
@@ -46,6 +46,11 @@ describe Rserve::Protocol::REXPFactory do
|
|
46
46
|
v.should be_close(a[i],1e-10)
|
47
47
|
}
|
48
48
|
end
|
49
|
+
it "should process integer" do
|
50
|
+
la=@r.eval("-10:10")
|
51
|
+
la.should be_instance_of(Rserve::REXP::Integer)
|
52
|
+
la.as_integers.should==(-10..10).to_a
|
53
|
+
end
|
49
54
|
it "should process double vector with NA" do
|
50
55
|
la=@r.eval("c(1,NA)")
|
51
56
|
la.should be_instance_of(Rserve::REXP::Double)
|
@@ -111,7 +116,12 @@ describe Rserve::Protocol::REXPFactory do
|
|
111
116
|
la.should be_true
|
112
117
|
la.attr.as_list['names'].to_ruby.should==%w{a b}
|
113
118
|
la.attr.as_list['class'].to_ruby.should=="data.frame"
|
114
|
-
|
119
|
+
la.attr.as_list['row.names'].to_ruby.should==[nil,-10]
|
120
|
+
end
|
121
|
+
it "should process a nested array" do
|
122
|
+
@r.void_eval("c=1:8; attr(c,'dim')<-c(2,2,2)")
|
123
|
+
la=@r.eval("c")
|
124
|
+
la.attr.as_list['dim'].to_ruby.should==[2,2,2]
|
115
125
|
end
|
116
126
|
|
117
127
|
it "should retrieve correct lenght for string" do
|
@@ -36,6 +36,17 @@ describe Rserve::WithNames do
|
|
36
36
|
@a['a'].should==1
|
37
37
|
@a['b'].should==2
|
38
38
|
end
|
39
|
+
it "should set values with numeric indexes" do
|
40
|
+
@a[0]=10
|
41
|
+
@a.should==[10,2,3,4]
|
42
|
+
end
|
43
|
+
it "should set values with string indexes" do
|
44
|
+
@a['a']=10
|
45
|
+
@a.should==[10,2,3,4]
|
46
|
+
@a.put('b',20)
|
47
|
+
@a.should==[10,20,3,4]
|
48
|
+
end
|
49
|
+
|
39
50
|
it "should push with or without name" do
|
40
51
|
@a.push(5)
|
41
52
|
@a.names.should==['a','b','c',nil,nil]
|
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
|
+
- 9
|
9
|
+
version: 0.1.9
|
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-06-
|
38
|
+
date: 2010-06-04 00:00:00 -04:00
|
39
39
|
default_executable:
|
40
40
|
dependencies:
|
41
41
|
- !ruby/object:Gem::Dependency
|
@@ -77,18 +77,22 @@ executables: []
|
|
77
77
|
extensions: []
|
78
78
|
|
79
79
|
extra_rdoc_files:
|
80
|
-
- Examples.txt
|
81
80
|
- History.txt
|
81
|
+
- Introduction.txt
|
82
82
|
- Manifest.txt
|
83
83
|
- README.txt
|
84
84
|
files:
|
85
|
-
-
|
85
|
+
- .autotest
|
86
86
|
- History.txt
|
87
|
+
- Introduction.txt
|
87
88
|
- Manifest.txt
|
88
89
|
- README.txt
|
89
90
|
- Rakefile
|
91
|
+
- data/gettysburg.txt
|
92
|
+
- examples/gettysburg.rb
|
90
93
|
- examples/hello_world.rb
|
91
94
|
- examples/lowless.rb
|
95
|
+
- examples/regression.rb
|
92
96
|
- lib/rserve.rb
|
93
97
|
- lib/rserve/connection.rb
|
94
98
|
- lib/rserve/engine.rb
|
metadata.gz.sig
CHANGED
Binary file
|