Baby steps with RSRuby in Rails

Plotting and charting libraries for Ruby (on Rails) abound. However, few are sophisticated enough for scientists and many are not actively maintained. Plotting in R, on the other hand, is about as sophisticated as it comes.

Can we bridge Ruby and R? Yes we can, thanks to Alex Gutteridge’s RSRuby. The next logical question: how to plot data using RSRuby in your shiny new Rails application?
Update Jul 22: recently, the code in this post has stopped working for me (unwanted X windows pop up, stack smashing errors) – so use at your discretion or not at all!

First, thanks to Peter Lane over at Ruby for Scientific Research, a relatively-new blog with some excellent introductory RSRuby articles. Thanks also to Alex for providing RSRuby.

  1. Install RSRuby
  2. My OS is Ubuntu 9.04 with rubygems 1.3.3, installed from source and Rails 2.3.2. This works for me:

    sudo gem install rsruby -- --with-R-home=/usr/lib/R --with-R-include=/usr/share/R/include
    
  3. Make the RSRuby gem available to Rails
  4. I edited config/environment.rb to include the gem:

    Rails::Initializer.run do |config|
      config.gem 'rsruby'
      # other config options here...
    end
    
  5. Create your Rails application
  6. Let’s call it Plotter. Usual procedure:

    rails Plotter
    cd Plotter
    rake db:create
    

    We won’t use the database (default sqlite3) in this example, but you may use it later on.

  7. Tell the application about R_HOME
  8. RSRuby will fail unless the environment variable R_HOME is set. To tell Plotter that R_HOME is /usr/lib/R, I put this in the file app/helpers/application_helper.rb:

    module ApplicationHelper
    # set R_HOME if not set
      if ENV['R_HOME'].nil?
        ENV['R_HOME'] = "/usr/lib/R"
      end
    end
    
  9. Make an instance of RSRuby available to all controllers
  10. RSRuby works by providing an instance of an “R object”, on which you call R functions as methods. I provided the object by editing app/controllers/application_controller.rb to include a method named InitR:

    class ApplicationController < ActionController::Base
    # Make R instance available to all
      def InitR
        @r = RSRuby.instance
        return @r
      end
    end
    
  11. Create a sample controller and view
  12. For testing purposes, I created a controller named Home with a single view named index:

    ./script/generate controller Home index
    rm public/index.html
    

    I edited config/routes.rb so as the root of the application is the index view for the home controller:

    ActionController::Routing::Routes.draw do |map|
      map.root      :controller => "home"  #default view is app/views/home/index.html.erb
      map.connect  ':controller/:action/:id'
      map.connect  ':controller/:action/:id.:format'
    end
    

    Now we can get to work on the file app/controllers/home_controller.rb. I wrote a sample method, show_image to plot a histogram using R via RSRuby:

      def show_image
      # next 6 lines use R to plot a histogram
        @r = InitR()
        @d = @r.rnorm(1000)
        @l = @r.range(-4,4,@d)
        @r.png "/tmp/plot.png"
        @r.par(:bg => "cornsilk")
        @r.hist(@d, :range => @l, :col => "lavender", :main => "My Plot")
        @r.eval_R("dev.off()")  #required for png output
        # then read the png file and deliver it to the browser
        @g = File.open("/tmp/plot.png", "rb") {|@f| @f.read}
        send_data @g, :type=>"image/png", :disposition=>'inline'
      end
    

    Not the prettiest code, but you get the idea. Obviously the owner of the HTTP daemon (www-data on Ubuntu) must have write permission to the location of the PNG file; /tmp in this case.

  13. Create the view
  14. All that remains is to edit app/views/home/index.html.erb so as it displays the image:

    <h1>Home#index</h1>
    <p>Find me in app/views/home/index.html.erb</p>
    <%= image_tag(url_for(:controller => "home", :action => "show_image")) %>
    

    Which just says “wrap whatever comes out of the method show_image in an IMG SRC tag”.

RSRuby on Rails histogram plot

RSRuby on Rails histogram plot


Fire up your application using “./script/server”, point your browser at http://localhost:3000 and you should see something like the image to the right.

One drawback of this approach is the need to write a file to disk and so have to worry about naming, cleaning up and so on. I have not found a way to make R output plots to stdout or as a base64 string. If you think this is possible, leave a comment.

OK – so now you can get serious, write some generic methods, design a database schema to store data for plotting and so on.

12 thoughts on “Baby steps with RSRuby in Rails

  1. Claudio Harringer

    cool article, but it wasn’t that easy. last error was the missing extra ” –” as in:

    sudo gem install rsruby — –with-R-dir=/usr/lib/R/ –with-R-include=/usr/share/R/include/

    and then i’ve also set R_HOME=/usr/lib/R and linked libR like this:

    sudo ln -s /usr/lib/R/lib/libR.so /usr/local/lib

    i can’t tell which of those actions really necessary. at least installation worked now, on Ubuntu 9.04 with gems 1.3.3 and current R and ruby-dev(!) installed

  2. nsaunders Post author

    Glad it worked for you Claudio – yes, I skipped over the RSRuby installation. I had those same minor issues myself, but could not remember what they were!

  3. Claudio Harringer

    jep, won’t work without:
    export R_HOME=/usr/lib/R
    (consider putting that in your .profile or .bashrc or even /etc/environment(?))

    for rails you need that in your environment:
    config.gem ‘rsruby’

    and don’t copy’n’paste my previous posting, cause it turned the double-dashes to some special long non-ascii dashes like that —

    my experiences running that rails action: works well a few times. displays a background full of number (?!). however, after reloading it sometimes (4-10 times) it screws up and crashes the server. last lines of the log are like that:

    http://nopaste.claudio.endofinternet.net/c/rah6xU

  4. Dhairya

    As Clausio pointed out, there is a missing extra ‘-’ after rsruby in

    sudo gem install rsruby –with-R-home=/usr/lib/R –with-R-include=/usr/share/R/include

    Without which it gives the following error

    ERROR: While executing gem … (OptionParser::InvalidOption)
    invalid option: –with-R-home=/usr/lib/R

  5. nsaunders Post author

    Thank you all, I’ve updated the install section. Installation is the boring part, you know? I forget all about it once it’s done.

  6. Bogdan

    I think I have somehow missed a transition – so “why Ruby”? Is that a well thought-through “platform change”, or just “trying something different”?

    Personally, after Python I stopped looking for any other quick-scripting alternative – it has innate eye-candy (unlike Perl), it is fast(er than PHP), is simple, and allows relatively easy py2c translation if needed.

    I’m not starting a “holy war” here, just want to know your stimuli to use ruby.

  7. Dhairya

    How does one access R packages through RSRuby, I wanted to access the kripp.alpha function in the Concord Package, I have installed the package as well as set Concord to load as a default package, I have tested the function through R and it runs just fine.

    However under RSRuby it says ‘variable kripp.alpha not found’ I am using -

    R=RSRuby.instance
    R.kripp_alpha( parameters )

    Could you please point out any mistake or suggest ?

  8. nsaunders Post author

    Dhairya, sent you an email. I think you need:

    R.library(“Concord”)

    to access Concord functions.

    Bogdan: Ruby is fun!

    1. trtg

      any idea what might be causing the issue that claudio mentioned with the background being set to a bunch of numbers instead of solid white? I see it if I use rsruby through irb or through rails, but not if I just use R by itself. Memory corruption in rsruby?

      1. nsaunders Post author

        No idea about that, I’m afraid. The only time I saw a background of numbers was when I did not set a plot title using “main”.

  9. Pingback: RSRuby and Rails revisited « What You’re Doing Is Rather Desperate

Comments are closed.