I'm trying to add some callback capabilities to a PowerShell function but I want to be able to define the behaviour of the callbacks outside of the function definition, so basically a very simple Inversion of Control for PowerShell.
I'm trying to be as backward compatible as is practical, and this is what I've come up with so far (error handling removed for brevity):
$ErrorActionPreference = "Stop";
Set-StrictMode -Version "Latest";
function Invoke-DoSomethingMethod
{
param
(
[hashtable] $Callbacks
)
$CallBacks["OnStarted"].Invoke();
for( $i = 1; $i -le 10; $i++ )
{
# ... do something here ...
$CallBacks["OnProgress"].Invoke($i * 10);
}
$CallBacks["OnFinished"].Invoke();
}
$myHostCallbacks = @{
"OnStarted" = { write-host "started" }
"OnProgress" = { write-host "processing $($args[0])%" }
"OnFinished" = { write-host "finished" }
}
$myFileCallbacks = @{
"OnStarted" = { Add-Content "C:\temp\log.txt" "started" }
"OnProgress" = { Add-Content "C:\temp\log.txt" "processing $($args[0])%" }
"OnFinished" = { Add-Content "C:\temp\log.txt" "finished" }
}
$myWebCallbacks = @{
"OnStarted" = { Invoke-WebRequest -Uri "http://example.org/OnStarted" }
"OnProgress" = { Invoke-WebRequest -Uri "http://example.org/OnProgress/$($args[0])" }
"OnFinished" = { Invoke-WebRequest -Uri "http://example.org/OnFinish" }
}
Invoke-DoSomethingMethod -Callbacks $myHostCallbacks;
When run, this outputs the following:
c:\temp>powershell .\callbacks.ps1
started
processing 10%
processing 20%
processing 30%
processing 40%
processing 50%
processing 60%
processing 70%
processing 80%
processing 90%
processing 100%
finished
Without using external modules or C# code, is there a more idomatic way of doing this with PowerShell that doesn't rely on matching up the magic strings for callback names and the order of $args[] parameters? (Preferrably compatible with version 2.0).
Cheers,
Mike
Update
For some context, my Invoke-DoSomething function is part of a CI build framework (https://github.com/ASOS/OctopusStepTemplateCi) which is currently hard-coded to send TeamCity service messages using Write-Host during the build process.
We're interested in supporting other build server products so I'm investigating a way to abstract out the various interactions with the build server into user-definable callbacks like "OnBuildStarted", "OnBuildStepFailed", etc, like you might do with an IoC framework in C#.
This would make it easier to ship support for new build servers from within the project, and also for users to write callbacks for build servers which don't come out-of-the-box.
This might help give a flavour of what I'm trying to do:
function Invoke-BuildScript
{
param( [hashtable] $Callbacks )
$CallBacks["OnBuildStarted"].Invoke();
... build script here ...
$CallBacks["OnBuildFinished"].Invoke();
}
$teamCityCallbacks = @{
"OnBuildStarted" = { write-host "started" }
"OnBuildFinished" = { write-host "finished" }
"OnBuildError" = { write-host "error '$($args[0])'" }
}
$jenkinsCallbacks = @{
"OnBuildStarted" = { ... }
"OnBuildFinished" = { ... }
"OnBuildError" = { ... }
}
Invoke-BuildScript -Callbacks $teamCityCallbacks;