rinruby 2.0.3 → 2.1.0
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/.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
|
+
|