rinruby 2.0.3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +24 -0
- data/Gemfile +4 -0
- data/History.txt +47 -4
- data/Manifest.txt +2 -0
- data/{README.txt → README.md} +27 -18
- data/Rakefile +1 -2
- data/lib/rinruby.rb +271 -217
- data/lib/rinruby_without_r_constant.rb +24 -0
- data/lib/version.rb +3 -0
- data/rinruby.gemspec +24 -0
- data/spec/rinruby_spec.rb +212 -125
- data/spec/rinruby_without_r_constant_spec.rb +9 -0
- data/spec/spec_helper.rb +16 -4
- metadata +59 -59
- data/.gemtest +0 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -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
data/History.txt
CHANGED
@@ -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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
data/Manifest.txt
CHANGED
data/{README.txt → README.md}
RENAMED
@@ -1,29 +1,38 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# rinruby
|
3
2
|
* http://rinruby.ddahl.org/
|
4
3
|
|
5
|
-
== DESCRIPTION:
|
6
4
|
|
7
|
-
|
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
|
-
|
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
|
-
|
20
|
+
*Maintainer*: Claudio Bustos
|
21
|
+
|
22
|
+
*Contributors*:
|
23
|
+
|
24
|
+
- [fenrir-naru](https://fenrir.naruoka.org)
|
17
25
|
|
18
|
-
|
26
|
+
### FEATURES/PROBLEMS
|
19
27
|
|
20
|
-
* Pure Ruby. Works on Ruby 1.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
63
|
+
### REQUIREMENTS
|
55
64
|
|
56
65
|
* R
|
57
66
|
|
58
|
-
|
67
|
+
### INSTALL
|
59
68
|
|
60
69
|
* sudo gem install rinruby
|
61
70
|
|
62
71
|
|
63
|
-
|
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.
|
18
|
-
|
17
|
+
self.urls = ["http://rinruby.ddahl.org/"]
|
19
18
|
end
|
20
19
|
|
21
20
|
# vim: syntax=ruby
|
data/lib/rinruby.rb
CHANGED
@@ -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
|
-
|
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
|
-
#{
|
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
|
-
|
193
|
-
|
194
|
-
|
195
|
-
@
|
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
|
-
|
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
|
-
|
516
|
-
|
517
|
-
|
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
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
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
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
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
|
-
|
555
|
-
} else if ( type == #{
|
556
|
-
|
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
|
-
|
561
|
-
}
|
568
|
+
value
|
569
|
+
})
|
570
|
+
}
|
562
571
|
EOF
|
563
572
|
end
|
564
|
-
|
573
|
+
|
565
574
|
def r_rinruby_pull
|
566
|
-
|
567
|
-
|
568
|
-
{
|
569
|
-
|
570
|
-
|
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
|
-
|
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
|
-
|
602
|
-
|
603
|
-
|
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
|
-
|
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
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
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
|
-
|
620
|
-
|
621
|
-
elsif value.
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
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
|
-
|
653
|
-
|
654
|
-
|
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
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
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
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
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
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
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
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
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
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
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
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
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
|
-
|
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/
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
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
|