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 CHANGED
@@ -59,7 +59,7 @@
59
59
 
60
60
  class RinRuby
61
61
 
62
- VERSION = '1.0.1'
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 OSX, 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).
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
- keep.trying <- TRUE
141
- while ( keep.trying ) {
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
- keep.trying <- FALSE
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
- begin
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
- rescue
202
- return false
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
- break if line == "[1] \"#{RinRuby_Eval_Flag}\""
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
- parse_okay = complete?(cmds)
264
- if parse_okay
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, as_integer=false)
345
- if value.kind_of?(String)
346
- type = RinRuby_Type_String
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
- @socket.write(value.pack( ( type==RinRuby_Type_Double ? 'G' : 'N' )*length ))
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 of numerics (i.e., integers or doubles) and characters (i.e., strings). Data in other formats must be coerced when copying to Ruby.
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 of numerics (i.e., integers or doubles) and characters (i.e., strings). Data in other formats must be coerced when copying to Ruby.
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
- #The echo method controls whether the eval method displays output from R.
520
- #
521
- #<b>Parameters that can be passed to the eval method</b>
522
- #
523
- #* enable: Setting enable to false will turn all output off until the echo command is used again with enable equal to true. Warnings are still sent to the output. The default is nil, which will return the current setting.
524
-
525
- def echo(enable=nil)
526
- @echo_enabled = enable if enable != nil
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 complete?(string)
561
- string = [ string ] if ! string.kind_of?(Array)
562
- eval("#{RinRuby_Commands_Vector} <- character(#{string.length})",false)
563
- string.each_index { |i| assign("#{RinRuby_Commands_Vector}[#{i}+1]",string[i]) }
564
- eval("sink(#{RinRuby_Null_File},type='message')
565
- #{RinRuby_Parse_Variable} <- parse(text=#{RinRuby_Commands_Vector})
566
- sink()",false)
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 'Cannot locate R executable' if path == '?'
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: !str 0
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: !str 0
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-08-20 05:00:00 +00:00
43
+ date: 2008-11-17 06:00:00 +00:00
42
44
  platform: ruby
43
45
  test_files: []
44
- version: !ruby/object:Gem::Version
45
- version: 1.0.1
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
@@ -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
-