#ThanksUnspace

Last night I went to the Ruby Job Fair hosted by the good people at Unspace. It was a lovely opportunity to chat with many smart, friendly people who are building good things with software and hoping to either hire or be hired. Here’s what I was thinking, and what I wish I had stood up to say out loud:

For a brief while, somewhere around beer number three, I had a flashback to 2005 when I moved to Toronto. Things were very quiet in the tech startup scene. Ruby on Rails was just starting to take off. DemoCamp had yet to begin. Lots of smart people were building things with software here, but most of them worked out of IT departments in office towers on Bay Street. The only “tech startup” people could commonly name was RIM, and it was already 10 years old and publicly traded by then.

Things have changed. A lot. There were over 100 attendees at Unspace HQ last night, with at least 20 employers pitching their companies, all vying to hire Ruby developers. I saw a lot of familiar faces, but also many new people I’d never met before. And pretty much every employer in the room was representing a startup. Joey de Villa was there from Shopify. Paul and Geoff from CommunityLend. Katherine Hague and Phil from ShopLocket. William and Bart from Engagio, freshly funded by Fred Wilson of Union Square Ventures. Paul from GaggleUp. Krista and Aidan from Winston. Mike with his University Health Network project. Startups everywhere.

That says a lot about Ruby on Rails; it’s clearly got great traction in the web startup space here. It also says a lot about Toronto and the community that has grown here around Ruby, Rails, and web startups. The barriers to creating startups are coming down. Starting a company is now much more socially acceptable here, desirable even. Designers and developers who want to be true craftspeople increasingly “drop out” of mainstream IT work to join startups. Technologies like Ruby, Rails, git and Heroku continue to reduce the cost of building and deploying great web software. And, at least from my little corner of the web, the local demand for web developers and designers seems to be at an all time high. All subjective, I have no hard numbers to offer, but I believe it’s real.

The startup scene in Toronto is really taking off.

I love that fact.

Much of this has come about organically, due to these enabling factors kicking in at the same time. But it really struck me last night that the community piece is different. This community didn’t just spontaneously assemble itself and become awesome overnight. Instead, a small handful of very diligent community builders, most notably Pete Forde, Meghann Millard and their amazing crew of coworkers at Unspace, worked hard and gave freely of their time and energy to help seed and build this. Free advice. Encouraging pats on the back. Rails Pub Nites. Technologic. Ruby Fringe. FutureRuby. Ruby Job Fairs. Friendship, even. They have done a ton to foster and ignite the web startup community here in Toronto. And they continue to do so.

So I will say it here, and hope others repeat it: Thank you, Unspace. You are doing great things for Toronto. We are proud of you.

#ThanksUnspace

Update 2012-02-13: Fred Wilson personally funded Egagio. The funding is not from Union Square Ventures. Thanks William for the correction.

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:

Rails 2.2.2: Noisy Translation Errors

[This is one of several posts on upgrading a Ruby on Rails app from Rails 1.x to 2.2.2.]

Rails 2.x added some nice support for internationalization. I’m using this to “Canadianize” the UI of my app by translating a few words from the EN-US locale to EN-CA. “Favorite” becomes “favourite”, “huh?” becomes “eh?”, “Coors Light” becomes “water”, and so on.

One gotcha I ran into is that translation errors don’t manifest themselves as exceptions. The default I18n implementation rescues translation errors and returns the failed translation keys as a string. This behavior is nice in the development environment, because you can see the strings showing up in your application’s UI. On the other hand it makes testing dangerous, because translation errors can easily go unnoticed, especially when running automated tests.

I prefer my tests to fail noisily. (Hmm… perhaps this should be the default behavior?) To that end, here’s a patch, also available on Pastie.

# The code below patches I18n to raise exceptions for all errors, including translation errors.
# The default (unpatched) behavior in Rails 2.2.2 rescues translation errors and returns the failed
# translation keys as a string. This behavior is undesirable in test, because it makes it too
# easy for translation errors to go unnoticed when running automated tests. Instead, we want to fail noisily.
#
# == Usage
# (within test_helper)
# require File.dirname(__FILE__) + '/i18n_patch'      

# Patch translation within views

module ActionView
  module Helpers
    module TranslationHelper
      def translate(key, options = {})
        options[:raise] = true
        I18n.translate(key, options)
      rescue I18n::MissingTranslationData => e
        raise e if RAILS_ENV == 'test'  # <<< this line is the patch. everything else in this method is original.
        keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
        content_tag('span', keys.join(', '), :class => 'translation_missing')
      end
      alias :t :translate

      def localize(*args)
        I18n.localize *args
      end
      alias :l :localize
    end
  end
end

# Patch translation in models and controllers
module I18n
  class << self
    def raise_all_exceptions(*args)
      raise args.first
    end
  end
end

I18n.exception_handler = :raise_all_exceptions
Follow

Get every new post delivered to your Inbox.

Join 345 other followers