hacks

Resque Admin in Rails 3 Routes with CanCan

Resque is a background jobs queue that’s highly recommended over Delayed::Job if you are processing a lot of jobs. It uses Redis as the backend which doesn’t suffer from db related bottlenecks under high load.

Resque comes with a built-in admin interface that’s Rack compatible. In Rails 3, you can mount the Resque server admin directly in your routes.rb file.

mount Resque::Server, at: '/resque'

But you’ll definitely want to add password protection. Ryan Bates in his Resque RailsCast covers the basics of using Devise and HTTP auth. However, you’ll probably want to hook into your existing ACL system. In my case, I’m using CanCan.

CanCan is not available in the routes.rb by default, but it’s pretty easy to manually load the user and check permissions.

# routes.rb
namespace :admin do
  constraints CanAccessResque do
    mount Resque::Server, at: 'resque'
  end
end
# config/initializers/admin.rb
class CanAccessResque
  def self.matches?(request)
    current_user = request.env['warden'].user
    return false if current_user.blank?
    Ability.new(current_user).can? :manage, Resque
  end
end
# ability.rb
class Ability
  include CanCan::Ability
  def initialize(user)
    user ||= User.new
    if user.is_admin?
      can :manage, Resque
    end
  end
end

You’ll need User.is_admin? method or change the logic in Ability to suit your project.

Now an authenticated user with is_admin? == true will be able to access Resque admin. Other users will get a 404 since no route matches.

Thanks to Arcath’s blog post for initially pointing me in the right direction.

Rescuing from Rack::Timeout to Close MongoDB Connection

I’m using Rack::Timeout on Heroku to kill requests before Heroku’s 30 second limit is reached. This helps applications play nice with cloud infrastructure but can cause some unexpected bugs with MongoDB connections being reused by the next request.

The errors showed up as…

Mongo::ConnectionFailure: Expected response 372 but got 371

Mongo ruby driver >=1.3 catches the reused request and raises an error, but this still means the first and next requests both returned errors. It’s much better to catch the initial timeout and close the connection.

It seems like rescue_from Timeout::Error in ApplicationController should work, but for some reason the exception passed in is a Class and not Timeout::Error – most likely due to Rack::Timeout wrapping the entire app.

I googled around and couldn’t find a more elegant solution, but the below snippet in ApplicationController does the trick.

  # note: can not rescue from Timeout::Error directly because a timeout from Rack::Timeout ends up passing in Class as the exception
  rescue_from Exception do |exception|
    # catch Timeout::Error or message from Rack::Timeout
    if exception.is_a?(Timeout::Error) || /execution expired/ =~ exception.message
      # prevent subsequent requests from reusing this mongo connection
      Mongoid.database.connection.close
    end
    raise
  end

As a side note, Heroku’s new cedar stack does not have the 30 second limit if you’re streaming data – Rails 3.1 supports streaming.

Rails 3 + STI: Making Associations Work Properly

I really like the idea of Single Table Inheritance (STI) for all sorts of applications to keep code DRY and make it easier to organize object behavior. The only problem is that Rails 3.0.3 doesn’t fully support STI with association collections.

Let’s say you have a User model that has many badges. The badges will be stored in the badges table but you want to implement each badge in a subclass. All you have to do is make sure there’s a :type field of type string in your badges table and Rails STI support should take care of the rest (well, in theory).

class User < ActiveRecord::Base
  has_many :badges
end

class Badge < ActiveRecord::Base
  belongs_to :user
  def award
    raise "Must implement in subclass"
  end
end

class Badges::Superhero < Badge
  def award
    user.status = 'superhero'
  end
end

Now you can do cool things like create a new Superhero badge and add it to a user’s badge collection.

user = User.first
badge = Badges::Superhero.new
user.badges << badge

But for some weird reason, you can’t use the best practice of building a badge directly from the user’s badges collection.

user = User.first
badge = user.badges.build(:type => Badges::Superhero)
# badge.class == Badge

This is particularly annoying if you’re trying to create new badges from a form where :type is a drop down menu.

The reason the collection build method doesn’t work as expected is because :type is a protected field and ActiveRecord::AssociationReflection doesn’t fully support STI (at least in Rails 3.0.3).

Not to fret, hacks to the rescue!

You have two options to make STI work as expected.

Option 1: Override the Badge.new method to handle :type

class Badge < ActiveRecord::Base
  belongs_to :user
  self.abstract_class = true

  class << self
    def new_with_cast(*a, &b)
      if (h = a.first).is_a? Hash and (type = h.symbolize_keys[self.class.inheritance_column.to_sym]) and (klass = type.to_s.constantize) != self
        raise "Must be a subclass of Badge" unless klass < self  # klass should be a descendant of self
        return klass.new_without_cast(*a, &b)
      end
      raise "Badge must be created through a subclass."
      new_without_cast(*a, &b)
    end
    alias_method_chain :new, :cast
  end
