Contrackt is a tool to make handling request and response objects more object-oriented.
In general, Ruby handles these request and response objects mostly using hashes. One would expect to see something along the lines of
{
body: {
"foo" => {
"bar" => "baz"
}
}
}
So your code might involve accessing variables like response[:body]['foo']['bar']
(always keeping track of which key is a string and which is a symbol) instead of the more object-oriented response.body.foo.bar
. Contracts are an attempt to represent these JSON hashes more clearly based on the objects they represent.
Contrackt
allows the user to define self-documenting "contracts" that they expect to receive or send based on whatever API tool (raml, swagger, blueprint) they use to specify an API. An example usage is
class MyContract < Contrackt::Base
required 'foo' => Foo
end
class Foo < Contrackt::Base
required 'bar'
end
You can then use those classes to work with the resulting JSON, e.g.
contract = MyContract.new(json_response[:body])
contract.foo.bar
contract.to_hash
You can also specify arrays of type:
# json = { albums: [{ name: "21", artist: "Adele" }, { name: "IV", artist: "Led Zeppelin" }] }
class Album < Contrackt::Base
required :name
required :artist
end
class AlbumPayload < Contrackt::Base
required albums: Album[]
end
The RAML spec provides for the concept of a "discriminator," i.e. a field that indicates the object's type. You can specify a discriminator as follows:
class Pet < Contrackt::Base
discriminator :type
required :name
required :owner
end
class PetsPayload < Contrackt::Base
required pets: Pet[]
end
class Cat < Pet
required :color
end
class Dog < Pet
required :breed
end
You can also use discriminator along with a map if the discriminator doesn't provide the class name:
class Pet < Contrackt::Base
discriminator :type, 'cat' => Cat, 'dog' => Dog
required :name
required :owner
end
class PetsPayload < Contrackt::Base
required pets: Pet[]
end
class Cat < Pet
required :color
end
class Dog < Pet
required :breed
end
If you have complicated code, you can write a custom parser, e.g.
class Thing < Contrackt::Base
required(:foo).with_custom_parser { |json| "#{json} (came from key foo)" }
end
Thing.new(json).foo