Episode #55 - March 2, 2010

Posted 7 days back at Ruby5

Internationalization, Oracle and Tomcat, and QR Codes are covered on today's episode of Ruby5. Also, we cover Frank, Tilt, acts_as_archive, and the SinatraFakeWebService.

Listen to this episode on Ruby5

This episode is sponsored by Jumpstart Lab

Are you a professional Rails developer and want to step up your tools and process?

Hashrocket and Jumpstart Lab are teaming up for a one-day course before RailsConf 2010 titled “The Hashrocket Way”. Attendees will practice Hashrocket’s whole development cycle from story carding, behavior driven development, management with Pivotal Tracker, pair programing, cloud deployment and customer acceptance.

The class is limited to just 30 attendees. The early bird price of $450 goes through March 23rd or the event sells out. Purchase tickets or get more information at jumpstartlab.com.


Rails 3: Let ActiveRecord Manage Your Translations
Rails 3 brings you improved internationalization with config/locales and a default en.yml to get you started. Now, if YAMLs aren't your thing, Franck Verrot just wrote up an interesting method to store the translations in a database and utilize Rails 3's memory cache to keep from pinging that database multiple times with every request.

Using Sinatra to Test Remote Services in Rails
When you're writing application tests and need to stub out an external resource, you've got options. Primarily, FakeWeb is the library of choice right now, easily recording and mocking fairly complex requests and interactions. However, FakeWeb does not easily differentiate requests which change based on the data parameters posted. So, if you like FakeWeb, but need something more robust, check out SinatraFakeWebService by Elad Meidar which allows you to script a fake resource within Sinatra and your tests.

Using Ruby on Rails with Oracle and Deploying to Tomcat
Recently, Alan Skorkin had to deploy a Ruby application to Tomcat and use an Oracle database. Scary, we know. But thanks to his effort, we've now got a tutorial on how to handle this situation with details for building the database.yml, building in MRI Ruby, and deploying to Tomcat with Warbler.

QR Coding
If you've ever seen QR Codes, those square 2D barcodes with the funny square/dots in the corners, then you'll know what Ecin Krispie was recently working on. He posted a bit about decoding QR Codes using Ruby, although found that there were no good, native decoders available and had to resort to JRuby to use some of the Java libraries already available. If you know of a good decoder (not just encoder!) or even a good image processing library for MRI, please let us know in the show notes.

acts_as_archive Instead of Soft Delete
If you're building (or working with) an application that doesn't actually delete your models, you may be interested in acts_as_archive. It's an alternative to creating manual, default scopes or the acts_as_paranoid plugin. It actually moves the deleted objects into a separate table, keeping your active objects table clean, which makes your finders and scopes more simple and even in some cases increases the efficiency of your data store. It also provides a simple way to restore data back into active duty.

Frank - Build Static HTML with Template Engines
So, what if you want to build a static HTML site, but would rather use something like HAML (or ERb, Less, Builder, Liquid, or Mustache). Frank was built for exactly this, allowing you to choose your engine while ultimately building static files.

Tilt - A Generic Wrapper for Ruby Template Engines
Tilt is a library built by Ryan Tomayko, which is being utilized by Frank, and builds a generic wrapping over popular Ruby template engines. You can think of it as a Rack-like framework for templates, standardizing the interface, allowing you and projects like Frank to seamlessly support your favorite engine.

My weekend project: When's your next "fun" birthday?

Posted 8 days back at Rail Spikes

When’s the next time your birthday is going to be on a Friday or Saturday, so you can go out and have fun?

That’s what my little weekend project will tell you.

This site came about because my wife and I figured out that the next time her birthday falls on a Friday, she’ll be 42 years old! Yikes.

This was a fun site to build because I got to play with some JavaScript libraries that I don’t often use, like date.js, mustache.js, and TypeWatch. I also made use of some cool CSS 3 features like @font-face with the font Tuffy Bold from Kernest. For the CSS, I used 1KB CSS Grid based on Geoffrey Grossenbach’s suggestion.

My weekend project: When's your next "fun" birthday?

Posted 8 days back at Rail Spikes

When’s the next time your birthday is going to be on a Friday or Saturday, so you can go out and have fun?

That’s what my little weekend project will tell you.

