rinruby 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rinruby.rb +204 -137
- metadata +17 -13
- data/lib/rinruby/animation.rb +0 -74
- data/lib/rinruby/gif.rb +0 -70
data/lib/rinruby.rb
CHANGED
@@ -59,7 +59,7 @@
|
|
59
59
|
|
60
60
|
class RinRuby
|
61
61
|
|
62
|
-
VERSION = '1.0
|
62
|
+
VERSION = '1.1.0'
|
63
63
|
|
64
64
|
require 'socket'
|
65
65
|
|
@@ -82,7 +82,7 @@ class RinRuby
|
|
82
82
|
#
|
83
83
|
#* echo: By setting the echo to false, output from R is suppressed, although warnings are still printed. This option can be changed later by using the echo method. The default is true.
|
84
84
|
#* interactive: When interactive is false, R is run in non-interactive mode, resulting in plots without an explicit device being written to Rplots.pdf. Otherwise (i.e., interactive is true), plots are shown on the screen. The default is true.
|
85
|
-
#* executable: The path of the R executable (which is "R" in Linux and Mac
|
85
|
+
#* executable: The path of the R executable (which is "R" in Linux and Mac OS X, or "Rterm.exe" in Windows) can be set with the executable argument. The default is nil which makes RinRuby use the registry keys to find the path (on Windows) or use the path defined by $PATH (on Linux and Mac OS X).
|
86
86
|
#* port_number: This is the smallest port number on the local host that could be used to pass data between Ruby and R. The actual port number used depends on port_width.
|
87
87
|
#* port_width: RinRuby will randomly select a uniform number between port_number and port_number + port_width - 1 (inclusive) to pass data between Ruby and R. If the randomly selected port is not available, RinRuby will continue selecting random ports until it finds one that is available. By setting port_width to 1, RinRuby will wait until port_number is available. The default port_width is 1000.
|
88
88
|
#
|
@@ -103,6 +103,7 @@ class RinRuby
|
|
103
103
|
end
|
104
104
|
end
|
105
105
|
@echo_enabled = echo
|
106
|
+
@echo_stderr = false
|
106
107
|
@interactive = interactive
|
107
108
|
@platform = case RUBY_PLATFORM
|
108
109
|
when /mswin/: 'windows'
|
@@ -137,17 +138,19 @@ class RinRuby
|
|
137
138
|
@reader = @engine
|
138
139
|
@writer = @engine
|
139
140
|
@writer.puts <<-EOF
|
140
|
-
|
141
|
-
while (
|
141
|
+
#{RinRuby_KeepTrying_Variable} <- TRUE
|
142
|
+
while ( #{RinRuby_KeepTrying_Variable} ) {
|
142
143
|
#{RinRuby_Socket} <- try(suppressWarnings(socketConnection("127.0.0.1",#{@port_number},blocking=TRUE,open="rb")),TRUE)
|
143
144
|
if ( inherits(#{RinRuby_Socket},"try-error") ) {
|
144
145
|
Sys.sleep(0.1)
|
145
146
|
} else {
|
146
|
-
|
147
|
+
#{RinRuby_KeepTrying_Variable} <- FALSE
|
147
148
|
}
|
148
149
|
}
|
150
|
+
rm(#{RinRuby_KeepTrying_Variable})
|
149
151
|
EOF
|
150
152
|
@socket = @server_socket.accept
|
153
|
+
echo(nil,true) if @platform =~ /.*-java/ # Redirect error messages on the Java platform
|
151
154
|
end
|
152
155
|
|
153
156
|
#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.
|
@@ -175,6 +178,7 @@ class RinRuby
|
|
175
178
|
# Min. 1st Qu. Median Mean 3rd Qu. Max.
|
176
179
|
# -1.88900 -0.84930 -0.45220 -0.49290 -0.06069 0.78160
|
177
180
|
# [1] 0.7327981
|
181
|
+
#
|
178
182
|
#This example used a string substitution to make the argument to first eval method equivalent to x <- rnorm(10). This example used three invocations of the eval method, but a single invoke is possible using a here document:
|
179
183
|
#
|
180
184
|
# >> R.eval <<EOF
|
@@ -195,11 +199,12 @@ class RinRuby
|
|
195
199
|
|
196
200
|
def eval(string, echo_override=nil)
|
197
201
|
echo_enabled = ( echo_override != nil ) ? echo_override : @echo_enabled
|
198
|
-
|
202
|
+
if complete?(string)
|
199
203
|
@writer.puts string
|
204
|
+
@writer.puts "warning('#{RinRuby_Stderr_Flag}',immediate.=TRUE)" if @echo_stderr
|
200
205
|
@writer.puts "print('#{RinRuby_Eval_Flag}')"
|
201
|
-
|
202
|
-
|
206
|
+
else
|
207
|
+
raise "Parse error"
|
203
208
|
end
|
204
209
|
Signal.trap('INT') do
|
205
210
|
@writer.print ''
|
@@ -208,7 +213,10 @@ class RinRuby
|
|
208
213
|
end
|
209
214
|
return true
|
210
215
|
end
|
216
|
+
found_eval_flag = false
|
217
|
+
found_stderr_flag = false
|
211
218
|
while true
|
219
|
+
echo_eligible = true
|
212
220
|
begin
|
213
221
|
line = @reader.gets
|
214
222
|
rescue
|
@@ -220,9 +228,17 @@ class RinRuby
|
|
220
228
|
while line.chomp!
|
221
229
|
end
|
222
230
|
line = line[8..-1] if line[0] == 27 # Delete escape sequence
|
223
|
-
|
231
|
+
if line == "[1] \"#{RinRuby_Eval_Flag}\""
|
232
|
+
found_eval_flag = true
|
233
|
+
echo_eligible = false
|
234
|
+
end
|
235
|
+
if line == "Warning: #{RinRuby_Stderr_Flag}"
|
236
|
+
found_stderr_flag = true
|
237
|
+
echo_eligible = false
|
238
|
+
end
|
239
|
+
break if found_eval_flag && ( found_stderr_flag == @echo_stderr )
|
224
240
|
return false if line == RinRuby_Exit_Flag
|
225
|
-
if echo_enabled
|
241
|
+
if echo_enabled && echo_eligible
|
226
242
|
puts line
|
227
243
|
$stdout.flush
|
228
244
|
end
|
@@ -244,11 +260,6 @@ class RinRuby
|
|
244
260
|
raise "The 'prompt' method only available in 'interactive' mode" if ! @interactive
|
245
261
|
return false if ! eval("0",false)
|
246
262
|
prompt = regular_prompt
|
247
|
-
if @platform =~ /windows/
|
248
|
-
eval("#{RinRuby_Null_File} <- file('NUL','w')",false)
|
249
|
-
else
|
250
|
-
eval("#{RinRuby_Null_File} <- file('/dev/null','w')",false)
|
251
|
-
end
|
252
263
|
while true
|
253
264
|
cmds = []
|
254
265
|
while true
|
@@ -260,19 +271,24 @@ class RinRuby
|
|
260
271
|
cmd = gets.strip
|
261
272
|
end
|
262
273
|
cmds << cmd
|
263
|
-
|
264
|
-
|
274
|
+
begin
|
275
|
+
if complete?(cmds.join("\n"))
|
276
|
+
prompt = regular_prompt
|
277
|
+
break
|
278
|
+
else
|
279
|
+
prompt = continue_prompt
|
280
|
+
end
|
281
|
+
rescue
|
282
|
+
puts "Parse error"
|
265
283
|
prompt = regular_prompt
|
284
|
+
cmds = []
|
266
285
|
break
|
267
|
-
else
|
268
|
-
prompt = continue_prompt
|
269
286
|
end
|
270
287
|
end
|
288
|
+
next if cmds.length == 0
|
271
289
|
break if cmds.length == 1 && cmds[0] == "exit()"
|
272
|
-
break if ! eval(cmds,true)
|
290
|
+
break if ! eval(cmds.join("\n"),true)
|
273
291
|
end
|
274
|
-
eval("close(#{RinRuby_Null_File})
|
275
|
-
rm(#{RinRuby_Null_File})",false)
|
276
292
|
true
|
277
293
|
end
|
278
294
|
|
@@ -321,15 +337,13 @@ class RinRuby
|
|
321
337
|
#
|
322
338
|
#Some care is needed when using the short-hand of the assign method since the label (i.e., people in this case) must be a valid method name in Ruby. For example, R.copy.of.names = names will not work, but R.copy_of_names = names is permissible.
|
323
339
|
#
|
324
|
-
#The assign method supports Ruby variables of type Fixnum (i.e., integer), Float (i.e., double), String, and arrays of one of those three fundamental types. Data in other formats must be coerced when copying to R.
|
340
|
+
#The assign method supports Ruby variables of type Fixnum (i.e., integer), Bignum (i.e., integer), Float (i.e., double), String, and arrays of one of those three fundamental types. Note that Fixnum or Bignum values that exceed the capacity of R's integers are silently converted to doubles. Data in other formats must be coerced when copying to R.
|
325
341
|
#
|
326
342
|
#<b>Parameters that can be passed to the assign method:</b>
|
327
343
|
#
|
328
344
|
#* name: The name of the variable desired in R.
|
329
345
|
#
|
330
|
-
#* value: The value the R variable should have. The assign method supports Ruby variables of type Fixnum (i.e., integer), Float (i.e., double), String, and arrays of one of those three fundamental types. Data in other formats must be coerced when copying to R.
|
331
|
-
#
|
332
|
-
#* as_integer: Numerical variables in R are stored as doubles by default, but R provides the ability to have vectors of integers. A vector of integers may be required when interfacing between programs such as R, Fortran, C, or C++. Setting as_integer to true will specify that the value should be type cast into integers. The default is false.
|
346
|
+
#* value: The value the R variable should have. The assign method supports Ruby variables of type Fixnum (i.e., integer), Bignum (i.e., integer), Float (i.e., double), String, and arrays of one of those three fundamental types. Note that Fixnum or Bignum values that exceed the capacity of R's integers are silently converted to doubles. Data in other formats must be coerced when copying to R.
|
333
347
|
#
|
334
348
|
#The assign method is an alternative to the simplified method, with some additional flexibility. When using the simplified method, the parameters of name and value are automatically used, in other words:
|
335
349
|
#
|
@@ -340,64 +354,15 @@ class RinRuby
|
|
340
354
|
# >> R.assign("test",144)
|
341
355
|
#
|
342
356
|
#Of course it would be confusing to use the shorthand notation to assign a variable named eval, echo, or any other already defined function. RinRuby would assume you were calling the function, rather than trying to assign a variable.
|
357
|
+
#
|
358
|
+
#Ruby supports arrays containing elements with different types, but in R all the elements of a vector must be of the same type. When assigning an array containing differing types of variables, RinRuby will follow R’s conversion conventions. An array that contains any Strings will result in a character vector in R. If the array does not contain any Strings, but it does contain a Float or a large integer (in absolute value), then the result will be a numeric vector of Doubles in R. If there are only integers that are suffciently small (in absolute value), then the result will be a numeric vector of integers in R.
|
343
359
|
|
344
|
-
def assign(name, value
|
345
|
-
if
|
346
|
-
|
347
|
-
length = 1
|
348
|
-
elsif value.kind_of?(Fixnum) || value.kind_of?(Float)
|
349
|
-
if as_integer
|
350
|
-
value = [ value.to_i ]
|
351
|
-
type = RinRuby_Type_Integer
|
352
|
-
else
|
353
|
-
value = [ value.to_f ]
|
354
|
-
type = RinRuby_Type_Double
|
355
|
-
end
|
356
|
-
length = 1
|
357
|
-
elsif value.kind_of?(Array)
|
358
|
-
begin
|
359
|
-
if value[0].kind_of?(String)
|
360
|
-
eval "#{name} <- character(#{value.length})"
|
361
|
-
for index in 0...value.length
|
362
|
-
assign("#{name}[#{index}+1]",value[index])
|
363
|
-
end
|
364
|
-
return
|
365
|
-
else
|
366
|
-
if as_integer
|
367
|
-
type = RinRuby_Type_Integer
|
368
|
-
value = value.collect { |x| x.to_i }
|
369
|
-
else
|
370
|
-
type = RinRuby_Type_Double
|
371
|
-
value = value.collect { |x| x.to_f }
|
372
|
-
end
|
373
|
-
end
|
374
|
-
rescue
|
375
|
-
raise "Unsupported data type on Ruby's end"
|
376
|
-
end
|
377
|
-
length = value.length
|
378
|
-
else
|
379
|
-
raise "Unsupported data type on Ruby's end"
|
380
|
-
end
|
381
|
-
@writer.puts <<-EOF
|
382
|
-
#{RinRuby_Type_Variable} <- readBin(#{RinRuby_Socket},integer(),1,endian="big")
|
383
|
-
#{RinRuby_Length_Variable} <- readBin(#{RinRuby_Socket},integer(),1,endian="big")
|
384
|
-
if ( #{RinRuby_Type_Variable} == #{RinRuby_Type_Double} ) {
|
385
|
-
#{name} <- readBin(#{RinRuby_Socket},numeric(),#{RinRuby_Length_Variable},endian="big")
|
386
|
-
} else if ( #{RinRuby_Type_Variable} == #{RinRuby_Type_Integer} ) {
|
387
|
-
#{name} <- readBin(#{RinRuby_Socket},integer(),#{RinRuby_Length_Variable},endian="big")
|
388
|
-
} else if ( #{RinRuby_Type_Variable} == #{RinRuby_Type_String} ) {
|
389
|
-
#{name} <- readBin(#{RinRuby_Socket},character(),1,endian="big")
|
390
|
-
} else { }
|
391
|
-
rm(#{RinRuby_Type_Variable},#{RinRuby_Length_Variable})
|
392
|
-
EOF
|
393
|
-
@socket.write([type,length].pack('NN'))
|
394
|
-
if ( type == RinRuby_Type_String )
|
395
|
-
@socket.write(value)
|
396
|
-
@socket.write([0].pack('C')) # zero-terminated strings
|
360
|
+
def assign(name, value)
|
361
|
+
if assignable?(name)
|
362
|
+
assign_engine(name,value)
|
397
363
|
else
|
398
|
-
|
364
|
+
raise "Parse error"
|
399
365
|
end
|
400
|
-
nil
|
401
366
|
end
|
402
367
|
|
403
368
|
#Data is copied from R to Ruby using the pull method or a short-hand equivalent. The R object x defined with an eval method can be copied to Ruby object copy_of_x as follows:
|
@@ -437,11 +402,11 @@ class RinRuby
|
|
437
402
|
# -0.06069
|
438
403
|
# 0.7816
|
439
404
|
#
|
440
|
-
#Notice the use above of the as.numeric function in R. This is necessary since the pull method only supports R vectors
|
405
|
+
#Notice the use above of the as.numeric function in R. This is necessary since the pull method only supports R vectors which are numeric (i.e., integers or doubles) and character (i.e., strings). Data in other formats must be coerced when copying to Ruby.
|
441
406
|
#
|
442
407
|
#<b>Parameters that can be passed to the pull method:</b>
|
443
408
|
#
|
444
|
-
#* string: The name of the variable that should be pulled from R. The pull method only supports R vectors
|
409
|
+
#* string: The name of the variable that should be pulled from R. The pull method only supports R vectors which are numeric (i.e., integers or doubles) or character (i.e., strings). The R value of NA is pulled as nil into Ruby. Data in other formats must be coerced when copying to Ruby.
|
445
410
|
#
|
446
411
|
#* singletons: R represents a single number as a vector of length one, but in Ruby it is often more convenient to use a number rather than an array of length one. Setting singleton=false will cause the pull method to shed the array, while singletons=true will return the number of string within an array. The default is false.
|
447
412
|
#
|
@@ -454,6 +419,146 @@ class RinRuby
|
|
454
419
|
# >> puts R.pull("test")
|
455
420
|
|
456
421
|
def pull(string, singletons=false)
|
422
|
+
if complete?(string)
|
423
|
+
result = pull_engine(string)
|
424
|
+
if ( ! singletons ) && ( result.length == 1 ) && ( result.class != String )
|
425
|
+
result = result[0]
|
426
|
+
end
|
427
|
+
result
|
428
|
+
else
|
429
|
+
raise "Parse error"
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
#The echo method controls whether the eval method displays output from R and, if echo is enabled, whether messages, warnings, and errors from stderr are also displayed.
|
434
|
+
#
|
435
|
+
#<b>Parameters that can be passed to the eval method</b>
|
436
|
+
#
|
437
|
+
#* enable: Setting enable to false will turn all output off until the echo command is used again with enable equal to true. The default is nil, which will return the current setting.
|
438
|
+
#
|
439
|
+
#* stderr: Setting stderr to true will force messages, warnings, and errors from R to be routed through stdout. Using stderr redirection is typically not needed for the C implementation of Ruby and is thus not not enabled by default for this implementation. It is typically necessary for jRuby and is enabled by default in this case. This redirection works well in practice but it can lead to interleaving output which may confuse RinRuby. In such cases, stderr redirection should not be used. Echoing must be enabled when using stderr redirection.
|
440
|
+
|
441
|
+
def echo(enable=nil,stderr=nil)
|
442
|
+
if ( enable == false ) && ( stderr == true )
|
443
|
+
raise "You can only redirect stderr if you are echoing is enabled."
|
444
|
+
end
|
445
|
+
if ( enable != nil ) && ( enable != @echo_enabled )
|
446
|
+
echo(nil,false) if ! enable
|
447
|
+
@echo_enabled = ! @echo_enabled
|
448
|
+
end
|
449
|
+
if @echo_enabled && ( stderr != nil ) && ( stderr != @echo_stderr )
|
450
|
+
@echo_stderr = ! @echo_stderr
|
451
|
+
if @echo_stderr
|
452
|
+
eval "sink(stdout(),type='message')"
|
453
|
+
else
|
454
|
+
eval "sink(type='message')"
|
455
|
+
end
|
456
|
+
end
|
457
|
+
[ @echo_enabled, @echo_stderr ]
|
458
|
+
end
|
459
|
+
|
460
|
+
private
|
461
|
+
|
462
|
+
#:stopdoc:
|
463
|
+
RinRuby_Type_NotFound = -2
|
464
|
+
RinRuby_Type_Unknown = -1
|
465
|
+
RinRuby_Type_Double = 0
|
466
|
+
RinRuby_Type_Integer = 1
|
467
|
+
RinRuby_Type_String = 2
|
468
|
+
RinRuby_Type_String_Array = 3
|
469
|
+
RinRuby_KeepTrying_Variable = ".RINRUBY.KEEPTRYING.VARIABLE"
|
470
|
+
RinRuby_Length_Variable = ".RINRUBY.PULL.LENGTH.VARIABLE"
|
471
|
+
RinRuby_Type_Variable = ".RINRUBY.PULL.TYPE.VARIABLE"
|
472
|
+
RinRuby_Socket = ".RINRUBY.PULL.SOCKET"
|
473
|
+
RinRuby_Variable = ".RINRUBY.PULL.VARIABLE"
|
474
|
+
RinRuby_Parse_String = ".RINRUBY.PARSE.STRING"
|
475
|
+
RinRuby_Eval_Flag = "RINRUBY.EVAL.FLAG"
|
476
|
+
RinRuby_Stderr_Flag = "RINRUBY.STDERR.FLAG"
|
477
|
+
RinRuby_Exit_Flag = "RINRUBY.EXIT.FLAG"
|
478
|
+
RinRuby_Max_Unsigned_Integer = 2**32
|
479
|
+
RinRuby_Half_Max_Unsigned_Integer = 2**31
|
480
|
+
RinRuby_NA_R_Integer = 2**31
|
481
|
+
RinRuby_Max_R_Integer = 2**31-1
|
482
|
+
RinRuby_Min_R_Integer = -2**31+1
|
483
|
+
#:startdoc:
|
484
|
+
|
485
|
+
def to_signed_int(y)
|
486
|
+
if y.kind_of?(Integer)
|
487
|
+
( y > RinRuby_Half_Max_Unsigned_Integer ) ? -(RinRuby_Max_Unsigned_Integer-y) : ( y == RinRuby_NA_R_Integer ? nil : y )
|
488
|
+
else
|
489
|
+
y.collect { |x| ( x > RinRuby_Half_Max_Unsigned_Integer ) ? -(RinRuby_Max_Unsigned_Integer-x) : ( x == RinRuby_NA_R_Integer ? nil : x ) }
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
def assign_engine(name, value)
|
494
|
+
original_value = value
|
495
|
+
if value.kind_of?(String)
|
496
|
+
type = RinRuby_Type_String
|
497
|
+
length = 1
|
498
|
+
elsif value.kind_of?(Integer)
|
499
|
+
if ( value >= RinRuby_Min_R_Integer ) && ( value <= RinRuby_Max_R_Integer )
|
500
|
+
value = [ value.to_i ]
|
501
|
+
type = RinRuby_Type_Integer
|
502
|
+
else
|
503
|
+
value = [ value.to_f ]
|
504
|
+
type = RinRuby_Type_Double
|
505
|
+
end
|
506
|
+
length = 1
|
507
|
+
elsif value.kind_of?(Float)
|
508
|
+
value = [ value.to_f ]
|
509
|
+
type = RinRuby_Type_Double
|
510
|
+
length = 1
|
511
|
+
elsif value.kind_of?(Array)
|
512
|
+
begin
|
513
|
+
if value.any? { |x| x.kind_of?(String) }
|
514
|
+
eval "#{name} <- character(#{value.length})"
|
515
|
+
for index in 0...value.length
|
516
|
+
assign_engine("#{name}[#{index}+1]",value[index])
|
517
|
+
end
|
518
|
+
return original_value
|
519
|
+
elsif value.any? { |x| x.kind_of?(Float) }
|
520
|
+
type = RinRuby_Type_Double
|
521
|
+
value = value.collect { |x| x.to_f }
|
522
|
+
elsif value.all? { |x| x.kind_of?(Integer) }
|
523
|
+
if value.all? { |x| ( x >= RinRuby_Min_R_Integer ) && ( x <= RinRuby_Max_R_Integer ) }
|
524
|
+
type = RinRuby_Type_Integer
|
525
|
+
else
|
526
|
+
value = value.collect { |x| x.to_f }
|
527
|
+
type = RinRuby_Type_Double
|
528
|
+
end
|
529
|
+
else
|
530
|
+
raise "Unsupported data type on Ruby's end"
|
531
|
+
end
|
532
|
+
rescue
|
533
|
+
raise "Unsupported data type on Ruby's end"
|
534
|
+
end
|
535
|
+
length = value.length
|
536
|
+
else
|
537
|
+
raise "Unsupported data type on Ruby's end"
|
538
|
+
end
|
539
|
+
@writer.puts <<-EOF
|
540
|
+
#{RinRuby_Type_Variable} <- readBin(#{RinRuby_Socket},integer(),1,endian="big")
|
541
|
+
#{RinRuby_Length_Variable} <- readBin(#{RinRuby_Socket},integer(),1,endian="big")
|
542
|
+
if ( #{RinRuby_Type_Variable} == #{RinRuby_Type_Double} ) {
|
543
|
+
#{name} <- readBin(#{RinRuby_Socket},numeric(),#{RinRuby_Length_Variable},endian="big")
|
544
|
+
} else if ( #{RinRuby_Type_Variable} == #{RinRuby_Type_Integer} ) {
|
545
|
+
#{name} <- readBin(#{RinRuby_Socket},integer(),#{RinRuby_Length_Variable},endian="big")
|
546
|
+
} else if ( #{RinRuby_Type_Variable} == #{RinRuby_Type_String} ) {
|
547
|
+
#{name} <- readBin(#{RinRuby_Socket},character(),1,endian="big")
|
548
|
+
} else { }
|
549
|
+
rm(#{RinRuby_Type_Variable},#{RinRuby_Length_Variable})
|
550
|
+
EOF
|
551
|
+
@socket.write([type,length].pack('NN'))
|
552
|
+
if ( type == RinRuby_Type_String )
|
553
|
+
@socket.write(value)
|
554
|
+
@socket.write([0].pack('C')) # zero-terminated strings
|
555
|
+
else
|
556
|
+
@socket.write(value.pack( ( type==RinRuby_Type_Double ? 'G' : 'N' )*length ))
|
557
|
+
end
|
558
|
+
original_value
|
559
|
+
end
|
560
|
+
|
561
|
+
def pull_engine(string)
|
457
562
|
@writer.puts <<-EOF
|
458
563
|
#{RinRuby_Variable} <- try(#{string})
|
459
564
|
if ( inherits(#{RinRuby_Variable},"try-error") ) {
|
@@ -510,64 +615,26 @@ class RinRuby
|
|
510
615
|
else
|
511
616
|
raise "Unsupported data type on Ruby's end"
|
512
617
|
end
|
513
|
-
if ( ! singletons ) && ( result.length == 1 ) && ( result.class != String )
|
514
|
-
result = result[0]
|
515
|
-
end
|
516
618
|
result
|
517
619
|
end
|
518
620
|
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
@echo_enabled
|
528
|
-
end
|
529
|
-
|
530
|
-
private
|
531
|
-
|
532
|
-
#:stopdoc:
|
533
|
-
RinRuby_Type_NotFound = -2
|
534
|
-
RinRuby_Type_Unknown = -1
|
535
|
-
RinRuby_Type_Double = 0
|
536
|
-
RinRuby_Type_Integer = 1
|
537
|
-
RinRuby_Type_String = 2
|
538
|
-
RinRuby_Type_String_Array = 3
|
539
|
-
RinRuby_Length_Variable = "RINRUBY.PULL.LENGTH.VARIABLE"
|
540
|
-
RinRuby_Type_Variable = "RINRUBY.PULL.TYPE.VARIABLE"
|
541
|
-
RinRuby_Socket = "RINRUBY.PULL.SOCKET"
|
542
|
-
RinRuby_Variable = "RINRUBY.PULL.VARIABLE"
|
543
|
-
RinRuby_Eval_Flag = "RINRUBY.EVAL.FLAG"
|
544
|
-
RinRuby_Exit_Flag = "RINRUBY.EXIT.FLAG"
|
545
|
-
RinRuby_Max_Unsigned_Integer = 2**32
|
546
|
-
RinRuby_Half_Max_Unsigned_Integer = 2**31
|
547
|
-
RinRuby_Parse_Variable = "RINRUBY.PARSE.VARIABLE"
|
548
|
-
RinRuby_Commands_Vector = "RINRUBY.COMMANDS.VECTOR"
|
549
|
-
RinRuby_Null_File = "RINRUBY.NULL.FILE"
|
550
|
-
#:startdoc:
|
551
|
-
|
552
|
-
def to_signed_int(y)
|
553
|
-
if y.kind_of?(Integer)
|
554
|
-
( y > RinRuby_Half_Max_Unsigned_Integer ) ? -(RinRuby_Max_Unsigned_Integer-y) : y
|
555
|
-
else
|
556
|
-
y.collect { |x| ( x > RinRuby_Half_Max_Unsigned_Integer ) ? -(RinRuby_Max_Unsigned_Integer-x) : x }
|
557
|
-
end
|
621
|
+
def complete?(string)
|
622
|
+
assign_engine(RinRuby_Parse_String,string)
|
623
|
+
result = pull_engine("unlist(lapply(c('.*','^Error in parse.*','^Error in parse.*unexpected end of input.*'),
|
624
|
+
grep,try({parse(text=#{RinRuby_Parse_String}); 1}, silent=TRUE)))")
|
625
|
+
@writer.puts "rm(#{RinRuby_Parse_String})"
|
626
|
+
return true if result.length == 1
|
627
|
+
return false if result.length == 3
|
628
|
+
raise "Parse error"
|
558
629
|
end
|
559
630
|
|
560
|
-
def
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
okay = pull("ifelse(exists('#{RinRuby_Parse_Variable}'),1,0)") == 1.0
|
568
|
-
eval("rm(#{RinRuby_Parse_Variable})",false) if okay
|
569
|
-
eval("rm(#{RinRuby_Commands_Vector})",false)
|
570
|
-
return okay
|
631
|
+
def assignable?(string)
|
632
|
+
raise "Parse error" if ! complete?(string)
|
633
|
+
assign_engine(RinRuby_Parse_String,string)
|
634
|
+
result = pull_engine("as.integer(ifelse(inherits(try({eval(parse(text=paste(#{RinRuby_Parse_String},'<- 1')))}, silent=TRUE),'try-error'),1,0))")
|
635
|
+
@writer.puts "rm(#{RinRuby_Parse_String})"
|
636
|
+
return true if result == [0]
|
637
|
+
raise "Parse error"
|
571
638
|
end
|
572
639
|
|
573
640
|
def find_R_on_windows(cygwin)
|
@@ -582,7 +649,7 @@ class RinRuby
|
|
582
649
|
end
|
583
650
|
break if path != '?'
|
584
651
|
end
|
585
|
-
raise
|
652
|
+
raise "Cannot locate R executable" if path == '?'
|
586
653
|
if cygwin
|
587
654
|
path = `cygpath '#{path}'`
|
588
655
|
while path.chomp!
|
metadata
CHANGED
@@ -1,50 +1,54 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
2
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
3
3
|
requirements:
|
4
4
|
- - '>='
|
5
|
-
- !ruby/object:Gem::Version
|
6
|
-
version:
|
5
|
+
- !ruby/object:Gem::Version
|
6
|
+
version: "0"
|
7
7
|
version:
|
8
8
|
email: rinruby@ddahl.org
|
9
9
|
cert_chain: []
|
10
|
+
|
10
11
|
summary: Accessing the R interpreter from pure Ruby
|
11
12
|
post_install_message:
|
12
13
|
extra_rdoc_files: []
|
14
|
+
|
13
15
|
homepage: http://rinruby.ddahl.org
|
14
16
|
signing_key:
|
15
17
|
name: rinruby
|
16
18
|
rdoc_options: []
|
19
|
+
|
17
20
|
autorequire:
|
18
21
|
rubyforge_project: rinruby
|
19
22
|
executables: []
|
23
|
+
|
20
24
|
description:
|
21
25
|
specification_version: 2
|
22
26
|
default_executable:
|
23
27
|
files:
|
24
|
-
- lib/rinruby
|
25
28
|
- lib/rinruby.rb
|
26
|
-
- lib/rinruby/gif.rb
|
27
|
-
- lib/rinruby/animation.rb
|
28
29
|
- README.txt
|
29
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
30
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
30
31
|
requirements:
|
31
32
|
- - '>='
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
34
35
|
version:
|
35
36
|
extensions: []
|
37
|
+
|
36
38
|
rubygems_version: 1.2.0
|
37
39
|
requirements:
|
38
40
|
- R (http://www.r-project.org), an environment for statistical computing and graphics
|
39
41
|
authors:
|
40
42
|
- David B. Dahl
|
41
|
-
date: 2008-
|
43
|
+
date: 2008-11-17 06:00:00 +00:00
|
42
44
|
platform: ruby
|
43
45
|
test_files: []
|
44
|
-
|
45
|
-
|
46
|
+
|
47
|
+
version: !ruby/object:Gem::Version
|
48
|
+
version: 1.1.0
|
46
49
|
require_paths:
|
47
50
|
- lib
|
48
51
|
dependencies: []
|
52
|
+
|
49
53
|
bindir: bin
|
50
54
|
has_rdoc: true
|
data/lib/rinruby/animation.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
require 'rinruby'
|
2
|
-
|
3
|
-
class Animation < RinRuby
|
4
|
-
|
5
|
-
Graphics_Drivers = ['pdf','swf']
|
6
|
-
|
7
|
-
def loop(n)
|
8
|
-
(1..n).each do |i|
|
9
|
-
print "#{100*i/n}% "
|
10
|
-
eval yield(i)
|
11
|
-
end
|
12
|
-
puts
|
13
|
-
end
|
14
|
-
|
15
|
-
def graphics(filename=nil)
|
16
|
-
if ! filename
|
17
|
-
eval "dev.off()",false
|
18
|
-
if @graphics_driver == 'swf'
|
19
|
-
system <<-EOF
|
20
|
-
pdf2swf -bl -o #{@filename}.swf #{@filename}.pdf > /dev/null 2>&1
|
21
|
-
swfdump --html #{@filename}.swf > #{@filename}.html
|
22
|
-
rm #{@filename}.pdf
|
23
|
-
EOF
|
24
|
-
end
|
25
|
-
else
|
26
|
-
graphics if pull("length(dev.list())") > 0
|
27
|
-
begin
|
28
|
-
@graphics_driver = filename[(filename.rindex('.')+1)..-1].downcase
|
29
|
-
rescue
|
30
|
-
raise "File extension must be in [ #{Graphics_Drivers.join(', ')} ]"
|
31
|
-
end
|
32
|
-
if ! Graphics_Drivers.include?(@graphics_driver)
|
33
|
-
raise "File extension must be in [ #{Graphics_Drivers.join(', ')} ]"
|
34
|
-
end
|
35
|
-
@filename = filename[0...filename.rindex('.')]
|
36
|
-
if @graphics_driver == 'pdf'
|
37
|
-
eval "pdf(paste('#{@filename}.pdf'))"
|
38
|
-
elsif @graphics_driver == 'swf'
|
39
|
-
eval "pdf(paste('#{@filename}.pdf'))"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def quit
|
45
|
-
graphics if pull("length(dev.list())") > 0
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
if $0 == __FILE__
|
51
|
-
|
52
|
-
A = Animation.new
|
53
|
-
|
54
|
-
A.graphics "example1.swf"
|
55
|
-
n = 40
|
56
|
-
A.eval "x <- rnorm(#{n})"
|
57
|
-
A.loop(n) do |i|
|
58
|
-
"plot(x[1:#{i}],xlim=c(1,#{n}),ylim=max(abs(x))*c(-1,1),type='l',ylab='')"
|
59
|
-
end
|
60
|
-
|
61
|
-
A.graphics "example2.pdf"
|
62
|
-
A.eval <<-EOF
|
63
|
-
plot(1,type="n",axes=FALSE,xlab="",ylab="")
|
64
|
-
text(1,1,"Thanks!")
|
65
|
-
EOF
|
66
|
-
A.eval <<-EOF
|
67
|
-
plot(1,type="n",axes=FALSE,xlab="",ylab="")
|
68
|
-
text(1,1,"Thanks a lot!")
|
69
|
-
EOF
|
70
|
-
|
71
|
-
A.quit
|
72
|
-
|
73
|
-
end
|
74
|
-
|
data/lib/rinruby/gif.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
require 'rinruby'
|
2
|
-
|
3
|
-
class GIF
|
4
|
-
|
5
|
-
def initialize(r_interpreter,filename)
|
6
|
-
@r = r_interpreter
|
7
|
-
@filename = filename
|
8
|
-
@frame_counter = 0
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.draw(r_interpreter,filename,delay)
|
12
|
-
echo = r_interpreter.echo
|
13
|
-
r_interpreter.echo false
|
14
|
-
animated = self.new(r_interpreter,filename)
|
15
|
-
yield(animated)
|
16
|
-
animated.close(delay)
|
17
|
-
r_interpreter.echo echo
|
18
|
-
end
|
19
|
-
|
20
|
-
def add(statement)
|
21
|
-
@frame_counter += 1
|
22
|
-
filename = name(@frame_counter)
|
23
|
-
@r.eval <<-EOF
|
24
|
-
png(paste('#{filename}.png'))
|
25
|
-
#{statement}
|
26
|
-
dev.off()
|
27
|
-
EOF
|
28
|
-
system "convert #{filename}.png #{filename}.gif"
|
29
|
-
File.delete "#{filename}.png"
|
30
|
-
end
|
31
|
-
|
32
|
-
def loop(n)
|
33
|
-
(1..n).each do |i|
|
34
|
-
puts "#{100*i/n}%"
|
35
|
-
add(yield(i))
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def close(delay)
|
40
|
-
system "gifsicle -O2 --delay #{delay} #{@filename}-*.gif > #{@filename}.gif"
|
41
|
-
if @frame_counter > 0
|
42
|
-
(1..@frame_counter).each { |i| File.delete("#{name(i)}.gif") }
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def name(index)
|
49
|
-
sprintf("%s-%016x",@filename,index)
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
if $0 == __FILE__
|
55
|
-
|
56
|
-
n = 40
|
57
|
-
R.eval "x <- rnorm(#{n})"
|
58
|
-
|
59
|
-
GIF.draw(R,'time-series',5) do |a|
|
60
|
-
a.loop(n) do |i|
|
61
|
-
"plot(x[1:#{i}],xlim=c(1,#{n}),ylim=max(abs(x))*c(-1,1),type='l',ylab='')"
|
62
|
-
end
|
63
|
-
a.add <<-EOF
|
64
|
-
plot(1,type="n",axes=FALSE,xlab="",ylab="")
|
65
|
-
text(1,1,"Thanks!")
|
66
|
-
EOF
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
|