rri 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +3 -0
- data/.gemtest +0 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +4 -0
- data/Gemfile +16 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +20 -0
- data/README.md +131 -0
- data/Rakefile +66 -0
- data/examples/create_pdf.rb +18 -0
- data/examples/ggplot.rb +42 -0
- data/examples/simple.rb +19 -0
- data/gemspec.yml +11 -0
- data/lib/rri.rb +11 -0
- data/lib/rri/callback_objects.rb +1 -0
- data/lib/rri/callback_objects/r_engine_std_output.rb +6 -0
- data/lib/rri/engine.rb +390 -0
- data/lib/rri/java_gd.rb +23 -0
- data/lib/rri/jri.rb +24 -0
- data/lib/rri/r_converters.rb +4 -0
- data/lib/rri/r_converters/array_converter.rb +59 -0
- data/lib/rri/r_converters/float_converter.rb +28 -0
- data/lib/rri/r_converters/integer_converter.rb +28 -0
- data/lib/rri/r_converters/string_converter.rb +28 -0
- data/lib/rri/rexp.rb +8 -0
- data/lib/rri/rri_exception.rb +5 -0
- data/lib/rri/ruby_converters.rb +2 -0
- data/lib/rri/ruby_converters/double_converter.rb +28 -0
- data/lib/rri/ruby_converters/integer_converter.rb +28 -0
- data/lib/rri/version.rb +5 -0
- data/rri.gemspec +15 -0
- data/spec/rri/engine_spec.rb +203 -0
- data/spec/rri/r_converters/array_converter_spec.rb +56 -0
- data/spec/rri/r_converters/float_converter_spec.rb +19 -0
- data/spec/rri/r_converters/integer_converter_spec.rb +19 -0
- data/spec/rri/r_converters/string_converter_spec.rb +19 -0
- data/spec/rri/ruby_converters/double_converter.rb +20 -0
- data/spec/rri/ruby_converters/integer_converter_spec.rb +20 -0
- data/spec/rri_spec.rb +8 -0
- data/spec/spec_helper.rb +4 -0
- metadata +126 -0
data/lib/rri/java_gd.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
module Rri
|
4
|
+
module JavaGd
|
5
|
+
|
6
|
+
def self.jars
|
7
|
+
["javaGD" ]
|
8
|
+
end
|
9
|
+
|
10
|
+
JavaGd.jars.each do |jar|
|
11
|
+
raise "You must set RRI_JAVAGD_JAR_PATH!" if ENV['RRI_JAVAGD_JAR_PATH'].nil?
|
12
|
+
begin
|
13
|
+
require File.join(ENV['RRI_JAVAGD_JAR_PATH'], jar)
|
14
|
+
rescue LoadError => e
|
15
|
+
STDERR.puts e.message
|
16
|
+
STDERR.puts "Make sure you have set RRI_JAVAGD_JAR_PATH to the result of the R command: system.file(\"java\", package=\"JavaGD\")"
|
17
|
+
exit -1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
include_package "org.rosuda.javaGD"
|
22
|
+
end
|
23
|
+
end
|
data/lib/rri/jri.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
module Rri
|
4
|
+
module Jri
|
5
|
+
|
6
|
+
def self.jars
|
7
|
+
["JRI", "JRIEngine", "REngine" ]
|
8
|
+
end
|
9
|
+
|
10
|
+
Jri.jars.each do |jar|
|
11
|
+
raise "You must set RRI_JRI_JAR_PATH!" if ENV['RRI_JRI_JAR_PATH'].nil?
|
12
|
+
begin
|
13
|
+
require File.join(ENV['RRI_JRI_JAR_PATH'], jar)
|
14
|
+
rescue LoadError => e
|
15
|
+
STDERR.puts e.message
|
16
|
+
STDERR.puts "Make sure you have set RRI_JRI_JAR_PATH to the result of the R command: system.file(\"jri\",package=\"rJava\")"
|
17
|
+
STDERR.puts "Also, make sure your OS can load dynamic libraries from that directory. E.g. on windows that means it needs to be part of the PATH"
|
18
|
+
exit -1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
include_package "org.rosuda.REngine.JRI"
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rri/rexp'
|
2
|
+
|
3
|
+
module Rri
|
4
|
+
module RConverters
|
5
|
+
|
6
|
+
# Converter for ruby Arrays
|
7
|
+
class ArrayConverter
|
8
|
+
|
9
|
+
# Convert ruby object to R format
|
10
|
+
#
|
11
|
+
# If the ruby object is an Array, converts it into an R object.
|
12
|
+
#
|
13
|
+
# Does not deal with empty arrays.
|
14
|
+
#
|
15
|
+
# Depending on what type of content the ruby array has, different things happen:
|
16
|
+
# * if all elements are doubles, convert to vector of doubles
|
17
|
+
# * if all elements are integers, convert to vector of integers
|
18
|
+
# * if all elements are strings, convert to vector of strings
|
19
|
+
#
|
20
|
+
# @param obj object to convert
|
21
|
+
# @return [Array] an array of size 2 where first element is a boolean indicating succes,
|
22
|
+
# and the second element is the converted object if conversion successful
|
23
|
+
def convert(obj)
|
24
|
+
if obj.kind_of? Array and obj.size > 0
|
25
|
+
if is_all_same_type?(obj)
|
26
|
+
o = obj[0]
|
27
|
+
return [true, create_integer_vector(obj)] if o.kind_of? Integer
|
28
|
+
return [true, create_double_vector(obj)] if o.kind_of? Float
|
29
|
+
return [true, create_string_vector(obj)] if o.kind_of? String
|
30
|
+
[false, nil]
|
31
|
+
end
|
32
|
+
else
|
33
|
+
[false, nil]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
########################## PRIVATE METHODS #########################
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def is_all_same_type?(array)
|
42
|
+
array.map {|x| x.class}.uniq.size == 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_integer_vector(array)
|
46
|
+
REXPInteger.new(array.to_java :int)
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_double_vector(array)
|
50
|
+
REXPDouble.new(array.to_java :double)
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_string_vector(array)
|
54
|
+
REXPString.new(array.to_java :string)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rri/rexp'
|
2
|
+
|
3
|
+
module Rri
|
4
|
+
module RConverters
|
5
|
+
|
6
|
+
# Converter for ruby Floats
|
7
|
+
#
|
8
|
+
# Convert ruby Floats to double in R
|
9
|
+
class FloatConverter
|
10
|
+
|
11
|
+
# Convert ruby object to R format
|
12
|
+
#
|
13
|
+
# If the ruby object is a Float, converts it into an R double
|
14
|
+
#
|
15
|
+
# @param obj object to convert
|
16
|
+
# @return [Array] an array of size 2 where first element is a boolean indicating succes,
|
17
|
+
# and the second element is the converted object if conversion successful
|
18
|
+
def convert(obj)
|
19
|
+
if obj.kind_of? Float
|
20
|
+
[true, REXPDouble.new(obj)]
|
21
|
+
else
|
22
|
+
[false, nil]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rri/rexp'
|
2
|
+
|
3
|
+
module Rri
|
4
|
+
module RConverters
|
5
|
+
|
6
|
+
# Converter for ruby Integers
|
7
|
+
#
|
8
|
+
# Convert ruby Integer to integer in R
|
9
|
+
class IntegerConverter
|
10
|
+
|
11
|
+
# Convert ruby object to R format
|
12
|
+
#
|
13
|
+
# If the ruby object is an Integer, converts it into an R integer
|
14
|
+
#
|
15
|
+
# @param obj object to convert
|
16
|
+
# @return [Array] an array of size 2 where first element is a boolean indicating succes,
|
17
|
+
# and the second element is the converted object if conversion successful
|
18
|
+
def convert(obj)
|
19
|
+
if obj.kind_of? Integer
|
20
|
+
[true, REXPInteger.new(obj)]
|
21
|
+
else
|
22
|
+
[false, nil]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rri/rexp'
|
2
|
+
|
3
|
+
module Rri
|
4
|
+
module RConverters
|
5
|
+
|
6
|
+
# Converter for ruby Strings
|
7
|
+
#
|
8
|
+
# Convert ruby Strings to character vector in R
|
9
|
+
class StringConverter
|
10
|
+
|
11
|
+
# Convert ruby object to R format
|
12
|
+
#
|
13
|
+
# If the ruby object is a String, converts it into an R character vector
|
14
|
+
#
|
15
|
+
# @param obj object to convert
|
16
|
+
# @return [Array] an array of size 2 where first element is a boolean indicating succes,
|
17
|
+
# and the second element is the converted object if conversion successful
|
18
|
+
def convert(obj)
|
19
|
+
if obj.kind_of? String
|
20
|
+
[true, REXPString.new(obj)]
|
21
|
+
else
|
22
|
+
[false, nil]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/rri/rexp.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rri/rexp'
|
2
|
+
|
3
|
+
module Rri
|
4
|
+
module RubyConverters
|
5
|
+
|
6
|
+
# Converter for R Integers
|
7
|
+
#
|
8
|
+
# Convert R double to ruby Fixnum
|
9
|
+
class DoubleConverter
|
10
|
+
|
11
|
+
# Convert R object to ruby format
|
12
|
+
#
|
13
|
+
# If the R object is a double, converts it into a ruby Float
|
14
|
+
#
|
15
|
+
# @param [REXP] rexp rexp to convert
|
16
|
+
# @return [Array] an array of size 2 where first element is a boolean indicating succes,
|
17
|
+
# and the second element is the converted object if conversion successful
|
18
|
+
def convert(rexp)
|
19
|
+
if rexp.kind_of?(REXP) and rexp.isNumeric and !rexp.isInteger and !rexp.isComplex and rexp.length == 1
|
20
|
+
[true, rexp.asDouble]
|
21
|
+
else
|
22
|
+
[false, nil]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rri/rexp'
|
2
|
+
|
3
|
+
module Rri
|
4
|
+
module RubyConverters
|
5
|
+
|
6
|
+
# Converter for R Integers
|
7
|
+
#
|
8
|
+
# Convert R integer to ruby Fixnum
|
9
|
+
class IntegerConverter
|
10
|
+
|
11
|
+
# Convert R object to ruby format
|
12
|
+
#
|
13
|
+
# If the R object is an integer, converts it into a ruby Fixnum
|
14
|
+
#
|
15
|
+
# @param [REXP] rexp rexp to convert
|
16
|
+
# @return [Array] an array of size 2 where first element is a boolean indicating succes,
|
17
|
+
# and the second element is the converted object if conversion successful
|
18
|
+
def convert(rexp)
|
19
|
+
if rexp.kind_of?(REXP) and rexp.isInteger and rexp.length == 1
|
20
|
+
[true, rexp.asInteger]
|
21
|
+
else
|
22
|
+
[false, nil]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/rri/version.rb
ADDED
data/rri.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
begin
|
4
|
+
Ore::Specification.new do |gemspec|
|
5
|
+
# custom logic here
|
6
|
+
end
|
7
|
+
rescue NameError
|
8
|
+
begin
|
9
|
+
require 'ore/specification'
|
10
|
+
retry
|
11
|
+
rescue LoadError
|
12
|
+
STDERR.puts "The '#{__FILE__}' file requires Ore."
|
13
|
+
STDERR.puts "Run `gem install ore-core` to install Ore."
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rri'
|
3
|
+
|
4
|
+
describe Rri::Engine do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
# Note: we can't create a new engine for each test because of bug
|
8
|
+
# in JRI/R. After 12 recreations in same process something in the
|
9
|
+
# C layer gets stuck, not sure if the JRI JNI code or the R lib.
|
10
|
+
# That's true even if we properly close and dispose of the engines.
|
11
|
+
# That wont be much of a limit in practice, just not ideal for testing.
|
12
|
+
@engine = Rri::Engine.new
|
13
|
+
end
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
# since we're not recreating engine each time we need to clear caches
|
17
|
+
# between runs
|
18
|
+
Rri::Engine.clear_r_converters
|
19
|
+
Rri::Engine.clear_ruby_converters
|
20
|
+
@engine.clear_r_converters
|
21
|
+
@engine.clear_ruby_converters
|
22
|
+
end
|
23
|
+
|
24
|
+
after(:all) do
|
25
|
+
@engine.close
|
26
|
+
@engine = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "instantiation" do
|
30
|
+
it "should instantiate passing no args to .new" do
|
31
|
+
@engine.should_not be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should instantiate passing options to .new" do
|
35
|
+
Rri::Engine.new(:r_arguments => ["--save"], :run_repl => false).should_not be_nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "when converting ruby objects to R objects" do
|
40
|
+
describe "at the instance level" do
|
41
|
+
it "should be able to add an instance level R converter" do
|
42
|
+
converter = mock('converter')
|
43
|
+
@engine.add_r_converter(converter)
|
44
|
+
@engine.r_converters.size.should == 1
|
45
|
+
@engine.r_converters[0] == converter
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should be able to add several instance level R converters and return them in reverse order" do
|
49
|
+
converter1 = mock('converter')
|
50
|
+
converter2 = mock('converter')
|
51
|
+
@engine.add_r_converter(converter1)
|
52
|
+
@engine.add_r_converter(converter2)
|
53
|
+
@engine.r_converters.size.should == 2
|
54
|
+
@engine.r_converters[0] == converter2
|
55
|
+
@engine.r_converters[1] == converter1
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should correctly convert to R objects using instance level custom converters" do
|
59
|
+
ruby_value = 123
|
60
|
+
converter = double('converter')
|
61
|
+
converter.should_receive(:convert).and_return([true, ruby_value.to_s])
|
62
|
+
@engine.add_r_converter(converter)
|
63
|
+
success, rexp = @engine.convert_to_r_object(ruby_value)
|
64
|
+
success.should be_true
|
65
|
+
rexp.should == ruby_value.to_s
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "at the class level" do
|
70
|
+
it "should be able to add a class level R converter" do
|
71
|
+
converter = mock('converter')
|
72
|
+
Rri::Engine.add_r_converter(converter)
|
73
|
+
Rri::Engine.r_converters.size.should == 1
|
74
|
+
Rri::Engine.r_converters[0] == converter
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should be able to add several class level R converters and return them in reverse order" do
|
78
|
+
converter1 = mock('converter')
|
79
|
+
converter2 = mock('converter')
|
80
|
+
Rri::Engine.add_r_converter(converter1)
|
81
|
+
Rri::Engine.add_r_converter(converter2)
|
82
|
+
Rri::Engine.r_converters.size.should == 2
|
83
|
+
Rri::Engine.r_converters[0] == converter2
|
84
|
+
Rri::Engine.r_converters[1] == converter1
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should correctly convert to R objects using class level custom converters" do
|
88
|
+
ruby_value = 123
|
89
|
+
converter = double('converter')
|
90
|
+
converter.should_receive(:convert).and_return([true, ruby_value.to_s])
|
91
|
+
Rri::Engine.add_r_converter(converter)
|
92
|
+
success, rexp = @engine.convert_to_r_object(ruby_value)
|
93
|
+
success.should be_true
|
94
|
+
rexp.should == ruby_value.to_s
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should correctly convert to R objects using default converters" do
|
99
|
+
ruby_value = 123
|
100
|
+
success, rexp = @engine.convert_to_r_object(ruby_value)
|
101
|
+
success.should be_true
|
102
|
+
rexp.class.should == REXPInteger
|
103
|
+
rexp.asInteger.should == ruby_value
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should return failure if no conversion available when converting ruby object to R object" do
|
107
|
+
ruby_value = @engine
|
108
|
+
success, rexp = @engine.convert_to_r_object(ruby_value)
|
109
|
+
success.should be_false
|
110
|
+
rexp.should == @engine
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "when converting R objects to ruby objects" do
|
115
|
+
describe "at the instance level" do
|
116
|
+
it "should be able to add an instance level ruby converter" do
|
117
|
+
converter = mock('converter')
|
118
|
+
@engine.add_ruby_converter(converter)
|
119
|
+
@engine.ruby_converters.size.should == 1
|
120
|
+
@engine.ruby_converters[0] == converter
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should be able to add several instance level converters and return them in reverse order" do
|
124
|
+
converter1 = mock('converter')
|
125
|
+
converter2 = mock('converter')
|
126
|
+
@engine.add_ruby_converter(converter1)
|
127
|
+
@engine.add_ruby_converter(converter2)
|
128
|
+
@engine.ruby_converters.size.should == 2
|
129
|
+
@engine.ruby_converters[0] == converter2
|
130
|
+
@engine.ruby_converters[1] == converter1
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should correctly convert to R objects using instance level custom converters" do
|
134
|
+
value = 123
|
135
|
+
rexp = REXPInteger.new(value)
|
136
|
+
converter = double('converter')
|
137
|
+
converter.should_receive(:convert).and_return([true, value])
|
138
|
+
@engine.add_ruby_converter(converter)
|
139
|
+
success, obj = @engine.convert_to_ruby_object(rexp)
|
140
|
+
success.should be_true
|
141
|
+
obj.should == value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "at the class level" do
|
146
|
+
it "should be able to add a class level ruby converter" do
|
147
|
+
converter = mock('converter')
|
148
|
+
Rri::Engine.add_ruby_converter(converter)
|
149
|
+
Rri::Engine.ruby_converters.size.should == 1
|
150
|
+
Rri::Engine.ruby_converters[0] == converter
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should be able to add several instance level ruby converters and return them in reverse order" do
|
154
|
+
converter1 = mock('converter')
|
155
|
+
converter2 = mock('converter')
|
156
|
+
Rri::Engine.add_ruby_converter(converter1)
|
157
|
+
Rri::Engine.add_ruby_converter(converter2)
|
158
|
+
Rri::Engine.ruby_converters.size.should == 2
|
159
|
+
Rri::Engine.ruby_converters[0] == converter2
|
160
|
+
Rri::Engine.ruby_converters[1] == converter1
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should correctly convert to ruby objects using class level custom converters" do
|
164
|
+
value = 123
|
165
|
+
rexp = REXPInteger.new(value)
|
166
|
+
converter = double('converter')
|
167
|
+
converter.should_receive(:convert).and_return([true, value])
|
168
|
+
Rri::Engine.add_ruby_converter(converter)
|
169
|
+
success, obj = @engine.convert_to_ruby_object(rexp)
|
170
|
+
success.should be_true
|
171
|
+
obj.should == value
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should correctly convert to ruby objects using default converters" do
|
176
|
+
value = 123
|
177
|
+
rexp = REXPInteger.new(value)
|
178
|
+
success, obj = @engine.convert_to_ruby_object(rexp)
|
179
|
+
success.should be_true
|
180
|
+
obj.should == value
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should return failure if no conversion available when converting ruby object to R object" do
|
184
|
+
rexp = @engine
|
185
|
+
success, obj = @engine.convert_to_ruby_object(rexp)
|
186
|
+
success.should be_false
|
187
|
+
obj.should == @engine
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should correctly convert a ruby object and assign it to an R variable" do
|
192
|
+
ruby_value = 1.23
|
193
|
+
@engine.convert_and_assign(ruby_value, :x)
|
194
|
+
rexp = @engine.get("x", nil, true)
|
195
|
+
rexp.class.should == REXPDouble
|
196
|
+
rexp.asDouble.should == ruby_value
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should raise if it can't correctly convert a ruby object and assign it to an R variable" do
|
200
|
+
expect { @engine.convert_and_assign(@engine, :x) }.to raise_error(Rri::RriException)
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|