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.
Files changed (42) hide show
  1. data/.document +3 -0
  2. data/.gemtest +0 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -0
  5. data/ChangeLog.md +4 -0
  6. data/Gemfile +16 -0
  7. data/Guardfile +6 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.md +131 -0
  10. data/Rakefile +66 -0
  11. data/examples/create_pdf.rb +18 -0
  12. data/examples/ggplot.rb +42 -0
  13. data/examples/simple.rb +19 -0
  14. data/gemspec.yml +11 -0
  15. data/lib/rri.rb +11 -0
  16. data/lib/rri/callback_objects.rb +1 -0
  17. data/lib/rri/callback_objects/r_engine_std_output.rb +6 -0
  18. data/lib/rri/engine.rb +390 -0
  19. data/lib/rri/java_gd.rb +23 -0
  20. data/lib/rri/jri.rb +24 -0
  21. data/lib/rri/r_converters.rb +4 -0
  22. data/lib/rri/r_converters/array_converter.rb +59 -0
  23. data/lib/rri/r_converters/float_converter.rb +28 -0
  24. data/lib/rri/r_converters/integer_converter.rb +28 -0
  25. data/lib/rri/r_converters/string_converter.rb +28 -0
  26. data/lib/rri/rexp.rb +8 -0
  27. data/lib/rri/rri_exception.rb +5 -0
  28. data/lib/rri/ruby_converters.rb +2 -0
  29. data/lib/rri/ruby_converters/double_converter.rb +28 -0
  30. data/lib/rri/ruby_converters/integer_converter.rb +28 -0
  31. data/lib/rri/version.rb +5 -0
  32. data/rri.gemspec +15 -0
  33. data/spec/rri/engine_spec.rb +203 -0
  34. data/spec/rri/r_converters/array_converter_spec.rb +56 -0
  35. data/spec/rri/r_converters/float_converter_spec.rb +19 -0
  36. data/spec/rri/r_converters/integer_converter_spec.rb +19 -0
  37. data/spec/rri/r_converters/string_converter_spec.rb +19 -0
  38. data/spec/rri/ruby_converters/double_converter.rb +20 -0
  39. data/spec/rri/ruby_converters/integer_converter_spec.rb +20 -0
  40. data/spec/rri_spec.rb +8 -0
  41. data/spec/spec_helper.rb +4 -0
  42. metadata +126 -0
@@ -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
@@ -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,4 @@
1
+ require 'rri/r_converters/float_converter'
2
+ require 'rri/r_converters/integer_converter'
3
+ require 'rri/r_converters/string_converter'
4
+ require 'rri/r_converters/array_converter'
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'rri/jri'
2
+
3
+ module Rri
4
+ java_import "org.rosuda.REngine.REXP"
5
+ java_import "org.rosuda.REngine.REXPInteger"
6
+ java_import "org.rosuda.REngine.REXPDouble"
7
+ java_import "org.rosuda.REngine.REXPString"
8
+ end
@@ -0,0 +1,5 @@
1
+ module Rri
2
+ # Exception class for all exceptions coming from rri itself
3
+ class RriException < StandardError
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require 'rri/ruby_converters/integer_converter'
2
+ require 'rri/ruby_converters/double_converter'
@@ -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
@@ -0,0 +1,5 @@
1
+ # Module to contain all rri related classes
2
+ module Rri
3
+ # rri version
4
+ VERSION = "0.1.0"
5
+ end
@@ -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