Skip to Content

SAP on Rails, and not on the skids

Ruby on Rails is an exciting new (in an old sense) way of developing Web Services.  It neatly combines convention with Best Practice, to help reduce the drag on development with the removal of declaritive cruft, allowing you to concentrate on the specifics of your application.  What I want to achieve with this article, is to give you a taste for what Rails can do, and show how access to SAP business related functions can be easily integrated.


Rails takes full advantage of the dynamic scripting language Ruby, which allows you to make most changes to your code without server restart, or the requirement of deployment tools.  Ruby is a worthy contender in the rapid development space of web languages such as Perl and Python, with OO concepts built into the core, from the word go.  With a natural intuitive style, and powerful text processing capabilities – it has all the things necessary for easy but scaleable Web Service development.

Rails Convention

Naming standards are used to reduce the amount of configuration required eg: tables relate specifically to Models, Ruby class naming conventions map directly to Controllers, Actions, and Views. Typically – the only configuration required in a Rails application is to specify the database connection parameters. Unlike some frameworks, which require extensive configuration in various incarnations of XML.

Rails Best Practice

Rails adheres to the industry design standard that is MVC , giving good separation of the data model, the application flow control, and the presentation layer composition.

On top of this, the Rails framework offers standard ways of handling errors, and error notification, templating and page componentisation, and the ability to swap these presentation formulas for different delivery channels.

Rails also has built in AJAX support available, to take advantage of the latest interest in DHTML, and flicker-free user experience.

Rails fundamentals

In order to cover the Rails fundamentals, I am going to digress here for a minute – it is important to get your head round this so that we can see where SAP integration may fit in, so please bare with me.

As was previously mentioned, Rails used the design principals of MVC (Model, View, Controller).  These parts of the architecture are not configured, but are bound together through naming convention.  So, if we take the classic example application of the  Cookbook then the Recipe model has a module name of recipe.rb, the associated views are held in a directory ./app/views/recipe/[list|show|…].rhtml, and the controller is RecipeController in recipe_controller.rb.


Classic Rails applications are centred around the ActiveRecord suite. This manages connections to a given supported database (MySQL, Postgresql, Oracle etc.). It also takes care of the auto-discovery of tables and schema, and the mapping of row/column information to OO access routines for creating, updating, deleting, and interrogating data points.

Extending the reference to the Cookbook application – the Recipe object will, by convention, be expecting a recipes table. Rails does this all automatically with some clever gramatical interogation tricks (Recipe becomes recipe, becomes recipes).

This allows a basic Model complete with CRUD operations, to be a simple incantation like this: (Note: the belongs_to: declares the association with another Model for categories – ActiveRecord manages this relationship on the fly)

Figure 1 – Recipe Model

class Recipe < ActiveRecord::Base
belongs_to :category


Methods of the comtroller class (RecipeController) become the actions available to the web application. These map directly to URI names. RecipeController#list maps to /recipe/list/…. (again evidence of convention over configuration).

As you can see from the sample Controller below – each method (Action) accesses the Model to invoke the appropriate business logic to obtain the relevent data. Then the appropriate flow control is exercised (do nothing means “render with the corresponding view”, where as redirect_to changes the flow to another Action).

Figure 2 – Recipe Controller

class RecipeController < ApplicationController
layout "standard-layout"
scaffold :recipe

def new
@recipe =
@categories = Category.find_all

def index
render_action "list"

def list
@category = @params['category']
@recipes = Recipe.find_all



The action name (for a controller) specifies which rhtml template within a view, to pick for the rendering of the output. This can also be further “skinned” by the specification of a layout (an html super set wrapper that the content is inserted into) by the “layout” directive given in the controller (see above).

Figure 3 -list() Action => list view => list.rhtml




