Tuesday, June 24, 2008

Rails: Exception handling has never been easier

Consider the typical case, where you've just finished writing your Rails application. You've considered all runtime errors that might appear in some specific known scenarios. You handled them all successfully, and you're about to deploy your application. The problem is, despite handling all "expected" thrown exceptions, there are still some typical exceptions that still might appear due to user input, rather than a bug in your logic.

A famous example of those exceptions is ActiveRecord::RecordNotFound. This exception is raised when calling 'find' on a model class using a non-existing id. In many cases, the model record id could be supplied as a parameter from the user. This means that such an exception, if not handled, could always be thrown, displaying an ugly stack trace to the user. Another example is ActionController::RoutingError. If a user tried a url that doesn't correspond to any application route, An ugly page will appear with a message "No route matching....". Of course, our concern also includes application bugs that might generate some exceptions. We want them to be logged and handled gracefully until we deal with them.

As expected, Rails provides an awesome generic mechanism for handling all unhandled exceptions. What's beautiful about it is that it can be added as a new aspect to the application, after we're done coding. The magical key is 'rescue_from', one of ActionController's class methods. Using rescue_from in ApplicationController, we declare a last line of defense for all unhandled exceptions in deeper levels. The following code snippet is a typical example of handling all unhandled exceptions using rescue_from:
rescue_from Exception, :with => :rescue_all_exceptions if RAILS_ENV == 'production'

