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.
- 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
|
+
[](https://travis-ci.org/clbustos/rinruby)
|
6
|
+
|
7
|
+
[](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
|