rinruby 2.0.3 → 2.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e778d372d5d1595f97fe69f2986f96fa9a56bf86
4
+ data.tar.gz: a767fb4e5e793719268c3a561a6bb3a7201a9a65
5
+ SHA512:
6
+ metadata.gz: f87a7359c4fd94d9a100ecf3ddfe40d2e7016a1e1f5d6144ad799feed1f8d3dc07272f9c4ceae8945cd926b011dc7320d6760b3281b6d38f25ad7d8fbc0d9b31
7
+ data.tar.gz: 6fb9adbe15972112b5a5a28cb4ccd783495ddbdc8c3a10e85fc95dbdfdcb6955fb2a56bdbcf6af4d4f366e60b5d0dc943220541df35bb439e0f3652ba5bd91fe
@@ -0,0 +1,4 @@
1
+ Gemfile.lock
2
+ pkg
3
+ doc
4
+ *~
@@ -0,0 +1,24 @@
1
+ language:
2
+ ruby
3
+
4
+ rvm:
5
+ - '2.1.1'
6
+ - '2.4.4'
7
+ - '2.5.1'
8
+ - jruby-head
9
+ # - rbx-2.1.1
10
+ cache: bundler
11
+ script:
12
+ bundle exec rspec
13
+
14
+ before_install:
15
+ - sudo apt-get update -qq
16
+ - sudo apt-get install -y r-base r-base-dev
17
+ before_script:
18
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
19
+ - chmod +x ./cc-test-reporter
20
+ - ./cc-test-reporter before-build
21
+ after_script:
22
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
23
+ env:
24
+ TEST_TRAVIS=1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rinruby.gemspec
4
+ gemspec
@@ -1,8 +1,51 @@
1
+ === 2.1.0 / 2018-08-19
2
+
3
+ 638df01 Ruby 2.0.0 deprecated
4
+ 86d2370 Omit to_signed_int() with signed pack for speed optimization
5
+ 7c182ec Use native endian on Ruby side for speed optimization
6
+ ca8c9a1 Add corner case tests
7
+ 555e7d2 Add logical value pull tests
8
+ 312b9c9 Add logical value assign/pull
9
+ 350ce77 Change type from String to String_Array when assign based on procedure
10
+ e2ce0e1 Optimize pull engine
11
+ c22f821 Separate numeric pull test into integer and float tests
12
+ 1ba51e1 Add after{r.quit} to each example because of socket reusability
13
+ 7f59977 Revive echo and interactive tests, and add comment of test cache
14
+ a034124 Fix to use a cached RinRuby instance
15
+ a8f4cf1 Update README.md
16
+ 17baae9 Update matrix assignment test to use a large matrix
17
+ 1a147fd Optimize assign_engine()
18
+ 8418a65 Change string assignment scheme to accept array in a same session
19
+ 8c6fd6a Add cleanup for socket when connection is persistent
20
+ 0ddab52 Add persistent mode to reuse connection, which is now default.
21
+ aa7c90e Optimize performing singleton of pull returned value
22
+ 1a29474 Move R socket manipulation procedure into socket_seession() on Ruby
23
+ e1b96ba Refactoring; move socket.close from each procedure to socket_session
24
+ de954ed Reduce namespace contamination by using ".RinRuby" environment
25
+ 9519355 Move socketConnection() and close() to session()
26
+ 59ffe63 Move rinruby R function assignment from global to base environment
27
+ 13056c4 Change socket connection scheme for GC resistance
28
+ 97cf371 Perform communication via #{RinRuby_Socket}.write and read functions
29
+ 0a2b411 Remove duplication by using dummy R constant temporary
30
+ 964d0ae Improve possibility to find R by using regtool on cygwin
31
+ 89b7304 Add the R wrapper class without R constant
32
+ bba176b Add a safety net when Windows R is not registered in registry
33
+ 67afbe1 Internationalization; workaround for "invalid byte sequence in UTF-8"
34
+ 61d0817 Change registry search query because of structure change of R3.4.3
35
+ 9433299 Change strategy to find R executable for cygwin-windows
36
+ 2515984 Error in README.md example code
37
+ 8ff82c1 Markdownify README.md
38
+ e09cc95 Rename README.txt to README.md
39
+ 1b05f3e added support for bundler and gemspec
40
+
1
41
  === 2.0.3 / 2012-07-31
2
- * 64-bit version is in x64
3
- * Adapt to different Rterm.exe locations. On Windows at least, Rterm.exe has moved under an i386/ folder
4
- * Update README.txt
5
- * Updated spec to rspec 2.0
42
+ * Update README.txt
43
+
44
+ === 2.0.2 / 2011-02-03
45
+
46
+ * Updated RSpec to 2.0. Should work on Windows [clbustos]
47
+ * 64-bit version is in x64 [Rob Heittman]
48
+ * Adapt to different Rterm.exe locations [Rob Heittman]
6
49
 
7
50
  === 2.0.1 / 2010-05-01
8
51
  * Reimplemented Rinruby.new with ordered parameters, for complete backwards compatibility
@@ -4,6 +4,8 @@ Manifest.txt
4
4
  README.txt
5
5
  Rakefile
6
6
  lib/rinruby.rb
