Rails 3.x tutorial

This tutorial describes how you can provide and consume Qworum services with Rails 3.2 and Ruby 1.9.

The web application presented here is available at GitHub.

Browse the documentation for in-depth information about Qworum.

Note: This tutorial is somewhat out of date, because it makes intensive use of XML messaging which needlessly increase the traffic between web browsers and servers.
Instead, we recommend the use of JavaScript and HTML bindings for implementing and calling interactive services. Also, XML capability is not available on all Qworum browser add-ons.

Boilerplate

In order to use Qworum, your application only needs to be able to:

To start with, implement a filter that parses XML documents in POST requests:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  require 'rexml/document'
protected
  def parse_xml_post 
    @xml = nil
    return unless request.post?
    xml = nil
    xml = request.raw_post if request.content_type == 'application/xml'
    xml = params[:qworum] unless xml
    @xml = REXML::Document.new xml
    @xml = nil unless @xml and @xml.root
  rescue
    @xml = nil
  end
  def render_xml
    render template: "#{params[:controller]}/#{params[:action]}", formats: [:xml], handlers: [:builder]
  end
end

Then add this route to config/routes.rb:

match ':controller(/:action(/:id))(.:format)'

Providing a Qworum service

Implement a service that receives a call argument, computes a result, shows the result to the end-user, and returns the result.

Best practice is to use one controller per Qworum service, and one action for each service phase. Here is a controller for that service:

# app/controllers/qworum_service_controller.rb
class QworumServiceController < ApplicationController
  before_filter :parse_xml_post, except: :result
  
  # Phase 1:
  # Receive call argument, compute result
  def index
    name = @xml.elements['/name'].text
    @sentence = "Hello #{name} !"
    render_xml
  end
  
  # Phase 2:
  # Show result to end-user
  def show
    @sentence = @xml.elements['/sentence'].text
  end
  
  # Phase 3:
  # Return result
  def result
    render_xml
  end
end

And here is are the views for that service:

# app/views/qworum_service/index.xml.builder
# Store result on browser, go to phase 2
xml.qrm :goto, href: url_for(action: :show), 'xmlns:qrm' => 'http://qworum.net/' do
  xml.qrm :variable, name: 'result' do
    xml.sentence @sentence
  end
end
<!DOCTYPE html>
<!-- app/views/qworum_service/show.html.erb -->
<html>
<head>
  <title>Qworum Service</title>
</head>
<body style='background: silver; margin: 20%'>
  <h1>Qworum Service say_hello</h1>
  <p>The current call will return: <%= @sentence %></p>
  <p style='font-size: 70%'><%= link_to 'Click to return', action: :result %></p>
</body>
</html>
# app/views/qworum_service/result.xml.builder
xml.variable name: 'result', xmlns: 'http://qworum.net/'

Phase 3 is unnecessary if the browser provides a qworum JavaScript object. So phase 2 can be rewritten as follows in order to reduce server workload:

<!DOCTYPE html>
<!-- app/views/qworum_service/show.html.erb -->
<html>
<head>
  <title>Qworum Service</title>
</head>
<body style='background: silver; margin: 20%'>
  <h1>Qworum Service say_hello</h1>
  <p>The current call will return: <%= @sentence %></p>
  <button id='return'>Return</button>

  <script>
    var button = document.getElementById('return');
    button.addEventListener('click', function(){
       evaluateQworumMessage(['variable','result'], 'result');
    });

    function evaluateQworumMessage(msg, msgUrl){
      if(typeof qworum == 'object')if(typeof qworum.eval == 'function'){
        qworum.eval(msg);
        return;
      }
      window.location = msgUrl;
    }
  </script>
</body>
</html>

Consuming a Qworum service

In order to call the service above, your site needs to generate a Qworum message that contains a call instruction. Implement that with a view for a second controller, as follows:

# app/views/site/call_service.xml.builder
xml.comment!("\n"+
  "  QWORUM SUPPORT IS MISSING IN YOUR BROWSER:\n"+
  "  This site only works correctly with Qworum enabled browsers. \n"+
  "  Please visit  http://www.qworum.com/products\n"
)
xml.qrm :goto, href: url_for(action: :receive_call_result), 'xmlns:qrm' => 'http://qworum.net/' do
  xml.qrm :call, href: url_for(controller: :qworum_service) do
    xml.name @name
  end
end

It is good practice to include a comment such as the one above in Qworum messages generated by web applications. This is obviously not needed for messages generated by Qworum services.

The goto instruction in the message above specifies the URL where the call result is to be posted. Here is a view for that URL:

<!DOCTYPE html>
<!-- app/views/site/receive_call_result.html.erb -->
<html>
<head>
  <title>Application</title>
</head>
<body style='margin: 20%'>
  <h1>Application</h1>
  <p>Result received from say_hello service is:  <%= @sentence %></p>
  <p style='font-size: 70%'><a href="/site">Go to home page</a></p>
</body>
</html>

And here is the controller:

# app/controllers/site_controller.rb
class SiteController < ApplicationController
  before_filter :parse_xml_post, only: :receive_call_result
  
  # Generate Qworum message for calling service defined above
  def call_service
    @name = 'Dave'
    render_xml
  end
  
  # Receive result of service call
  def receive_call_result
    @sentence = @xml.elements['/sentence'].text
  end
end

Here is the start page (service call is initiated when the end-user clicks on the provided link):

<!DOCTYPE html>
<!-- app/views/site/index.html.erb -->
<!-- URL: http://HOST:PORT/site -->
<html>
<head>
  <title>Application</title>
</head>
<body style='margin: 20%'>
  <h1>Application</h1>
  <p><%= link_to 'Call say_hello service with parameter "Dave"', 
    controller: :site, action: :call_service %></p>
</body>
</html>

User experience

You should observe the following: