-
Notifications
You must be signed in to change notification settings - Fork 0
Migration Helpers
Migration helpers are utility functions that simplify common saved variable migration tasks. They are designed to reduce repetitive table manipulation code and make migrations easier to read, maintain, and test.
While a custom migration function can perform any transformation required, migration helpers provide a safer and more consistent way to handle common operations such as:
- Moving settings.
- Copying settings.
- Removing obsolete settings.
- Creating missing table paths.
- Migrating between account-wide and character storage.
Migration helpers are typically used inside a version migration callback.
Example:
settings:Migrate(2, function(savedVars)
-- migration helper operations
end)Without helpers, migrations often contain repeated code:
settings:Migrate(2, function(savedVars)
if savedVars.oldSetting ~= nil then
savedVars.newSetting = savedVars.oldSetting
savedVars.oldSetting = nil
end
end)This approach works, but every migration must manually handle:
- Missing values.
- Existing destination values.
- Nested table creation.
- Removing obsolete data.
- Avoiding accidental overwrites.
Migration helpers centralize these behaviors and make the intent of the migration clearer.
Many migrations involve moving data between different locations in the saved variable table.
For example, changing from:
settings.scaleto:
settings.ui.scalerequires creating the ui table before moving the value.
Migration helpers handle these path operations automatically.
When moving data into a new location, intermediate tables may not exist.
Example:
Before:
{
scale = 1.0
}After:
{
ui = {
scale = 1.0
}
}A path creation helper ensures that the destination exists before storing the value.
Conceptually:
CreatePath(
savedVars,
"ui"
)creates:
savedVars.ui = {}when required.
A move operation transfers a value from one location to another.
A move:
- Copies the value to the destination.
- Removes the original value.
- Preserves the user's setting.
Example:
Version 1:
{
opacity = 0.8
}Version 2:
{
ui = {
opacity = 0.8
}
}Migration:
settings:Migrate(2, function(savedVars)
Move(
savedVars,
"opacity",
"ui.opacity"
)
end)After migration:
{
ui = {
opacity = 0.8
}
}Move helpers are commonly used for:
- Renaming settings.
- Reorganizing tables.
- Consolidating configuration sections.
A copy operation creates a new value while preserving the original.
This is useful when adding a new setting while keeping the old data available.
Example:
Before:
{
color = "red"
}After:
{
color = "red",
primaryColor = "red"
}Migration:
settings:Migrate(2, function(savedVars)
Copy(
savedVars,
"color",
"primaryColor"
)
end)Use copying when:
- The old setting may still be required.
- Multiple systems need the same value.
- A gradual transition is desired.
Old settings that are no longer used can be removed during migration.
Example:
Before:
{
enabled = true,
debugMode = false
}Migration:
settings:Migrate(3, function(savedVars)
Remove(
savedVars,
"debugMode"
)
end)After:
{
enabled = true
}Removing unused settings keeps SavedVariables files clean and prevents obsolete data from accumulating.
Migration helpers should support nested paths when working with complex settings.
Example:
Move:
settings.display.position.xto:
settings.ui.location.xConceptually:
Move(
savedVars,
"display.position.x",
"ui.location.x"
)The helper handles:
- Creating missing tables.
- Reading the existing value.
- Removing the old path.
LibSavedVars supports migrating data between different storage models.
Common scenarios include:
Example:
Version 1:
Account-wide
└── build = "Tank"
Version 2:
Character
├── Character A
│ └── build = "Tank"
└── Character B
└── build = "Tank"
The migration copies the account-wide value into the character storage.
Example:
Version 1:
Character
├── Character A
│ └── theme = "Dark"
├── Character B
│ └── theme = "Dark"
Version 2:
Account-wide
└── theme = "Dark"
The migration consolidates the character values into shared storage.
Good migration helpers should follow these principles.
A migration should handle missing data gracefully.
Example:
Move(
savedVars,
"oldSetting",
"newSetting"
)should do nothing if oldSetting no longer exists.
Helpers should avoid overwriting existing values unless explicitly requested.
Example:
savedVars.newSetting = savedVars.oldSettingcould overwrite a user's existing value.
A safer approach:
if savedVars.newSetting == nil then
savedVars.newSetting = savedVars.oldSetting
endMigration helpers do not determine when they run.
The version system controls execution:
settings:Migrate(4, function(savedVars)
Move(
savedVars,
"oldName",
"newName"
)
end)The version answers:
"Should this migration run?"
The helper answers:
"How should the data change?"
A custom migration callback is better when the transformation requires logic.
Examples:
- Combining multiple settings.
- Calculating new values.
- Validating data.
- Converting formats.
Example:
settings:Migrate(5, function(savedVars)
savedVars.position = {
x = savedVars.oldX * 2,
y = savedVars.oldY * 2,
}
end)A helper would not make this clearer.
When using migration helpers:
- Keep each migration tied to one version.
- Use helpers for simple, predictable transformations.
- Use custom callbacks for complex conversions.
- Remove obsolete settings after migration.
- Test migrations using older SavedVariables data.
- Never modify released migrations after users have received them.
Migration helpers provide reusable building blocks for maintaining saved variable compatibility across addon versions.
Use them for:
| Task | Recommended Helper |
|---|---|
| Rename a setting | Move |
| Move data into a new table | CreatePath + Move |
| Duplicate a setting | Copy |
| Delete obsolete data | Remove |
| Change storage location | Account/Character migration helpers |
| Complex transformation | Custom migration callback |
Together with versioning, migration helpers allow LibSavedVars-based addons to evolve safely while preserving user settings.