rinruby-edge 2.1.0.edge.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|