-
Notifications
You must be signed in to change notification settings - Fork 165
How to upgrade to 1.0.0 or newer
There're several backward incompatible changes.
-
Model.slug
andModel.slug_history
are replaced by Model._slugs, a single field ofArray
type. If you are migrating from an older version you need to copy all slugs from theModel.slug
andModel.slug_history
into a new field called_slugs
. See the "Data Migration" section below. -
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
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.
-
Declare the _slugs array in any model that use Mongoid slug:
field :_slugs, type: Array, default: []
-
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
-
Verify that the copying in the second step was successful and upgrade Mongoid slug.
Important: please note that the following instructions do not support migrating slug history, which you're welcome to contribute.
Important: The slow migration is community contributed. Please report any issues with the v0.10.0-mongoid3-with-slugs branch in step #1 in the @dblock fork issue tracker.
-
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.
-
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
-
Upgrade to the latest Mongoid slug 2.x.
gem "mongoid_slug", "~> 2.0"
-
Remove the old
slug
field. See below for a monkey patch that preserves and writes theslug
value in case you have processes that rely on it.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
If you have processes that rely on the old slug
value after data migration, you may choose to write it every time a slug changes. This can be achieved with a monkey patch.
module Mongoid
module Slug
alias_method :__build_slug, :build_slug
def build_slug
__build_slug
self["slug"] = self.slug
true
end
end
end
Any reason why this, from the console, is a bad idea? No need to temporarily modify models or add fields.
User.all.each { |u| u._slugs = [u.attributes["slug"]] unless u.attributes["slug"].nil? ; u.save ; u.unset(:slug) unless u.attributes["slug"].nil? }