Validation error objects (and hashes)

TL;DR. You can add Hash or custom class instances as error. It does not have to be a String.

Let's start with a simple Rails code that uses standard validation errors and see what kind of problems we have with such approach.

This is how it looks like when validation error occurs:

Let's just say that for a better ui experience we want to change the formatting of such
validation message (ex. numbers can be bold so that user pays attention to them). However, current solution does not allow us to do it easily. That is because of the fact that error is simply a String. Also the whole message is generated on the model side and just displayed on the view. This is a very quick and simple approach but does not give as a wide spectrum for improvement. Fortunately our errors does not to be of such simple type. We can put there something more complex like a Hash.

Looks perfect but sadly this implementation leads to I18n::InvalidPluralizationData exception so we must get a little dirty to deal with this error.

Now it works :

And now our error messages can be much more customizable and event look totally different. There is no need to stick with plain text. We can do better. There is a nice separation here between view and model. Model tells us as much about the error as possible. View generates the message and presents it nicely.

What is another useful benefit of such approach ? Imagine now, that Rails is just an API for mobile client (iphone, android, blackberry, windows, whatever). It is the responsibility of the view created on the phone to get the errors, translate them and display in a meaningful way for that platform. But you can help as an api creator as much as possible and make your colleges job easier in the future by simply providing lot of information. We don't want to hardcode our minimal and maximal requirements for the login length. That should be easy to change. Neither we want to generate the message in every possible language that can be used. We just want to provide enough information for the api clients so that they can get what went wrong, how to fix and how to display it to the user.

And guess what. This solution already does it. If you send the request to /users.json you will get a response like this:
{"login":[{"code":"INVALID_LENGTH","max":10,"min":2,"current":65,"description":"Invalid length"}]}

Your mobile clients can use the code, min, and max properties to display sensible error message for the user. In case when server is upgraded and a new validation is added, old clients can just show the text from "description" key until they are upgraded. After upgrade they should recognize the new error message code and relay on other provided keys when displaying the message.

All of that was nice but I think that is too much of "hash driven development". Let's make some OBJECTS.

Unfortunately, now data returnd for json clients are not identical.


Let's fix it with a simple approach:

But i don't think that this should be part of that model. After all it is just a json representation of the error. So probably we should go here with roar gem or any other that would simplify this. I see a value in an approach in which we have one representer to our Json client and one to Html clients (being just actionview part of rails).

Another problem is that we basically had to reimplement the validator that already exists in Rails. Let's check if that could be done better:
As you can see I had to fix some parts of Active Model to make it possible. I think it could be a good idea to submit a pull request with those changes. It's in my mind. Also there was no easy way to fill "current" attribute. Filling it would require a rewrite of few more lines in active model as well as making possible to add custom error types as part of validation declaration which is something that did not work well in step 2 (this part that leads to I18n::InvalidPluralizationData exception). I will definitelly tell you went all that happens :)

You can get the whole exemplary application at:

Popularne posty z tego bloga

Ruby vs Kotlin: Custom Comparable classes