def rescue_all_exceptions(exception)
case exception
when ActiveRecord::RecordNotFound
render :text => "The requested resource was not found", :status => :not_found
when ActionController::RoutingError, ActionController::UnknownController, ActionController::UnknownAction
render :text => "Invalid request", :status => :not_found
else
EXCEPTION_LOGGER.error( "\nWhile processing a #{request.method} request on #{request.path}\n
parameters: #{request.parameters.inpect}\n
#{exception.message}\n#{exception.clean_backtrace.join( "\n" )}\n\n" )
render :text => "An internal error occurred. Sorry for inconvenience", :status => :internal_server_error
end
end


Now, what exactly does this code chunk do? It's quite simple. We just declare that we want to handle exceptions of type 'Exception' (parent of all exceptions) using the method 'rescue_all_exceptions'. We add a condition to do this only if the application is running in production environment (of course we need the exception stacktrace in development rather than a clean error apology). Then we define the implementation of our exception handler 'rescue_all_exceptions'. Basically, we act according to the type of the passed exception. If it's RecordNotFound, we display a clean "not found" message. If it's a routing problem, we display a clean "invalid request" message. If it's neither of the mentioned, we assume that the exception is generated due to a bug. We log the details of the request and the exception, and display a clean apology for an internal server error.

What have we just done? We have transferred our application from an unsafe state where any runtime error could generate an ugly user-irrelevant page, to a safe state where any runtime error generates an appropriate message, logging its details for future analysis and fixing.


Sunday, June 22, 2008

RESTful Rails: param_parsers and XML issue

Rails, as a framework, and REST, as an architecture, have always been bound since the first appearance of Rails. Rails seamlessly promotes REST through a set of awesome features, including RESTful routing, resource url helpers, respond_to blocks, and others.

One of the most handy features that promotes the use of REST in Rails is ActionController::Base.param_parsers. param_parsers is a hash that maps standard (or even user-defined) MIME types to a parser procedure. The use of such procedures appears when the 'content-type' header of the request is set to one of the known MIME type names. In such a case, the parser is automatically invoked to parse the request body and evaluate the famous 'params' hash from it.

For example, "text/xml" and "application/xml" content-types both map to 'XML' MIME type. If a request is issued with such a content-type header, the procedure ActionController::Base.param_parsers[Mime::XML] is automatically invoked, evaluating params from the XML request body. This is really magnificent when you're writing a uniform RESTful application that's wanted to be able to comprehend, and respond with, multiple formats. You just write your action logic depending on the presence of the 'params' hash, letting the param_parsers to handle the translation.

However, there is a fundamental problem when talking XML. In a typical case, one of your controller actions could be waiting for more than one root param. The fundamental problem with XML is that it could only have a single root element. This means that, when posting XML, you can only post one root parameter. For example, the action logic could be operating on two parameters: params[:name] and params [:email]. With no single parent to both, XML form of this params hash can never be formed. And, of course, we don't want to change our application logic to overcome this problem. we need to attack it in its heart, the parsing part.

The good news is, it's hackable !! Rails lets you define a custom MIME type and its param_parser, or even override an existing MIME type param_parser. All we need to do is to define a conventional wrapper to be used in such a case. For example, we could announce that, in all cases of sending multiple parameters in XML, a single parent called 'hash' (or anything else) should wrap all of them. Then we override the XML param_parser to extract the wrapped tags to params.
ActionController::Base.param_parsers[Mime::XML] = Proc.new do |data|
source = Hash.from_xml data
result = {}
if source.keys.length == 1 and source.keys[0] == "hash"
source['api_hash'].each { |k, v| result[k.to_sym] = v }
result
else
source
end
end

All we have done is that we formed the hash from the posted XML (data), then checking for the very case of "a single root named hash", extracting its inner values to the root. Like this, our application logic needn't be touched.

Wednesday, June 18, 2008

Rails: Dynamic Test Fixtures

One of the clearest virtues of the Rails framework is the powerful testing support. Each generated model is created along with its unit test and test fixture. Likewise, each generated controller is created along with its functional test. Among others, test fixtures have proven to be the most handy testing feature introduced by Rails.

Rails test fixtures are named preset database records, written in YAML, that allow you to define the state of the database before each test case is run. Also, being named, a fixture can be accessed as a model instance through its name.

Well, old news I know. What I am introducing here is an extended feature of Rails fixtures, called "dynamic fixtures". Let's describe it with an example. Assume that we have a model called 'customer' with the following fixture:
bob:
name: Bob
email: bob@looney.com
appointment_date: 2008-07-01

Now assume that we need to write some code that does some logic on customers that have appointments today.
if customer.appointment_date == Date.today
...
Obviously, Any code that tests this logic using the given form fixtures, will succeed only on 2008-07-01, and fail otherwise. What we really want is to declare a fixture whose appointment_date is always set today. Well, cheer up, dynamic features allow you to declare fixtures like the following:
<% TODAY = Date.today %>

today_customer:
name: bob
email: bob@looney.com
appointment_date: <%= TODAY %>
not_today_customer:
name: alice
email: alice@looney.com
appointment_date: <%= TODAY.advance(:days => 10) %>
Using this dynamic fixture, tests will always run successfully without any need to change the fixtures every time tests are run.

Another well known situation where a dynamic fixture is needed is when we need to save in the database values that are computed in runtime. A typical example is when we save a hashed version of users passwords instead of plain ones. we compute the hashed password in runtime. The fixture can look like that:
Bob:
username: bob
password: <%= User.hash('1234') %>
Like this, we store a valid hashed version of the plain password '0000'. Handy, huh?


Monday, June 16, 2008

Rails: Order of requiring libraries actually counts

While working on a Rails project, I got stuck in a very confusing problem. I simply wanted to define a cache sweeper. The well known definition of any cache sweeper is
class MySweeper < ActionController::Caching::Sweeper
end

To my surprise, I got an error: Uninitialized constant "ActionController::Caching::Sweeper"!!!! What the heck? Sweeper class is not defined.

After a significant period of time spent snorkeling in the code of Rails ActionController, I found the line where the Sweeper class is defined (actionpack-2.0.2/lib/action_controller/caching.rb - line 627).
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
class Sweeper < ActiveRecord::Observer #:nodoc:
...

It turned out that the Sweeper class wasn't defined in my environment, just because ActiveRecord wasn't defined by the time 'action_controller' is required. I found out that actually I was requiring 'action_controller' before 'active_record'. I switched their order, and everything worked like a charm.

So, the bottom line is, ActiveRecord turned out to be a prerequisite for definition of certain classes and modules in other libraries like ActionController and ActiveSupport, so always require 'active_record' first.


Monday, June 2, 2008

Rails: Getting the best out of ActionMailer and smtp.gmail.com

A typical Rails developer casually needs to answer an important question when it comes to using ActionMailer to send mails; shall I use a local smtp server (like exim or sendmail) or shall I use a trustful, more reliable smtp server like smtp.gmail.com ?

Let's list the pros and cons of each choice. Using a local smtp server residing on the same machine with the application reduces communication overhead and consequently improves performance. However, mails sent using such smtp servers will mostly be considered as spam by most famous mail providers, because the sender is not a trustful email account. Using smtp.gmail.com eliminates this hazard, as the mail messages are sent from a trustful account, but dramatically decreases performance due to blocking communication overhead. Also, the used gmail account could be blocked if gmail sensed multiple connections using it, which is the typical case when the application is serving multiple requests.

Now let's tweak the second choice to eliminate its cons, getting the best out of ActionMailer and smtp.gmail.com. The basic idea is to prepare the mail to be sent, until the only step left is calling ActionMailer::Base.deliver. Instead of delivering the mail at this point, we'll dump it to the database using a model for all emails in the application. Then we'll do a simple rake task that gets those mails from the database, sends them and deletes them. Following is a detailed example.

ActionMailer configuration, normally, will look like this (after installing the TLS plugin)
  config.action_mailer.smtp_settings = {
:address => 'smtp.gmail.com',
:port => 587,
:domain => "www.yourdomainname.com",
:authentication => :login,
:user_name => "account@gmail.com",
:password => "@cc0unt_p@$$w0rd",
:tls => true
}


Assume one of the mailers in the application is called 'InvitationMailer'. The typical three lines of code that send an email are:
  mail = InvitationMailer.create_invite(invitation)
mail.set_content_type("text/html")

#deliver mail
InvitationMailer.deliver mail


Instead of this, we'll add a new model, PendinMail, with two attributes: id (integer) and serialized_mail (text). We'll replace the last line to serialize the mail object and save it in the database:
  mail = InvitationMailer.create_invite(invitation)
mail.set_content_type("text/html")

#dumping the mail to DB instead of calling deliver
PendingMail.new(:serialized_mail => Marshal.dump(mail)).save


The only thing left is a rake task that runs periodically on the server to send those mails:
  desc "Send all pending emails stored in DB table pending_mails"
task :send_pending_mails do
mails = PendingMail.find :all
for mail in mails do
ActionMailer::Base.deliver(Marshal.load(mail.serialized_mail))
mail.destroy
end
end


Now, what did we gain from this scheme?
- first: We used smtp.gmail.com and avoided considering our emails as spam.
- second: we removed the blocking communication overhead with gmail's smtp server, during which the request being served is blocked.
- third: we avoided the hazard of using the same gmail account from multiple connections, because we're sure that only one process will be using this account all the time.