-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
213 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,9 @@ const Stage = Function.inherits('Alchemy.Base', 'Alchemy.Stages', function Stage | |
// The main pledge | ||
this[MAIN_PLEDGE] = new Pledge.Swift(); | ||
|
||
// The dependencies of this stage | ||
this.depends_on = null; | ||
|
||
// Pre-tasks | ||
this.pre_tasks = new Map(); | ||
|
||
|
@@ -55,6 +58,17 @@ const Stage = Function.inherits('Alchemy.Base', 'Alchemy.Stages', function Stage | |
this.ended = null; | ||
}); | ||
|
||
/** | ||
* Is this the root stage? | ||
* | ||
* @author Jelle De Loecker <[email protected]> | ||
* @since 1.4.0 | ||
* @version 1.4.0 | ||
*/ | ||
Stage.setProperty(function is_root() { | ||
return this.root_stage === this; | ||
}); | ||
|
||
/** | ||
* Get the main pledge | ||
* | ||
|
@@ -66,6 +80,50 @@ Stage.setProperty(function pledge() { | |
return this[MAIN_PLEDGE]; | ||
}); | ||
|
||
/** | ||
* Add a dependency to this stage | ||
* | ||
* @author Jelle De Loecker <[email protected]> | ||
* @since 1.4.0 | ||
* @version 1.4.0 | ||
* | ||
* @param {string[]} stage_ids The id of the stage it depends on. | ||
* | ||
* @return {Alchemy.Stages.Stage} | ||
*/ | ||
Stage.setMethod(function dependsOn(stage_ids) { | ||
|
||
if (!this.depends_on) { | ||
this.depends_on = []; | ||
} | ||
|
||
stage_ids = Array.cast(stage_ids); | ||
|
||
for (let i = 0; i < stage_ids.length; i++) { | ||
let id = stage_ids[i]; | ||
|
||
if (typeof id != 'string') { | ||
throw new Error('Stage id should be a string'); | ||
} | ||
|
||
if (!this.is_root && !id.startsWith(this.root_stage.name)) { | ||
id = this.root_stage.name + '.' + id; | ||
} | ||
|
||
stage_ids[i] = id; | ||
|
||
} | ||
|
||
this.depends_on.push(...stage_ids); | ||
|
||
// Push these dependencies to the already existing children | ||
for (let [name, stage] of this.child_stages) { | ||
stage.dependsOn(stage_ids); | ||
} | ||
|
||
return this; | ||
}); | ||
|
||
/** | ||
* Add a new child stage | ||
* | ||
|
@@ -86,6 +144,10 @@ Stage.setMethod(function createStage(name, fnc) { | |
|
||
let stage = new Stage(name, this); | ||
|
||
if (this.depends_on?.length) { | ||
stage.dependsOn(this.depends_on); | ||
} | ||
|
||
this.child_stages.set(name, stage); | ||
|
||
if (fnc) { | ||
|
@@ -177,7 +239,7 @@ Stage.setMethod(function _doTasks(type) { | |
} else if (Pledge.isThenable(value)) { | ||
tasks.push(value); | ||
|
||
value.done((err, result) => { | ||
Pledge.cast(value).done((err, result) => { | ||
task_map.set(fnc, result || err || true); | ||
}); | ||
} | ||
|
@@ -293,7 +355,7 @@ Stage.setMethod(function getStage(id) { | |
|
||
let parts = id.split('.'); | ||
|
||
if (parts[0] == this.name) { | ||
if (this.is_root && parts[0] == this.name) { | ||
parts.shift(); | ||
} | ||
|
||
|
@@ -348,6 +410,102 @@ Stage.setMethod(function afterStages(stages, callback) { | |
return Function.series(tasks, callback); | ||
}); | ||
|
||
/** | ||
* Recursively get all the child-stages (including this one) | ||
* | ||
* @author Jelle De Loecker <[email protected]> | ||
* @since 1.4.0 | ||
* @version 1.4.0 | ||
* | ||
* @param {string[]} filter | ||
* | ||
* @return {Alchemy.Stage.Stage[]} | ||
*/ | ||
Stage.setMethod(function getFlattenedStages(filter) { | ||
|
||
let result = []; | ||
|
||
result.push(this); | ||
|
||
// If the filter is given, split each entry up into parts | ||
if (filter) { | ||
filter = Array.cast(filter); | ||
|
||
for (let i = 0; i < filter.length; i++) { | ||
let parts = filter[i].split('.'); | ||
|
||
if (this.is_root && parts[0] == this.name) { | ||
parts.shift(); | ||
} | ||
|
||
filter[i] = parts; | ||
} | ||
} | ||
|
||
for (let [name, stage] of this.child_stages) { | ||
|
||
let sub_filter; | ||
|
||
if (filter) { | ||
let matches_filter = false; | ||
|
||
sub_filter = []; | ||
|
||
for (let i = 0; i < filter.length; i++) { | ||
let parts = filter[i]; | ||
|
||
if (parts[0] == name) { | ||
matches_filter = true; | ||
|
||
// Create a clone of the parts array | ||
parts = parts.slice(0); | ||
|
||
// Remove the first part | ||
parts.shift(); | ||
|
||
if (parts.length) { | ||
// And add it to the sub filter | ||
sub_filter.push(parts); | ||
} | ||
} | ||
} | ||
|
||
if (!matches_filter) { | ||
continue; | ||
} | ||
|
||
if (!sub_filter.length) { | ||
sub_filter = null; | ||
} | ||
} | ||
|
||
result.push(...stage.getFlattenedStages(sub_filter)); | ||
} | ||
|
||
return result; | ||
}); | ||
|
||
/** | ||
* Recursively get all the child-stages (including this one) | ||
* sorted in launch order. | ||
* | ||
* @author Jelle De Loecker <[email protected]> | ||
* @since 1.4.0 | ||
* @version 1.4.0 | ||
* | ||
* @param {string[]} filter | ||
* | ||
* @return {Alchemy.Stage.Stage[]} | ||
*/ | ||
Stage.setMethod(function getSortedStages(filter) { | ||
|
||
let stages = this.getFlattenedStages(filter); | ||
|
||
stages.sortTopological('id', 'depends_on'); | ||
|
||
return stages; | ||
}); | ||
|
||
/** | ||
* Launch this stage and all the given child stages. | ||
* | ||
|
@@ -357,23 +515,21 @@ Stage.setMethod(function afterStages(stages, callback) { | |
* | ||
* @param {string[]} child_stages The child stages to launch | ||
*/ | ||
Stage.setMethod(function launch(child_stages) { | ||
Stage.setMethod(async function launch(child_stages) { | ||
|
||
if (child_stages == null) { | ||
throw new Error('Unable to launch a stage without allowed child stages'); | ||
} | ||
|
||
if (!this.started) { | ||
this.started = Date.now(); | ||
if (child_stages === true) { | ||
child_stages = undefined; | ||
} | ||
|
||
this.emit('launching', this); | ||
let stages = this.getSortedStages(child_stages); | ||
|
||
if (this !== this.root_stage) { | ||
this.root_stage.emit('launching', this); | ||
} | ||
for (let stage of stages) { | ||
await stage._launch(); | ||
} | ||
|
||
return this._launch(child_stages); | ||
}); | ||
|
||
/** | ||
|
@@ -387,6 +543,16 @@ Stage.setMethod(function launch(child_stages) { | |
*/ | ||
Stage.setMethod(async function _launch(child_stages) { | ||
|
||
if (!this.started) { | ||
this.started = Date.now(); | ||
|
||
this.emit('launching', this); | ||
|
||
if (!this.is_root) { | ||
this.root_stage.emit('launching', this); | ||
} | ||
} | ||
|
||
if (!this[STATUS]) { | ||
this[STATUS] = PRE_STATUS; | ||
} | ||
|
@@ -399,68 +565,37 @@ Stage.setMethod(async function _launch(child_stages) { | |
|
||
await this._doTasks('main_tasks'); | ||
|
||
if (child_stages === true) { | ||
child_stages = []; | ||
|
||
for (let [name, stage] of this.child_stages) { | ||
child_stages.push(name); | ||
} | ||
} else if (typeof child_stages == 'string') { | ||
child_stages = [child_stages]; | ||
} | ||
|
||
if (child_stages.length) { | ||
await this.pre_tasks[STATUS_PLEDGE]; | ||
await this.main_tasks[STATUS_PLEDGE]; | ||
|
||
if (this[STATUS] != POST_STATUS) { | ||
this[STATUS] = CHILD_STATUS; | ||
} | ||
|
||
let stage_tasks = []; | ||
|
||
for (let name of child_stages) { | ||
let stage = this.child_stages.get(name); | ||
|
||
if (!stage) { | ||
throw new Error('Child stage "' + name + '" not found'); | ||
} | ||
|
||
stage_tasks.push(async (next) => { | ||
await stage.launch(true); | ||
next(); | ||
}); | ||
} | ||
await this.pre_tasks[STATUS_PLEDGE]; | ||
await this.main_tasks[STATUS_PLEDGE]; | ||
|
||
await Function.series(stage_tasks); | ||
if (this[STATUS] != POST_STATUS) { | ||
this[STATUS] = CHILD_STATUS; | ||
} | ||
|
||
this[STATUS] = POST_STATUS; | ||
|
||
if (this.hasFinishedAllChildStages()) { | ||
await this._doTasks('post_tasks'); | ||
this.refreshStatus(); | ||
} | ||
await this.refreshStatus(); | ||
}); | ||
|
||
|
||
/** | ||
* Check if everything is finished | ||
* | ||
* @author Jelle De Loecker <[email protected]> | ||
* @since 1.4.0 | ||
* @version 1.4.0 | ||
*/ | ||
Stage.setMethod(function refreshStatus() { | ||
Stage.setMethod(async function refreshStatus() { | ||
|
||
if (!this.hasFinishedAllChildStages()) { | ||
return; | ||
} | ||
|
||
await this._doTasks('post_tasks'); | ||
|
||
if (!this.ended) { | ||
this.ended = Date.now(); | ||
} | ||
|
||
this[STATUS] = POST_STATUS; | ||
|
||
this.pledge.resolve(); | ||
|
||
if (this.parent) { | ||
|
@@ -483,6 +618,32 @@ Stage.setMethod(function createSputnikShim(mapping) { | |
return new SputnikShim(this, mapping); | ||
}); | ||
|
||
/** | ||
* Custom Janeway representation (left side) | ||
* | ||
* @author Jelle De Loecker <[email protected]> | ||
* @since 1.4.0 | ||
* @version 1.4.0 | ||
* | ||
* @return {string} | ||
*/ | ||
Stage.setMethod(Symbol.for('janeway_arg_left'), function janewayClassIdentifier() { | ||
return 'A.S.' + this.constructor.name; | ||
}); | ||
|
||
/** | ||
* Custom Janeway representation (right side) | ||
* | ||
* @author Jelle De Loecker <[email protected]> | ||
* @since 1.4.0 | ||
* @version 1.4.0 | ||
* | ||
* @return {String} | ||
*/ | ||
Stage.setMethod(Symbol.for('janeway_arg_right'), function janewayInstanceInfo() { | ||
return this.id; | ||
}); | ||
|
||
/** | ||
* The SputnikShim class | ||
* | ||
|