Concierge Sample

Posted on June 21, 2007

Since the article introducing Concierge rambled somewhat, here's a sample Concierge Rails app.

Download the sample.

Installation

$ sudo gem install concierge

Make sure you have Rails 1.2.3 and Sqlite3 installed.

Running The Greeter

You might like to run the sample app first as proof this stuff works:

$ cd ~/Projects
$ wget http://rubyforge.org/frs/download.php/21986/greeter.tgz
$ tar xzvf greeter.tgz
$ cd greeter
$ script/server
$ open http://localhost:3000/greetings

Enjoy your greeting.

The Code

Yes, it's the hello world service. The relevant bits follow.

config/environment.rb

require 'concierge'
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"

On edge rails config/initializers/concierge.rb is probably a better choice.

config/services/greeter.yml

development:
  greeting: hello
test:
  greeting: hola
production:
  greeting: buenos dias

app/controllers/application.rb

class ApplicationController < ActionController::Base
  include CodeRhythm::Concierge::Consumer
end

app/controllers/greetings_controller.rb

class GreetingsController < ApplicationController
  consumes_service :greeter
  def index
    render :text => greeter.greet
  end
end

lib/services/greeter.rb

class Greeter < CodeRhythm::Concierge::Provider
  provides_service :greeter
  def greet
    config.greeting
  end
end

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.