ś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.


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.

niedziela, 26 czerwca 2011

Faster rails development part 2 - Annoucing Active Reload

As you may know I have been experimenting recently with speeding up rails development mode.

In my previous post I described a solution that decreases number of code reloads by checking for changed files. However there were few caveats and the solution was definitely not beautiful and simply to apply.

I invented a second solution based on guard which initially looked promising but after using it for some time on my project I noticed that it does not work well all the time and I was not able to determine why.

Here it is in case you are curious:

I would be glad to heare some opinions about why it sometimes stops working after some time... Seriously, I have no idea.

Anyway, I decided to go third way and build "simplest thing that could possibly work"

What is it ? Instead of relaying on third party tool that can detect file changes (like watchr or guard) lets just check mtime of every file before request to check if the code needs to be reloaded or not. After all, it takes only 0.01s to check mtime of 300 files on my few years old computer. And that's how Active Reload was born.

Feel free to install the gem, try it with your project and enjoy higher productivity. You can even support me if Active Reload saved you some time. I would be glad to know if it works for you or if there are any issues.

niedziela, 22 maja 2011

Get faster rails development environment for Rails 3.0.7 in few seconds

I would like to describe two techniques for speeding up your Rails development environment.

Faster rendering

All credits for mentioning this goes to Eric.

This will only be helpful if your view renders lot of small partials. Ex. you render a big table and every cell is rendered by the same partial.

Faster code reloading

Rails "forgets" your code after every request so that next time the code is loaded again using autoloading. However this is totally unnecessary if you don't change the code between requests. Let's reload the code before every request if the code changes between them.

We use 'config/environments/development.rb' to execute watchr script for observing files. We only want to observer them when 'rails server' command was executed. In 'rails console' mode we use the 'reload!' command for manual code reloading.

The watchr script is going to update modification time of '.watchr' file and notify us about it if we want. You can enable this functionality by creating 'config/watchr.notify' file. Simple remove the file to disable it. Obviously you might need to change it a little bit to work under OS X.

Add the 'config/watchr.nofity' file to .gitignore so every member of your team can have its own settings:

Create the empty '.watchr' file which is needed only for reading and updating its mtime.

Change your 'Gemfile' to include watchr gem and point rails to my branch which contains commit that changes code reloading to be executed before request:

As you can see in the mentioned commit the mtime of '.watchr' file is used to discover whether changes occurred or not:

Your application code won't reload faster but it will only reload when it is necessary. It means the if you have big application which takes 15 seconds to reload then it is done only once after a change and subsequent requests won't trigger code reloading until next change occurs. I believe that this is still useful because usually people make at least few clicks after every code change.

Known caveats

  • :-( Biggest problem: New files not automatically watched. I would really appreciate some help.

  • This code assumes that 'lib/' directory is not added to your autoload_path.

  • You must change the file to reflect database changes.

  • fork() is used to spawn watchr script. This won't work on some systems (windows?) or ruby implementations (jruby?). However you can still execute the script manually (just don't forget to do it or you code wont' reload at all!) or use some other technique to do it automatically. Also it might be necessary to run 'bundle exec watchr' instead of just runnning 'watchr'.

I would love to see the idea implemented in Rails with all those caveats fixed.

piątek, 13 maja 2011

The Witcher 2 - Installation error [pack3581.rra]

In case you encountered an error:: "There is not enough space on the disk." for file f:\Program Files\Wiedźmin 2\CookedPC\pack3581.rra you should know that it might mean 2 things:

  • there is not enough space on the disc;

  • you tried to install the game on FAT filesystem which does not handle such big files. Try to install the game on NTFS filesystem or convert existing one.

Wiedźmin 2 - błąd instalacji [pack3581.rra]

Jeśli w czasie Twojej instalacji Wiedźmina 2 wystąpił błąd: "Za mało miejsca na dysku" dla pliku f:\Program Files\Wiedźmin 2\CookedPC\pack3581.rra to może oznaczać, że:

  • faktycznie masz za mało miejsca na dysku;

  • próbowałeś zainstalować grę na systemie plików FAT, który nie obsługuje tak dużych plików. Zainstaluj na grę na dysku z system plików NTFS