Rails gets sane

Some time ago, I wrote about The Wrong Defaults, explaining how nearly all templating systems for the web default to “insecure”.

Well, it looks like some rails people are paying attention: Auto sanitized templates with Erubis


Deleting a session item

A few days ago, I spent a little while attempting to work out how to delete an item from a session (in Rails). I wanted to remove session[:user_id] when a user logged out.

After nosing around for a bit, it turns out that you can’t actually do this. You can set the value of :user_id to nil, which is probably “good enough” most of the time. It’s still a little bit irritating however.

The reason that you can’t do this is not because of poor design in Rails though. Rails reuses part of the standard Ruby distribution. And look at CGI::Session.delete. It takes no arguments and deletes the entire session. Contrast this with Hash.delete. To my mind, they both implement the same interface.

It would probably have made more sense to have CGI::Session.destroy, but I doubt that’s going to change now after the code has been in the wild for such a long time.


Prototype and window.onload

We’ve known for some time that simply assigning to window.onload is bad. Simon Willison created addLoadEvent a long time back to work around the problem. But I’m in Rails, and we have prototype. So what’s the correct idiom?

Well, after a bit of playing, it seems to be this:

  <% content_for("page_scripts") do %>
    Event.observe(window, 'load',
      function() { $('username').focus() }
  <% end %>

I do love that $() function.

This assumes that you have a layout that looks something like this, in order to insert bits of JavaScript into the head.

  <script type="text/javascript">
    <%= @content_for_page_scripts %>

Anyway, the solution seems a little more verbose than addLoadEvent(), but not disastrously so.



I’ve spent some time this evening playing with SwitchTower. It’s a tool for deploying Rails applications. It’s just perfect for sending what’s on my test environment (Mac) up to my server (FreeBSD). Not only that, it’s relatively simple to get going, compared to many other deployment tools I’ve used (and written) in the past.

But it’s also very flexible. For example, just when I thought I’d gotten everything set up and running, I discovered that my database.yml file was wrong. It’s fine for development, but not the live server. Yet switchtower has a really handy feature where it can render a template into a file. So now I get my database.yml generated on install, pointing at the right place.

task :after_update_code do
  buffer = render("config/database.yml.rhtml", :shared_path => shared_path)
  put buffer, "#{release_path}/config/database.yml", :mode => 0644

Anyway, if you’re doing Rails at all, invest some time in learning SwitchTower. It doesn’t take long, and you’ll be much better off for it.


Rails Deficiences

There’s been quite a lot of criticism of Rails recently:

But the fact of the matter is that Rails gets so much right, that it’s still worth using. And I intend to.

Not only that, but I have every confidence that these issues will be addressed. In my case, I can see from the tickets that the whole area of database defaults is being revamped for Rails 1.1, so I’ll probably end up upgrading shortly after it comes out.

I’m reminded of the furore about the Google Web Accelerator last year. In that case, Rails was clearly in the wrong, and I said as much back then (although not fingering rails, apparently). In the end, a moderately sensible comprimise was reached: You can supply a :post parameter to the link_to command, in order to try and ensure that something gets submitted via post. It’s not perfect, as it relies on JavaScript to work, but it goes a good way to making it easier for people who don’t care to do the right thing.

In summary, Rails ain’t perfect, but it’s getting better all the time. And the way to speed it up? Patches Welcome.


Typo Times Fix

After a bit of searching, I’ve found the problems with the odd times on this weblog. It’s a problem with ActiveRecord (part of Rails) and in particular, PostgreSQLAdaptor.

Basically, it computes a default value once, inside Ruby, and then caches it. Which is a bit odd, to say the least.

There are a number of open tickets related to this: 2873, 2877, 2257 and probably more. But looking at them, it seems that the problem will be fixed in Rails 1.1, which should be released “soon”.

Meanwhile, I’ll patch this particular instance of Typo to update the time on create. This is what I’ve used, and it seems to be working ok.

Index: app/models/article.rb
--- app/models/article.rb       (revision 727)
+++ app/models/article.rb       (working copy)
@@ -94,6 +94,9 @@
     self.extended_html = HtmlEngine.transform(extended, self.text_filter)

+  # Work around PostgreSQL adaptor (
+  before_create { |art| art.created_at = }
   def self.time_delta(year, month = nil, day = nil)
     from = Time.mktime(year, month || 1, day || 1)

Index: app/models/comment.rb
--- app/models/comment.rb       (revision 727)
+++ app/models/comment.rb       (working copy)
@@ -29,4 +29,7 @@
     self.body_html = HtmlEngine.transform(body, config["comment_text_filter"], [:filter_html])

+  # Work around PostgreSQL adaptor (
+  before_create { |art| art.created_at = }

Rails Migrations

Recently, I’ve been playing around with Rails and one of the things that has really impressed me is the support for migrations. If you haven’t come across them before, it’s a well defined way of doing agile development with your databases. Your database contains a table schema_info with a column version. And then you define an ordered series of scripts which each contain up and down methods to migrate between versions.

If that sounds complicated, check out the example from the documentation:

  class AddSsl < ActiveRecord::Migration
    def self.up
      add_column :accounts, :ssl_enabled, :boolean, :default => 1

    def self.down
      remove_column :accounts, :ssl_enabled

You’ll notice that we’re not doing any SQL here. That means that as a side effect of falling into line with Rails, you get a level of database portability for free. Marvellous!

All this was prompted by looking at the recipe on migrations in the new Rails Recipes book.

This is not to say that migrations are a panacea, however. As always, by trading SQL for a domain language, you lose access to some features. Foreign key relationships aren’t easy to express in this manner, for instance. But that doesn’t mean they can’t be used—for an example see robby’s post on Rails Migrations and PostgreSQL Constraints. It just ties you to a particular platform. Which is probably no big deal for most people, as they’re likely only using one database anyway.

So far, I’ve had two major tripups with migrations.

  1. The SQLite adaptor doesn’t appear to support some things properly. I think it managed to nuke the contents of a column I was playing with. But I need to look further at this.
  2. Purely my own fault. I had set up a load of migrations in my development environment and I forgot to transfer them to the test environment. I got very confused for a while until I remembered to do that… Again, it’s my own fault because I was running the individual test files by hand, instead of using rake.

But still, compared to everything else I’ve seen, migrations in rails are superb. Particularly after going through similar pains at work in the last week! Rails once again hits the sweet spot, providing just enough framework to get things done easily, without getting in the way too much.

P.S. If you are playing with migrations, I recommend installing Jamis Buck’s verbose_migrations plugin. It gives you a little more feedback on what’s going on. Normally, I like the Unix way of “no feedback until it goes wrong”, but whilst I’m still learning to trust migrations, this plugin is helpful.


Beta Books

I’ve recently purchased a beta copy of Rails Recipes. It’s obviously not 100% finished, but appears to be largely complete. It’s already been really, really helpful to me, in particular the chapter on migrations.

But what’s really, really cool is the link at the bottom right of each page in the PDF. It just says “Report erratum”. Clicking it launches a web browser pointing at the erratum page for that book. It’s the easiest way of giving feedback that I have ever seen.

It’s not just erratum of course. Because it’s still a Beta Book, suggestions are also solicited. So I added a little note about how linking to the docs for migrations would be a good idea. Yes, it’s a very small point. But together, lots of people with lots of small points adds up to a much, much better book. I can’t wait to get my print copy of the final version!


Locomotive / Typo bug

I’ve been trying to get typo working on my mac, using the very shiny Locomotive. Sadly, I kept getting display errors in my sidebar. After spending 3 hours looking at it last night, I’m now pretty sure that it’s a bug in YAML::load, which is called from inside ActiveRecord::Base#object_from_yaml. It works fine on my FreeBSD server running ruby 1.8.4. But under Locomotive (ruby 1.8.2), Instead of getting back a hash, I get some internal YAML class. Hmmm.

Thankfully, a simple patch is all that’s required to get things working properly.

Index: app/models/sidebar.rb
--- app/models/sidebar.rb       (revision 724)
+++ app/models/sidebar.rb       (working copy)
@@ -27,7 +27,10 @@

   def active_config
-    self[:active_config]||{}
+    ac = self[:active_config]||{}
+    # XXX Work around a bug in some versions of Ruby / YAML.
+    ac = ac.value if ac.respond_to? "value"
+    ac

   def html_id

I’m so glad that I imported typo into subversion on a vendor branch. It makes this sort of thing a heck of a lot easier.

I should go and file a bug in the typo trac. But I have to admit to not being sure that it’s up and running properly yet…


Reboots for Users

Jamis Buck has a good article on how to run TextDrive and Lighttpd together. It involves switching the Rails FastCGI processes to be managed independently from the web server. Essentially, the Rails FastCGI runners become independent daemons.

But what’s really useful is the tip in the first comment. It mentions an obscure crontab feature: @reboot. This will let you, as a user, execute a script when the machine boots up. This is dead handy, as it means that your application can be automatically restarted on server boot without having to bother the sysadmins.

More info: