rri 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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