amps.psm1

Set-StrictMode -Version Latest

<#
.SYNOPSIS
   Enter-Object destructures an object and makes its properties available as variables in a new scope.
    
.DESCRIPTION
   Enter-Object makes available as variables the properties of an object, or the items of a hashtable,
   within a new scope without needing to refer to the object itself. It has an alias of 'with' to provide
   a shorthand version that makes it feel like a built-in language construct.
 
   The most common anticipated use of this function is to perform splatting operations on a nested object's
   properties or nested hashtable's items without having to perform temporary variable assignments to refer
   to the nested properties/items.
 
   Enter-Object removes the use of temporary variables and becomes more concise when dealing with complex
   configuration objects where multiple splatting operations are needed.
 
.EXAMPLE
   >
   $loginResult = @{
       Success = $false
       Reason = 'Expired'
       Message = @{
           Object = "Your password has expired, please reset your password."
           BackgroundColor = "Red"
           ForegroundColor = "Yellow"
       }
   }
 
   Enter-Object $loginResult {
       Write-Host @Message
   }
 
   This example shows how a nested hashtable can be splatted directly without having to assign it to a new
   temporary variable as would normally be needed as follows:
 
   $message = $loginResult.Message
   Write-Host @message
 
.EXAMPLE
   >
   with $loginResult {
       Write-Host @Message
   }
 
   Building upon the first example, this shows the use of the 'with' alias which makes this feel like a
   built-in keyword. The 'with' alias was chosen because it's a keyword found in the VB.NET language that
   performs the same purpose.
 
.OUTPUTS
   Any objects written to the output stream will be returned.
#>

function Enter-Object {
    [CmdletBinding()]
    [Alias("with")]
    param (
        # The object or hashtable to destructure and make available as variables within the script block.
        # When supplying an object the properties will be exposed as the variables.
        # When supplying a hashtable the items will be exposed as the variables.
        # Only the first-level of properties/items from the input object are converted into variables.
        [Parameter(Mandatory,ValueFromPipeline)]
        [ValidateNotNull()]
        [Object[]]
        $InputObject,

        # The script block with commands to execute in a new nested scope with access to the variables
        # from the decomposed input object.
        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [ScriptBlock]
        $ScriptBlock
    )
    process
    {
        foreach($io in $InputObject) {
            Write-Verbose "InputObject type: $($io.GetType().FullName)"

            $variables = if($io -is [Hashtable]) {
                $io.Keys | ForEach-Object { [PSVariable]::new($_,$io.Item($_)) }
            } else {
                $io.PSObject.Properties | ForEach-Object { [PSVariable]::new($_.Name,$_.Value) }
            }

            Write-Verbose "ScriptBlock variables: $($variables.Name -join ',')"

            # functions, variables, arguments
            $ScriptBlock.InvokeWithContext($null,[PSVariable[]]$variables,$null)
        }
    }
}