środa, 28 grudnia 2011

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.

{"login":[{"min":2,"max":10,"current":65}]}

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: https://github.com/paneq/active-model-errors-as-objects-example

piątek, 16 grudnia 2011

Communication between controllers and views via symbol messages

Sometimes a new, simple idea allows us to see the world better and understand the limitations that previously we did not even know we have. We are all accustomed to code like this:



The problem is that sometimes we want to format the flash message. Maybe part of the text needs to be in bold. Maybe we want to display it entirely different. In such case we just need to remind ourselves that there is no need to generate and put the message into flash object in the controller. Maybe we should not generate such informational messages in controllers at all. If that action was performed by mobile device you probably would not generate the message that phone is supposed to display to the user. That does not feel right, does it? Instead, you would provide enough information so that the device can provide a meaningful message. Also, it would be the job of the phone to display it in a proper language. So, why don't we try the same approach with browsers.



And here is the final effect:



Despite the fact the almost every example in the web uses the first approach, we should remember the we are not limited to that. We can do better. The solution is very simple, yet it is so easy to forget about it. We are not limited to displaying sentences generated by controllers. We can even make such messages a state of the art. Do you know any good examples of that?

wtorek, 6 grudnia 2011

convert: Unrecognized option (+repage)

If you are getting "convert: Unrecognized option (+repage)" error for example from paperclip gem it probably means that you need to install imagemagick package instead of relaying on graphicsmagick-imagemagick-compat package that is in Ubuntu and provides binary named same way.