Maintaining database column order with migrations

If you’re anything like me, you like to keep database columns in a reasonably logical order. In my case I generally keep primary keys as the first column, then content columns, then special usage columns, then foreign keys e.g.

id
name
description
created_on
updated_on
account_id

Now, say I added a ‘slug’ column with a migration:

def self.up
  add_column "projects", "slug", :string
end

That’s fine, I have my new column but it’s after my foreign keys:

id
name
description
created_on
updated_on
account_id
slug

What’s it doing down there? I want it after the ‘name’ column! …wouldn’t it be nice if you could specify an :after option for add_column.

We can make this possible by monkey patching the add_column method at the top of our migration file (I will make this into a plugin when I get time):

module ActiveRecord
  module ConnectionAdapters # :nodoc:
    module SchemaStatements
      def add_column(table_name, column_name, type, options = {})
        add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
        after_column = options.delete(:after)
        add_column_options!(add_column_sql, options)
        add_column_sql << " AFTER #{after_column}" if after_column
        execute(add_column_sql)
      end
    end
  end
end

This lets us do the following:

def self.up
  add_column "projects", "slug", :string, :after => 'name'
end

Which gives us:

id
name
slug
description
created_on
updated_on
account_id

Much better… this may seem a little petty, however as your tables get more and more columns it makes things much easier to follow.

Please note: I have only tested this with MySQL

h4. Update: this is now a plugin and can be “downloaded here”:http://svn.soniciq.com/public/rails/plugins/iq_migration_extensions/

CSS and JS directories in Rails

One thing that annoyed me when I first started using Rails at 0.14 was that I was forced to put my .css files in a ‘stylesheets’ directory and my .js files in a ‘javascripts’ directory.

Previous to Rails I would put .css files in a ‘css’ directory and .js files in a ‘js’ directory (call me old fashioned). I use the following code in my application helper to allow me to do this:

module ApplicationHelper
  def javascript_path(source) compute_public_path(source, 'js', 'js') end
  def stylesheet_path(source) compute_public_path(source, 'css', 'css') end
end

Just incase anyone else has the same niggle.

*Update 29-11-08:* for Rails 2.2, see new post.

Disabling plugin code in generators/migrations

I have found on numerous occasions that I need to disable certain plugin functionality if running a generator / rake db:migrate etc.

An easy way to disable certain functionality follows:

def method_that_shouldnt_be_run_in_migrations_or_generators
  # Return if we are using a generator or migrations
  script = File.basename($0)
  return if (script == 'generate') || (script == 'rake' && ARGV[0] =~ /migrate$/)
end

Simply RESTful… “The missing action”

UPDATE 15/03/10: The debate continues…

The ideas in this article came about whilst I was test-driving the Simply RESTful plugin following DHH’s RailsConf keynote on the subject.

The philosophy

The first thing I came across whilst experimenting with Simply RESTful (which is great by the way), was that there is no real way of deleting items with javascript disabled. Since I am currently working on a project that needs to function on a variety of mobile devices, this instantly caused me concern.

I could think of a few ways to hack around this limitation, however I was sure there had to be a better way, hence this article. I wanted to keep the current javascript functionality but in addition have a clean non-javascript fallback.

Consider the following:

CRUD Form (GET request) POST action
(C)reate /products/new create
(R)ead /products/24 n/a
(U)pdate /products/24/edit update
(D)elete - destroy

There are three “state changing” actions in CRUD, they are the ‘create’, ‘update’ and ‘delete’. You will notice from the table above that all three have a POST action1, however only two have GET actions… why is this?

Now, you see that dash in the second column… that’s “the missing action”. There is no good reason why our ‘destroy’ action shouldn’t have a corresponding form action (GET request) also. Let me explain myself…

1 The HTTP actions are PUT, POST and DELETE, however in this implementation (due to the limitations of HTML) they are all technically POST’s.

Putting it into practice

So we give ‘destroy’ it’s missing action which will act as a confirmation of our post… and what shall we call this missing action? …why let’s call it delete.

If we fill in this missing piece in our RESTful Rails puzzle, all becomes clear:

CRUD Form (GET request) POST action
(C)reate /products/new create
(R)ead /products/24 n/a
(U)pdate /products/24/edit update
(D)elete /products/24/delete destroy

Our routes would look something like:

map.resource :product, :member => { :delete => :get }

In our controller would be:

def delete
  @product = Product.find(params[:id])
end
 
def destroy
  Product.find(params[:id]).destroy if request.delete?
  redirect_to product_url
end

Our delete.rhtml would look like this:

<h1>Are you sure you wish to delete ?</h1>

Slight complication…

Update (13 Oct 2007): This has been fixed in more recent versions or Rails.

Now comes the slight complication… we want the javascript POST to /projects/24 to function as normal, however if javascript is disabled we want to request /projects/24;delete.

Wouldn’t it be nice if you could specify a fallback (non-javascript) href in the link_to helper, something that I’ve pondered with on many occasions. Unfortunately the link_to helper doesn’t let you override the href attribute (currently it adds a second one instead), until now.

Enter iq_noscript_friendly plugin which fixes this shortfall (I also have this as a Rails patch however the ticketing system on Trac is currently broken).

Install the plugin using:

./script/plugin install http://svn.soniciq.com/public/rails/plugins/iq_noscript_friendly/

In our listing view (index.rhtml) we are now able to do the following:

link_to 'Delete', product_url(product),
          :confirm => 'Are you sure?',
          :method => 'delete',
          :href => delete_product_url(product)

Ideally you would just give the link a class of “delete” and use unobtrusive javascript to make it do the delete request.

Beautiful.

Summary

By adding “the missing action”, we are able to POST as usual (using javascript) to ‘destroy’ but gracefully fallback to our ‘delete’ form when javascript is not available. Besides, why shouldn’t ‘destroy’ get it’s own form action… ‘create’ has ‘new’ and ‘update’ has ‘edit’?

Now to make this whole thing even better, lets make it part of the convention. ‘delete’ should default to GET and therefore negate the need for :member => { :delete => :get } in our routes.rb… DHH?

I would love to hear peoples comments on this technique as I’m using it for everything now and it works a treat.

Com’on… use “the missing action”, be kind to those without javascript, and lets make it the convention!

Rock on RESTfulness.

Finally running on the latest Typo (4.0)

I’ve been putting off upgrading Typo for a while now (due to lack of time). This blog is now running on -3.99.4- 4.0 (missed the update by 2 hours!).

I have added a temporary fix to “TheLucid Typo theme”:/typo_themes/lucid.zip however keep a lookout for the new version coming soon as promised.

Please reply to this post with any features you would like to see in the next theme release.