Add some regular ruby code in your rails migrations

For long, I’ve used rails migrations only as a way to update my database schema, no more.

Today, I had to do a more complex operation.

I have a Message model and a Conversation model; for historical reasons, the read / unread was stored in the Message model, but I actually don’t need to keep a track of what message was read or not, only conversations.

So I had to do a simple migration that would create a filed in the Conversation model and remove it from the Message model.

However, I’ll have to apply this migration to production, and this will cause some data loss, how can I mark as unread the unread conversations ?

A few months ago, I would have created a complex script for doing that.

Today, I tried something pretty intuitive :

class MoveUnreadToConversation < ActiveRecord::Migration
  def change
    conversations = {}
    Conversation.includes(:messages).each do |conversation|
      conversations[conversation.id] = (conversation.messages.count { |m| m.read_user == false}) > 0
    end

    remove_column :messages, :read_user

    add_column :conversations, :unread, :boolean, :default => true

    conversations.each do |index, unread|
      c = Conversation.find(index)
      c.unread = unread
      c.save
    end
  end
end

And it works !

Just add some regular ruby code to your migration, it will work like a charm, the schema is updated and reloaded after the database operations so you won’t have any problem with ActiveRecord.

This may seem to be a pretty standard feature of rails migrations, but I did not know it until today, thanks to ruby and rails, I can learn new things every single day !

Happy Rubying !

 
14
Kudos
 
14
Kudos

Now read this

Use roles in your rails models for mass assignment

As you probably know, with ActiveRecord (more exactly ActiveModel), you can whitelist some fields of your models so they are safe to be mass-assigned from a form. Sometimes, you want some fields to be alterable by normal users, and other... Continue →