rri 0.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.
- 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
|