<% @recipes.each do |recipe| %>
   <% if (@category == nil) || (@category ==>
        <%= link_to recipe.title,
                   :action => “show”,
                   :id => %>
        <font size=-1>

        <%= link_to “(delete)”,
                    {:action => “delete”, :id =>},
                    :confirm => “Really delete #{recipe.title}?” %>
        <%= link_to,
                    :action => “list”,
          :category => “#{}” %>
      <td><%= %></td>
   <% end %>
<% end %>

Figure 4 – layout


Online Cookbook

   *<%= @content_for_layout %>*
     <%= link_to “Create new recipe”,
                 :controller => “recipe”,
                 :action => “new” %>
   <%= link_to “Show all recipes”,
               :controller => “recipe”,
               :action => “list” %>
   <%= link_to “Show all categories”,
               :controller => “category”,
               :action => “list” %>

Note: *@content_for_layout* determines where the body of each rendered template associated with an action is inserted.
Further reading and tutorials for Rails can be found at RubyOnRails .  There are several excellent primers including:

The Cookbook part 1 and part 2

The whole reference guide is available at API .

SAP on Rails

SAP on Rails focuses on providing an alternative type of Model template to the default – ActiveRecord.

This is done via SAP::Rfc which allows RFC calls to SAP from Ruby, with automatic handling of data types, and discovery
of interface definitions for RFCs.  Some of this has been covered in a previous  article , and on SDN .<br/>
The model templating class is SAP4Rails.  It’s job is to automatically build up  connections to R/3, look up the interface definitions for the collection of associated RFCs, and to hold the attribute definitions for the given model.  This, coincidentally, corresponds to the SAP Business Objects view of the world in BAPIs.<br/>

Currencies and Exchange Rates

The example I have to work through is based on the two BAPI objects – Currency (BUS1090), and ExchangeRate (BUS1093).  These can be found using the transaction BAPI under “Basis Services / Communication Interfaces”.  I have used these objects as they exist in my NW4 test drive system, so they should be available as a lowest common denominator across the board.  The complete example is available at exrates.tgz .  The walk through below is going to centre around the “list” action, of the exrate controller.  This will show how to generate a list of exchange rates out of SAP.  For this we will need to develop the Exrate Model, the ExrateController controller, and the list.rhtml view associated.  Download the entire application to see all the other controller/action functions including listing, and showing details of Currencies, and the list, show, and create of Exchange rates.

Figure 5 – Currency and ExchangeRate objects in the BAPI explorer

!|height=40px|width=50px|src=! Click to view

Each method of a business object has an associated RFC – these are what get mapped into the Rails data model.

Figure 6 – GetCurrentRates of the ExchangeRate object maps to the RFC BAPI_EXCHRATE_GETCURRENTRATES

!|height=40px|width=50px|src=! Click to View

Start developing the Rails application

First install SAP::Rfc for Ruby.  It is critical that this module is loadable in the Ruby library path – so check this first with: ruby -r “SAP/Rfc” -e 1 – this will give an error “ruby: No such file to load — SAP/Rfc (LoadError)” if this module cannot be found.  If you have installed it in an unusal way, or location, then you can try setting the environment variable RUBYLIB to the relevent directory.

Next – start the project by building the basic framework – this is done using the Rails supplied tools – execute these commands to start it off:

rails Exrates
cd Exrates
./scripts/generate controller Exrate
./scripts/generate model exrate
./scripts/generate scaffold exrate

The file layout of the application is something like this (below). Some of the files shown we have yet to create/put in place.

Figure 7 – Application layout

./config/sap.yml <== RFC connection configuration
./lib/ - SAP::Rfc files installed here
./lib/SAP/Rfc.rb /
./lib/SAP4Rails.rb <== Data Model template SAP4Rails

Note: I installed SAP::Rfc into the ./lib directory along side SAP4Rails.rb – this is just a matter of preference.

Grab SAP4Rails.rb from here and place it in the

SAP4Rails – the Data Model

When ./scripts/generate model exrate is executed a number of files are created, including ./app/models/exrate.rb.  This contains the basic definition using ActiveRecord:

Figure 8 – Default Exrate code

class Exrate < ActiveRecord::Base

This needs to be changed to use SAP4Rails:

Figure 9 – Our Exrate code

require “SAP4Rails”
class Exrate < SAP4Rails

  1. You must define a list of RFCs to preload

  @@funcs = [

  1. You must define a list of attribute accessors to preload

  @@params = [ ‘from’,

  1. User defined instance methods here

  attr_accessor :message


  1. user defined Class methods

  class << self
    def list
      t =
      tday = sprintf(“%04d%02d%02d”, t.year.to_i, t.month.to_i,
      RAILS_DEFAULT_LOGGER.warn(“[Exrate] date: ” + tday) = tday
      return Exrate.BAPI_EXCHRATE_GETCURRENTRATES.exch_rate_list.rows()
The noticeable difference here is the two class attributes, @@funcs, and @@params.  As the comments suggest – these are how we tell SAP4Rails what RFCs need to be preloaded, and what attributes our model is going to have.  Technically speaking, this example does not need the @@params as this is used when building Rails update, and create features.
When the Exrate class is defined at run time, these values are processed and as the first method is accessed all of the RFC definitions, and attributes are loaded up.  The full definition can be found here .
However – before SAP4Rails can interact with an SAP R/3 system, we need to tell it how to connect to one.  This is done via a YAML based config file – ./config/sap.yml.

Figure 10 – RFC connection configuration

Create the file ./config/sap.yml with your connection settings, like this:

sysnr: "00"
client: "010"
user: developer
passwd: developer
lang: EN
trace: "1"

Note: these values are repeated for test and production.

SAP4Rails ensures that a separate RFC connection is created for each Model – this allows a separate SAP session context per data Model, which is useful for COMMIT related issues (transactions).

The Controller

When ./scripts/generator controller Exrate is executed, amoung other things, a file ./app/controllers/exrate_controller.rb containing ExrateController, is created.  The outline for this is:

Figure 11 – Default ExrateController code

class ExrateController < ApplicationController

We amend this with a directive for a layout to use (standard-layout), and methods index(), and list().

Figure 12 – Our ExrateController code

class ExrateController < ApplicationController
layout "standard-layout"

def index
render_action 'list'

def list
@exrates = Exrate.list()
RAILS_DEFAULT_LOGGER.warn("[LIST] exchange rates: " + @exrates.length.to_s)

These correspond directly to the Actions index and list for the Controller exrate, which will map to a URI of /exrate/index and /exrate/list.
The Action index() is just like a redirect to list() (the list() action is executed and then rendered). Within list(), we see a call upon the class method of our Model Exrate – Exrate.list(). If we refer back to the code above for class Exrate, we can see the definition for list(). It calls upon a dynamic method BAPI_EXCHRATE_GETCURRENTRATES which gives us access to an object representation of the same-named RFC.

Figure 13 – Code from Exrate#list

t =
tday = sprintf("%04d%02d%02d", t.year.to_i, t.month.to_i, = tday
return Exrate.BAPI_EXCHRATE_GETCURRENTRATES.exch_rate_list.rows()

Looking closer at the Exrate.list() call, we see the date parameter of the RFC being set to todays date, and then the call() being made, and finally exch_rate_list.rows() is returned.

Figure 14 – Inteface definition of BAPI_EXCHRATE_GETCURRENTRATES as seen in SE37

FUNCTION bapi_exchrate_getcurrentrates.

“Lokale Schnittstelle:

The interface definition for BAPI_EXCHRATE_GETCURRENTRATES (above – via transaction SE37) shows us that exch_rate_list is a table parameter, and the rows() method is returning an Array of table lines. SAP::Rfc takes care of all of this, including carving up the rows based on the table structure definition, making each line a hash of fieldname/value pairs.

Going back to the Controller – at this level, SAP RFC specificness has been abstracted away – the only hint is directly calling error methods (see the Exrate#save method definitition) – this is usually more concealed in ActiveRecord, because of the Database Schema related information being stored in the Model – this is a lot harder to achieve in relation to RFC calls, as the schema relating to an RFC is less definitive, than that of classic Rails database table design – see the Todo list, and Cookbook tutorials to get further details.  SAP4Rails uses the ActiveRecord::Errors class which is bound to an inherited attribute of the Model called @errors.  When the facilities of this class are used then error testing, recording, and then subsequent display is trivial with the inclusion of the error_messages_for directive in a view template (see fragment _form.rhtml via new.rhtml for exrate, and observe how the Exrate#save interacts with it).

Other than that – from here on in it is just plain Rails.

The Views

Now on to rendering the output.  The embedded Ruby code of the list.rhtml template gets executed in the context of the instantiated ExrateController, so it has access to all the attributes of that object.  In the interests of tidiness, I have put the column/field names relating to list() Array results in a Helper module ./app/helpers/exrate_helper.rb.  This is inherited by the controller at run time, so anything defined here is also available in the template.

Figure 15 – ExrateController helper module ExrateHelper

module ExrateHelper

  1. Fields for form output

  def formFields
    return    [ ‘FROM_CURR’,

There is so much going on within the template, but specific to our SAP4Rails example, we can see the code iterating over the attribute Array of @exrates – each element of which, holds a hash of field/value pairs. This is where the field names from formFields() are also iterated over to print out the columns.

Figure 16 – exrate template list.rhtml

Listing Exchange Rates

<%= link_to ‘Currencies’, :controller => ‘currency’, :action => ‘list’ %>
  <!– get fields from ExrateHelper –>
  <% formFields.each {|key| %>
    <th><%= key %></th>
  <% } %>
  <% for exrate in @exrates %>
    <% formFields.each {|val| %>
      <% if val == “FROM_CURR” or val == “TO_CURRNCY” %>
        <%= link_to exrate[val], :controller => ‘currency’, :action => ‘show’,
                                    :id => exrate[val].strip %>
      <% else %>
        <%=h exrate[val] %>
      <% end %>
    <% } %>
    <%= link_to ‘Show’, :action => ‘show’, :id => exrate[‘RATE_TYPE’].strip + ‘:’
                   + exrate[‘FROM_CURR’].strip + ‘:’ +
                     exrate[‘TO_CURRNCY’].strip %>
    <%= link_to ‘Edit’, :action => ‘new’, :id => exrate[‘RATE_TYPE’].strip + ‘:’
                   + exrate[‘FROM_CURR’].strip + ‘:’ +
                     exrate[‘TO_CURRNCY’].strip %>
  <% end %>

And with that – here are the results.
Figure 17 – Listing Exchange Rates – rendered list.rhtml

In conclusion

The approach laid out in this article, has enabled a high level of integration with the Rails framework.  The amount of code required to make this possible (SAP4Rails.rb) is less than 130 lines, which I think is testimony to how flexible, and well designed Rails is.  Further more – in the data Models shown, the heavy lifting of wrestling with RFC parameters, and their associated data types has been made trivial with SAP::Rfc and it’s auto-interface-discovery features.  Have a closer look at the resources included (see above) – and you can see why Rails is creating such a storm in the world of Open Source, and making waves in the “Traditional Commercial Web development” Arena.

Now you can get on track with your Web Services development. 🙂

Note: since this was first published, SAP4Rails has moved on, and is now available as a separately installable module which you can get from here.  A modified version of the Exrates example is held within the source package.

You must be Logged on to comment or reply to a post.
  • Hi Piers – great article.

    (Disclaimer: I’m a big fan of Ruby on Rails).

    Ruby on Rails is a great example of a framework where “convention over configuration” affords huge gains in productivity and clarity, in comparison to the turgid complexity that plagues the J2EE platform.

    Furthermore, it’s only a matter of time before the enterprise software industry catches up with where Ruby (as an example) is and starts to realise the power of dynamic languages. (Incidentally, this is why the ‘enlightened ones’ refer to IDEs as “programming crutches” – requiring an IDE like Eclipse to program within should be setting off alarm bells, not championed as progress 🙂


  • Thanks for great article!

    For some time word combination SAP & PHP sounds for me like oxymoron. And now SAP & Rails! SAP & Flex… Oh Dear! It is becoming unpopular to be Java developer these days 😉

    Btw, have to admit that RoR + SAP RFC code looks far simplier then WebDynpro + Adaptive RFC.


  • Hi Piers,

    thanks for the great article. At sdn meets labs I briefly spoke with some people about ruby and told them that it is still on my todo-list. Now, some months later I have installed everything I need but still need some time to get fully started 🙂
    As I side note for the testing geeks. I found it nice that ruby will generate all the basic unit tests when you first create a new application. Furthermore ruby comes with his own unit testing tool, cool stuff =)
    Btw if someboy does not want to read a lot about ruby just watch it in action:


  • First of all; Great work Piers

    Now If SAP would pour some millions into the ruby on rails project(as they’ve done with PHP, ) , there might be official support on such an integration, but is it realistic to expect this?

    It’d be interesting to hear the official thoughts(or for that matter unofficial) of SAP on future support for dynamic languages.

    Still I think there will go many many years before dynamic languages are not only used by the “enlightened ones”, but also used for enterprise applications.

    just my two cents though

    • Thanks Dagfinn – Now we’re talking.
      I’ve been trying to garner interest in binding dynamic  languages to R/3  the whole time I’ve been on SDN.
      What I would like to see is an opening up of the interface APIs for RFC (for example, I’ve been trying for over 12 months now to get someone in SAP to tell me how the new complex data types work here) so I can add greater support in Perl, Python, and Ruby, and
      Then I’d like to see SAP create a generic API within ABAP for grafting on other programming languages for communication in the opposite direction.  I’m not pretending that this is a trivial exercise, but I think it is extremely important to SAP in the area of extending its capabilities for integration, and interfacing with other products, and services that are increasingly coming from Open Source stock.
  • Hi Piers,

    I’m trying to get your Example Application running on a Debian Sarge system. I’ve installed SAP::Rfc for Ruby with the WebAS 6.20 RFCSDK. ruby -r “SAP/Rfc” -e 1 does not return an error. My ruby version is “ruby 1.8.2 (2005-04-11) [i386-linux]”, gem versin is: 0.8.11. When I call the application I got this error:

    NoMethodError in Exrate#list
    undefined method `toHash’ for nil:NilClass
    /app/models/exrate.rb:83:in `list’
    /app/controllers/exrate_controller.rb:15:in `list’

    The R/3 connection works. I’ve looked in the trace file and the BAPI is called sucessfully.

    Hope you can help.


  • Hi, Pierce,

    Thank you for the great work you’ve done!
    I think the Ruby module is quite useful and is going
    to be used by many developers.

    Unfortunately I was not able to find any documentation or examples
    on the net. I think it would be useful if you could
    provide a brief description of the basic usage
    of the classes.

    For example I need to discover an RFC function,
    set the import parameters, make the call and
    then get the output parameter.
    Still haven’t made it run, but I know I am close.

    P.S. I am not very experienced in Ruby, but know
    the basic stuff.


    • P.S. I forgot to write down the conditions and what error I get.

      I am using your module with the following code:

      require ‘rubygems’
      require_gem “saprfc”

      # connect
      rfc = . .)

      # lookup the BW RFC interface
      i =“…”)

      # Import parameters
      E–>  i.getParm(“PAR1”).value = par1

      # call and test the return

      # Export parameters
      par2 = i.getParm(“PAR2”).value
      . . .

      And I got error on the line, marked with “E–>”.
      When I debugged the code of Rfc.rb, I located the
      exception in this code area:

      . . .
      # getParm method of Iface class.
      def getParm(parm)
            return @parmsindex.has_key?(parm.upcase) ? @fieldsindex[parm.upcase] : nil
      . . .

      And the error description was:
      C:/Program Files/Ruby/lib/ruby/gems/1.8/gems/saprfc-0.11-mswin32/lib/SAP/Rfc.rb:765:in `getParm’: undefined method `[]’ for nil:NilClass (NoMethodError)
           from C:/SAP_Files/PHP/workspace/Ruby/rfccall.rbx:27

      As long as I see, all other calls before the problematic line were successful and I got also
      the result of the function discovery. I was able to see all the parameters in the Watch window.
      It seems like having this “@fieldsindex” variable set to nil and trying to search in it.

      Maybe I am not using the module properly, but I have no idea how should my call look like.

      Thanks in advance.

  • Hi, I was just trying out your library and I am very amazed to see that it plain works.

    Even though I am quite able to fetch exporting values and tables, I am having trouble with simple exporting structures like the ones like BAPIRETURN. It is a structure not a table like a structure. I tried two different RFCs and both returned this structure as a concatenated long string.

    Are you aware of this problem, am I doing something wrong?

    Another thing: I am very used to the SAP Java Connector and I know that sapjco does connection pooling. I can even configure how many connections I want to maintain opened.

    How does this library works, along with Rails? Does it have something similar, or it open a connection, execute and than close for each request? Or am I reading the architecture the wrong way?

    • Hi Fabio,
      With parameters that have structures, you should be automatically returned  a hash (key/value pairs) of the fields within – please post you sample piece of code so I can see what the problem is.

      Connection pooling is a separate problem.  I feel it should be up to the library user to determine what kind of pooling scheme they want to implement – for example, you may have more than one R/3 system tat you wish to communicate (I do at my current employment), therefore you might need to maintain separate system connections.
      In a ROR specific environment – I’m sure that it would be possible to come up with something a bit more dedicated and friendly – what do you have in mind?
      Piers Harding.

  • Thank you for sharing this topic with the forum. Ruby on Rails has been near the center of my research this past year and you have help further my understanding of how SAP may work under the Rails.


  • Hi,
    I did it exactly as in the tutorial (for a method called ZFM_ATLAS_TASKS_INIT, that we have).

    So I have an array @@funcs = [



    and this Class Method:

    class << self
    def list

    When I call Unit.list() from the controller I get the following error:

    NoMethodError in UnitController#view

    You have a nil object when you didn’t expect it!
    The error occurred while evaluating

    Has anyone experienced a similar error? Obviously the RFC object does not exist. I think the connection to the R/3 system is working, as I managed to get rid of some connection errors before 😀

    Thanks in advance

    • What you need is something like:
      require_gem “sap4rails”
      class MyModel<SAP4Rails::Base
        function_module :FM1,
  • Dear Piers,
    thank you very much for this fast answer. Now I could successfully set an RFC call. However, I would like to access a table that is defined as


    I tried it that way:
    return Unit2.ZFM_ATLAS_SEARCH_FE.et_force.rows()
    just like in the example.
    Unfortunately “et_force” seems to be nil and the trace file says
    ***Discarding unrequested T:ET_FORCE
    How can I make the RFC call to request this table?

  • It is still useful after 2 years.

    Thought a newbie might wonder.

    I read earlier but didn’t try it out myself. When I tried I realized that there is no sirectory named ‘scripts’. I guess it should be ‘script’.

    Any more work in the same lines => rails+ruby+SAP?