Minimize or disable WEBrick logging

Posted by Bob Showalter Mon, 25 Jun 2007 02:22:00 GMT

I wrote a simple application today that used WEBrick servlets to serve up some content, and I wanted to minimize the logging that WEBrick puts out.

There are two kings of logging used by WEBrick:

  • Server logging, which is controlled by the :Server parameter passed to WEBrick::HTTPServer.new. This uses syslog-style log levels.
  • Access logging, which is controlled by the :AccessLog parameter. This logs each request, and is similar to the Apache access log.

The default server log level is INFO, but I wanted to change it to WARN. I also wanted to disable Access logging altogether.

Here’s what I ended up using:
include WEBrick
server = HTTPServer.new(
  :Port => 8000,
  :Logger => Log.new(nil, BasicLog::WARN),
  :AccessLog => []
)
Now the server is silent unless an unexpected problem occurs.

autotest errors after migration?

Posted by Bob Showalter Sun, 24 Jun 2007 00:15:00 GMT

If you generate a new model or run a migration that makes changes to your database, autotest will probably start reporting errors.

For instance, if you generate a new model, edit the migration and then rake db:migrate, you might see something like this:

ActiveRecord::StatementInvalid: Mysql::Error: #42S02Table 'sample_test.widgets' doesn't exist: DELETE FROM widgets

autotest is really smart, which is great. But this one trips it up a bit. The problem is that autotest is running your new widget_test.rb, which is trying to load your new Widget fixtures. However, rake db:migrate only affects your production database, not your test database.

The fix is simple:

  1. Run rake db:test:prepare. This applies the current development schema to your test database.
  2. Press Ctrl-C (once) in your autotest window. That tells autotest to restart itself and run the full test suite.

Write at least a simple functional test for each of your actions

Posted by Bob Showalter Sat, 23 Jun 2007 18:03:00 GMT

If you’re like me, you focus on writing unit tests more than functional tests. Most of the key business logic should be in your models anyway, and it’s easier to test at that level.

Nevertheless, it’s a good idea to at least write a basic functional test for each action to make sure the action at least runs through without any exceptions.

If you run autotest (which is a great tool), or at least run rake:test before any commits, these simple tests can catch boneheaded mistakes like one I made today.

I modified a controller and added an instance varible, which I then referenced in a partial. Everything worked fine and it looked good in the browser, so I committed it.

Unfortunately, I stupidly forgot that the partial was used in another context, and that action promptly broke, because I didn’t create the instance variable there. As it turns out, the proper fix was to move my instance variable out of the partial and into the main template.

A simple functional test on each action would have revealed my mistake right away.

Controller Code Smell in the Wild

Posted by Bob Showalter Thu, 21 Jun 2007 20:37:00 GMT

One of the trends you should be practicing is keeping unnecessary code out of your controllers. Here’s a real-world example of a controller Code Smell that I found in an application I’m doing maintenance on.

Here’s the original controller method:
  def clone_billing_address
    @customer = Customer.find(params[:id])
    l = Location.new
    l.address = @customer.address
    l.addr2 = @customer.addr2
    if @customer.is_commercial? then location_name = 'Main Building' else location_name = 'Home' end
    l.name = location_name
    l.city = @customer.city
    l.state = @customer.state
    l.zip = @customer.zip
    l.save
    @customer.locations << l
    l.customer = @customer
    l.save
    @customer.save
    redirect_to :action => 'show', :id => @customer
  end

The purpose of this action is to take some information from the Customer record and create a Location record, which is then linked to the Customer.

The smell is all the @customer method calls. That’s a hint that the Customer object should know how to clone itself. Think in terms of the “Tell, don’t ask” principle. Instead of asking the Customer object for a bunch of details about itself in order to create a Location, tell the object to create a location.

Here’s the refactored method:
  def clone_billing_address
    @customer = Customer.find(params[:id])
    @customer.create_default_location
    begin
      @customer.save!
    rescue
      flash[:warning] = "Unable to clone billing address: #{$!}"
    end
    redirect_to :action => 'show', :id => @customer
  end

All the logic for creating the location has been moved to the Customer model. Note that the original method didn’t do any error checking, so I added some.

Getting on the bandwagon

Posted by Bob Showalter Thu, 21 Jun 2007 15:13:00 GMT

Hey, all the cool cats are blogging on Rails, so here’s my go at it…