Giter Site home page Giter Site logo

rekord's Introduction

Rekords in Java   Build Status

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.

Why?

Duplication is difficult to exterminate in Java code. In particular, one type of structural duplication is scattered throughout our software. It looks something like this:

public class Person {
    private final String firstName;
    private final String lastName;
    private final LocalDate dateOfBirth;
    private final Address address;

    public Person(String firstName, String lastName,
                  LocalDate dateOfBirth, Address address) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.dateOfBirth = dateOfBirth;
        this.address = address;
    }

    public String getFirstName() {
        return firstName;
    }

    // I can't go on. You know the rest.
}

Of course, that's not all. We then have to make a builder, a matcher for readable test cases, and everything else to support this, the dumbest of all classes.

OK, now we can use our Person type. It's beautiful, right? It just needs some annotations to serialize to JSON, then some JPA annotations for persistence to the database, and…

UGH.

Rekord to the Rescue

Code like the above makes me angry. It's such a waste of space. The same thing, over and over again.

With Rekord, the above suddenly becomes a lot smaller.

public interface Person {
    Key<Person, String> firstName = SimpleKey.named("first name");
    Key<Person, String> lastName = SimpleKey.named("last name");
    Key<Person, LocalDate> dateOfBirth = SimpleKey.named("date of birth");
    Key<Person, FixedRekord<Address>> address = RekordKey.named("address");

    Rekord<Person> rekord = Rekords.of(Person.class)
        .accepting(firstName, lastName, dateOfBirth, address);
}

That Rekord<Person> object is a rekord builder. You can construct new people with it. Like so:

Rekord<Person> woz = Person.rekord
   .with(Person.firstName, "Steve")
   .with(Person.lastName, "Wozniak")
   .with(Person.dateOfBirth, LocalDate.of(1950, 8, 11))
   .with(Person.address, Address.rekord
       .with(Address.city, "Cupertino"));

woz has the type Rekord<Person>, but you can treat it basically as if it were a Person as shown above. There's only one real difference. Instead of:

woz.getFirstName()

You call:

woz.get(Person.firstName)

Simple, right?

What else?

Rekord is designed to 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.

For free, you also get:

  • builders
  • matchers
  • validation
  • serialization
  • transformations
  • equals and hashCode
  • toString

Builders

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.

Matchers

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.

Rekord<Person> steve = Person.rekord
    .with(Person.firstName, "Steve")
    .with(Person.lastName, "Wozniak")
    .with(Person.dateOfBirth, LocalDate.of(1950, 8, 11));

assertThat(steve, is(aRekordOf(Person.class)
    .with(Person.firstName, equalToIgnoringCase("steVE"))
    .with(Person.lastName, containsString("Woz"))));

assertThat(steve, hasProperty(Person.dateOfBirth, lessThan(LocalDate.of(1970, 1, 1))));

Validation

The matchers play 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.

The same matchers you can use in your tests are used for validation.

When you fix a validating rekord, one of two things happen. It will either return a ValidRekord, which implements the FixedRekord interface, providing you the get method (and a few others), or it will throw an InvalidRecordException. Because we use Hamcrest matchers, the exception should have a decent error message which explains why the validation failed.

ValidatingRekord<Person> namedPerson = ValidatingRekord.validating(Person.rekord)
    .expecting(hasProperties(Person.firstName, Person.lastName));
    
ValidRekord<Person> steve = namedPerson
    .with(Person.lastName, "Wozniak")
    .with(Person.dateOfBirth, LocalDate.of(1950, 8, 11))
    .fix(); // throws InvalidRekordException

Transformation

Rekord properties can be transformed on storage and on retrieval. The rekord-keys library adds a number of keys that wrap existing keys. As of the time of writing, you can:

Serialization

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>")));

The available serializers are, at the time of writing:

  • StringSerializer, which is used by Rekord::toString to create a string representation of a rekord
  • MapSerializer, which converts a Rekord into a Map<String, Object>
  • DomXmlSerializer, which converts a Rekord into a Document object. It's demonstrated above
  • JacksonSerializer, which converts a Rekord into JSON, and can either return a String or write it directly to a Writer

Note: to use JacksonSerializer, you'll need to include rekord-jackson as a separate dependency. This is to avoid including the Jackson JSON Processor as a dependency of Rekord.

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.3 by dropping the following into your Maven pom.xml. It's in Maven Central.

<dependency>
    <groupId>com.noodlesandwich</groupId>
    <artifactId>rekord</artifactId>
    <version>0.3</version>
</dependency>

If you want to serialize to JSON, grab this one too:

<dependency>
    <groupId>com.noodlesandwich</groupId>
    <artifactId>rekord-jackson</artifactId>
    <version>0.3</version>
</dependency>

If you're not using Maven, alter as appropriate for your dependency management system.

There are also individual JARs available if you don't want all of Rekord, or if you want to manually manage your dependencies. You can get them all from Maven Central.

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.

rekord's People

Contributors

samirtalwar avatar franziskas avatar arkangelofkaos avatar

Stargazers

平江 avatar Mahmoud Rusty Abdelkader avatar Tirumalesh avatar Oleksandr Zakusylo avatar  avatar Nolan Mack avatar Khaled Ezzughayyar avatar Frederico Honório avatar Andrew NS Yeow avatar Patrick Cieplak avatar Piero Divasto avatar Gregor Trefs avatar Daniel Manchon avatar Zoran Regvart avatar Jay Peper avatar Dan avatar Amit Kumar Mondal avatar Heneli Kailahi avatar migwellian avatar  avatar Nicolai Parlog avatar Sofiane Laouini avatar Callum Rogers avatar Mehran Heidarzadeh avatar Ryan Schmitt avatar Tim Molter avatar Derek Mortimer avatar Rakhmad Azhari avatar Micha Kops avatar Bastien LEMALE avatar MK Tan avatar HQM avatar Dominic Fox avatar Pradeep Gowda avatar Miguel Eduardo Gil Biraud avatar Guillaume Rose avatar Armin Keyvanloo avatar Roman Boiko avatar  avatar Pascal Erb avatar Johannes Seitz avatar Sim Yih Tsern avatar Dmitry Kandalov avatar Michael Deardeuff avatar Israel Ferrer Camacho avatar MEDDAH Julien avatar Julien Gotteland avatar

Watchers

 avatar Dmitry Gusev avatar Rakhmad Azhari avatar Mike Hemp avatar Guy Burton avatar  avatar James Cloos avatar  avatar  avatar

rekord's Issues

RekordType not necessary

It's limiting to require that Rekord only accepts types that extend/implement RekordType, and I don't see much benefit from it.

I suggest removing the constraints from all classes and methods.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.