This site came about because my wife and I figured out that the next time her birthday falls on a Friday, she’ll be 42 years old! Yikes.

This was a fun site to build because I got to play with some JavaScript libraries that I don’t often use, like date.js, mustache.js, and TypeWatch. I also made use of some cool CSS 3 features like @font-face with the font Tuffy Bold from Kernest. For the CSS, I used 1KB CSS Grid based on Geoffrey Grossenbach’s suggestion.

Using acts_as_archive instead of soft delete

Posted 11 days back at Rail Spikes

For the application I am working on right now, the ability to restore content that has been deleted is one of the requirements. A lot of people would just go ahead and add acts_as_paranoid or is_paranoid and be done with it, but I've had trouble with that approach before.

I've been reading a lot about the trouble with "soft deletes" (flagging a record as deleted instead of deleting it). Using a plugin that monkey patches ActiveRecord can go a long way towards fixing thesee problems, but it's a leaky abstraction and will bite you in the ass in unexpected ways. For example, all your uniqueness validations (and indexes) become much more complicated.

That's why Jeffrey Chupp decided to kill is_paranoid and Rick Olson doesn't use acts_as_paranoid any more.

There are other problems too. If you delete a lot of records, and you keep them in the same table, your table can get quite large, and all your queries slow down. At this point you have to use partitioning or partial indexes to get acceptable performance.

Alternatives to soft delete

In my reading, I found two alternatives to soft delete to be compelling.

The first was the suggestion to properly model your domain. Why do you want to delete a record? What does that mean? Udi Dahan puts it this way:

Orders aren’t deleted – they’re cancelled. There may also be fees incurred if the order is canceled too late.

Employees aren’t deleted – they’re fired (or possibly retired). A compensation package often needs to be handled.

Jobs aren’t deleted – they’re filled (or their requisition is revoked).

Keeping that in mind, what if the task at hand really is to delete the record? The other idea that I liked was to archive the records in another table.

The first Rails plugin I came across that implemented this was acts_as_soft_deletable which besides being misnamed doesn't appear to be actively maintained. The author even disavows the plugin somewhat for Rails 2.3:

Before using this with a new Rails 2.3 app, you may want to consider using the new default_scope feature (or named_scopes) with a deleted_at flag.

Then I found acts_as_archive which is more recently maintained and used in production for a major Rails website.

There was only one problem -- acts_as_archive didn't support PostgreSQL. Fortunately, that was easy enough to fix.

Restoring deleted records with acts_as_archive

acts_as_archive has the ability to restore a deleted record, but only that record, not associated records.

I was troubled by this at first, but after thinking about it I came to the conclusion that restoring a network of objects is an application-dependant problem. Here's one way to achieve it.

Imagine you have a model like this, with Posts having many Comments and Votes.

Post model

A Post can be deleted, and when it is, it should take the Comments and Votes with it:

class Post
  acts_as_archive

  has_many :votes, :dependent => :destroy
  has_many :comments, :dependent => :destroy
end

(Assume Comment and Vote also have acts_as_archive.)

Now, I can restore a Post with its associated Votes and Comments like this:

def self.restore(id)
  transaction do
    Post.restore_all(["id = ?", id])
    post = Post.find(id)

    Vote.restore_all(Vote::Archive.all(:conditions => ["post_id = ?", id]).map(&:id))
    Comment.restore_all(Comment::Archive.all(:conditions => ["post_id = ?", id]).map(&:id))
  end

In my real code, I've broken apart the two pieces of this into a class method restore and an instance method post_restore which the freshly restored object uses to find its associated records and restore them. post_restore also takes care of post-restore tasks like putting the object back in the Solr index.

This all works great. But now let's say Comments can be deleted individually, and we want to restore them.

Here the logic is a little different, because a Comment can't be restored unless its parent Post still exists (unless it's being restored by the Post, as above).

I take care of this logic in the administrative controller, by only showing child objects that it's valid to restore, and my foreign key constraints prevent anyone from getting around that.

I really wanted to delete that!

Sometimes you don't want to archive a deleted object. For example, in the application I'm working on, votes are canceled by re-voting. I don't want to save those votes -- there's no point, and it can even cause problems with restoring. Imagine having several archived votes from a user for a Post, and then deleting and restoring that Post. The restoration will try to bring back all the votes. Again, I catch this with a uniqueness constraint, but I don't want it to happen in the first place.

