Archive for the 'rails' Category

Promiserver alpha is online

It isn’t much to see at this point, but last week Promiserver went live on my PLW site.

The challenge now is to move beyond descriptive natural language to a more procedural, structured form. I still feel like Ruby is the way here, with some sort of Ruby DSL that is interpretted within a sandbox on the server. I’ve actually been tempted to go back to Scheme just to prototype something, since it lends itself so well to this style of programming.

As a small diversion I’m also becoming interested in wiki-style collaborative contract and license authoring, including sophisticated diff and windowed timeline views. This falls out of an idea Takashi mentioned a while back about community defined licenses. He’s currently putting together a madlibs style license that I’m looking forward to seeing. And as my first effort in this arena today I added a humble timeline slider to the openstudio tag cloud. It’s a little slow with the amount of Ruby processing happening on each Ajax request, but serves as a decent first step. I’d next like to augment the simple slider with additional information. It would be nice to get a macroview of community activity, something along the lines of Martin Wattenberg’s history flow, but less overwhelming in its detail. For now at least I am just satisfied to be able to look back on the past days when flower was a more popular tag than abstract.

Rails Layered Dispatching Hints

For my current Rails project I’m setting up my web services with layered dispatch, which nicely organizes all the methods while keeping them at the same endpoint URI. There’s some good documentation already out there in the Agile Web Development with Rails book as well as the ActionWebServices manual, but I have a few extra tips that could be helpful.

If we’re using a before_invocation interceptor, like say for authentication, we can clean things up by creating a subclass of ActionWebService::Base that includes the authentication method:

app/apis/authenticated_web_service.rb

class AuthenticatedWebService < ActionWebService::Base
  def authenticate name, args
    @authenticated_user = User.find_by_login args[0]
    unless @authenticated_user.authenticated?(args[1])
      raise "Not authenticated"
    end
  end
end

Because we are actually using an instance variable here, @authenticated_user, we have to set up our layered web service controller to use instances of the services rather than the classes.

app/controllers/services_controller.rb

class ServicesController < ApplicationController
  web_service_dispatching_mode :layered
  wsdl_service_name 'contract_services'
  web_service_scaffold :invoke

  # Here we use instances...
  web_service(:contract) {ContractService.new}
  web_service(:account) {AccountService.new}

  # rather than classes...
  # web_service :contract, ContractService
  # web_service :account, AccountService
end

Then your various services can inherit from this "abstract" AuthenticatedWebService. Each can make use of the authenticate method and the @authenticated_user variable.

app/apis/contract_service.rb

class ContractApi < ActionWebService::API::Base

  api_method :all,
             :returns => [[Contract]]
  api_method :new,
             :expects => [{:login => :string},
                          {:password => :string},
                          {:description => :string}],
             :returns => [Contract]
end

class ContractService < AuthenticatedWebService
  web_service_api ContractApi

  def all
    Contract.find :all
  end

  def find contract_id
    c = Contract.find(contract_id)
    raise "Contract not found." if c.nil?
    c
  end

  ## Authenticated API Methods
  before_invocation :authenticate, \:only => [:new]

  def new l, p, description
    c = Contract.new :creator => @authenticated_user,
                     :description => description
    c.save!
    c
  end
end

Once your service is put together, keep in mind that XML-RPC requests will use a method of the form service.method. So for example, my contract service's find method is contract.find. I have to admit that this really threw me off for a little while when I was testing out the API in XML-RPC Client and I kept getting back a "no such web service 'api'" error message. The manual does point this out, but I missed it the first time, and only on a close reread did I figure this out, at which point I realized how much good sense this approach makes. Note that for SOAP requests the service API is encoded in the header.

One more final note is that Rails logger object is not available in the layered services. This could be a deal breaker in certain cases.