Last Updated: February 25, 2016
·
924
· Keeguon

Start a Rack app with multiple interpreters

Late last week I was looking at source of discourse and I stumbled upon their Gemfile which is particularly interesting since it shows a way to have a different lockfile depending on a condition. They mainly use it to ensure compatibility between Rails 3 and Rails 4 but this gave me an idea. Since I'm often required to run Rails apps using different interpreters, mainly MRI and jRuby I thought it might be a good idea to use their code to fit my needs so I quickly added the following lines to my Gemfile:

# monkey patching to support dual booting
module Bundler::SharedHelpers
  def default_lockfile=(path)
    @default_lockfile = path
  end
  def default_lockfile
    @default_lockfile ||= Pathname.new("#{default_gemfile}.lock")
  end
end

module ::Kernel
  def jruby?
    !(RUBY_PLATFORM =~ /java/).nil?
  end
end

if jruby?
  Bundler::SharedHelpers.default_lockfile = Pathname.new("#{Bundler::SharedHelpers.default_gemfile}_jruby.lock")

  # Bundler::Dsl.evaluate already called with an incorrect lockfile ... fix it
  class Bundler::Dsl
    # A bit messy, this can be called multiple times by bundler, avoid blowing the stack
    unless self.method_defined? :to_definition_unpatched
      alias_method :to_definition_unpatched, :to_definition
    end
    def to_definition(bad_lockfile, unlock)
      to_definition_unpatched(Bundler::SharedHelpers.default_lockfile, unlock)
    end
  end
end

It ables me to handle both MRI and jRuby at the same time and run my Rails apps either using one or the other at the same time. For example it means I can start them this way:

$ ruby -S bundle exec rails s -p 3000 -P tmp/pid/server.pid
$ jruby -S bundle exec rails s -p 3001 -P tmp/pid/server_jruby.pid

Then if you use it nginx you could set up an upstream which proxies your request to the different instances which is kinda cool IMO.