Tag: Rails

Rails handles exceptions that occur in production mode by rendering the file public/500.html or one of the other two (404.html and 422.html) if appropriate. Generally this is a good default, but in some cases it is a requirement to redirect the user to, or render specific pages for different types of exceptions. For instance, when an application provides a JSON API, it would be bad to return a 500.html to the caller when something goes wrong.

There are generally two ways do this in Rails. Either overwrite rescue_action_in_public in your controller or use the newer rescue_from to map exceptions to a handler. Ryan Daigle wrote a short but comprehensive blog post on this topic.

However, testing this is a bit tricky. config.consider_all_requests_local needs to be set to false and local_request? should be set to false as well. Testing this manually in the browser is cumbersome and error prone, so I needed a way to verify the behavior with a controller test. A quick ack –ruby rescue_action_in_public actionpack-2.3.4 turned up /actionpack-2.3.4/test/controller/rescue_test.rb exactly what I needed.

As it turns out, this is actually quiet easy to test. Basically, there are four things to consider and you are good to go:

1. Simulate the production environment in a setup method

2. Create a dummy controller with actions that reproduce the errors you want to test

3. Generate a route for your dummy controller if you have deleted the default routes in routes.rb

4. Call those methods in your test cases

With these four steps we got most uses cases covered. I ran into a problem, where I wanted to test an error that occurred during template rendering and required some special exception handling code. This is what worked for me:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Two weeks ago I stumbled upon a weird globalize2 behavior in a Ruby on Rails application I was working on.

globalize2 creates a has many association on a translated model that points to a proxy class. This proxy class holds the model translation for any given language. So if I tell it to translate a model called Tag, it will create a class called TagTranslation on the fly.

I needed to add behavior to TagTranslation. I wanted to add a before save hook that makes transformations on an attribute before it gets saved. Therefore, I created an active record model called TagTranslation and added the hook. This worked wonderfully in test environment but not in the development environment. For some reason, the transformations weren’t made.

A short debugging session in script/console revealed that there was no before save hook on TagTranslation in development mode. More precisely, the class never got loaded. So I dug into the globalize2 code and found out that it just overrides an existing class with the same name.

So what happened? In that particular case, this happened when config.cache_classes was set to false in the rails config. This is the default in the development environment. What is difference then? When config.cache_classes is set to true (Default in production and test) then Rails eager loads the classes at startup. The class Tag gets loaded before TagTranslation and therefore globalize has already created a proxy class. So when our TagTranslation gets loaded it just reopens an existing class and adds stuff to it. What happens in development mode? Rails only loads models when triggered through Object.const_missing. When the constant Tag is referenced somewhere in the code, the class Tag is loaded and globalize creates a proxy class called TagTranslation as well as a has many association. The association normally triggers the associated class to be loaded (our TagTranslation model) but in this case, the class already exists and therefore our TagTranslation model never gets loaded.

How to fix it? I have a fork on github with a failing test and a fix: http://github.com/phuesler/globalize2. Alternatively you can make sure that your class gets loaded explicitly. You have to do this after you call globalize’s translate method. Here is how you could do it for the aforementioned Tag model:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

ActiveRecord, the ORM of Ruby on Rails is a great tool to model database backed applications without writing a lot of repetitive SQL queries. By following the principle “Convention over Configuration”, you don’t need to setup a lot of configuration to get started. So no getter and setters and no mapping definitions if you follow the conventions. At first, it feels a little bit like magic, except that it is not. In the end, it is still generating SQL for you. That means, that even Active Record does a great job for you, it cannot protect you from bad queries.

During the past few months, I have seen my fair share of code that makes your database cry havoc and even worse, takes down an entire application on subsequent requests. Most of these mistakes could have been easily avoided by following one simple guidline: “Be reasonable. Before committing your code, take a look at the generated SQL and keep in mind, that the production database might have more than just 10 records inside.”

So let’s take a look at some example code that might be trouble for your application:

At first sight, this doesn’t look very bad. It gets the job done and probably performs nicely in a development environment. First, let’s look at the SQL being generated:

We are fetching every single column of every user in our system just to get the id’s. Even worse, for every user in the database, ActiveRecord creates a new user object. That takes time and uses up memory. Just imagine what would happen if you have a couple of thousands or more user in your database. At best, the request will just timeout after 60 seconds and everything goes back to normal. Worst case, memory consumption rockets, takes out the server and the database is jammed with long running queries. Not bad for just one line of code.

Let’s assume this line of code was somewhere inside a view in the admin interface providing the option to assign a project owner. To fix the problem, we can do the following:

  1. Paginate
  2. Don’t fetch all columns
  3. Move the code to the controller, that’s where it belongs

Here are some other gems I have found hidden somewhere in a view:


Tips

So here are my tips to prevent these kind of time bombs in your application:

  • Look at the generated SQL
  • Let AR/SQL do the math (conditions,count,sum,
    named_scope, sorting, ordering) instead of using ruby’s enumerable functions
  • Paginate, but paginate in SQL not on ruby collections (User.paginate vs. User.all.paginate)
  • Only fetch what you really need (:select => “id, name”)
  • Eager load associations with :include to avoid the n + 1 problem (Post.all.each{|post| puts post.author.name} vs. Post.all(:include => :author))
  • Whenever possible fetch all records you need from inside a controller or a model

Check SQL logs

To get a feeling of what is happening behind the scenes, I like to try things out in script/console and look at the generated SQL. To see it directly in irb, I change the Rails logger to STDOUT in my ~/.irbrc file:

To get even more information, there are plenty of cool profiling tools that hook into your Rails app and give you a nice GUI with all the information you need. Try out New Relic, FiveRuns TuneUp or Rack::Bug. There is also great Railscast explaining all of these three tools.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]