Class: RinRuby
- Inherits:
-
Object
- Object
- RinRuby
- Defined in:
- lib/rinruby.rb,
lib/rinruby/version.rb
Constant Summary collapse
- EngineClosed =
Exception for closed engine
Class.new(Exception)
- ParseError =
Parse error
Class.new(Exception)
- UnsupportedTypeError =
Cannot convert data type to one that can be sent over wire
Class.new(Exception)
- DEFAULT_OPTIONS =
{ :echo => true, :executable => nil, :port_number => 38442, :port_width => 1000, :hostname => '127.0.0.1' }.freeze
- VERSION =
"3.3.0"
Instance Attribute Summary collapse
-
#echo_enabled ⇒ Object
Returns the value of attribute echo_enabled.
-
#engine_process_status ⇒ Object
readonly
Returns the value of attribute engine_process_status.
-
#executable ⇒ Object
readonly
Returns the value of attribute executable.
-
#hostname ⇒ Object
readonly
Returns the value of attribute hostname.
-
#port_number ⇒ Object
readonly
Returns the value of attribute port_number.
-
#port_width ⇒ Object
readonly
Returns the value of attribute port_width.
Instance Method Summary collapse
-
#assign(name, value) ⇒ Object
Data is copied from Ruby to R using the assign method or a short-hand equivalent.
-
#capture(&_block) ⇒ Object
Captures the stdout from R for the duration of the block Usage: output = r.capture do r.eval “1 + 1” end.
- #complete?(string) ⇒ Boolean
-
#echo(enable = nil, stderr = nil) ⇒ Object
The echo method controls whether the eval method displays output from R and, if echo is enabled, whether messages, warnings, and errors from stderr are also displayed.
-
#engine_closed? ⇒ Boolean
Returns true if engine is completely closed, false otherwise.
-
#engine_pid ⇒ Object
Returns the pid of the engine process.
-
#eval(string, echo_override = nil) ⇒ Object
The eval instance method passes the R commands contained in the supplied string and displays any resulting plots or prints the output.
-
#initialize(*args) ⇒ RinRuby
constructor
RinRuby is invoked within a Ruby script (or the interactive “irb” prompt denoted >>) using:.
-
#pull(string, singletons = false) ⇒ Object
Data is copied from R to Ruby using the pull method or a short-hand equivalent.
-
#quit ⇒ Object
The quit method will properly close the bridge between Ruby and R, freeing up system resources.
Constructor Details
#initialize(*args) ⇒ RinRuby
RinRuby is invoked within a Ruby script (or the interactive “irb” prompt denoted >>) using:
>> require "rinruby"
The previous statement reads the definition of the RinRuby class into the current Ruby interpreter and creates an instance of the RinRuby class named
-
There is a second method for starting an instance of R which allows the
user to use any name for the instance, in this case myr:
>> require "rinruby"
>> myr = RinRuby.new
>> myr.eval "rnorm(1)"
Any number of independent instances of R can be created in this way.
Parameters that can be passed to the new method using a Hash:
-
:echo: By setting the echo to false, output from R is suppressed, although warnings are still printed. This option can be changed later by using the echo method. The default is true.
-
:executable: The path of the R executable (which is “R” in Linux and Mac OS X, or “Rterm.exe” in Windows) can be set with the executable argument. The default is nil which makes RinRuby use the registry keys to find the path (on Windows) or use the path defined by $PATH (on Linux and Mac OS X).
-
:port_number: This is the smallest port number on the local host that could be used to pass data between Ruby and R. The actual port number used depends on port_width.
-
:port_width: RinRuby will randomly select a uniform number between port_number and port_number + port_width - 1 (inclusive) to pass data between Ruby and R. If the randomly selected port is not available, RinRuby will continue selecting random ports until it finds one that is available. By setting port_width to 1, RinRuby will wait until port_number is available. The default port_width is 1000.
It may be desirable to change the parameters to the instance of R, but still call it by the name of R. In that case the old instance of R which was created with the ‘require “rinruby”’ statement should be closed first using the quit method which is explained below. Unless the previous instance is killed, it will continue to use system resources until exiting Ruby. The following shows an example by changing the parameter echo:
>> require "rinruby"
>> R.quit
>> R = RinRuby.new(false)
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/rinruby.rb', line 148 def initialize(*args) opts = Hash.new if args.size == 1 and args[0].is_a? Hash opts = args[0] else opts[:echo] = args.shift unless args.size==0 opts[:executable] = args.shift unless args.size==0 opts[:port_number] = args.shift unless args.size==0 opts[:port_width] = args.shift unless args.size==0 end @opts = DEFAULT_OPTIONS.merge(opts) @port_width = @opts[:port_width] @executable = @opts[:executable] @hostname = @opts[:hostname] @echo_enabled = @opts[:echo] @echo_stderr = false @echo_writer = @opts.fetch(:echo_writer, $stdout) # find available port while true begin @port_number = @opts[:port_number] + rand(port_width) @server_socket = TCPServer::new(@hostname, @port_number) break rescue Errno::EADDRINUSE sleep 0.5 if port_width == 1 end end @executable ||= "R" cmd = "#{executable} --slave" # spawn R process @engine = IO.popen(cmd,"w+") @reader = @engine @writer = @engine raise "Engine closed" if @engine.closed? # connect to the server @writer.puts <<-EOF #{RinRuby_KeepTrying_Variable} <- TRUE while ( #{RinRuby_KeepTrying_Variable} ) { #{RinRuby_Socket} <- try(suppressWarnings(socketConnection("#{@hostname}", #{@port_number}, blocking=TRUE, open="rb")),TRUE) if ( inherits(#{RinRuby_Socket},"try-error") ) { Sys.sleep(0.1) } else { #{RinRuby_KeepTrying_Variable} <- FALSE } } rm(#{RinRuby_KeepTrying_Variable}) EOF r_rinruby_get_value r_rinruby_pull r_rinruby_parseable @socket = @server_socket.accept end |
Instance Attribute Details
#echo_enabled ⇒ Object
Returns the value of attribute echo_enabled.
76 77 78 |
# File 'lib/rinruby.rb', line 76 def echo_enabled @echo_enabled end |
#engine_process_status ⇒ Object (readonly)
Returns the value of attribute engine_process_status.
77 78 79 |
# File 'lib/rinruby.rb', line 77 def engine_process_status @engine_process_status end |
#executable ⇒ Object (readonly)
Returns the value of attribute executable.
78 79 80 |
# File 'lib/rinruby.rb', line 78 def executable @executable end |
#hostname ⇒ Object (readonly)
Returns the value of attribute hostname.
81 82 83 |
# File 'lib/rinruby.rb', line 81 def hostname @hostname end |
#port_number ⇒ Object (readonly)
Returns the value of attribute port_number.
79 80 81 |
# File 'lib/rinruby.rb', line 79 def port_number @port_number end |
#port_width ⇒ Object (readonly)
Returns the value of attribute port_width.
80 81 82 |
# File 'lib/rinruby.rb', line 80 def port_width @port_width end |
Instance Method Details
#assign(name, value) ⇒ Object
Data is copied from Ruby to R using the assign method or a short-hand equivalent. For example:
>> names = ["Lisa","Teasha","Aaron","Thomas"]
>> R.assign "people", names
>> R.eval "sort(people)"
produces the following:
[1] "Aaron" "Lisa" "Teasha" "Thomas"
The short-hand equivalent to the assign method is simply:
>> R.people = names
Some care is needed when using the short-hand of the assign method since the label (i.e., people in this case) must be a valid method name in Ruby. For example, R.copy.of.names = names will not work, but R.copy_of_names = names is permissible.
The assign method supports Ruby variables of type Fixnum (i.e., integer), Bignum (i.e., integer), Float (i.e., double), String, and arrays of one of those three fundamental types. Note that Fixnum or Bignum values that exceed the capacity of R’s integers are silently converted to doubles. Data in other formats must be coerced when copying to R.
Parameters that can be passed to the assign method:
-
name: The name of the variable desired in R.
-
value: The value the R variable should have. The assign method supports Ruby variables of type Fixnum (i.e., integer), Bignum (i.e., integer), Float (i.e., double), String, and arrays of one of those three fundamental types. Note that Fixnum or Bignum values that exceed the capacity of R’s integers are silently converted to doubles. Data in other formats must be coerced when copying to R.
When assigning an array containing differing types of variables, RinRuby will follow R’s conversion conventions. An array that contains any Strings will result in a character vector in R. If the array does not contain any Strings, but it does contain a Float or a large integer (in absolute value), then the result will be a numeric vector of Doubles in R. If there are only integers that are suffciently small (in absolute value), then the result will be a numeric vector of integers in R.
360 361 362 363 364 365 366 367 |
# File 'lib/rinruby.rb', line 360 def assign(name, value) raise EngineClosed if @engine.closed? if assignable?(name) assign_engine(name,value) else raise ParseError, "Parse error" end end |
#capture(&_block) ⇒ Object
Captures the stdout from R for the duration of the block Usage:
output = r.capture do
r.eval "1 + 1"
end
477 478 479 480 481 482 483 484 485 486 487 488 489 |
# File 'lib/rinruby.rb', line 477 def capture(&_block) old_echo_enabled, old_echo_writer = @echo_enabled, @echo_writer @echo_enabled = true @echo_writer = StringIO.new yield @echo_writer.rewind @echo_writer.read ensure @echo_enabled = old_echo_enabled @echo_writer = old_echo_writer end |
#complete?(string) ⇒ Boolean
730 731 732 733 734 735 736 737 738 |
# File 'lib/rinruby.rb', line 730 def complete?(string) assign_engine(RinRuby_Parse_String, string) @writer.puts "rinruby_parseable(#{RinRuby_Parse_String})" buffer="" @socket.read(4,buffer) @writer.puts "rm(#{RinRuby_Parse_String})" result = to_signed_int(buffer.unpack('N')[0].to_i) return result==-1 ? false : true end |
#echo(enable = nil, stderr = nil) ⇒ Object
The echo method controls whether the eval method displays output from R and, if echo is enabled, whether messages, warnings, and errors from stderr are also displayed.
Parameters that can be passed to the eval method
-
enable: Setting enable to false will turn all output off until the echo command is used again with enable equal to true. The default is nil, which will return the current setting.
-
stderr: Setting stderr to true will force messages, warnings, and errors from R to be routed through stdout. Using stderr redirection is typically not needed for the C implementation of Ruby and is thus not not enabled by default for this implementation. It is typically necessary for jRuby and is enabled by default in this case. This redirection works well in practice but it can lead to interleaving output which may confuse RinRuby. In such cases, stderr redirection should not be used. Echoing must be enabled when using stderr redirection.
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/rinruby.rb', line 453 def echo(enable=nil,stderr=nil) if ( enable == false ) && ( stderr == true ) raise "You can only redirect stderr if you are echoing is enabled." end if ( enable != nil ) && ( enable != @echo_enabled ) echo(nil,false) if ! enable @echo_enabled = ! @echo_enabled end if @echo_enabled && ( stderr != nil ) && ( stderr != @echo_stderr ) @echo_stderr = ! @echo_stderr if @echo_stderr eval "sink(stdout(),type='message')" else eval "sink(type='message')" end end [ @echo_enabled, @echo_stderr ] end |
#engine_closed? ⇒ Boolean
Returns true if engine is completely closed, false otherwise.
498 499 500 |
# File 'lib/rinruby.rb', line 498 def engine_closed? @engine.closed? end |
#engine_pid ⇒ Object
Returns the pid of the engine process.
492 493 494 495 |
# File 'lib/rinruby.rb', line 492 def engine_pid raise EngineClosed if @engine.closed? @engine.pid end |
#eval(string, echo_override = nil) ⇒ Object
The eval instance method passes the R commands contained in the supplied string and displays any resulting plots or prints the output. For example:
>> sample_size = 10
>> R.eval "x <- rnorm(#{sample_size})"
>> R.eval "summary(x)"
>> R.eval "sd(x)"
produces the following:
Min. 1st Qu. Median Mean 3rd Qu. Max.
-1.88900 -0.84930 -0.45220 -0.49290 -0.06069 0.78160
[1] 0.7327981
This example used a string substitution to make the argument to first eval method equivalent to x <- rnorm(10). This example used three invocations of the eval method, but a single invoke is possible using a here document:
>> R.eval <<EOF
x <- rnorm(#{sample_size})
summary(x)
sd(x)
EOF
Parameters that can be passed to the eval method
-
string: The string parameter is the code which is to be passed to R, for example, string = “hist(gamma(1000,5,3))”. The string can also span several lines of code by use of a here document, as shown:
R.eval <<EOF x<-rgamma(1000,5,3) hist(x) EOF
-
echo_override: This argument allows one to set the echo behavior for this call only. The default for echo_override is nil, which does not override the current echo behavior.
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
# File 'lib/rinruby.rb', line 264 def eval(string, echo_override=nil) raise EngineClosed if @engine.closed? echo_enabled = ( echo_override != nil ) ? echo_override : @echo_enabled if complete?(string) @writer.puts string @writer.puts "warning('#{RinRuby_Stderr_Flag}',immediate.=TRUE)" if @echo_stderr @writer.puts "print('#{RinRuby_Eval_Flag}')" else raise ParseError, "Parse error on eval:#{string}" end Signal.trap('INT') do @writer.print '' @reader.gets Signal.trap('INT') do end return true end found_eval_flag = false found_stderr_flag = false while true echo_eligible = true begin line = @reader.gets rescue return false end if ! line return false end while line.chomp! end line = line[8..-1] if line[0] == 27 # Delete escape sequence if line == "[1] \"#{RinRuby_Eval_Flag}\"" found_eval_flag = true echo_eligible = false end if line == "Warning: #{RinRuby_Stderr_Flag}" found_stderr_flag = true echo_eligible = false end break if found_eval_flag && ( found_stderr_flag == @echo_stderr ) return false if line == RinRuby_Exit_Flag if echo_enabled && echo_eligible @echo_writer.puts(line) @echo_writer.flush end end Signal.trap('INT') do end true end |
#pull(string, singletons = false) ⇒ Object
Data is copied from R to Ruby using the pull method or a short-hand equivalent. The R object x defined with an eval method can be copied to Ruby object copy_of_x as follows:
>> R.eval "x <- rnorm(10)"
>> copy_of_x = R.pull "x"
>> puts copy_of_x
which produces the following :
-0.376404489256671
-1.0759798269397
-0.494240140140996
0.131171385795721
-0.878328334369391
-0.762290423047929
-0.410227216105828
0.0445512804225151
-1.88887454545995
0.781602719849499
The explicit assign method, however, can take an arbitrary R statement. For example:
>> summary_of_x = R.pull "as.numeric(summary(x))"
>> puts summary_of_x
produces the following:
-1.889
-0.8493
-0.4522
-0.4929
-0.06069
0.7816
Notice the use above of the as.numeric function in R. This is necessary since the pull method only supports R vectors which are numeric (i.e., integers or doubles) and character (i.e., strings). Data in other formats must be coerced when copying to Ruby.
Parameters that can be passed to the pull method:
-
string: The name of the variable that should be pulled from R. The pull method only supports R vectors which are numeric (i.e., integers or doubles) or character (i.e., strings). The R value of NA is pulled as nil into Ruby. Data in other formats must be coerced when copying to Ruby.
-
singletons: R represents a single number as a vector of length one, but in Ruby it is often more convenient to use a number rather than an array of length one. Setting singleton=false will cause the pull method to shed the array, while singletons=true will return the number of string within an array. The default is false.
422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/rinruby.rb', line 422 def pull(string, singletons=false) raise EngineClosed if @engine.closed? if complete?(string) result = pull_engine(string) if !singletons && result && result.length == 1 && result.class != String result = result[0] end result else raise ParseError, "Parse error" end end |
#quit ⇒ Object
The quit method will properly close the bridge between Ruby and R, freeing up system resources. This method does not need to be run when a Ruby script ends.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/rinruby.rb', line 211 def quit begin @writer.puts "q(save='no')" # TODO: Verify if read is needed @socket.read() @engine.close @engine_process_status = $? @server_socket.close true ensure @engine.close unless @engine.closed? @server_socket.close unless @server_socket.closed? end end |