7
+ lib/rinruby_without_r_constant.rb
7
8
  spec/rinruby_spec.rb
9
+ spec/rinruby_without_r_constant_spec.rb
8
10
  spec/spec.opts
9
11
  spec/spec_helper.rb
@@ -1,29 +1,38 @@
1
- = rinruby
2
-
1
+ # rinruby
3
2
  * http://rinruby.ddahl.org/
4
3
 
5
- == DESCRIPTION:
6
4
 
7
- RinRuby is a Ruby library that integrates the R interpreter in Ruby, making R's statistical routines and graphics available within Ruby. The library consists of a single Ruby script that is simple to install and does not require any special compilation or installation of R. Since the library is 100% pure Ruby, it works on a variety of operating systems, Ruby implementations, and versions of R. RinRuby's methods are simple, making for readable code. The {website [rinruby.ddahl.org]}[http://rinruby.ddahl.org] describes RinRuby usage, provides comprehensive documentation, gives several examples, and discusses RinRuby's implementation.
5
+ [![Build Status](https://travis-ci.org/clbustos/rinruby.svg?branch=master)](https://travis-ci.org/clbustos/rinruby)
6
+
7
+ [![Maintainability](https://api.codeclimate.com/v1/badges/d6cdb002c01f4a696ff3/maintainability)](https://codeclimate.com/github/clbustos/rinruby/maintainability)
8
+
9
+ ### DESCRIPTION
10
+
11
+ RinRuby is a Ruby library that integrates the R interpreter in Ruby, making R's statistical routines and graphics available within Ruby. The library consists of a single Ruby script that is simple to install and does not require any special compilation or installation of R. Since the library is 100% pure Ruby, it works on a variety of operating systems, Ruby implementations, and versions of R. RinRuby's methods are simple, making for readable code. The [website *rinruby.ddahl.org*](http://rinruby.ddahl.org) describes RinRuby usage, provides comprehensive documentation, gives several examples, and discusses RinRuby's implementation.
8
12
 
9
13
 
10
14
  Copyright 2005-2008 David B. Dahl
11
15
 
12
- Developed by David B. Dahl
13
- Documented by David B. Dahl and Scott Crawford
16
+ Developed by David B. Dahl. Documented by David B. Dahl and Scott Crawford
17
+
14
18
  Homepage: http://rinruby.ddahl.org
15
19
 
16
- Contributors: Claudio Bustos
20
+ *Maintainer*: Claudio Bustos
21
+
22
+ *Contributors*:
23
+
24
+ - [fenrir-naru](https://fenrir.naruoka.org)
17
25
 
18
- == FEATURES/PROBLEMS:
26
+ ### FEATURES/PROBLEMS
19
27
 
20
- * Pure Ruby. Works on Ruby 1.8.7, 1.9 and JRuby 1.4
28
+ * Pure Ruby. Works on Ruby 2.1, 2.2, 2.4 and JRuby-head (2018/03/29). There isn't any specific code that impides to use Ruby < 2.0, but is deprecated.
21
29
  * Slower than RSRuby, but more robust
22
30
 
23
- == SYNOPSIS:
31
+ ### SYNOPSIS
32
+
24
33
  Below is a simple example of RinRuby usage for simple linear regression. The simulation parameters are defined in Ruby, computations are performed in R, and Ruby reports the results. In a more elaborate application, the simulation parameter might come from input from a graphical user interface, the statistical analysis might be more involved, and the results might be an HTML page or PDF report.
25
34
 
26
- <b>Code</b>:
35
+ #### Code
27
36
 
28
37
  require "rinruby"
29
38
  n = 10
@@ -33,34 +42,34 @@ Below is a simple example of RinRuby usage for simple linear regression. The sim
33
42
  seed = 23423
34
43
  R.x = (1..n).entries
35
44
  R.eval <<EOF
36
- set.seed({seed})
37
- y <- {beta_0} + {beta_1}*x + rnorm({n})
45
+ set.seed(#{seed})
46
+ y <- #{beta_0} + #{beta_1}*x + rnorm(#{n})
38
47
  fit <- lm( y ~ x )
39
48
  est <- round(coef(fit),3)
40
49
  pvalue <- summary(fit)$coefficients[2,4]
41
50
  EOF
42
- puts "E(y|x) ~= {R.est[0]} + {R.est[1]} * x"
51
+ puts "E(y|x) ~= #{R.est[0]} + #{R.est[1]} * x"
43
52
  if R.pvalue < alpha
44
53
  puts "Reject the null hypothesis and conclude that x and y are related."
45
54
  else
46
55
  puts "There is insufficient evidence to conclude that x and y are related."
47
56
  end
48
57
 
49
- <b>Output</b>:
58
+ #### Output
50
59
 
51
60
  E(y|x) ~= 1.264 + 0.273 * x
52
61
  Reject the null hypothesis and conclude that x and y are related.
53
62
 
54
- == REQUIREMENTS:
63
+ ### REQUIREMENTS
55
64
 
56
65
  * R
57
66
 
58
- == INSTALL:
67
+ ### INSTALL
59
68
 
60
69
  * sudo gem install rinruby
61
70
 
62
71
 
63
- == LICENSE:
72
+ ### LICENSE
64
73
 
65
74
  GPL-3. See LICENSE.txt for more information.
66
75
 
data/Rakefile CHANGED
@@ -14,8 +14,7 @@ Hoe.spec 'rinruby' do
14
14
  # self.rubyforge_name = 'rinruby' # if different than 'rinruby2'
15
15
  self.developer('David Dahl', 'rinruby_AT_ddahl.org')
16
16
  self.developer('Claudio Bustos', 'clbustos_AT_gmail.com')
17
- self.url = "http://rinruby.ddahl.org/"
18
-
17
+ self.urls = ["http://rinruby.ddahl.org/"]
19
18
  end
20
19
 
21
20
  # vim: syntax=ruby
@@ -2,7 +2,7 @@
2
2
  #
3
3
  #RinRuby is a Ruby library that integrates the R interpreter in Ruby, making R's statistical routines and graphics available within Ruby. The library consists of a single Ruby script that is simple to install and does not require any special compilation or installation of R. Since the library is 100% pure Ruby, it works on a variety of operating systems, Ruby implementations, and versions of R. RinRuby's methods are simple, making for readable code. The {website [rinruby.ddahl.org]}[http://rinruby.ddahl.org] describes RinRuby usage, provides comprehensive documentation, gives several examples, and discusses RinRuby's implementation.
4
4
  #
5
- #Below is a simple example of RinRuby usage for simple linear regression. The simulation parameters are defined in Ruby, computations are performed in R, and Ruby reports the results. In a more elaborate application, the simulation parameter might come from input from a graphical user interface, the statistical analysis might be more involved, and the results might be an HTML page or PDF report.
5
+ #Below is a simple example of RinRuby usage for simple linear regression. The simulation parameters are defined in Ruby, computations are performed in R, and Ruby reports the results. In a more elaborate application, the simulation parameter might come from input from a graphical user interface, the statistical analysis might be more involved, and the results might be an HTML page or PDF report.
6
6
  #
7
7
  #<b>Code</b>:
8
8
  #
@@ -34,6 +34,7 @@
34
34
  #
35
35
  #Coded by:: David B. Dahl
36
36
  #Documented by:: David B. Dahl & Scott Crawford
37
+ #Maintained by:: Claudio Bustos
37
38
  #Copyright:: 2005-2009
38
39
  #Web page:: http://rinruby.ddahl.org
39
40
  #E-mail:: mailto:rinruby@ddahl.org
@@ -58,20 +59,20 @@
58
59
  #The files "java" and "readline" are used when available to add functionality.
59
60
  require 'matrix'
60
61
  class RinRuby
61
-
62
+
62
63
  require 'socket'
63
64
 
64
-
65
65
  VERSION = '2.0.3'
66
66
 
67
-
68
67
  attr_reader :interactive
69
68
  attr_reader :readline
70
69
  # Exception for closed engine
71
70
  EngineClosed=Class.new(Exception)
72
71
  # Parse error
73
72
  ParseError=Class.new(Exception)
74
-
73
+
74
+ RinRuby_Env = ".RinRuby"
75
+ RinRuby_Endian = ([1].pack("L").unpack("C*")[0] == 1) ? (:little) : (:big)
75
76
 
76
77
  #RinRuby is invoked within a Ruby script (or the interactive "irb" prompt denoted >>) using:
77
78
  #
@@ -98,7 +99,7 @@ class RinRuby
98
99
  # >> require "rinruby"
99
100
  # >> R.quit
100
101
  # >> R = RinRuby.new(false)
101
- attr_reader :echo_enabled
102
+ attr_accessor :echo_enabled
102
103
  attr_reader :executable
103
104
  attr_reader :port_number
104
105
  attr_reader :port_width
@@ -114,8 +115,8 @@ def initialize(*args)
114
115
  opts[:port_number]=args.shift unless args.size==0
115
116
  opts[:port_width]=args.shift unless args.size==0
116
117
  end
117
- default_opts= {:echo=>true, :interactive=>true, :executable=>nil, :port_number=>38442, :port_width=>1000, :hostname=>'127.0.0.1'}
118
-
118
+ default_opts= {:echo=>true, :interactive=>true, :executable=>nil, :port_number=>38442, :port_width=>1000, :hostname=>'127.0.0.1', :persistent => true}
119
+
119
120
  @opts=default_opts.merge(opts)
120
121
  @port_width=@opts[:port_width]
121
122
  @executable=@opts[:executable]
@@ -166,37 +167,26 @@ def initialize(*args)
166
167
  @writer = @engine
167
168
  raise "Engine closed" if @engine.closed?
168
169
  @writer.puts <<-EOF
169
- #{RinRuby_KeepTrying_Variable} <- TRUE
170
- while ( #{RinRuby_KeepTrying_Variable} ) {
171
- #{RinRuby_Socket} <- try(suppressWarnings(socketConnection("#{@hostname}", #{@port_number}, blocking=TRUE, open="rb")),TRUE)
172
- if ( inherits(#{RinRuby_Socket},"try-error") ) {
173
- Sys.sleep(0.1)
174
- } else {
175
- #{RinRuby_KeepTrying_Variable} <- FALSE
176
- }
177
- }
178
- rm(#{RinRuby_KeepTrying_Variable})
170
+ assign("#{RinRuby_Env}", new.env(), baseenv())
179
171
  EOF
172
+ @socket = nil
173
+ r_rinruby_socket_io
180
174
  r_rinruby_get_value
181
175
  r_rinruby_pull
182
176
  r_rinruby_parseable
183
- @socket = @server_socket.accept
184
177
  echo(nil,true) if @platform =~ /.*-java/ # Redirect error messages on the Java platform
185
178
  end
186
179
 
187
180
  #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.
188
181
 
189
182
  def quit
190
- begin
183
+ begin
191
184
  @writer.puts "q(save='no')"
192
- # TODO: Verify if read is needed
193
- @socket.read()
194
- #@socket.close
195
- @engine.close
196
-
197
-
198
- @server_socket.close
199
- #@reader.close
185
+ @engine.close
186
+
187
+
188
+ @server_socket.close
189
+ #@reader.close
200
190
  #@writer.close
201
191
  true
202
192
  ensure
@@ -464,11 +454,7 @@ def initialize(*args)
464
454
  def pull(string, singletons=false)
465
455
  raise EngineClosed if @engine.closed?
466
456
  if complete?(string)
467
- result = pull_engine(string)
468
- if ( ! singletons ) && ( result.length == 1 ) && ( result.class != String )
469
- result = result[0]
470
- end
471
- result
457
+ pull_engine(string, singletons)
472
458
  else
473
459
  raise ParseError, "Parse error"
474
460
  end
@@ -511,231 +497,259 @@ def initialize(*args)
511
497
  RinRuby_Type_String = 2
512
498
  RinRuby_Type_String_Array = 3
513
499
  RinRuby_Type_Matrix = 4
500
+ RinRuby_Type_Boolean = 5
514
501
 
515
- RinRuby_KeepTrying_Variable = ".RINRUBY.KEEPTRYING.VARIABLE"
516
- RinRuby_Length_Variable = ".RINRUBY.PULL.LENGTH.VARIABLE"
517
- RinRuby_Type_Variable = ".RINRUBY.PULL.TYPE.VARIABLE"
518
- RinRuby_Socket = ".RINRUBY.PULL.SOCKET"
519
- RinRuby_Variable = ".RINRUBY.PULL.VARIABLE"
520
- RinRuby_Parse_String = ".RINRUBY.PARSE.STRING"
502
+ RinRuby_Socket = "#{RinRuby_Env}$socket"
503
+ RinRuby_Parse_String = "#{RinRuby_Env}$parse.string"
504
+
521
505
  RinRuby_Eval_Flag = "RINRUBY.EVAL.FLAG"
522
506
  RinRuby_Stderr_Flag = "RINRUBY.STDERR.FLAG"
523
507
  RinRuby_Exit_Flag = "RINRUBY.EXIT.FLAG"
508
+
524
509
  RinRuby_Max_Unsigned_Integer = 2**32
525
510
  RinRuby_Half_Max_Unsigned_Integer = 2**31
526
511
  RinRuby_NA_R_Integer = 2**31
527
512
  RinRuby_Max_R_Integer = 2**31-1
528
513
  RinRuby_Min_R_Integer = -2**31+1
529
514
  #:startdoc:
530
-
515
+
516
+ def r_rinruby_socket_io
517
+ @writer.puts <<-EOF
518
+ #{RinRuby_Socket} <- NULL
519
+ #{RinRuby_Env}$session <- function(f){
520
+ invisible(f(#{RinRuby_Socket}))
521
+ }
522
+ #{RinRuby_Env}$write <- function(con, v, ...){
523
+ invisible(lapply(list(v, ...), function(v2){
524
+ writeBin(v2, con, endian="#{RinRuby_Endian}")}))
525
+ }
526
+ #{RinRuby_Env}$read <- function(con, vtype, len){
527
+ invisible(readBin(con, vtype(), len, endian="#{RinRuby_Endian}"))
528
+ }
529
+ EOF
530
+ end
531
531
 
532
532
  def r_rinruby_parseable
533
533
  @writer.puts <<-EOF
534
- rinruby_parseable<-function(var) {
535
- result=try(parse(text=var),TRUE)
536
- if(inherits(result, "try-error")) {
537
- writeBin(as.integer(-1),#{RinRuby_Socket}, endian="big")
538
- } else {
539
- writeBin(as.integer(1),#{RinRuby_Socket}, endian="big")
540
- }
534
+ #{RinRuby_Env}$parseable <- function(var) {
535
+ #{RinRuby_Env}$session(function(con){
536
+ result=try(parse(text=var),TRUE)
537
+ if(inherits(result, "try-error")) {
538
+ #{RinRuby_Env}$write(con, as.integer(-1))
539
+ } else {
540
+ #{RinRuby_Env}$write(con, as.integer(1))
541
+ }
542
+ })
541
543
  }
542
544
  EOF
543
545
  end
544
546
  # Create function on ruby to get values
545
547
  def r_rinruby_get_value
546
548
  @writer.puts <<-EOF
547
- rinruby_get_value <-function() {
548
- value <- NULL
549
- type <- readBin(#{RinRuby_Socket}, integer(), 1, endian="big")
550
- length <- readBin(#{RinRuby_Socket},integer(),1,endian="big")
551
- if ( type == #{RinRuby_Type_Double} ) {
552
- value <- readBin(#{RinRuby_Socket},numeric(), length,endian="big")
549
+ #{RinRuby_Env}$get_value <- function() {
550
+ #{RinRuby_Env}$session(function(con){
551
+ value <- NULL
552
+ type <- #{RinRuby_Env}$read(con, integer, 1)
553
+ length <- #{RinRuby_Env}$read(con, integer, 1)
554
+ if ( type == #{RinRuby_Type_Double} ) {
555
+ value <- #{RinRuby_Env}$read(con, numeric, length)
553
556
  } else if ( type == #{RinRuby_Type_Integer} ) {
554
- value <- readBin(#{RinRuby_Socket},integer(), length, endian="big")
555
- } else if ( type == #{RinRuby_Type_String} ) {
556
- value <- readBin(#{RinRuby_Socket},character(),1,endian="big")
557
+ value <- #{RinRuby_Env}$read(con, integer, length)
558
+ } else if ( type == #{RinRuby_Type_Boolean} ) {
559
+ value <- #{RinRuby_Env}$read(con, logical, length)
560
+ } else if ( type == #{RinRuby_Type_String_Array} ) {
561
+ value <- character(length)
562
+ for(i in 1:length){
563
+ value[i] <- #{RinRuby_Env}$read(con, character, 1)
564
+ }
557
565
  } else {
558
566
  value <-NULL
559
567
  }
560
- value
561
- }
568
+ value
569
+ })
570
+ }
562
571
  EOF
563
572
  end
564
-
573
+
565
574
  def r_rinruby_pull
566
- @writer.puts <<-EOF
567
- rinruby_pull <-function(var)
568
- {
569
- if ( inherits(var ,"try-error") ) {
570
- writeBin(as.integer(#{RinRuby_Type_NotFound}),#{RinRuby_Socket},endian="big")
571
- } else {
572
- if (is.matrix(var)) {
573
- writeBin(as.integer(#{RinRuby_Type_Matrix}),#{RinRuby_Socket},endian="big")
574
- writeBin(as.integer(dim(var)[1]),#{RinRuby_Socket},endian="big")
575
- writeBin(as.integer(dim(var)[2]),#{RinRuby_Socket},endian="big")
576
-
577
- } else if ( is.double(var) ) {
578
- writeBin(as.integer(#{RinRuby_Type_Double}),#{RinRuby_Socket},endian="big")
579
- writeBin(as.integer(length(var)),#{RinRuby_Socket},endian="big")
580
- writeBin(var,#{RinRuby_Socket},endian="big")
581
- } else if ( is.integer(var) ) {
582
- writeBin(as.integer(#{RinRuby_Type_Integer}),#{RinRuby_Socket},endian="big")
583
- writeBin(as.integer(length(var)),#{RinRuby_Socket},endian="big")
584
- writeBin(var,#{RinRuby_Socket},endian="big")
585
- } else if ( is.character(var) && ( length(var) == 1 ) ) {
586
- writeBin(as.integer(#{RinRuby_Type_String}),#{RinRuby_Socket},endian="big")
587
- writeBin(as.integer(nchar(var)),#{RinRuby_Socket},endian="big")
588
- writeBin(var,#{RinRuby_Socket},endian="big")
589
- } else if ( is.character(var) && ( length(var) > 1 ) ) {
590
- writeBin(as.integer(#{RinRuby_Type_String_Array}),#{RinRuby_Socket},endian="big")
591
- writeBin(as.integer(length(var)),#{RinRuby_Socket},endian="big")
575
+ @writer.puts <<-EOF
576
+ #{RinRuby_Env}$pull <- function(var){
577
+ #{RinRuby_Env}$session(function(con){
578
+ if ( inherits(var ,"try-error") ) {
579
+ #{RinRuby_Env}$write(con, as.integer(#{RinRuby_Type_NotFound}))
592
580
  } else {
593
- writeBin(as.integer(#{RinRuby_Type_Unknown}),#{RinRuby_Socket},endian="big")
581
+ if (is.matrix(var)) {
582
+ #{RinRuby_Env}$write(con,
583
+ as.integer(#{RinRuby_Type_Matrix}),
584
+ as.integer(dim(var)[1]))
585
+ } else if ( is.double(var) ) {
586
+ #{RinRuby_Env}$write(con,
587
+ as.integer(#{RinRuby_Type_Double}),
588
+ as.integer(length(var)),
589
+ var)
590
+ } else if ( is.integer(var) ) {
591
+ #{RinRuby_Env}$write(con,
592
+ as.integer(#{RinRuby_Type_Integer}),
593
+ as.integer(length(var)),
594
+ var)
595
+ } else if ( is.character(var) && ( length(var) == 1 ) ) {
596
+ #{RinRuby_Env}$write(con,
597
+ as.integer(#{RinRuby_Type_String}),
598
+ as.integer(nchar(var)),
599
+ var)
600
+ } else if ( is.character(var) && ( length(var) > 1 ) ) {
601
+ #{RinRuby_Env}$write(con,
602
+ as.integer(#{RinRuby_Type_String_Array}),
603
+ as.integer(length(var)))
604
+ } else if ( is.logical(var) ) {
605
+ #{RinRuby_Env}$write(con,
606
+ as.integer(#{RinRuby_Type_Boolean}),
607
+ as.integer(length(var)),
608
+ var)
609
+ } else {
610
+ #{RinRuby_Env}$write(con, as.integer(#{RinRuby_Type_Unknown}))
611
+ }
594
612
  }
595
- }
613
+ })
596
614
  }
597
615
  EOF
598
-
599
-
600
616
  end
601
- def to_signed_int(y)
602
- if y.kind_of?(Integer)
603
- ( y > RinRuby_Half_Max_Unsigned_Integer ) ? -(RinRuby_Max_Unsigned_Integer-y) : ( y == RinRuby_NA_R_Integer ? nil : y )
617
+
618
+ def socket_session(&b)
619
+ socket = @socket
620
+ # TODO check still available connection?
621
+ unless socket then
622
+ t = Thread::new{socket = @server_socket.accept}
623
+ @writer.puts <<-EOF
624
+ #{RinRuby_Socket} <- socketConnection(
625
+ "#{@hostname}", #{@port_number}, blocking=TRUE, open="rb")
626
+ #{"on.exit(close(#{RinRuby_Socket}, add = T))" if @opts[:persistent]}
627
+ EOF
628
+ t.join
629
+ end
630
+ res = b.call(socket)
631
+ if @opts[:persistent]
632
+ @socket = socket
604
633
  else
605
- y.collect { |x| ( x > RinRuby_Half_Max_Unsigned_Integer ) ? -(RinRuby_Max_Unsigned_Integer-x) : ( x == RinRuby_NA_R_Integer ? nil : x ) }
634
+ @writer.puts <<-EOF
635
+ close(#{RinRuby_Socket})
636
+ #{RinRuby_Socket} <- NULL
637
+ EOF
638
+ socket.close
606
639
  end
640
+ res
607
641
  end
608
642
 
609
643
  def assign_engine(name, value)
610
644
  original_value = value
611
- # Special assign for matrixes
612
- if value.kind_of?(::Matrix)
613
- values=value.row_size.times.collect {|i| value.column_size.times.collect {|j| value[i,j]}}.flatten
614
- eval "#{name}=matrix(c(#{values.join(',')}), #{value.row_size}, #{value.column_size}, TRUE)"
615
- return original_value
645
+
646
+ r_exp = "#{name} <- #{RinRuby_Env}$get_value()"
647
+
648
+ if value.kind_of?(::Matrix) # assignment for matrices
649
+ r_exp = "#{name} <- matrix(#{RinRuby_Env}$get_value(), nrow=#{value.row_size}, ncol=#{value.column_size}, byrow=T)"
650
+ value = value.row_vectors.collect{|row| row.to_a}.flatten
651
+ elsif !value.kind_of?(Array) then # check Array
652
+ value = [value]
616
653
  end
617
654
 
618
- if value.kind_of?(String)
619
- type = RinRuby_Type_String
620
- length = 1
621
- elsif value.kind_of?(Integer)
622
- if ( value >= RinRuby_Min_R_Integer ) && ( value <= RinRuby_Max_R_Integer )
623
- value = [ value.to_i ]
624
- type = RinRuby_Type_Integer
625
- else
626
- value = [ value.to_f ]
627
- type = RinRuby_Type_Double
628
- end
629
- length = 1
630
- elsif value.kind_of?(Float)
631
- value = [ value.to_f ]
632
- type = RinRuby_Type_Double
633
- length = 1
634
- elsif value.kind_of?(Array)
635
- begin
636
- if value.any? { |x| x.kind_of?(String) }
637
- eval "#{name} <- character(#{value.length})"
638
- for index in 0...value.length
639
- assign_engine("#{name}[#{index}+1]",value[index])
640
- end
641
- return original_value
642
- elsif value.any? { |x| x.kind_of?(Float) }
643
- type = RinRuby_Type_Double
644
- value = value.collect { |x| x.to_f }
645
- elsif value.all? { |x| x.kind_of?(Integer) }
646
- if value.all? { |x| ( x >= RinRuby_Min_R_Integer ) && ( x <= RinRuby_Max_R_Integer ) }
647
- type = RinRuby_Type_Integer
648
- else
649
- value = value.collect { |x| x.to_f }
650
- type = RinRuby_Type_Double
655
+ type = (if value.any?{|x| x.kind_of?(String)}
656
+ value = value.collect{|v| v.to_s}
657
+ RinRuby_Type_String_Array
658
+ elsif value_b = value.collect{|v|
659
+ case v
660
+ when true; 1
661
+ when false; 0
662
+ else; break false
651
663
  end
652
- else
653
- raise "Unsupported data type on Ruby's end"
654
- end
664
+ }
665
+ value = value_b
666
+ RinRuby_Type_Boolean
667
+ elsif value.all?{|x|
668
+ x.kind_of?(Integer) && (x >= RinRuby_Min_R_Integer) && (x <= RinRuby_Max_R_Integer)
669
+ }
670
+ RinRuby_Type_Integer
671
+ else
672
+ begin
673
+ value = value.collect{|x| Float(x)}
655
674
  rescue
656
675
  raise "Unsupported data type on Ruby's end"
657
676
  end
658
- length = value.length
659
- else
660
- raise "Unsupported data type on Ruby's end"
661
- end
662
- @writer.puts "#{name} <- rinruby_get_value()"
663
-
664
- @socket.write([type,length].pack('NN'))
665
- if ( type == RinRuby_Type_String )
666
- @socket.write(value)
667
- @socket.write([0].pack('C')) # zero-terminated strings
668
- else
669
- @socket.write(value.pack( ( type==RinRuby_Type_Double ? 'G' : 'N' )*length ))
670
- end
677
+ RinRuby_Type_Double
678
+ end)
679
+
680
+ socket_session{|socket|
681
+ @writer.puts(r_exp)
682
+ socket.write([type, value.size].pack('LL'))
683
+ case type
684
+ when RinRuby_Type_String_Array
685
+ value.each{|v|
686
+ socket.write(v)
687
+ socket.write([0].pack('C')) # zero-terminated strings
688
+ }
689
+ else
690
+ socket.write(value.pack("#{(type == RinRuby_Type_Double) ? 'D' : 'l'}#{value.size}"))
691
+ end
692
+ }
693
+
671
694
  original_value
672
695
  end
673
696
 
674
- def pull_engine(string)
675
- @writer.puts <<-EOF
676
- rinruby_pull(try(#{string}))
677
- EOF
678
-
679
- buffer = ""
680
- @socket.read(4,buffer)
681
- type = to_signed_int(buffer.unpack('N')[0].to_i)
682
- if ( type == RinRuby_Type_Unknown )
683
- raise "Unsupported data type on R's end"
684
- end
685
- if ( type == RinRuby_Type_NotFound )
686
- return nil
687
- end
688
- @socket.read(4,buffer)
689
- length = to_signed_int(buffer.unpack('N')[0].to_i)
690
-
691
- if ( type == RinRuby_Type_Double )
692
- @socket.read(8*length,buffer)
693
- result = buffer.unpack('G'*length)
694
- elsif ( type == RinRuby_Type_Integer )
695
- @socket.read(4*length,buffer)
696
- result = to_signed_int(buffer.unpack('N'*length))
697
- elsif ( type == RinRuby_Type_String )
698
- @socket.read(length,buffer)
699
- result = buffer.dup
700
- @socket.read(1,buffer) # zero-terminated string
701
- result
702
- elsif ( type == RinRuby_Type_String_Array )
703
- result = Array.new(length,'')
704
- for index in 0...length
705
- result[index] = pull "#{string}[#{index+1}]"
697
+ def pull_engine(string, singletons = true)
698
+ pull_proc = proc{|var, socket|
699
+ @writer.puts "#{RinRuby_Env}$pull(try(#{var}))"
700
+ type = socket.read(4).unpack('l').first
701
+ case type
702
+ when RinRuby_Type_Unknown
703
+ raise "Unsupported data type on R's end"
704
+ when RinRuby_Type_NotFound
705
+ return nil
706
706
  end
707
- elsif (type == RinRuby_Type_Matrix)
708
- rows=length
709
- @socket.read(4,buffer)
710
- cols = to_signed_int(buffer.unpack('N')[0].to_i)
711
- elements=pull "as.vector(#{string})"
712
- index=0
713
- result=Matrix.rows(rows.times.collect {|i|
714
- cols.times.collect {|j|
715
- elements[(j*rows)+i]
707
+ length = socket.read(4).unpack('l').first
708
+
709
+ case type
710
+ when RinRuby_Type_Double
711
+ result = socket.read(8 * length).unpack("D#{length}")
712
+ (!singletons) && (length == 1) ? result[0] : result
713
+ when RinRuby_Type_Integer
714
+ result = socket.read(4 * length).unpack("l#{length}")
715
+ (!singletons) && (length == 1) ? result[0] : result
716
+ when RinRuby_Type_String
717
+ result = socket.read(length)
718
+ socket.read(1) # zero-terminated string
719
+ result
720
+ when RinRuby_Type_String_Array
721
+ Array.new(length){|i|
722
+ pull_proc.call("#{var}[#{i+1}]", socket)
716
723
  }
717
- })
718
- def result.length; 2;end
719
- else
720
- raise "Unsupported data type on Ruby's end"
721
- end
722
- result
724
+ when RinRuby_Type_Matrix
725
+ Matrix.rows(length.times.collect{|i|
726
+ pull_proc.call("#{var}[#{i+1},]", socket)
727
+ })
728
+ when RinRuby_Type_Boolean
729
+ result = socket.read(4 * length).unpack("l#{length}").collect{|v| v > 0}
730
+ (!singletons) && (length == 1) ? result[0] : result
731
+ else
732
+ raise "Unsupported data type on Ruby's end"
733
+ end
734
+ }
735
+ socket_session{|socket|
736
+ pull_proc.call(string, socket)
737
+ }
723
738
  end
724
739
 
725
740
  def complete?(string)
726
741
  assign_engine(RinRuby_Parse_String, string)
727
- @writer.puts "rinruby_parseable(#{RinRuby_Parse_String})"
728
- buffer=""
729
- @socket.read(4,buffer)
730
- @writer.puts "rm(#{RinRuby_Parse_String})"
731
- result = to_signed_int(buffer.unpack('N')[0].to_i)
742
+ result = socket_session{|socket|
743
+ @writer.puts "#{RinRuby_Env}$parseable(#{RinRuby_Parse_String})"
744
+ socket.read(4).unpack('l').first
745
+ }
732
746
  return result==-1 ? false : true
733
-
734
- =begin
735
-
747
+
748
+ =begin
749
+
736
750
  result = pull_engine("unlist(lapply(c('.*','^Error in parse.*','^Error in parse.*unexpected end of input.*'),
737
751
  grep,try({parse(text=#{RinRuby_Parse_String}); 1}, silent=TRUE)))")
738
-
752
+
739
753
  return true if result.length == 1
740
754
  return false if result.length == 3
741
755
  raise ParseError, "Parse error"
@@ -746,7 +760,6 @@ def initialize(*args)
746
760
  raise ParseError, "Parse error" if ! complete?(string)
747
761
  assign_engine(RinRuby_Parse_String,string)
748
762
  result = pull_engine("as.integer(ifelse(inherits(try({eval(parse(text=paste(#{RinRuby_Parse_String},'<- 1')))}, silent=TRUE),'try-error'),1,0))")
749
- @writer.puts "rm(#{RinRuby_Parse_String})"
750
763
  return true if result == [0]
751
764
  raise ParseError, "Parse error"
752
765
  end
@@ -754,29 +767,70 @@ def initialize(*args)
754
767
  def find_R_on_windows(cygwin)
755
768
  path = '?'
756
769
  for root in [ 'HKEY_LOCAL_MACHINE', 'HKEY_CURRENT_USER' ]
757
- `reg query "#{root}\\Software\\R-core\\R" /v "InstallPath"`.split("\n").each do |line|
758
- next if line !~ /^\s+InstallPath\s+REG_SZ\s+(.*)/
759
- path = $1
760
- while path.chomp!
770
+ if cygwin then
771
+ [:w, :W].collect{|opt| # [64bit, then 32bit registry]
772
+ [:R64, :R].collect{|mode|
773
+ `regtool list -#{opt} /#{root}/Software/R-core/#{mode} 2>/dev/null`.lines.collect{|v|
774
+ v =~ /^\d\.\d\.\d/ ? $& : nil
775
+ }.compact.sort{|a, b| # latest version has higher priority
776
+ b <=> a
777
+ }.collect{|ver|
778
+ ["-#{opt}", "/#{root}/Software/R-core/#{mode}/#{ver}/InstallPath"]
779
+ }
780
+ }
781
+ }.flatten(2).each{|args|
782
+ v = `regtool get #{args.join(' ')}`.chomp
783
+ unless v.empty? then
784
+ path = v
785
+ break
786
+ end
787
+ }
788
+ else
789
+ proc{|str| # Remove invalid byte sequence
790
+ if RUBY_VERSION >= "2.1.0" then
791
+ str.scrub
792
+ elsif RUBY_VERSION >= "1.9.0" then
793
+ str.chars.collect{|c| (c.valid_encoding?) ? c : '*'}.join
794
+ else
795
+ str
796
+ end
797
+ }.call(`reg query "#{root}\\Software\\R-core" /v "InstallPath" /s`).each_line do |line|
798
+ next if line !~ /^\s+InstallPath\s+REG_SZ\s+(.*)/
799
+ path = $1
800
+ while path.chomp!
801
+ end
802
+ break
761
803
  end
762
- break
763
804
  end
764
805
  break if path != '?'
765
806
  end
766
- raise "Cannot locate R executable" if path == '?'
807
+ if path == '?'
808
+ # search at default install path
809
+ path = [
810
+ "Program Files",
811
+ "Program Files (x86)"
812
+ ].collect{|prog_dir|
813
+ Dir::glob(File::join(
814
+ cygwin ? "/cygdrive/c" : "C:",
815
+ prog_dir, "R", "*"))
816
+ }.flatten[0]
817
+ raise "Cannot locate R executable" unless path
818
+ end
767
819
  if cygwin
768
820
  path = `cygpath '#{path}'`
769
821
  while path.chomp!
770
822
  end
771
- path.gsub!(' ','\ ')
823
+ path = [path.gsub(' ','\ '), path]
772
824
  else
773
- path.gsub!('\\','/')
825
+ path = [path.gsub('\\','/')]
774
826
  end
775
- for hierarchy in [ 'bin', 'bin/i386', 'bin/x64' ]
776
- target = "#{path}/#{hierarchy}/Rterm.exe"
777
- if File.exists? target
778
- return %Q<"#{target}">
779
- end
827
+ for hierarchy in [ 'bin', 'bin/x64', 'bin/i386']
828
+ path.each{|item|
829
+ target = "#{item}/#{hierarchy}/Rterm.exe"
830
+ if File.exists? target
831
+ return %Q<"#{target}">
832
+ end
833
+ }
780
834
  end
781
835
  raise "Cannot locate R executable"
782
836
  end