rinruby-edge 2.1.0.edge.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.appveyor.yml +68 -0
- data/.gitignore +6 -0
- data/.travis.yml +23 -0
- data/Gemfile +10 -0
- data/History.txt +66 -0
- data/LICENSE.txt +646 -0
- data/Manifest.txt +11 -0
- data/README.md +9 -0
- data/README_ORIG.md +75 -0
- data/Rakefile +18 -0
- data/lib/rinruby/version.rb +3 -0
- data/lib/rinruby.rb +1028 -0
- data/lib/rinruby_without_r_constant.rb +24 -0
- data/rinruby.gemspec +22 -0
- data/spec/rinruby_spec.rb +441 -0
- data/spec/rinruby_without_r_constant_spec.rb +9 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +35 -0
- metadata +118 -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/rinruby.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require_relative 'lib/rinruby/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = "rinruby-edge"
|
6
|
+
spec.version = RinRuby::VERSION
|
7
|
+
spec.authors = ["David Dahl", "Scott Crawford", "Claudio Bustos"]
|
8
|
+
spec.email = ["rinruby@ddahl.org", "scott@ddahl.org", "clbustos@gmail.com"]
|
9
|
+
spec.summary = %q{RinRuby is a Ruby library that integrates the R interpreter in Ruby}
|
10
|
+
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.}
|
11
|
+
spec.homepage = "http://rinruby.ddahl.org"
|
12
|
+
spec.license = "GPL-3.0"
|
13
|
+
|
14
|
+
spec.files = `git ls-files`.split($/)
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "rake"
|
20
|
+
spec.add_development_dependency "rspec", ">= 3.0"
|
21
|
+
spec.add_development_dependency "simplecov"
|
22
|
+
end
|
@@ -0,0 +1,441 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'rinruby'
|
3
|
+
puts "RinRuby #{RinRuby::VERSION} specification"
|
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
|
+
}
|
15
|
+
describe "on init" do
|
16
|
+
after{(r.quit rescue nil) if defined?(r)}
|
17
|
+
it "should accept parameters as specified on Dahl & Crawford(2009)" do
|
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-cygwin/ then
|
22
|
+
expect(r.executable).to match(/(^R|Rterm\.exe["']?)$/)
|
23
|
+
when /^windows/ then
|
24
|
+
expect(r.executable).to match(/Rterm\.exe["']?$/)
|
25
|
+
else
|
26
|
+
expect(r.executable).to eq("R")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
it "should accept :echo and :interactive parameters" do
|
30
|
+
params.merge!(:echo_enabled => true, :interactive => true)
|
31
|
+
expect(r.echo_enabled).to be_truthy
|
32
|
+
expect(r.interactive).to be_truthy
|
33
|
+
end
|
34
|
+
it "should accept custom :port_number" do
|
35
|
+
params.merge!(:port_number => 38442+rand(3), :port_width => 1)
|
36
|
+
expect(r.port_number).to eq(params[:port_number])
|
37
|
+
end
|
38
|
+
it "should accept custom :port_width" do
|
39
|
+
params.merge!(:port_number => 38442, :port_width => rand(10)+1)
|
40
|
+
expect(r.port_width).to eq(params[:port_width])
|
41
|
+
expect(r.port_number).to satisfy {|v|
|
42
|
+
((params[:port_number])...(params[:port_number] + params[:port_width])).include?(v)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "R interface" do
|
48
|
+
# In before(:each) or let(including subject) blocks, Assignment to instance variable
|
49
|
+
# having a same name defined in before(:all) will not work intentionally,
|
50
|
+
# because a new instance variable will be created for the following examples.
|
51
|
+
# For workaround, two-step indirect assignment to a hash created in before(:all) is applied.
|
52
|
+
before(:all){@cached_env = {:r => nil}} # make placeholder
|
53
|
+
subject{@cached_env[:r] ||= r}
|
54
|
+
after(:all){@cached_env[:r].quit rescue nil}
|
55
|
+
describe "basic methods" do
|
56
|
+
it {is_expected.to respond_to(:eval)}
|
57
|
+
it {is_expected.to respond_to(:assign)}
|
58
|
+
it {is_expected.to respond_to(:pull)}
|
59
|
+
it {is_expected.to respond_to(:quit)}
|
60
|
+
it {is_expected.to respond_to(:echo)}
|
61
|
+
it {is_expected.to respond_to(:prompt)}
|
62
|
+
it "return true for complete? for correct expressions" do
|
63
|
+
["", "x<-1", "x<-\n1", "'123\n456'", "1+\n2+\n3"].each{|str|
|
64
|
+
expect(subject.complete?(str)).to be true
|
65
|
+
}
|
66
|
+
end
|
67
|
+
it "return false for complete? for incorrect expressions" do
|
68
|
+
["x<-", "'123\n", "1+\n2+\n"].each{|str|
|
69
|
+
expect(subject.complete?(str)).to be false
|
70
|
+
}
|
71
|
+
end
|
72
|
+
it "raise error for complete? for unrecoverable expression" do
|
73
|
+
[";", "x<-;"].each{|str|
|
74
|
+
expect{subject.complete?(str)}.to raise_error(RinRuby::ParseError)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
it "correct eval should return true" do
|
78
|
+
["", "x<-1", "x<-\n1", "'123\n456'"].each{|str|
|
79
|
+
expect(subject.eval(str)).to be_truthy
|
80
|
+
}
|
81
|
+
end
|
82
|
+
it "incorrect eval should raise an ParseError" do
|
83
|
+
[
|
84
|
+
"x<-", "'123\n", # incomplete
|
85
|
+
";", "x<-;", # unrecoverable
|
86
|
+
].each{|str|
|
87
|
+
expect{subject.eval(str)}.to raise_error(RinRuby::ParseError)
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def gen_matrix_cmp_per_elm_proc(&cmp_proc)
|
93
|
+
proc{|a, b|
|
94
|
+
expect(a.row_size).to eql(b.row_size)
|
95
|
+
expect(a.column_size).to eql(b.column_size)
|
96
|
+
a.row_size.times{|i|
|
97
|
+
a.column_size.times{|j|
|
98
|
+
cmp_proc.call(a[i,j], b[i,j])
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
context "on pull" do
|
105
|
+
it "should pull a Character" do
|
106
|
+
['Value', ''].each{|v| # normal string and zero-length string
|
107
|
+
subject.eval("x<-'#{v}'")
|
108
|
+
expect(subject.pull('x')).to eql(v)
|
109
|
+
}
|
110
|
+
subject.eval("x<-as.character(NA)")
|
111
|
+
expect(subject.pull('x')).to eql(nil)
|
112
|
+
end
|
113
|
+
it "should pull an Integer" do
|
114
|
+
[0x12345678, -0x12345678].each{|v| # for check endian, and range
|
115
|
+
subject.eval("x<-#{v}L")
|
116
|
+
expect(subject.pull('x')).to eql(v)
|
117
|
+
}
|
118
|
+
end
|
119
|
+
it "should pull a Double" do
|
120
|
+
[1.5, 1.0].each{|v|
|
121
|
+
subject.eval("x<-#{v}e0")
|
122
|
+
expect(subject.pull('x')).to eql(v)
|
123
|
+
}
|
124
|
+
[1 << 32, -(1 << 32)].each{|v| # big integer will be treated as double
|
125
|
+
subject.eval("x<-#{v}")
|
126
|
+
expect(subject.pull('x')).to eql(v.to_f)
|
127
|
+
}
|
128
|
+
subject.eval("x<-NaN")
|
129
|
+
expect(subject.pull('x').nan?).to be_truthy
|
130
|
+
subject.eval("x<-as.numeric(NA)")
|
131
|
+
expect(subject.pull('x')).to eql(nil)
|
132
|
+
end
|
133
|
+
it "should pull a Logical" do
|
134
|
+
{:T => true, :F => false, :NA => nil}.each{|k, v|
|
135
|
+
subject.eval("x<-#{k}")
|
136
|
+
expect(subject.pull('x')).to eql(v)
|
137
|
+
}
|
138
|
+
end
|
139
|
+
it "should pull an Array of Character" do
|
140
|
+
{
|
141
|
+
"c('a','b','',NA)" => ['a','b','',nil],
|
142
|
+
"as.character(NULL)" => [],
|
143
|
+
}.each{|k, v|
|
144
|
+
subject.eval("x<-#{k}")
|
145
|
+
expect(subject.pull('x')).to eql(v)
|
146
|
+
}
|
147
|
+
end
|
148
|
+
it "should pull an Array of Integer" do
|
149
|
+
{
|
150
|
+
"c(1L,2L,-5L,-3L,NA)" => [1,2,-5,-3,nil],
|
151
|
+
"as.integer(NULL)" => [],
|
152
|
+
}.each{|k, v|
|
153
|
+
subject.eval("x<-#{k}")
|
154
|
+
expect(subject.pull('x')).to eql(v)
|
155
|
+
}
|
156
|
+
end
|
157
|
+
it "should pull an Array of Double" do
|
158
|
+
subject.eval("x<-c(1.1,2.2,5,3,NA,NaN)") # auto-conversion to numeric vector
|
159
|
+
expect(subject.pull('x')[0..-2]).to eql([1.1,2.2,5.0,3.0,nil])
|
160
|
+
expect(subject.pull('x')[-1].nan?).to be_truthy
|
161
|
+
|
162
|
+
subject.eval("x<-c(1L,2L,5L,3.0,NA,NaN)") # auto-conversion to numeric vector
|
163
|
+
expect(subject.pull('x')[0..-2]).to eql([1.0,2.0,5.0,3.0,nil])
|
164
|
+
expect(subject.pull('x')[-1].nan?).to be_truthy
|
165
|
+
|
166
|
+
subject.eval("x<-as.numeric(NULL)")
|
167
|
+
expect(subject.pull('x')).to eql([])
|
168
|
+
end
|
169
|
+
it "should pull an Array of Logical" do
|
170
|
+
{
|
171
|
+
"c(T, F, NA)" => [true, false, nil],
|
172
|
+
"as.logical(NULL)" => [],
|
173
|
+
}.each{|k, v|
|
174
|
+
subject.eval("x<-#{k}")
|
175
|
+
expect(subject.pull('x')).to eql(v)
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should pull a Matrix" do
|
180
|
+
threshold = 1e-8
|
181
|
+
[
|
182
|
+
proc{ # integer matrix
|
183
|
+
v = rand(100000000) # get 8 digits
|
184
|
+
[v, "#{v}L"]
|
185
|
+
},
|
186
|
+
[ # double matrix
|
187
|
+
proc{
|
188
|
+
v = rand(100000000) # get 8 digits
|
189
|
+
[Float("0.#{v}"), "0.#{v}"]
|
190
|
+
},
|
191
|
+
gen_matrix_cmp_per_elm_proc{|a, b|
|
192
|
+
expect(a).to be_within(threshold).of(b)
|
193
|
+
}
|
194
|
+
],
|
195
|
+
].each{|gen_proc, cmp_proc|
|
196
|
+
nrow, ncol = [10, 10] # 10 x 10 small matrix
|
197
|
+
subject.eval("x<-matrix(nrow=#{nrow}, ncol=#{ncol})")
|
198
|
+
rx = Matrix[*((1..nrow).collect{|i|
|
199
|
+
(1..ncol).collect{|j|
|
200
|
+
v_rb, v_R = gen_proc.call
|
201
|
+
subject.eval("x[#{i},#{j}]<-#{v_R}")
|
202
|
+
v_rb
|
203
|
+
}
|
204
|
+
})]
|
205
|
+
(cmp_proc || proc{|a, b| expect(a).to eql(b)}).call(subject.pull('x'), rx)
|
206
|
+
}
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should pull partially" do
|
210
|
+
subject.eval("x<-c(1L,2L,-5L,-3L,NA)")
|
211
|
+
[1,2,-5,-3,nil].each.with_index{|v, i|
|
212
|
+
expect(subject.pull("x[[#{i + 1}]]")).to eql(v)
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should be the same using pull than R# methods" do
|
217
|
+
subject.eval("x <- #{rand(100000000)}")
|
218
|
+
expect(subject.pull("x")).to eql(subject.x)
|
219
|
+
end
|
220
|
+
it "should raise an NoMethod error on getter with 1 or more parameters" do
|
221
|
+
expect{subject.unknown_method(1)}.to raise_error(NoMethodError)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context "on assign (PREREQUISITE: all pull tests are passed)" do
|
226
|
+
it "should assign a Character" do
|
227
|
+
x = 'Value'
|
228
|
+
subject.assign("x", x)
|
229
|
+
expect(subject.pull('x')).to eql(x)
|
230
|
+
end
|
231
|
+
it "should assign an Integer" do
|
232
|
+
[0x12345678, -0x12345678].each{|x|
|
233
|
+
subject.assign("x", x)
|
234
|
+
expect(subject.pull('x')).to eql(x)
|
235
|
+
}
|
236
|
+
end
|
237
|
+
it "should assign a Double" do
|
238
|
+
[rand, 1 << 32, -(1 << 32)].each{|x|
|
239
|
+
subject.assign("x", x)
|
240
|
+
expect(subject.pull('x')).to eql(x.to_f)
|
241
|
+
}
|
242
|
+
subject.assign("x", Float::NAN)
|
243
|
+
expect(subject.pull('x').nan?).to be_truthy
|
244
|
+
end
|
245
|
+
it "should assign a Logical" do
|
246
|
+
[true, false, nil].each{|x|
|
247
|
+
subject.assign("x", x)
|
248
|
+
expect(subject.pull('x')).to eql(x)
|
249
|
+
}
|
250
|
+
end
|
251
|
+
it "should assign an Array of Character" do
|
252
|
+
x = ['a', 'b', nil]
|
253
|
+
subject.assign("x", x)
|
254
|
+
expect(subject.pull('x')).to eql(x)
|
255
|
+
end
|
256
|
+
it "should assign an Array of Integer" do
|
257
|
+
x = [1, 2, -5, -3, nil]
|
258
|
+
subject.assign("x", x)
|
259
|
+
expect(subject.pull('x')).to eql(x)
|
260
|
+
end
|
261
|
+
it "should assign an Array of Double" do
|
262
|
+
x = [rand(100000000), rand(0x1000) << 32, # Integer
|
263
|
+
rand, Rational(rand(1000), rand(1000) + 1), # Numeric except for Complex with available .to_f
|
264
|
+
nil, Float::NAN]
|
265
|
+
subject.assign("x", x)
|
266
|
+
expect(subject.pull('x')[0..-2]).to eql(x[0..-3].collect{|v| v.to_f} + [nil])
|
267
|
+
expect(subject.pull('x')[-1].nan?).to be_truthy
|
268
|
+
end
|
269
|
+
it "should assign an Array of Logical" do
|
270
|
+
x = [true, false, nil]
|
271
|
+
subject.assign("x", x)
|
272
|
+
expect(subject.pull('x')).to eql(x)
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should assign a Matrix" do
|
276
|
+
threshold = Float::EPSILON * 100
|
277
|
+
[
|
278
|
+
proc{rand(100000000)}, # integer matrix
|
279
|
+
proc{v = rand(100000000); v > 50000000 ? nil : v}, # integer matrix with NA
|
280
|
+
[ # double matrix
|
281
|
+
proc{rand},
|
282
|
+
gen_matrix_cmp_per_elm_proc{|a, b|
|
283
|
+
expect(a).to be_within(threshold).of(b)
|
284
|
+
},
|
285
|
+
],
|
286
|
+
[ # double matrix with NA
|
287
|
+
proc{v = rand; v > 0.5 ? nil : v},
|
288
|
+
gen_matrix_cmp_per_elm_proc{|a, b|
|
289
|
+
if b.kind_of?(Numeric) then
|
290
|
+
expect(a).to be_within(threshold).of(b)
|
291
|
+
else
|
292
|
+
expect(a).to eql(nil)
|
293
|
+
end
|
294
|
+
},
|
295
|
+
],
|
296
|
+
].each{|gen_proc, cmp_proc|
|
297
|
+
x = Matrix::build(100, 200){|i, j| gen_proc.call} # 100 x 200 matrix
|
298
|
+
subject.assign("x", x)
|
299
|
+
(cmp_proc || proc{|a, b| expect(a).to eql(b)}).call(subject.pull('x'), x)
|
300
|
+
}
|
301
|
+
end
|
302
|
+
|
303
|
+
it "should assign partially" do
|
304
|
+
x = [1, 2, -5, -3, nil]
|
305
|
+
subject.assign("x", x)
|
306
|
+
expect(subject.pull('x')).to eql(x)
|
307
|
+
subject.assign("x[[3]]", x[2] *= 10)
|
308
|
+
expect(subject.pull('x')).to eql(x)
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should be the same using assign than R#= methods" do
|
312
|
+
x = rand(100000000)
|
313
|
+
subject.assign("x1", x)
|
314
|
+
subject.x2 = x
|
315
|
+
expect(subject.pull("x1")).to eql(subject.pull("x2"))
|
316
|
+
end
|
317
|
+
it "should raise an ArgumentError error on setter with 0 parameters" do
|
318
|
+
expect{subject.unknown_method=() }.to raise_error(ArgumentError)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
describe "echo changes eval output" do
|
324
|
+
def check_output(echo_args, stdout, stderr)
|
325
|
+
r.echo(*echo_args)
|
326
|
+
expect{r.eval("write('out', stdout())")}.to output(stdout ? /^out/ : "").to_stdout
|
327
|
+
expect{r.eval("write('err', stderr())")}.to output(stderr ? /^err/ : "").to_stdout
|
328
|
+
end
|
329
|
+
it "should output both stdout and stderr when echo(true, true)" do
|
330
|
+
check_output([true, true], true, true)
|
331
|
+
end
|
332
|
+
it "should output stdout only when echo(true, false)" do
|
333
|
+
check_output([true, false], true, false)
|
334
|
+
end
|
335
|
+
it "should output nothing when echo(false)" do
|
336
|
+
check_output(false, false, false)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
context "on eval in interactive mode" do
|
341
|
+
let(:params){
|
342
|
+
super().merge({:interactive => true})
|
343
|
+
}
|
344
|
+
it "should be interrupted by SIGINT" do
|
345
|
+
if r.instance_variable_get(:@platform) =~ /java$/ then
|
346
|
+
pending("JRuby does not give fully support for signal handling")
|
347
|
+
fail
|
348
|
+
end
|
349
|
+
int_invoked = false
|
350
|
+
int_handler = Signal::trap(:INT){int_invoked = true}
|
351
|
+
printed = []
|
352
|
+
eval_res = r.eval(<<-__TEXT__){|line|
|
353
|
+
for(i in 1:10){
|
354
|
+
print(i)
|
355
|
+
Sys.sleep(1)
|
356
|
+
}
|
357
|
+
__TEXT__
|
358
|
+
line =~ /^\[1\] *(\S+)/
|
359
|
+
printed << Integer($1)
|
360
|
+
Process::kill(:INT, $$) if (printed[-1] > 2)
|
361
|
+
}
|
362
|
+
Signal::trap(:INT, int_handler)
|
363
|
+
expect(int_invoked).to be_truthy
|
364
|
+
expect(eval_res).to be_falsy
|
365
|
+
expect(printed).not_to include(10)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
context "on prompt" do
|
370
|
+
let(:params){
|
371
|
+
super().merge({:interactive => true})
|
372
|
+
}
|
373
|
+
let(:input){@input ||= []}
|
374
|
+
before(:all){
|
375
|
+
begin
|
376
|
+
require 'readline'
|
377
|
+
rescue LoadError
|
378
|
+
end
|
379
|
+
}
|
380
|
+
before(:each){
|
381
|
+
allow(Readline).to receive(:readline){|prompt, add_hist|
|
382
|
+
print(prompt)
|
383
|
+
input.shift
|
384
|
+
} if defined?(Readline)
|
385
|
+
allow(r).to receive(:gets){input.shift}
|
386
|
+
r.echo(true, true)
|
387
|
+
}
|
388
|
+
it "should exit with exit() input" do
|
389
|
+
['exit()', ' exit ( ) '].each{|str|
|
390
|
+
input.replace([str])
|
391
|
+
expect{r.prompt}.to output(/^> /).to_stdout
|
392
|
+
}
|
393
|
+
end
|
394
|
+
it "should respond normally with correct inputs" do
|
395
|
+
[
|
396
|
+
[['1'], "> [1] 1"],
|
397
|
+
[['1 +', '2'], "> + [1] 3"],
|
398
|
+
[['1 +', '2 +', '3'], "> + + [1] 6"],
|
399
|
+
[['a <- 1'], "> "],
|
400
|
+
[['a <-', '1'], "> + "],
|
401
|
+
].each{|src, dst|
|
402
|
+
input.replace(src + ['exit()'])
|
403
|
+
expect{r.prompt}.to output(/^#{Regexp::escape(dst)}/).to_stdout
|
404
|
+
}
|
405
|
+
end
|
406
|
+
it "should print error gently with incorrect inputs" do
|
407
|
+
[
|
408
|
+
['1 +;'],
|
409
|
+
['a <-;'],
|
410
|
+
].each{|src|
|
411
|
+
input.replace(src + ['exit()'])
|
412
|
+
expect{r.prompt}.to output(/Unrecoverable parse error/).to_stdout
|
413
|
+
}
|
414
|
+
end
|
415
|
+
it "should print R error gently" do
|
416
|
+
[
|
417
|
+
['stop("something wrong!"); print("skip")', 'print("do other")'],
|
418
|
+
].each{|src|
|
419
|
+
input.replace(src + ['exit()'])
|
420
|
+
expect{r.prompt}.to output(/something wrong\!.*(?!skip).*do other/m).to_stdout
|
421
|
+
}
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
context "on quit" do
|
426
|
+
it "return true" do
|
427
|
+
expect(r.quit).to be_truthy
|
428
|
+
end
|
429
|
+
it "returns an error if used again" do
|
430
|
+
r.quit
|
431
|
+
expect{r.eval("x=1")}.to raise_error(RinRuby::EngineClosed)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
describe RinRuby do
|
437
|
+
let(:r){
|
438
|
+
RinRuby.new(*([:echo_enabled, :interactive, :executable, :port_number, :port_width].collect{|k| params[k]}))
|
439
|
+
}
|
440
|
+
include_examples 'RinRubyCore'
|
441
|
+
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.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
unless ENV['NO_COV']
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter '/spec/'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
9
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
|
10
|
+
require 'rspec'
|
11
|
+
require 'matrix'
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.expect_with :rspec do |c|
|
15
|
+
c.syntax = [:should, :expect]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Use color in STDOUT
|
19
|
+
config.color = true
|
20
|
+
|
21
|
+
# Use color not only in STDOUT but also in pagers and files
|
22
|
+
config.tty = true
|
23
|
+
|
24
|
+
# Use the specified formatter
|
25
|
+
config.formatter = :documentation # :progress, :html, :textmate
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
class String
|
30
|
+
def deindent
|
31
|
+
gsub /^[ \t]*/, ''
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rinruby-edge
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.1.0.edge.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Dahl
|
8
|
+
- Scott Crawford
|
9
|
+
- Claudio Bustos
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2022-07-24 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rake
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: rspec
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '3.0'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '3.0'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: simplecov
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
description: RinRuby is a Ruby library that integrates the R interpreter in Ruby,
|
58
|
+
making R's statistical routines and graphics available within Ruby. The library
|
59
|
+
consists of a single Ruby script that is simple to install and does not require
|
60
|
+
any special compilation or installation of R. Since the library is 100% pure Ruby,
|
61
|
+
it works on a variety of operating systems, Ruby implementations, and versions of
|
62
|
+
R. RinRuby's methods are simple, making for readable code. The {website [rinruby.ddahl.org]}[http://rinruby.ddahl.org]
|
63
|
+
describes RinRuby usage, provides comprehensive documentation, gives several examples,
|
64
|
+
and discusses RinRuby's implementation.
|
65
|
+
email:
|
66
|
+
- rinruby@ddahl.org
|
67
|
+
- scott@ddahl.org
|
68
|
+
- clbustos@gmail.com
|
69
|
+
executables: []
|
70
|
+
extensions: []
|
71
|
+
extra_rdoc_files: []
|
72
|
+
files:
|
73
|
+
- ".appveyor.yml"
|
74
|
+
- ".gitignore"
|
75
|
+
- ".travis.yml"
|
76
|
+
- Gemfile
|
77
|
+
- History.txt
|
78
|
+
- LICENSE.txt
|
79
|
+
- Manifest.txt
|
80
|
+
- README.md
|
81
|
+
- README_ORIG.md
|
82
|
+
- Rakefile
|
83
|
+
- lib/rinruby.rb
|
84
|
+
- lib/rinruby/version.rb
|
85
|
+
- lib/rinruby_without_r_constant.rb
|
86
|
+
- rinruby.gemspec
|
87
|
+
- spec/rinruby_spec.rb
|
88
|
+
- spec/rinruby_without_r_constant_spec.rb
|
89
|
+
- spec/spec.opts
|
90
|
+
- spec/spec_helper.rb
|
91
|
+
homepage: http://rinruby.ddahl.org
|
92
|
+
licenses:
|
93
|
+
- GPL-3.0
|
94
|
+
metadata: {}
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: 1.3.1
|
109
|
+
requirements: []
|
110
|
+
rubygems_version: 3.3.7
|
111
|
+
signing_key:
|
112
|
+
specification_version: 4
|
113
|
+
summary: RinRuby is a Ruby library that integrates the R interpreter in Ruby
|
114
|
+
test_files:
|
115
|
+
- spec/rinruby_spec.rb
|
116
|
+
- spec/rinruby_without_r_constant_spec.rb
|
117
|
+
- spec/spec.opts
|
118
|
+
- spec/spec_helper.rb
|