rinruby 2.0.3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +24 -0
- data/Gemfile +4 -0
- data/History.txt +47 -4
- data/Manifest.txt +2 -0
- data/{README.txt → README.md} +27 -18
- data/Rakefile +1 -2
- data/lib/rinruby.rb +271 -217
- data/lib/rinruby_without_r_constant.rb +24 -0
- data/lib/version.rb +3 -0
- data/rinruby.gemspec +24 -0
- data/spec/rinruby_spec.rb +212 -125
- data/spec/rinruby_without_r_constant_spec.rb +9 -0
- data/spec/spec_helper.rb +16 -4
- metadata +59 -59
- data/.gemtest +0 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
#=RinRuby: Accessing the R[http://www.r-project.org] interpreter from pure Ruby
|
2
|
+
#
|
3
|
+
#RinRuby is a Ruby library that integrates the R interpreter in Ruby, making R's statistical routines and graphics available within Ruby. The library consists of a single Ruby script that is simple to install and does not require any special compilation or installation of R. Since the library is 100% pure Ruby, it works on a variety of operating systems, Ruby implementations, and versions of R. RinRuby's methods are simple, making for readable code. The {website [rinruby.ddahl.org]}[http://rinruby.ddahl.org] describes RinRuby usage, provides comprehensive documentation, gives several examples, and discusses RinRuby's implementation.
|
4
|
+
#
|
5
|
+
|
6
|
+
if defined?(R)
|
7
|
+
require 'rinruby'
|
8
|
+
else
|
9
|
+
R = :dummy
|
10
|
+
require 'rinruby'
|
11
|
+
Object::send(:remove_const, :R)
|
12
|
+
end
|
13
|
+
|
14
|
+
class RinRubyWithoutRConstant < RinRuby
|
15
|
+
DEFAULT_PORT_NUMBER = 38542
|
16
|
+
def initialize(*args)
|
17
|
+
if args.size == 1 and args[0].kind_of?(Hash) then
|
18
|
+
args[0][:port_number] ||= DEFAULT_PORT_NUMBER
|
19
|
+
else
|
20
|
+
args[3] ||= DEFAULT_PORT_NUMBER
|
21
|
+
end
|
22
|
+
super(*args)
|
23
|
+
end
|
24
|
+
end
|
data/lib/version.rb
ADDED
data/rinruby.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rinruby"
|
8
|
+
spec.version = Rinruby::VERSION
|
9
|
+
spec.authors = ["David Dahl", "Scott Crawford", "Claudio Bustos"]
|
10
|
+
spec.email = ["rinruby@ddahl.org", "scott@ddahl.org", "clbustos@gmail.com"]
|
11
|
+
spec.summary = %q{RinRuby is a Ruby library that integrates the R interpreter in Ruby}
|
12
|
+
spec.description = %q{RinRuby is a Ruby library that integrates the R interpreter in Ruby, making R's statistical routines and graphics available within Ruby. The library consists of a single Ruby script that is simple to install and does not require any special compilation or installation of R. Since the library is 100% pure Ruby, it works on a variety of operating systems, Ruby implementations, and versions of R. RinRuby's methods are simple, making for readable code. The {website [rinruby.ddahl.org]}[http://rinruby.ddahl.org] describes RinRuby usage, provides comprehensive documentation, gives several examples, and discusses RinRuby's implementation.}
|
13
|
+
spec.homepage = "http://rinruby.ddahl.org"
|
14
|
+
spec.license = "Copyright 2005-2008 David B. Dahl"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
spec.add_development_dependency "rspec", ">= 2.11"
|
23
|
+
spec.add_development_dependency "hoe"
|
24
|
+
end
|
data/spec/rinruby_spec.rb
CHANGED
@@ -1,148 +1,235 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'rinruby'
|
2
3
|
puts "RinRuby #{RinRuby::VERSION} specification"
|
3
4
|
|
4
|
-
|
5
|
+
shared_examples 'RinRubyCore' do
|
6
|
+
let(:params){
|
7
|
+
{
|
8
|
+
:echo_enabled => false,
|
9
|
+
:interactive => false,
|
10
|
+
:executable => nil,
|
11
|
+
:port_number => 38500,
|
12
|
+
:port_width => 1000,
|
13
|
+
}
|
14
|
+
}
|
5
15
|
describe "on init" do
|
16
|
+
after{(r.quit rescue nil) if defined?(r)}
|
6
17
|
it "should accept parameters as specified on Dahl & Crawford(2009)" do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
when /
|
11
|
-
|
12
|
-
else
|
13
|
-
"
|
14
|
-
end
|
15
|
-
if platform=='windows'
|
16
|
-
pending("Difficult to test without specific location of R executable on Windows")
|
17
|
-
else
|
18
|
-
|
19
|
-
r=RinRuby.new(false, false, "R", 38500, 1)
|
20
|
-
r.echo_enabled.should_not be_true
|
21
|
-
r.interactive.should_not be_true
|
22
|
-
r.executable.should=="R"
|
23
|
-
r.port_number.should==38500
|
24
|
-
r.port_width.should==1
|
25
|
-
end
|
18
|
+
expect(r.echo_enabled).to be_falsy
|
19
|
+
expect(r.interactive).to be_falsy
|
20
|
+
case r.instance_variable_get(:@platform)
|
21
|
+
when /^windows/ then
|
22
|
+
expect(r.executable).to match(/Rterm\.exe["']?$/)
|
23
|
+
else
|
24
|
+
expect(r.executable).to eq("R")
|
25
|
+
end
|
26
26
|
end
|
27
27
|
it "should accept :echo and :interactive parameters" do
|
28
|
-
|
29
|
-
r.echo_enabled.
|
30
|
-
r.interactive.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
r=RinRuby.new(:port=>port, :port_width=>port_width)
|
43
|
-
r.port_width.should==port_width
|
44
|
-
r.port_number.should satisfy {|v| v>=port and v < port+port_width}
|
45
|
-
end
|
46
|
-
end
|
47
|
-
before do
|
48
|
-
R.echo(false)
|
49
|
-
end
|
50
|
-
subject {R}
|
51
|
-
context "basic methods" do
|
52
|
-
it {should respond_to :eval}
|
53
|
-
it {should respond_to :quit}
|
54
|
-
it {should respond_to :assign}
|
55
|
-
it {should respond_to :pull}
|
56
|
-
it {should respond_to :quit}
|
57
|
-
it {should respond_to :echo}
|
58
|
-
it "return correct values for complete?" do
|
59
|
-
R.eval("x<-1").should be_true
|
60
|
-
end
|
61
|
-
it "return false for complete? for incorrect expressions" do
|
62
|
-
R.complete?("x<-").should be_false
|
63
|
-
end
|
64
|
-
it "correct eval should return true" do
|
65
|
-
R.complete?("x<-1").should be_true
|
66
|
-
end
|
67
|
-
it "incorrect eval should raise an ParseError" do
|
68
|
-
lambda {R.eval("x<-")}.should raise_error(RinRuby::ParseError)
|
28
|
+
params.merge!(:echo_enabled => true, :interactive => true)
|
29
|
+
expect(r.echo_enabled).to be_truthy
|
30
|
+
expect(r.interactive).to be_truthy
|
31
|
+
end
|
32
|
+
it "should accept custom :port_number" do
|
33
|
+
params.merge!(:port_number => 38442+rand(3), :port_width => 1)
|
34
|
+
expect(r.port_number).to eq(params[:port_number])
|
35
|
+
end
|
36
|
+
it "should accept custom :port_width" do
|
37
|
+
params.merge!(:port_number => 38442, :port_width => rand(10)+1)
|
38
|
+
expect(r.port_width).to eq(params[:port_width])
|
39
|
+
expect(r.port_number).to satisfy {|v|
|
40
|
+
((params[:port_number])...(params[:port_number] + params[:port_width])).include?(v)
|
41
|
+
}
|
69
42
|
end
|
70
43
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
44
|
+
|
45
|
+
describe "R interface" do
|
46
|
+
# In before(:each) or let(including subject) blocks, Assignment to instance variable
|
47
|
+
# having a same name defined in before(:all) will not work intentionally,
|
48
|
+
# because a new instance variable will be created for the following examples.
|
49
|
+
# For workaround, two-step indirect assignment to a hash created in before(:all) is applied.
|
50
|
+
before(:all){@cached_env = {:r => nil}} # make placeholder
|
51
|
+
subject{@cached_env[:r] ||= r}
|
52
|
+
after(:all){@cached_env[:r].quit rescue nil}
|
53
|
+
describe "basic methods" do
|
54
|
+
it {is_expected.to respond_to(:eval)}
|
55
|
+
it {is_expected.to respond_to(:assign)}
|
56
|
+
it {is_expected.to respond_to(:pull)}
|
57
|
+
it {is_expected.to respond_to(:quit)}
|
58
|
+
it {is_expected.to respond_to(:echo)}
|
59
|
+
it "return correct values for complete?" do
|
60
|
+
expect(subject.eval("x<-1")).to be_truthy
|
61
|
+
end
|
62
|
+
it "return false for complete? for incorrect expressions" do
|
63
|
+
expect(subject.complete?("x<-")).to be_falsy
|
64
|
+
end
|
65
|
+
it "correct eval should return true" do
|
66
|
+
expect(subject.complete?("x<-1")).to be_truthy
|
67
|
+
end
|
68
|
+
it "incorrect eval should raise an ParseError" do
|
69
|
+
expect{subject.eval("x<-")}.to raise_error(RinRuby::ParseError)
|
70
|
+
end
|
98
71
|
end
|
99
72
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
73
|
+
context "on pull" do
|
74
|
+
it "should pull a String" do
|
75
|
+
subject.eval("x<-'Value'")
|
76
|
+
expect(subject.pull('x')).to eql('Value')
|
77
|
+
end
|
78
|
+
it "should pull an Integer" do
|
79
|
+
[0x12345678, -0x12345678].each{|v| # for check endian, and range
|
80
|
+
subject.eval("x<-#{v}L")
|
81
|
+
expect(subject.pull('x')).to eql(v)
|
82
|
+
}
|
83
|
+
end
|
84
|
+
it "should pull a Float" do
|
85
|
+
[1.5, 1.0].each{|v|
|
86
|
+
subject.eval("x<-#{v}e0")
|
87
|
+
expect(subject.pull('x')).to eql(v)
|
88
|
+
}
|
89
|
+
[1 << 32, -(1 << 32)].each{|v| # big integer will be treated as float
|
90
|
+
subject.eval("x<-#{v}")
|
91
|
+
expect(subject.pull('x')).to eql(v.to_f)
|
92
|
+
}
|
93
|
+
end
|
94
|
+
it "should pull a Logical" do
|
95
|
+
{:T => true, :F => false}.each{|k, v|
|
96
|
+
subject.eval("x<-#{k}")
|
97
|
+
expect(subject.pull('x')).to eql(v)
|
98
|
+
}
|
99
|
+
end
|
100
|
+
it "should pull an Array of String" do
|
101
|
+
subject.eval("x<-c('a','b')")
|
102
|
+
expect(subject.pull('x')).to eql(['a','b'])
|
103
|
+
end
|
104
|
+
it "should pull an Array of Integer" do
|
105
|
+
subject.eval("x<-c(1L,2L,-5L,-3L)")
|
106
|
+
expect(subject.pull('x')).to eql([1,2,-5,-3])
|
107
|
+
end
|
108
|
+
it "should pull an Array of Float" do
|
109
|
+
subject.eval("x<-c(1.1,2.2,5,3)")
|
110
|
+
expect(subject.pull('x')).to eql([1.1,2.2,5.0,3.0])
|
111
|
+
subject.eval("x<-c(1L,2L,5L,3.0)") # numeric vector
|
112
|
+
expect(subject.pull('x')).to eql([1.0,2.0,5.0,3.0])
|
113
|
+
end
|
114
|
+
it "should pull an Array of Logical" do
|
115
|
+
subject.eval("x<-c(T, F)")
|
116
|
+
expect(subject.pull('x')).to eql([true, false])
|
117
|
+
end
|
120
118
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
matrix
|
119
|
+
it "should pull a Matrix" do
|
120
|
+
[
|
121
|
+
proc{ # integer matrix
|
122
|
+
v = rand(100000000) # get 8 digits
|
123
|
+
[v, "#{v}L"]
|
124
|
+
},
|
125
|
+
proc{ # float matrix
|
126
|
+
v = rand(100000000) # get 8 digits
|
127
|
+
[Float("0.#{v}"), "0.#{v}"]
|
128
|
+
},
|
129
|
+
].each{|gen_proc|
|
130
|
+
nrow, ncol = [10, 10] # 10 x 10 small matrix
|
131
|
+
subject.eval("x<-matrix(nrow=#{nrow}, ncol=#{ncol})")
|
132
|
+
rx = Matrix[*((1..nrow).collect{|i|
|
133
|
+
(1..ncol).collect{|j|
|
134
|
+
v_rb, v_R = gen_proc.call
|
135
|
+
subject.eval("x[#{i},#{j}]<-#{v_R}")
|
136
|
+
v_rb
|
137
|
+
}
|
138
|
+
})]
|
139
|
+
expect(subject.pull('x')).to eql(rx)
|
128
140
|
}
|
129
|
-
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should be the same using pull than R# methods" do
|
144
|
+
subject.eval("x <- #{rand(100000000)}")
|
145
|
+
expect(subject.pull("x")).to eql(subject.x)
|
146
|
+
end
|
147
|
+
it "should raise an NoMethod error on getter with 1 or more parameters" do
|
148
|
+
expect{subject.unknown_method(1)}.to raise_error(NoMethodError)
|
149
|
+
end
|
130
150
|
end
|
131
151
|
|
132
|
-
|
152
|
+
context "on assign (PREREQUISITE: all pull tests are passed)" do
|
153
|
+
it "should assign a String" do
|
154
|
+
x = 'Value'
|
155
|
+
subject.assign("x", x)
|
156
|
+
expect(subject.pull('x')).to eql(x)
|
157
|
+
end
|
158
|
+
it "should assign an Integer" do
|
159
|
+
[0x12345678, -0x12345678].each{|x|
|
160
|
+
subject.assign("x", x)
|
161
|
+
expect(subject.pull('x')).to eql(x)
|
162
|
+
}
|
163
|
+
end
|
164
|
+
it "should assign a Float" do
|
165
|
+
[rand, 1 << 32, -(1 << 32)].each{|x|
|
166
|
+
subject.assign("x", x)
|
167
|
+
expect(subject.pull('x')).to eql(x.to_f)
|
168
|
+
}
|
169
|
+
end
|
170
|
+
it "should assign a Logical" do
|
171
|
+
[true, false].each{|x|
|
172
|
+
subject.assign("x", x)
|
173
|
+
expect(subject.pull('x')).to eql(x)
|
174
|
+
}
|
175
|
+
end
|
176
|
+
it "should assign an Array of String" do
|
177
|
+
x = ['a', 'b']
|
178
|
+
subject.assign("x", x)
|
179
|
+
expect(subject.pull('x')).to eql(x)
|
180
|
+
end
|
181
|
+
it "should assign an Array of Integer" do
|
182
|
+
x = [1, 2, -5, -3]
|
183
|
+
subject.assign("x", x)
|
184
|
+
expect(subject.pull('x')).to eql(x)
|
185
|
+
end
|
186
|
+
it "should assign an Array of Float" do
|
187
|
+
subject.assign("x", [1.1, 2.2, 5, 3])
|
188
|
+
expect(subject.pull('x')).to eql([1.1,2.2,5.0,3.0])
|
189
|
+
end
|
190
|
+
it "should assign an Array of Logical" do
|
191
|
+
x = [true, false]
|
192
|
+
subject.assign("x", x)
|
193
|
+
expect(subject.pull('x')).to eql(x)
|
194
|
+
end
|
133
195
|
|
196
|
+
it "should assign a Matrix" do
|
197
|
+
[
|
198
|
+
proc{rand(100000000)}, # integer matrix
|
199
|
+
proc{rand}, # float matrix
|
200
|
+
].each{|gen_proc|
|
201
|
+
x = Matrix::build(100, 200){|i, j| gen_proc.call} # 100 x 200 matrix
|
202
|
+
subject.assign("x", x)
|
203
|
+
expect(subject.pull('x')).to eql(x)
|
204
|
+
}
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should be the same using assign than R#= methods" do
|
208
|
+
x = rand(100000000)
|
209
|
+
subject.assign("x1", x)
|
210
|
+
subject.x2 = x
|
211
|
+
expect(subject.pull("x1")).to eql(subject.pull("x2"))
|
212
|
+
end
|
213
|
+
it "should raise an ArgumentError error on setter with 0 parameters" do
|
214
|
+
expect{subject.unknown_method=() }.to raise_error(ArgumentError)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
134
219
|
context "on quit" do
|
135
|
-
before(:each) do
|
136
|
-
@r=RinRuby.new(:echo=>false)
|
137
|
-
end
|
138
220
|
it "return true" do
|
139
|
-
|
221
|
+
expect(r.quit).to be_truthy
|
140
222
|
end
|
141
223
|
it "returns an error if used again" do
|
142
|
-
|
143
|
-
|
224
|
+
r.quit
|
225
|
+
expect{r.eval("x=1")}.to raise_error(RinRuby::EngineClosed)
|
144
226
|
end
|
145
227
|
end
|
146
|
-
|
147
|
-
|
148
228
|
end
|
229
|
+
|
230
|
+
describe RinRuby do
|
231
|
+
let(:r){
|
232
|
+
RinRuby.new(*([:echo_enabled, :interactive, :executable, :port_number, :port_width].collect{|k| params[k]}))
|
233
|
+
}
|
234
|
+
include_examples 'RinRubyCore'
|
235
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'rinruby_without_r_constant'
|
3
|
+
|
4
|
+
describe RinRubyWithoutRConstant do
|
5
|
+
let(:r){
|
6
|
+
RinRubyWithoutRConstant.new(*([:echo_enabled, :interactive, :executable, :port_number, :port_width].collect{|k| params[k]}))
|
7
|
+
}
|
8
|
+
include_examples 'RinRubyCore'
|
9
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,16 +1,28 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
2
|
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
|
3
|
-
require 'rinruby'
|
4
3
|
require 'rspec'
|
5
|
-
|
6
4
|
require 'matrix'
|
7
5
|
|
8
6
|
RSpec.configure do |config|
|
9
|
-
|
7
|
+
config.expect_with :rspec do |c|
|
8
|
+
c.syntax = [:should, :expect]
|
9
|
+
end
|
10
|
+
|
11
|
+
# Use color in STDOUT
|
12
|
+
config.color = true
|
13
|
+
|
14
|
+
# Use color not only in STDOUT but also in pagers and files
|
15
|
+
config.tty = true
|
16
|
+
|
17
|
+
# Use the specified formatter
|
18
|
+
config.formatter = :documentation # :progress, :html, :textmate
|
10
19
|
end
|
11
20
|
|
21
|
+
|
12
22
|
class String
|
13
23
|
def deindent
|
14
|
-
gsub /^[ \t]*/, ''
|
24
|
+
gsub /^[ \t]*/, ''
|
15
25
|
end
|
16
26
|
end
|
27
|
+
|
28
|
+
|