I’ve been working with Orchestrate the past few weeks to help create an official ruby client for their Rest API. Based on a library put together by Orchestrate customer James Carrasquer, I’ve been working to give the library a solid foundation and recently published the first two releases of the official ruby gem.

Let’s kick the tires.

Installation

You can download the client by running gem install orchestrate, or use Bundler and put it in your Gemfile.

Basic Usage

Let’s create a client:

require 'orchestrate'

client = Orchestrate::Client.new('your api key')

You can verify everything is setup correctly with the ping method:

client.ping

If your API key is wrong or you receive an error from Orchestrate, it will raise an exception.

The client class is a light wrapper around the API, with a goal of maximizing flexibility. A future version will include some more ruby-like abstractions around Key/Values, Refs, Events and Relations.

Let’s create some Key/Value items:

r = client.put(:items, 'hello', {'hello' => 'world'})
r.location # "/v0/items/hello/refs/023b3774fa949ef2"
r.ref      # "023b3774fa949ef2"

r = client.put(:items, 'hello', {'hello' => 'there'})
r.location # "/v0/items/hello/refs/66bdd4cf4f8aa5c9"
r.ref      # "66bdd4cf4f8aa5c9"

r = client.get(:items, 'hello')
r.location # "/v0/items/hello/refs/66bdd4cf4f8aa5c9"
r.ref      # "66bdd4cf4f8aa5c9"
r.body     # { "hello" => "there" }

We can list and retrieve refs:

r = client.list_refs(:items, 'hello')
r.results  
# [ { "path" => { "collection" => "items",
#                 "key" => "hello",
#                 "ref" => "66bdd4cf4f8aa5c9"},
#     "reftime" => 1402953109406 },
#   { "path" => { "collection" => "items",
#                 "key" => "hello",
#                 "ref" => "023b3774fa949ef2"},
#     "reftime" => 1402952296024 }
# ]

r = client.get(:items, 'hello', r.results.last, "023b3774fa949ef2")
r.body    # { "hello" => "world" }

There are methods on the client for interacting with each of the API endpoints in Orchestrate’s API documentation, and you can read more about how the client wraps them in its documentation.

Errors

When the client receives an error response from Orchestrate, it raises an exception. There are individual exception classes for each of the documented errors, but to help with your exception handling, 400-class errors raise a subclass of Orchestrate::API::RequestError indicating something was wrong with your request, and 500-class errors raise a subclass of Orchestrate::API::ServiceError, indicating something went wrong on Orchestrate’s end.

Perhaps the best way to show how this works is with a demonstration:

r = client.put_if_absent(:items, "nothing_here", {"foo" => "bar"})
r.location   # "/v0/items/nothing_here/refs/e64018c4a26ac350"

r = client.put_if_absent(:items, "nothing_here", {"foo" => "bee"})
Orchestrate::API::AlreadyPresent: The item is already present.

Configuring HTTP

HTTP in ruby has always been a bit thorny — the built-in Net::HTTP library is slow, and substituting it for a library wrapping libcurl or using it in EventMachine proves difficult. Fortunately, the Faraday library makes this easy. If you want to use Typhoeus, which for example wraps libcurl and enables parallel requests, telling the Orchestrate client to do this is simple. In the configuration, you can pass a lambda that will be called with the Faraday::Connection instance. To use Typhoeus:

require 'typhoeus/adapters/faraday'

client = Orchestrate::Client.new("your api key") do |conn|
  conn.adapter :typhoeus
end

Further examples are provided in the Readme.

Typhoeus, EM-HTTP-Request, and EM-Synchrony among others enable parallel requests. When using one of these adapters, you can issue multiple requests to Orchestrate simultaneously, and it will return when they have all completed:

responses = client.in_parallel do |r|
  r[:list] = client.list(:my_collection)
  r[:user] = client.get(:users, current_user_id)
  r[:user_events] = client.list_events(:users, current_user_id, :notices)
end

When I’ve used API clients for HTTP services in Ruby in the past, dealing with HTTP performance issues has been one of the most problematic things, and exposing the underlying library for extension will allow you to use logging, instrumentation, etc, all by using Faraday Middleware.

Future Development

I’m just getting started on this library — I want to provide a first-class ruby experience of the Orchestrate API, and that’s going to include classes for Orchestrate’s Collections, KeyValue documents, Events, etc, and cursors for iterating over the results of list and search results (and you can watch the work in progress), but first I wanted to have a solid and flexible underlying base client.

If you find any bugs, or have any thoughts on the shape of the Orchestrate Ruby library, I’m happy to hear about them on the issue tracker!