Skip to content

How to upgrade to 1.0.0 or newer

digitalplaywright edited this page Nov 13, 2012 · 5 revisions

There're several backward incompatible changes.

  1. Model.slug and Model.slug_history are replaced by Model._slugs, a single field of Array type. If you are migrating from an older version you need to copy all slugs from the Model.slug and Model.slug_history into a new field called _slugs. See the "Data Migration" section below.

  2. If you're using a block to define a slug in Mongoid 0.10, you must change it to return a slug. The older version would automatically convert it to a valid slug, which is no longer the case. For example

    slug :first, :last do |doc|
        [ doc.first, doc.last ].compact.join(" ")
    end

    becomes

    slug :first, :last do |doc|
        [ doc.first, doc.last ].compact.join(" ").to_url
    end

Data Migration

It is recommended to do the data migration in code. If you can take downtime, the process is pretty straightforward, copy the data. You can also do a slow migration without any production impact, described below.

Migration with Downtime

  1. Declare the _slugs array in any model that use Mongoid slug:

    field :_slugs, type: Array, default: []
  2. Copy the value from the slug field to _slugs. Also copy the value from the slug history array if you used slug history. The current slug should be the last entry in the _slugs array (e.g _slugs = ["old_slug", "current_slug"]). You can use dynamic fields for this before Mongoid slug is updated to 1.0:

    def copy_slug(e)
      slugs = []
      if e[:slug_history] != nil
        e[:slug_history].each do |h|
          slugs << h unless (h == nil || h == "")
        end
      end
      slugs << e[:slug]
      e[:_slugs] = slugs
      e.save
      p " - #{e[:_slugs]}"
    end
  3. Verify that the copying in the second step was successful and upgrade Mongoid slug.

Slow Migration

Important: please note that the following instructions do not support migrating slug history, which you're welcome to contribute.

  1. Upgrade to Mongoid 3.x and use a forked 0.10 implementation of Mongoid slug from https://github.com/dblock/mongoid-slug/tree/v0.10.0-mongoid3-with-slugs. Change your Gemfile reference to:

    gem "mongoid_slug", :git => "https://github.com/dblock/mongoid-slug.git", :branch => "v0.10.0-mongoid3-with-slugs"

The code in this fork will write both the slug and the _slugs fields when a document is updated. You can see the implementation here.

  1. Now that any new update creates a corresponding _slugs array, you can do the copy for any old record with a Rake task.

    namespace :db do
      namespace :slugs do
    
        desc "Migrate all model slugs."
        task :migrate => :environment do
          Dir[File.join(Rails.root, "app/models/**/*.rb")].each do |f|
            klass = File.basename(f, ".rb").camelize.constantize
            next unless klass.respond_to? :slug
            logger.info "Migrating #{klass.count} #{klass} instance(s) ..."
            count = 0
            klass.all.each do |instance|
              raise "missing slug: #{instance.inspect}" if instance.slug.blank?
              next unless instance[:_slugs].empty?
              klass.collection.where({ _id: instance.id }).update({ "$set" => { _slugs: [ instance.slug ] }})
              count += 1
            end
            logger.info " => #{count}" if count > 0
          end
        end
    
      end
    end
  2. Upgrade to the latest Mongoid slug 2.x.

    gem "mongoid_slug", "~> 2.0"
  3. Remove the old slug field.

    namespace :db do
      namespace :slugs do
    
        desc "Remove all obsolete model slugs."
        task :remove => :environment do
          Dir[File.join(Rails.root, "app/models/**/*.rb")].each do |f|
            klass = File.basename(f, ".rb").camelize.constantize
            next unless klass.respond_to? :slug
            slugs_count = klass.where({ slug: { "$exists" => 1 }}).count
            logger.info "Removing :slug in #{slugs_count} #{klass} instance(s) ..."
            klass.collection.indexes.drop rescue nil
            klass.create_indexes
            klass.collection.where({ slug: { "$exists" => 1 }}).update({ "$unset" => { slug: 1 } }, { multi: true })
          end
        end
    
      end
    end
Clone this wiki locally