Getting Heroku Cedar and Rails 3.1 Asset Pipeline to Play Nicely Together

I recently migrated a Ruby on Rails 3.1 app from the Heroku Bamboo stack to Heroku Cedar. If you’re doing the same, here are a few notes to help avoid snags on getting the Rails Asset Pipeline working efficiently.

Unlike Bamboo, Cedar does not offer Varnish as a reverse proxy cache, nor does it automatically gzip content. You need to do it yourself. Heroku recommends:

  1. Use memcached, with Dalli as the memcached client. Make sure to follow the Rails 3 section.
  2. Use Rack::Cache as a substitute for Varnish. Heroku links to this article which explains how to integrate Heroku with Rack::Cache. I couldn’t get it to work, so I hunted around and pieced together the folllowing riff.
In your runtime environment file (e.g. production.rb), add this:
require 'rack-cache'
My::Application.configure do
...
  # Enable Rack::Cache
  config.middleware.use Rack::Cache,
   :metastore => "memcached://#{ENV['MEMCACHE_SERVERS']}/meta",
   :entitystore => "memcached://#{ENV['MEMCACHE_SERVERS']}/body"

You can also set HTTP headers in production.rb as follows. (3600 is an example value, make this whatever you want.)

# Add HTTP headers to cache static assets for an hour
config.static_cache_control = "public, max-age=3600"

And you may want to add this to your config.ru to get gzip working:

use Rack::Deflater

References:

ActionController::RoutingError (undefined method ‘sub’ for nil:NilClass)

Or, “Man I’m glad Ruby on Rails is an open source framework”.

Ruby on Rails normally provides pretty good debugging information for dealing with errors, but I ran into an odd error today that had no debug info other than the exception itself. Here’s what I did to find and fix the problem.

Symptoms: “So, tell me, how long have you been feeling this way?” I had just added a new controller, model, views, and helpers to my app. Requesting a route to any of the new controller’s actions caused Rails to die with the exception “ActionController::RoutingError (undefined method `sub’ for nil:NilClass)”. No stack trace. No filename / line numbers. I knew one of the 20-odd new files I had just created must be causing the problem, but I didn’t know which one.

Diagnosis: “Breathe in. Breathe again. Again. OK, now cough. Ah, yes, now I see the problem.”: The only way I know of to diagnose these kind of problems is brute force. I added a debugger statement to the top of application_controller.rb and restarted the app. Then I requested a URL that was causing crashes, and from the breakpoint, stepped forward in the debugger (next, next, next…) until the app died. Studying the code that executed just before the app died didn’t yield an obvious result, but about 10 frames up the stack I could see a call to ActiveSupport::Inflector#constantize. I edited that method to print (puts) the parameters it was being called with, then restarted the server and tried again. Voila: the last parameter spit out by #constantize before the server died was the culprit.

#constantize PlaceLinksHelper for name PlaceLinksHelper

The Cure: “Take three of these and call me in the morning.” : It turns out I had created a helper module with the file name “places_helper.rb”, but within that file I mistakenly defined “module PlaceLinksHelper”. For some reason that shall remain a mystery to me, this caused things to fall-down-go-boom. The fix was simply to give the module the name Rails expected to find: “PlacesHelper”.

Upon Reflection… “I sure hope that doesn’t happen to me again. But it probably will.” I don’t know how you do this sort of diagnosis efficiently in a closed-source or compiled environment.  This is about the 5th time I’ve edited Rails source code to find and fix an oddball problem. I guess you could try attaching a debugger to binaries and then study the gobbledy-gook, but… no thanks. The only other thing I  could have done in a compiled/closed source setup is study each of my own files line by line. That would have worked, but it would have taken far more time.

Source code good.

Ubuntu upgrade breaks RMagick on Ruby Enterprise Edition

I recently upgraded the server running 5 Blocks Out from Ubuntu version 8.0.4 to 10.04. Somewhere along the way the binding between the rmagick gem and the underlying imagemagick libraries broke, probably because Ubuntu upgraded imagemagick on my behalf. And as I discovered over coffee this morning, if your Rails app depends on rmagick, and rmagick can’t bind to imagemagick, you’re hosed. So… here’s the solution I found after some panicked schnoozling around the web.

First, the error: when trying to start Rails you get an error that looks like this:

libWand.so.10: cannot open shared object file: No such file or directory - /opt/ruby-enterprise-1.8.6-20090520/lib/ruby/gems/1.8/gems/rmagick-2.12.2/lib/RMagick2.so

As you can see, we’re running REE (Ruby Enterprise Edition). This complicates matters a little, because you need to ensure you install rmagick in the directory where REE expects it to be.

Here is the series of magic chants needed to get rmagick and imagemagick working together on Ubuntu 10.0.4 with REE (Ruby Enterprise Edition).

$ sudo apt-get install imagemagick
$ dpkg -l | grep imagemagick
ii  imagemagick                       7:6.5.7.8-1ubuntu1                                         image manipulation programs

You can see we picked up imagemagick v6.5.7.8.

Next, verify where your REE binaries are installed, and install rmagick, making sure it ends up in the REE gem directory:

$ which ruby
/opt/ruby-enterprise-1.8.6-20090520/bin/ruby
$ sudo /opt/ruby-enterprise-1.8.6-20090520/bin/gem install rmagick

This step can take a while. Once it completes, check to see that rmagick is bound properly to imagemagick:

$ irb -rubygems -r RMagick
>> puts Magick::Long_versionThis is RMagick 2.13.1 ($Date: 2009/12/20 02:33:33 $) Copyright (C) 2009 by Timothy P. Hunter
Built with ImageMagick 6.5.7-8 2009-11-26 Q16 http://www.imagemagick.org
Built for ruby 1.8.6
Web page: http://rmagick.rubyforge.org
Email: rmagick@rubyforge.org=> nil

If that works, start the Rails console and try the same thing. You should get the same results, and the fact that console runs is a good indicator your app server will probably run too.

Done!

References:

Follow

Get every new post delivered to your Inbox.

Join 345 other followers