This post is about how to run your favorite rack application on IIS 7 using IronRuby. I’ve been unsatisfied with most other windows ruby app hosting I’ve tried, and IronRuby-Rack looks like it will fix that. (I haven’t tried deploying to JRuby on Windows, but I assume that experience would be pretty good.)

Surely I’m not the first to the punch on this, but there were some things I had to figure out that I thought I’d share.

I’m doing this in the context of a sinatra application I’m writing. More on the specific app later, but it wasn’t worth writing if it wasn’t going to run on IIS, or at least on Windows.

Also, I tried the ironruby-rack gem, but it’s pretty rough at this point. The best thing about it is that it included IronRuby.Rack.dll. My major complaint is that it put web.config in the root of the app, which meant that all the .rb files are in the web root. It seemed much classier to make public the web root, with web.config in there.

It wasn’t too hard to get the app running.

A rackup file seemed like a sensible first step, and it was. You can’t get very far these days without a rackup file.

I snagged IronRuby.Rack.dll from the ironruby gem, and checked it in public/bin. This was done because I’m lazy and didn’t want to build it myself. It’d be really nice if IronRuby.Rack was a stand-alone github project so I could fork it and patch it. Cloning all of ironruby just for a version of IronRuby.Rack that probably isn’t current wasn’t very interesting to me.

My rake tasks build the rest of the aspnet application. The tasks are aspnet:copybin, aspnet:logdir, aspnet:webconfig, and aspnet. The last just invokes the others.

aspnet:copybin finds IronRuby.Rack’s dependencies in the current ironruby environment and copies them into public/bin.

aspnet:logdir creates a directory for IronRuby.Rack to put its logs into. IronRuby.Rack is fussy about this directory existing, and about its ability to write to said directory.

aspnet:webconfig is more interesting. The web.config file it generates sets up the ASP.NET handler for ironruby.rack and tells it where everything is. I do bindingRedirects so that IronRuby.Rack can find the IronRuby version that I grabbed in aspnet:copybin. I started with the templates in the ironruby-rack gem and trimmed it down to what my app needed.

Here’s what I learned while crafting the web.config file:

IronRuby.Rack includes two hooks for ASP.NET: a module and a handler. The module seemed like the way to go, so I tried it first. I was a bit disappointed that it grabbed each request at the beginning of the application pipeline, and called EndRequest. It would have been fine if I didn’t care about anything that IIS was doing for me, but I did. I needed other modules to run (particularly the WindowsAuthentication module), and having IronRuby short-circuit the process broke that. I switched to the handler, and was much happier.

Also, IronRuby.Rack doesn’t mess with Environment.CurrentDirectory at all, so if your app needs to know about the directory it lives in, you need to tell it about that. Rails is pretty tolerant about this, with its Rails.root stuff, but bundler isn’t. Bundler was looking in c:\windows for my Gemfile. My first impulse was to set environment variables in web.config, but IronRuby.Rack doesn’t have hooks for that. So my app.rb has another bit of bundler bootstrapping that most apps can leave out: ENV['BUNDLE_GEMFILE'] ||= File.expand_path(__FILE__ + '/../Gemfile')

As a nice side-effect of using ASP.NET, to restart the application I just need to “rake aspnet:webconfig”. ASP.NET reloads the application whenever web.config changes.

Github is where to go to see the complete Rakefile.

One of the things that has been nagging me as rails has gone threadsafe is how to do something like store the current user. In non-threadsafe rails, you just store the user id in a class variable or global variable, and you’re done. In threadsafe rails, you end up with obvious concurrency issues from doing that.

The logical place to store the information is in the current thread’s context. That works well for most cases, but what happens if you want to spread work out across threads? And what if those threads need the information in the thread context?

A possible solution is to create a sub-class of Thread that copies parts of the thread context to the new thread’s context. This ended up being pretty easy to do, so I tossed up an example on gist. Here’s the core of it:

class ContextCopyingThread < Thread
  def initialize
    self[:x] = Thread.current[:x].dup
    super
  end
end