ActiveRecord::Store: migrate data in store

Posted . Visible to the public.

When you need to store structured data (like Ruby hashes) in a single database column with ActiveRecord, a simple way is to use PostgreSQL's jsonb columns. ActiveRecord will automatically serialize and deserialize your Hash to and from JSON, and you can index JSON paths for fast reads.

As an alternative, ActiveRecord::Store Show archive.org snapshot offers a way to store hashes in a single database column. This card will show you how to migrate those hashes in an ActiveRecord::Migration by example:

Let's assume you have got a model User with settings stored as YAML in a single column:

class User < ActiveRecord::Base
  typed_store :settings, coder: YAML do |s|
    s.string :lang
    s.integer :show_number_of_posts
  end
end

Note: in this example we used a typed_store Show archive.org snapshot , which basically is a store with type constraints.

Now you notice that the setting lang was not named well and you'd rather have language. So let's rename the settings key:

class RenameLanguageKeyInUser < ActiveRecord::Migration

  class User < ActiveRecord::Base
    def rename_settings_key!(from, to)
      from, to = from.to_s, to.to_s
      old_settings = YAML.load(self.settings)
      new_settings = old_settings.dup
      
      if new_settings.has_key?(from)
        new_settings[to] = new_settings.delete(from)
        self.update_attributes!(settings: YAML.dump(new_settings))
        puts "Migrated User settings from #{old_settings.to_s} to #{new_settings.to_s}"
      else
        puts "Did not migrate User settings, '#{from}' was not a settings key"
      end
    end
  end

  def up
    User.find_each { |user| user.rename_settings_key!(:lang, :language) }
  end

  def down
    User.find_each { |user| user.rename_settings_key!(:language, :lang) }
  end

end

Here are some key pointers of this migration:

  • you should never use your actual application models in migrations as those might change over time. That's why we embed the model into the migration
  • use the specified coder to deserialize/serialize your store
  • manipulate the data in between
  • the output is optional of course but might be helpful when you first test the migration and rollback
Daniel Straßner
Last edit
Thomas Eisenbarth
License
Source code in this card is licensed under the MIT License.
Posted by Daniel Straßner to makandra dev (2018-05-24 09:15)