Fortunately acts_as_archive has me covered.

To destroy a record without archiving it, you can use destroy!. Likewise for deleting, there is delete_all!.

Using acts_as_archive instead of soft delete

Posted 11 days back at Rail Spikes

For the application I am working on right now, the ability to restore content that has been deleted is one of the requirements. A lot of people would just go ahead and add acts_as_paranoid or is_paranoid and be done with it, but I've had trouble with that approach before.

I've been reading a lot about the trouble with "soft deletes" (flagging a record as deleted instead of deleting it). Using a plugin that monkey patches ActiveRecord can go a long way towards fixing thesee problems, but it's a leaky abstraction and will bite you in the ass in unexpected ways. For example, all your uniqueness validations (and indexes) become much more complicated.

That's why Jeffrey Chupp decided to kill is_paranoid and Rick Olson doesn't use acts_as_paranoid any more.

There are other problems too. If you delete a lot of records, and you keep them in the same table, your table can get quite large, and all your queries slow down. At this point you have to use partitioning or partial indexes to get acceptable performance.

Alternatives to soft delete

In my reading, I found two alternatives to soft delete to be compelling.

The first was the suggestion to properly model your domain. Why do you want to delete a record? What does that mean? Udi Dahan puts it this way:

Orders aren’t deleted – they’re cancelled. There may also be fees incurred if the order is canceled too late.

Employees aren’t deleted – they’re fired (or possibly retired). A compensation package often needs to be handled.

Jobs aren’t deleted – they’re filled (or their requisition is revoked).

Keeping that in mind, what if the task at hand really is to delete the record? The other idea that I liked was to archive the records in another table.

The first Rails plugin I came across that implemented this was acts_as_soft_deletable which besides being misnamed doesn't appear to be actively maintained. The author even disavows the plugin somewhat for Rails 2.3:

Before using this with a new Rails 2.3 app, you may want to consider using the new default_scope feature (or named_scopes) with a deleted_at flag.

Then I found acts_as_archive which is more recently maintained and used in production for a major Rails website.

There was only one problem -- acts_as_archive didn't support PostgreSQL. Fortunately, that was easy enough to fix.

Restoring deleted records with acts_as_archive

acts_as_archive has the ability to restore a deleted record, but only that record, not associated records.

I was troubled by this at first, but after thinking about it I came to the conclusion that restoring a network of objects is an application-dependant problem. Here's one way to achieve it.

Imagine you have a model like this, with Posts having many Comments and Votes.

Post model

A Post can be deleted, and when it is, it should take the Comments and Votes with it:

class Post
  acts_as_archive

  has_many :votes, :dependent => :destroy
  has_many :comments, :dependent => :destroy
end

(Assume Comment and Vote also have acts_as_archive.)

Now, I can restore a Post with its associated Votes and Comments like this:

def self.restore(id)
  transaction do
    Post.restore_all(["id = ?", id])
    post = Post.find(id)

    Vote.restore_all(Vote::Archive.all(:conditions => ["post_id = ?", id]).map(&:id))
    Comment.restore_all(Comment::Archive.all(:conditions => ["post_id = ?", id]).map(&:id))
  end

In my real code, I've broken apart the two pieces of this into a class method restore and an instance method post_restore which the freshly restored object uses to find its associated records and restore them. post_restore also takes care of post-restore tasks like putting the object back in the Solr index.

This all works great. But now let's say Comments can be deleted individually, and we want to restore them.

Here the logic is a little different, because a Comment can't be restored unless its parent Post still exists (unless it's being restored by the Post, as above).

I take care of this logic in the administrative controller, by only showing child objects that it's valid to restore, and my foreign key constraints prevent anyone from getting around that.

I really wanted to delete that!

Sometimes you don't want to archive a deleted object. For example, in the application I'm working on, votes are canceled by re-voting. I don't want to save those votes -- there's no point, and it can even cause problems with restoring. Imagine having several archived votes from a user for a Post, and then deleting and restoring that Post. The restoration will try to bring back all the votes. Again, I catch this with a uniqueness constraint, but I don't want it to happen in the first place.

