I’m currently working on two Rails projects and I wanted to share some random hints.
Trouble with unit tests and fixtures
Fixtures are a great help for unit testing. With fixtures you can define test data in YAML files and this data can be used in the unit tests. The problem with fixtures is that you often forget to include them in the test and spend hours to debug a test only because the test data is not in place. This happens especially when you introduce a new class into the test, remember to include the fixture.
Catch all exceptions in one place
A nice feature of Rails is the ability to handle all exceptions in one place. You don not have to wrap a begin…rescue…end around each method body. In the controller (or the ApplicationController in order to make this application-wide) define the following function:
def rescue_action_in_public(exception)...end
This function will be called whenever there is a non handled exception in public methods. You can then decide what to do with them, like log and redirect to an error page. I use this:
def rescue_action_in_public(exception)
case exception
when ActiveRecord::RecordNotFound, ActionController::UnknownAction
error(“File not found”)
else
logger.error “Exception: ” + exception.to_s
render(:file => ”#{RAILS_ROOT}/public/500.html”, :status => “500 Error”)
end
end
where error is a wrapper around redirect_to.
If you want to test this locally remember that this is only in effect if the function local_request? returns false and that local_request? always returns true in the development environment. Either use the production environment or redefine the local_request? method to return false and temporally set ActionController::Base.consider_all_requests_local = false in config/environemtns/development.rb.
Problems with create
The common way to create and save new objects is to have a new action + new.rhtml view and a create action that is called by the form in new.rhtml.
def new
@prof = Prof.new
end
def create
@prof = Prof.new(params[:prof])
if @prof.save
flash[:notice] = ‘Professor saved.’
redirect_to :action => ‘list’
else
render :action => ‘new’
end
end
Often you need another object in the new.rhtml view. Like every prof has a department and the user has to choose one with a drop down menu.
def new
@prof = Prof.new
@departments = Department.find(:all)
end
Now everything seems to work but if somebody wants to create a prof that does not validate, the create action renders the new.rhtml view. Then boom, you get an exception that some method was called on a nil object. And you spend some time searching and not knowing what happened.
The problem is that if the create action renders the new.rhtml view it must have all the needed objects in place. It misses a @departments = Department.find(:all) statement. And as you only need the @departments object if there were validation errors, do not load it upfront.
def create
@prof = Prof.new(params[:prof])
if @prof.save
flash[:notice] = ‘Professor saved.’
redirect_to :action => ‘list’
else
@departments = Department.find(:all)
render :action => ‘new’
end
end
Restart the webserver
Another common problem is when you get weird uninitialized constant exceptions. For example you define a new global array in a settings.rb and you can access it from ruby script/console but you get these errors in the webserver. Just restart the webserver in order to reload the environment. Only so some files are parsed again and Rails can access their content.
