Cistern
Cistern helps you consistenly build your API clients and faciliates building mock support.
Usage
Service
This represents the remote service that you are wrapping. If the service name is 'foo' then a good name is 'Foo::Client'.
Service initialization will only accept parameters enumerated by requires
and recognizes
. model
, collection
, and request
enumerate supported features and require them directly within the context of the model_path
and request_path
.
Mock.data
is commonly used to store mock data. It is often easiest to use identity to raw response mappings within the Mock.data
hash.
class Foo::Client < Cistern::Service
model_path "foo/models"
request_path "foo/requests"
model :bar
collection :bars
request :create_bar
request :get_bar
request :get_bars
requires :hmac_id, :hmac_secret
recognizes :host
class Real
def initialize(={})
# setup connection
end
end
class Mock
def self.data
@data ||= {
:bars => {},
}
end
def self.reset!
@data = nil
end
def data
self.class.data
end
def initialize(={})
# setup mock data
end
end
end
Model
connection
represents the associated Foo::Client
instance.
class Foo::Client::Bar < Cistern::Model
identity :id
attribute :flavor
attribute :keypair_id, aliases: "keypair", squash: "id"
attribute :private_ips, type: :array
def destroy
params = {
"id" => self.identity
}
self.connection.(params).body["request"]
end
def save
requires :keypair_id
params = {
"keypair" => self.keypair_id,
"bar" => {
"flavor" => self.flavor,
},
}
if new_record?
request_attributes = connection.(params).body["request"]
merge_attributes(new_attributes)
end
end
end
Collection
model
tells Cistern which class is contained within the collection. Cistern::Collection
inherits from Array
and lazy loads where applicable.
class Foo::Client::Bars < Cistern::Collection
model Foo::Client::Bar
def all(params = {})
response = connection.(params)
data = self.clone.load(response.body["bars"])
collection.attributes.clear
collection.merge_attributes(data)
end
def discover(provisioned_id, ={})
params = {
"provisioned_id" => provisioned_id,
}
params.merge!("location" => [:location]) if .key?(:location)
connection.requests.new(connection.(params).body["request"])
end
def get(id)
if data = connection.("id" => id).body["bar"]
new(data)
else
nil
end
end
end
Request
module Foo
class Client
class Real
def (={})
request(
:body => {"bar" => },
:method => :post,
:path => '/bar'
)
end
end # Real
class Mock
def (={})
id = Foo.random_hex(6)
= {
"id" => id
}.merge()
self.data[:bars][id]=
response(
:body => {"bar" => },
:status => 201,
:path => '/bar',
)
end
end # Mock
end # Client
end # Foo
Examples
Releasing
$ gem bump -trv (major|minor|patch)
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Added some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request