Fortunately acts_as_archive has me covered.

To destroy a record without archiving it, you can use destroy!. Likewise for deleting, there is delete_all!.

Bundler and I are breaking up

Posted 19 days back at Rail Spikes

Bundler may be the future, but after way too many hours of trying to get my app working with Rails 2.3.5, bundler 0.9.x, and Heroku I have decided to throw in the towel and switch back to Heroku’s gem manifest system.

I had Bundler 0.8 working very nicely but for whatever reason I couldn’t get the gems to play nice with each other in the new version. I had the app working locally and the tests passing, but on Heroku the app wouldn’t boot. This could have something to do with Heroku running Bundler 0.9.5 while I was running 0.9.7 locally. Whatever the reason, I’ve decided to take a break from bundler and wait until its development stabilizes a bit—at least on Heroku.

If you’re in the same boat, you can use this script to convert your Gemfile back to a .gems file and config.gem statements.

Rake task for deploying to Heroku

Posted 25 days back at Rail Spikes

Deploying to Heroku is pretty easy, but I’ve often found myself needing to do additional tasks after pushing to Heroku’s git repository. For example, if you have to migrate, you have to do that after pushing; and after migrating you have to restart the app server.

So here is a Rake task to automate that. It uses Heroku’s client library to find the git remotes you need to push to. Use it like this:

rake deploy # deploys to your default app for this directory

rake deploy APP=some-other-app # deploy to another app (e.g., a staging server)

Fixing raw HTML error pages from Facebooker

Posted about 1 month back at Rail Spikes

I am using Facebooker for Facebook Connect with Rails 2.3.5 with the rails_xss plugin, which escapes HTML by default unless you use raw.

I recently started seeing exceptions that looked like this:

The top of the HTML contains a <fb:fbml> tag which led me to suspect Facebooker. A quick git bisect confirmed this. But why is it happening?

I spent some time looking through the Facebooker source code and located the suspicious-sounding facebooker_pretty_errors.rb file. Sure enough, that file renders a template for errors that look good on the Facebook Canvas (assuming you’re not using rails_xss anyway…).

Fortunately, it is easy to turn this off, by setting this in your facebooker.yml file:

development:
  pretty_errors: true

Now it’s back to normal, and I can read my exceptions again.

Fixing raw HTML error pages from Facebooker

Posted about 1 month back at Rail Spikes

I am using Facebooker for Facebook Connect with Rails 2.3.5 with the rails_xss plugin, which escapes HTML by default unless you use raw.

I recently started seeing exceptions that looked like this:

The top of the HTML contains a <fb:fbml> tag which led me to suspect Facebooker. A quick git bisect confirmed this. But why is it happening?

I spent some time looking through the Facebooker source code and located the suspicious-sounding facebooker_pretty_errors.rb file. Sure enough, that file renders a template for errors that look good on the Facebook Canvas (assuming you’re not using rails_xss anyway…).

Fortunately, it is easy to turn this off, by setting this in your facebooker.yml file:

development:
  pretty_errors: true

Now it’s back to normal, and I can read my exceptions again.

Fixing the Heroku "Too many authentication failures for git" problem

Posted about 1 month back at Rail Spikes

Getting an error like this when you push to Heroku?

electricsheep:herokuapp look$ git push heroku master
Received disconnect from 75.101.163.44: 2: Too many authentication failures for git
fatal: The remote end hung up unexpectedly

If you like to create an ssh key for each server you use, you run this risk.

The reason is that unless you specify which key to use for a host, ssh-agent sends each key in turn until one works. However some server configure sshd to reject connections after too many attempted logins. For example, Dreamhost does this (see Dealing with SSH’s key spam problem for details). This is especially annoying if you weren’t even planning to use key-based authentication (as is the case on Heroku).

You can fix this by setting IdentitiesOnly yes in your ~/.ssh/config file. You can do this on a host-by-host basis.

host foobar.dreamhost.com        
        IdentitiesOnly yes

Heroku is a bit difficult to do this for because they don’t have a single IP address or domain (that I know of) you can configure this for.

As a workaround, clear your identities:

ssh-add -D

(Thanks to my friend McClain for his help with the ssh-add command.)


1 2 3 ... 6