Archive for the 'Mailman' Category

Ruby on Rails as Mailman External Archiver

Monday, January 23rd, 2006

I set up a Mailman mailing list. I want archives. I want these archives to look like other dynamic parts of the site (brought to you by Ruby on Rails). And I want to integrate a couple years’ worth of Topica messages.

At I first, I figured I’d use MHonArc or something similar. Mailman’s external interface is lightly documented, but simple and capable once you figure it out. Looking at the Python code helps in /usr/mailman/Mailman.

I decided to use Rails in the end. Maybe not the best choice, but I like keeping all my UI code in once place as much as possible.

I have an ActionMailer subclass with a delivery method:

class MailingListMailer < ActionMailer::Base
  def receive(email)
    post = Post.new()
    post.subject = email.subject
    post.body = email.body
    post.sender = email.friendly_from
    post.date = email.date
    post.save!
  end
end

class Post << ActionRecord::Base ....

I added an entry to PUBLIC_EXTERNAL_ARCHIVER in Mailman's mm_cfg.py file. On Suse 10, it's in /usr/lib/mailman/Mailman. I've add backslash line breaks for legibility here.
PUBLIC_EXTERNAL_ARCHIVER = '/srv/www/rails/obra/current/script/runner \
-e "production" "MailingListMailer.receive(STDIN.read)" \
>> /srv/www/rails/obra/shared/log/production.log'

I made several mistakes:

  • You need to restart Mailman for it to pickup your changes: /usr/lib/mailman/bin/mailmanctl restart
  • The apostrophes need to be around your command: '/srv/www ... production.log' Otherwise, you are trying to execute Python code, not assign a string variable.
  • Leave out -e "production" and Rails will execute in its development environment.
  • Ensure your logs are writeable by the mailman user, or nothing will work very well.

A typical Mailman error message in /var/lib/mailman/logs/error is "external archiver non-zero exit status: 127." Not very helpful. My shell foo isn't up to the task of redirecting standard error and standard out from the PUBLIC_EXTERNAL_ARCHIVER command. I got better info with Mailman calling a Bash script that then called the Rails runner. I validated that things worked at each small step, and I had to take baby steps because I kept putting a single quote where I needed a double quote, etc.:

  • Unit test for the ActionMailer
  • Run the ActionMailer in irb
  • Run the runner: sudo -u mailman /srv/www/rails/obra/current/script/runner -e "production" "MailingListMailer.receive(STDIN.read)"
  • Write a shell script to call the runner

Of course, it's helpful the public Mailman pages point to my new Rails-based archive. Here's how I do it. In Mailman/mm_cfg.py:
PUBLIC_ARCHIVE_URL = 'http://%(hostname)s/mailing_lists/%(listname)s/posts'

I like the end result, and it's easy to do once you know how to do it. Next step is to see if Rails can sew a header and footer onto the Mailman list info pages. I am keeping the gnu, of course.

gears