MetaProgramming.psm1
Set-StrictMode -Version 2 ################################################################################ function New-ParameterAttribute { <# .Synopsis Creates a PowerShell ParameterAttribute. .Description This command is used in metaprogramming scenarios where you dynamically generate a cmdlet on the fly. Parameters of Cmdlets must have a Parameter Attribute attached to them to tell PowerShell what to do with the parameter. This command generates that attribute. .Parameter Position What position to map this parameter to when users don't use named parameters. .Parameter ParameterSetName .Parameter Mandatory Is this parameter Mandatory .Parameter ValueFromPipeline Does this parameter take a value from the pipeline object .Parameter ValueFromPipelineByPropertyName Does the parameter take a value from a property of the pipeline object .Parameter ValueFromRemainingArguments Does this collect the remaining arguments on the command line .Example New-ParameterAttribute .ReturnValue System.Management.Automation.ParameterAttribute .Link New-ProxyCommand .Notes NAME: New-ParameterAttribute AUTHOR: Jeffrey Snover LASTEDIT: 1/4/2009 8:53:35 AM #Requires -Version 2.0 #> [CmdletBinding( SupportsShouldProcess=$False, SupportsTransactions=$False, ConfirmImpact="None", DefaultParameterSetName="")] param( [Parameter()] [Int32] $Position = [int32]::MinValue, [Parameter()] [String] $ParameterSetName, [Parameter()] [Switch] $Mandatory, [Parameter()] [Switch] $ValueFromPipeline, [Parameter()] [Switch] $ValueFromPipelineByPropertyName, [Parameter()] [Switch] $ValueFromRemainingArguments ) New-Object System.Management.Automation.ParameterAttribute -Property @{ Position = $Position ParameterSetName = $ParameterSetName Mandatory = $Mandatory ValueFromPipeline = $ValueFromPipeline ValueFromPipelineByPropertyName = $ValueFromPipelineByPropertyName ValueFromRemainingArguments = $ValueFromRemainingArguments } } # New-ParameterAttribute ################################################################################ function New-ProxyCommand { <# .Synopsis Generate a script for a ProxyCommand to call a base Cmdlet adding or removing parameters. .Description This command generates command which calls another command (a ProxyCommand). In doing so, it can add additional attributes to the existing parameters. This is useful for things like enforcing corporate naming standards. It can also ADD or REMOVE parameters. If you ADD a parameter, you'll have to implement the semantics of that parameter in the code that gets generated. .Parameter Name Name of the Cmdlet to proxy. .Parameter CommandType Type of Command we are proxying. In general you dont' need to specify this but it becomes necessary if there is both a cmdlet and a function with the same name .Parameter AddParameter List of Parameters you would like to add. NOTE - you have to edit the resultant code to implement the semantics of these parameters. ALSO - you need to remove them from $PSBOUND .Parameter RemoveParameter .Example New-ProxyCommand get-process -CommandType all -RemoveParameter ` FileVersionInfo,Module,ComputerName -AddParameter SortBy > c:\ps\get-myprocess.ps1 .ReturnValue System.String .Link New-ParameterAttribute .Notes NAME: New-ProxyCommand AUTHOR: Jeffrey Snover ToDo: Need to modify script to emit template help for the proxy command. Probably should add a -AsFunction switch LASTEDIT: 1/4/2009 8:53:35 AM ################################################################################ #Requires -Version 2.0 #> [CmdletBinding( SupportsShouldProcess=$False, SupportsTransactions=$False, ConfirmImpact="None", DefaultParameterSetName="")] [Outputtype([String])] param( [Parameter(Position=0, Mandatory=$True)] [String]$Name , [Parameter(Position=1)] [Alias("Type")] [System.Management.Automation.CommandTypes]$CommandType="All" , [Parameter(Position=2)] [String[]]$AddParameter , [Parameter(Position=3)] [String[]]$RemoveParameter , [Parameter(Position=4)] [HashTable]$AddParameterAttribute ) $Cmd = Get-Command -Name $Name -CommandType $CommandType if (!$cmd) { Throw "No such Object [$Name : $CommandType]" }elseif (@($cmd).Count -ne 1) { Throw "Ambiguous reference [$Name : $CommandType]`n$($Cmd |Out-String)" } $MetaData = New-Object System.Management.Automation.CommandMetaData $cmd if ($RemoveParameter) { foreach ($p in @($RemoveParameter)) { [Void]$MetaData.Parameters.Remove($p) } } if ($AddParameter) { @' <# You are responsible for implementing the logic for added parameters. These parameters are bound to $PSBoundParameters so if you pass them on the the command you are proxying, it will almost certainly cause an error. This logic should be added to your BEGIN statement to remove any specified parameters from $PSBoundParameters. In general, the way you are going to implement additional parameters is by modifying the way you generate the $scriptCmd variable. Here is an example of how you would add a -SORTBY parameter to a cmdlet: if ($SortBy) { [Void]$PSBoundParameters.Remove("SortBy") $scriptCmd = {& $wrappedCmd @PSBoundParameters |Sort-Object -Property $SortBy} }else { $scriptCmd = {& $wrappedCmd @PSBoundParameters } } ################################################################################ New ATTRIBUTES '@ foreach ($p in @($AddParameter)) { [Void]$MetaData.Parameters.Add($p, $(New-object System.Management.Automation.ParameterMetadata $p )) @" if (`$$p) { [Void]`$PSBoundParameters.Remove("$p") } "@ } " ################################################################################ #> " } [System.Management.Automation.ProxyCommand]::create($MetaData) }#New-ProxyCommand |