Recently by Frank Showalter

The Problem

You want to deploy your Rails 3.0.7 app (let's call it "myapp") to a subdirectory (let's call it "apps"), rather than the root of your website (let's call it "www.example.com"). So, you want to access your app from this URL:

http://www.example.com/apps/myapp
Let's assume you're using Passenger, and have followed the instructions here here for Nginx or here for Apache. You deploy your app, either manually or via Capistrano, and hit the site only to see:

404. Not Found.

Undaunted (hey, no deployment works the first time) you check your web server logs and find that Passenger is starting just fine. Hmnn... you move on to your Rails production log and find something like this:

No route matches "/apps/myapp/widgets/index"

(Where /widgets/index is the resource mapped to your root route.)

This tells us that Rails doesn't realize "/apps/myapp" is the root it should use for the relative URLs it generates. This is an easy enough fix, just wrap our routes in a scope, something like:

scope "/apps/myapp" do  
  resources :widgets
  root :to => "widgets#index"
end

Restart Passenger, and you can see your site!

(Alternatively, we could wrap the whole rack application in config.ru, but I prefer to do it in the routes.)

But Wait!

Something's wrong. You can see your site, but all the content's unstyled and the image links are broken. You check the outputted markup and discover, sure enough, all the asset links are still referencing root as their relative URL. For instance, your stylesheet is pointing to:

/stylesheets/application.css

Instead of:

/apps/myapp/stylesheets/application.css

Ditto any javascript or image links.

What?

Here's where things get rough. A bit of Googling will point you toward setting:

config.action_controller.relative_url_root = "/apps/myapp"

And indeed that did work in older versions of Rails, only when you try and set it in 3.0.7, it throws an error:

/actionpack-3.0.7/lib/action_controller/deprecated/base.rb:11:in 
  `relative_url_root=': wrong number of arguments (1 for 0) (ArgumentError)

This is because the method was deprecated, but incorrectly, as the signature looks like this:

def relative_url_root= 
  ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root= is ineffective. " <<
  "Please stop using it.", caller
end

This bug has been present since Rails 3.0's initial release, and it's true, setting it is ineffective, unfortunately, the variable is still being used. Take a look in:

/actionpack/lib/action_view/helpers/asset_tag_helper.rb

And you'll see that all the javascript_include_tag and stylesheet_link_tag helpers call down to this method:

def compute_public_path(source, dir, ext = nil, include_host = true)
  return source if is_uri?(source)

  source += ".#{ext}" if rewrite_extension?(source, dir, ext)
  source  = "/#{dir}/#{source}" unless source[0] == ?/
  source = rewrite_asset_path(source, config.asset_path)

  has_request = controller.respond_to?(:request)
  if has_request && include_host && source !~ %r{^#{controller.config.relative_url_root}/}
    source = "#{controller.config.relative_url_root}#{source}"
  end
  source = rewrite_host_and_protocol(source, has_request) if include_host

  source
end

Which, of course, uses controller.config.relative_url_root to set the root URL, which will always be blank. Indeed, the variable does seem to be getting set in:

/actionpack/lib/action_controller/metal/compatibility.rb
      # ROUTES TODO: This should be handled by a middleware and route generation
      # should be able to handle SCRIPT_NAME
      self.config.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']

The Solution

So, you'd think setting ENV['RAILS_RELATIVE_URL_ROOT'] in your production.rb config would work, but, like the deprecated comment says, setting config.relative_url_root is ineffective. My solution was to monkey patch compute_public_path to read directly from ENV['RAILS_RELATIVE_URL_ROOT'].

While this certainly isn't ideal, it'll do, as this looks fixed in the 3.1 branch (but not 3.0.8).

So how'd this bug make it through Rails regression testing? It looks like they're using a mock controller with a config method in the tests, and use that method to set config.action_controller.relative_url_root on the mock, which isn't deprecated, and thus works.

Developers, how confident are you that you could go from an empty virtual machine and an OS installation DVD, to a fully-functioning instance of your application? Before you laugh it off, think about it. When was the last time you sat down and installed your server OS? When was the last time you installed and configured Linux, Apache and MySql? Or Windows Server, IIS and SQL Server if you're deploying to Windows?

How many times have you dealt with bugs that you couldn't reproduce locally? How many of those were due to a configuration difference between your development and production environments? Here's the harsh truth: your development environment is a lab and production is the real world and you'll never be able to perfectly mirror the two.

But you can educate yourself about the differences.

Over the last week, I got a chance to build both Linux and Windows production servers from scratch. While things have certainly gotten a lot easier in the years since I last did this sort of build, I still found myself turning to Google for a myriad of things, whether it be best practices for securing a MySQL install or how to enable external connections to SQL Server Express.

These are the sorts of things we tend not to think about as developers, but we should. We tend to take for granted the infrastructure that supports our applications and the result is a rather narrow view of our application's world. Getting our hands dirty with a scratch OS install helps us appreciate the big picture, and is something I'd recommend every developer do, at least once per platform release. The end result will not only be a better understanding of the nuances of your production environment, but a better understanding of your application as a whole.

The Problem

You downloaded and installed the latest 64-bit DMG from MySQL and now you’re getting “could not load driver” errors in DBI (Ruby or Perl) and Rails.

The Cause

In Ruby, the ‘mysql’ and ‘mysql-2’ gems were compiled against older version of the MySql library. Ditto DBD:Mysql module in Perl.

The Solution

Update 6/6/2011: Google lead me to a better solution:

export DYLD_LIBRARY_PATH=/usr/local/mysql/lib/

That should work for everything! But in case it doesn’t, this will fix Rails:

sudo install_name_tool -change libmysqlclient.16.dylib \ 
/usr/local/mysql/lib/libmysqlclient.16.dylib \
/usr/local/lib/ruby/gems/1.9.1/gems/mysql2-0.2.6/lib/mysql2/mysql2.bundle

And this will fix Ruby’s DBI:

sudo install_name_tool -change libmysqlclient.16.dylib  \ 
/usr/local/mysql/lib/libmysqlclient.16.dylib \
/usr/local/lib/ruby/gems/1.9.1/gems/mysql-2.8.1/lib/mysql_api.bundle

Note: you’ll need to substitute both the appropriate path to the gems, and the appropriate version of the MySQL library.

Fixing Perl’s DBI is a little trickier. You need to download the MySql DBD source and run the install_name_tool on the bundle before you install. Something like:

sudo install_name_tool -change libmysqlclient.16.dylib \ 
/usr/local/mysql/lib/libmysqlclient.16.dylib \
/path-to-my-sql-dbd-source-folder-with-the-bundle-file/mysql.bundle