Concierge Library

Posted on June 16, 2007

The Library

Concierge does for back-end ruby services what Rails does for web MVC. It provides conventions and plumbing to join service implementations with service consumers. Service discovery and configuration are made painless through the use of convention.

In this context, service means "the action of helping or doing work for someone." The "someone" receiving help is typically a collaborating object, a controller in a Rails application, for example. The "someone" offering help may be a payment gateway, a shipping calculator, a legacy application, or whatever else you can dream up.

Installation

The simple way:

$ sudo gem install concierge

Or direct from the repository:

$ svn co svn://rubyforge.org/var/svn/concierge/trunk concierge

Configuration

Concierge needs to know where to look for services and configuration. It also requires that the dependency loader from Active Support be configured correctly. Here's an example:

CodeRhythm::Concierge::Loader.configure do |config|
  config.service_config_path = "#{RAILS_ROOT}/config/services"
  config.env                 = RAILS_ENV
end
Dependencies.load_paths.unshift "#{RAILS_ROOT}/lib/services"

You need the Active Support and Open Struct libraries available.

An Annotated Example

A typical Rails application contains code that looks something like this:

authorization = PaymentGateway.authorize(:cc => params[:cc], :amt => params[:amt])

It's not terrible but it is tightly coupled. You now have some object, a controller in this case, which knows exactly how a service (the payment gateway) is implemented. Besides tight coupling, you also have boiler plate code hanging around loading and configuring your service. It might look like this in your development.rb (or test.rb or production.rb):

# This effectively discovers the service.
require 'payment_gateway'

# Now we give the service some config to work with.
PaymentGateway.config.host = 'somehost'
PaymentGateway.config.port = '443'
# ...

Again, not terrible but somewhat sloppy; especially when you have a few of these services in your application.

With Concierge, collaborators (e.g. your controller) declare themselves as such with some meta-programming sugar:

class OrdersController < ApplicationController

consumes_service :payment_gateway

def create
  authorization = payment_gateway.authorize(:cc => params[:cc], :amt => params[:amt])
  # Do some other stuff
end

We broke the coupling between the PaymentGateway implementation and it's consumer. Decoupling and delegating service discovery and configuration to Concierge enables convention-based goodness such as service discovery from your lib/services directory and service configuration based on yaml files from your config/services directory.

Your lib/services/payment_gateway.rb now looks something like this:

class PaymentGateway < CodeRhythm::Concierge::Provider
  provides_service :payment_gateway
  
  def authorize(opts)
    # Do the authorization.
    # Configuration params are available using self.config (e.g. config.host).
  end
end

Your config/services/payment_gateway.yml now looks something like this:

development:
  host: devhost
  port: 8080
test:
  host: testhost
  port: 9090
production:
  host: prodhost
  port: 443

Interested in how Concierge can help you isolate your collaborators during testing? Have a look at the examples/sham_test.rb file.

Comments
  1. aiwilliamsJune 21, 2007 @ 02:31 PM

    Chris, this looks great. Glad to see you make this available to other folks.

    I believe the biggest gain from using Concierge comes when folks write tests. Perhaps an article working through the creation of a test which uses a service is in order… maybe over at FaithfulCode.com ;)