https://github.com/cgkineo/adapt-migrations/blob/master/api/commands.js
load({ cwd, cachePath, scripts })
- loads all migration taskscapture({ cwd, content, fromPlugins })
- captures current plugins and contentmigrate({ cwd, toPlugins })
- migrates content from capture to new pluginstest({ cwd })
- tests the migrations with dummy content
Functions:
describe(description, describeFunction)
Describe a migrationwhereContent(description, contentFilterFunction)
Limit when the migration runs, return true/false/throw ErrorwhereFromPlugin(description, fromPluginFilterFunction)
Limit when the migration runs, return true/false/throw ErrorwhereToPlugin(description, toPluginFilterFunction)
Limit when the migration runs, return true/false/throw ErrormutateContent(contentFunction)
Change content, return true/false/throw ErrorcheckContent(contentFunction)
Check content, return true/false/throw ErroraddPlugin(description, pluginConfig)
Add a pluginupdatePlugin(description, pluginConfig)
Update a pluginremovePlugin(description, pluginConfig)
Remove a pluginthrowError(description)
Throw an errortestSuccessWhere(description, { fromPlugins, toPlugins, content })
Supply some tests content which should end in successtestStopWhere(description, { fromPlugins, toPlugins, content })
Supply some tests content which should end prematurelytestErrorWhere(description, { fromPlugins, toPlugins, content })
Supply some tests content which will trigger an error
Arguments:
describeFunction = () => {}
Function body has a collection of migration script functionscontentFilterFunction = content => {}
Function body should return true/false/throw ErrorfromPluginFilterFunction = fromPlugins => {}
Function body should return true/false/throw ErrortoPluginFilterFunction = toPlugins => {}
Function body should return true/false/throw ErrorcontentFunction = content => { }
Function body should mutate or check the content, returning true/false/throw ErrorfromPlugins = [{ name: 'quickNav , version: '1.0.0' }]
Test data describing the original pluginstoPlugins = [{ name: 'pageNav , version: '1.0.0' }]
Test data describing the destination pluginspluginConfig = { name: 'pageNav , version: '1.0.0' }
Describes a plugincontent = [{ _id: 'c-05, ... }]
Test content for the course content
grunt migration:capture # captures current plugins and content
# do plugin/fw updates
grunt migration:migrate # migrates content from capture to new plugins
grunt migration:test # tests the migrations with dummy content
grunt migration:test --file=adapt-contrib-text/migrations/text.js # tests the migrations with dummy content
The whole describe
function block is executed as a normal function, from top to bottom, always. It does not return early.
When the describe
function block is executed, we're effectively using javascript function calls (the step functions) to define a single migration script (task) and its steps and then that migration script (task, and its steps) is run in part, to ascertain if it is applicable (using the where section), or in full when it is applicable by running through every step.
The step functions (whereFromPlugins, mutateContent, etc) have two phases:
- Task definition phase: Adding themselves as steps inside a task for later execution
describe(description, async () => { // Make a task
// where/selection/applicability section
whereFromPlugin(description, version) // Define as step 1 in the task
whereContent(description, () => {}) // Define as step 2 in the task
// mutation section to make changes
mutateContent(async content => {}) // Define as step 3 in the task
// checking section to ensure changes, content is immutable
checkContent(async content => {}) // Define as step 4 in the task
// plugin progression section
addPlugin(description, { name, version }) // Define as step 5 in the task
updatePlugin(description, { name, version }) // Define as step 6 in the task
removePlugin(description, { name, version }) // Define as step 7 in the task
// testing data for grunt migration:test
testSuccessWhere({ fromPlugins, toPlugins, content })
testStopWhere({ fromPlugins, toPlugins, content })
testErrorWhere({ fromPlugins, toPlugins, content })
})
- Execution phase: When used inside any other executing utility function block
describe(name, async () => {
mutateContent(name, async content => { // Execute the task step 1
if (whereFromPlugin(name, version)) { // Execute the function immediately
// the plugin version is matched
}
})
})
It's these two phases which decouple the definition of and execution of migration scripts, whilst using the same block of javascript. This was the simplest form I could think of, without having too many rules or nesting but whilst also providing flexibility, imply an order and convey concise meaning in as few words as possible.
We define variables and steps in the definition phase of the migration script (task) for later execution. The describe function doesn't return early at whereFromPlugins
, both because functions can't implicitly return early, and because the function is executed in a definition phase, where it's just adding a description of itself to a task for later use.
whereFromPlugins
is a single step in a task, it will be executed multiple times as the migrations progress, this is to find out whether the task is applicable and whether the task should proceed through all of the steps until conclusion.
Tasks and steps have three results: success (true), stop (false) or error (throw Error). Using those return values and having some of the step functions marked up as "where" functions, we can selectively define and execute a variety of migrations scripts, for a variety of courses, with a fun array of predictable outcomes.
References: Migration files are loaded:
Lines 293 to 296 in 1b156d8
adapt-migrations/api/describe.js
Line 11 in 1b156d8
Lines 297 to 302 in 1b156d8
Lines 4 to 10 in 1b156d8
adapt-migrations/lib/lifecycle.js
Line 23 in 1b156d8
Lines 320 to 323 in 1b156d8
Lines 184 to 197 in 1b156d8
Lines 330 to 336 in 1b156d8
adapt-migrations/lib/lifecycle.js
Lines 35 to 41 in 1b156d8
Lines 13 to 24 in 1b156d8