Rekords in Java ![Build Status](https://camo.githubusercontent.com/a9ddb834cea75bde96b0361ece9792676ebe95103520085909e6ebea8cc5073d/68747470733a2f2f7472617669732d63692e6f72672f53616d697254616c7761722f52656b6f72642e706e67)
A rekord is an immutable data structure of key-value pairs. Kind of like an immutable map of objects, but completely type-safe, as the keys themselves contain the type information of the value.
It can be used as an alternative to classes with getters (immutable beans, if you will) so you don't have to implement a
new concrete class for every value concept—instead, a single type has you covered. You also get builders for free,
equals
and hashCode
are implemented for you, validation and serialization are covered, and other concepts, such as
default values, can be implemented once and used for all rekords. Finally, all Rekords, being immutable, are thread-safe
to construct and to use.
And there's no magic.
An example:
Rekord<Sandvich> sandvich = Sandvich.rekord
.with(Sandvich.filling, Lettuce)
.with(Sandvich.style, Burger);
assertThat(sandvich.get(Sandvich.bread), is(Brown));
assertThat(sandvich.get(Sandvich.filling), is(Lettuce));
assertThat(sandvich.get(Sandvich.style), is(Burger));
How's that work? And why is the bread brown? We didn't specify that.
The magic is really in the key. It's defined as follows:
public interface Sandvich {
Key<Sandvich, Bread> bread = Key.named("bread").that(defaultsTo(Brown));
Key<Sandvich, Filling> filling = Key.named("filling");
Key<Sandvich, Style> style = Key.named("style");
Rekord<Sandvich> rekord = Rekord.of(Sandvich.class).accepting(filling, bread, style);
}
So all you need is one interface and a few constants. The return type of the Rekord::get
method is the type embodied
in the key, so for the sandvich filling, the return type is Filling
.
What else?
There's more. Every Rekord is also a builder. Rekords themselves are immutable, so the with
method returns a new
Rekord each time. Use them, pass them around, make new rekords out of them; because they don't mutate, they're perfectly
safe.
There are matchers for the builders. You can assert that a rekord conforms to a specific specification,
just check they have specific keys, or anywhere in between. Take a look at RekordMatchers
for
more information.
This plays into validation. Rather than just building a rekord and using it, you can also create a
ValidatingRekord
which allows you to build a rekord up, then ensure it passes a
specification. Just like the matchers, Hamcrest is used for validation.
Finally, rekords can be serialized. Whether you want it to be JSON, XML or just a Java map, we've got you covered. It's pretty simple. For example:
Rekord<Person> spongebob = Person.rekord
.with(Person.firstName, "Spongebob")
.with(Person.lastName, "Squarepants");
Document document = spongebob.serialize(new DomXmlSerializer());
assertThat(the(document), isSimilarTo(the(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<person>" +
" <first-name>Spongebob</first-name>" +
" <last-name>Squarepants</last-name>" +
"</person>")));
You can see the list of serializers in the serialization
package. If you don't spot the one you're
looking for, just implement your own. The API is fairly simple.
There are a couple of extra pieces of functionality in the extra
package. At the moment, there are
transformers that use Guava, and a serializer that uses Jackson. They're hidden away because you'll get
compilation failures if you try and use them without the correct JAR dependencies. If you're interested, grab the
libraries and get going.
There's almost certainly a bunch of stuff we haven't covered. More examples can be found in the tests.
Installation
You can use Rekord v0.2 by dropping the following into your Maven pom.xml
. It's in Maven Central.
<dependency>
<groupId>com.noodlesandwich</groupId>
<artifactId>rekord</artifactId>
<version>0.2</version>
</dependency>
If you're not using Maven, alter as appropriate for your dependency management system. If you just want a JAR, you can download it directly from Maven.
Why "Rekord"?
I was in Germany, at SoCraTes 2013, when I named it. So I thought I'd make the name a little more German. ;-)
Credits
Thanks go to:
- Nat Pryce, for coming up with the idea of "key" objects in Make It Easy.
- Dominic Fox, for extending the idea by delegating to a simple map in karg.
- Quentin Spencer-Harper, for working with me on the initial implementation of this library.