end

Option 2: Patch AssociationReflection to behave more intelligently

class ActiveRecord::Reflection::AssociationReflection
  def build_association(*opts)
    col = klass.inheritance_column.to_sym
    if (h = opts.first).is_a? Hash and (type = h.symbolize_keys
) and type.class == Class opts.first
.to_s.constantize.new(*opts) elsif klass.abstract_class? raise "#{klass.to_s} is an abstract class and can not be directly instantiated" else klass.new(*opts) end end end

My preference is Option 2 even though it might break in future releases of Rails. I’d rather have Rails behaving as expected than pepper my models code with repetitive hacks.

The above solutions were inspired from a couple of different posts and sources.

I submitted Option 2 as a patch for Rails.

Building the Wikitude Sample App for iPhone 4

I’m new to iPhone development but wanted to see how quickly I could build an augmented reality prototype for a simple AR treasure hunt game.  I looked at the APIs for Layar, ARToolKit, Junaio, and settled on Wikitude for this test.  Wikitude provides an augmented reality library that you can drop into your iPhone project in less than an hour.  Bad ass.  Assuming you’re not an iPhone dev n00b like me.  Then it takes you three hours.

If you’ve never tried to build a downloaded sample app, hopefully this guide will save you a couple hours of futzing around with plist and project configuration.

I’m using XCode 3.2.3 with the iOS SDK 4.0.1 and targeting an iPhone 4 device connected to my MacBook Pro.

Getting Started

  1. Download the Wikitude iPhone API and unzip the sample app
  2. Open WikitudeAPI-SCM-Test.xcodeproj in XCode
  3. Click “Build and Run” on the XCode toolbar

You’ll probably get one or more of the following errors:

  • There is no SDK with the name or path ‘iphoneos3.1.2′.
  • Code Sign error: a valid provisioning profile matching the application’s Identifier ‘com.companyname.productname’ could not be found

Time for to fix the sample app configuration.  Search paths, target architecture, base SDK, and code signing will all need to be changed in order to successfully build the project.

Before you go any further, make sure you’re set up through the iPhone Dev Center Provisioning Portal to sign code.  The Wikitude API will not run in a simulator, and building for devices requires a valid code signing identity.  You can apparently use the XCode Organizer to create your code signing profiles.  However, I kept getting an unexpected error and ended up doing everything through the web portal.  For simplicity, I created a wildcard domain (com.simple10.*) for the App ID so that I can use the same provisioning profile for other apps.

Once your provisioning profile is ready to go, it’s time to fix the XCode project configuration so the sample app will build.

Fixing Config Options in XCode

Open the project info build options panel by double clicking on WikitudeAPI-SCM-Test project.

Wikitude Sample App Configuration

Make the following changes to the build options…

Architectures

  • Architectures: Standard (armv6 armv7)
  • Base SDK: iPhone Device 4.0
  • Build Active Architecture Only: [checked]

Code Signing

  • Code Signing Identity > Any iPhone OS Device: [select your provisioned profile]

Open the targets build option panel by expanding the Targets tree and double clicking on WikitudeAPI-SCM-Test.

Wikitude Sample App Config Screen 2

Make the following changes to the target build options…

Search Paths > Library Search Paths

  • Remove “$(SRCROOT)/../WikitudeAPI-SCM/build/Debug-iphoneos”
  • Remove “$(SRCROOT)/../../../Desktop/WikitudeAPI”

Open WikitudeAPI_SCM_Test-Info.plist and edit the bundler identifier to match the domain used in your provisioning profile.

Wikitude Sample App Screen 3

Finally, make sure that Active Architecture setting in the build target drop down menu is set to armv6.

Click Build and Run.  The app should build, deploy, and automatically run on your connected iPhone.

Macports to Homebrew

Thanks to a new macbook pro with a clean OS install and minimal dev tools, I made the switch from macports to homebrew.  Easy enough with a few caveats.

The homebrew people recommend uninstalling macports first.

Then install homebrew.

Everything worked fine until trying to ‘brew install imagemagick‘.  The compilation fails.  This seems to be a common problem.

The quick solution was to ‘sudo brew install imagemagick‘.  Compilation worked.  However, sudo installing ghostscript after installing imagemagick failed to find the jpeg lib which was previously installed without sudo.  Yuck!

The final solution was to brew uninstall all image packages (jpeg, libtiff, ghostscript, etc.) and then run the following…

$ brew list
git
$ sudo brew install ghostscript
$ sudo brew install imagemagick
$ brew list
ghostscript    imagemagick    jpeg
little-cms     git        jasper        libtiff
pkg-config

I’m not sure if it’s possible to install ghostscript and imagemagick without using sudo.  I tried several times, and in the end sudo worked where other attempts failed.