Advertisement
XT-8147

New-Namespace.ps1

Mar 22nd, 2015
429
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#
  2. .SYNOPSIS
  3. Creates a namespace for your testing variables.
  4. .DESCRIPTION
  5. PowerShell has the Variable PSProvider, which contains a bunch of stuff by default.  While it prevents you from deleting anything crucial, it often becomes difficult to look through it to find your test variables so you can remove them or work with them.
  6.  
  7. My first thought was, "well, since we have the Variable PSProvider, and the Variable: PSDrive is just an instance of that, why don't we just make a second one and use that?".  However, that doesn't work like we want.  As it turns out, all PSDrives using the Variable PSProvider share the same set of variables.  There's no way to make an empty one that doesn't pollute the others.  Also, that wouldn't store functions very easily.  I know, I know, script blocks can be stored in variables and run with their .invoke() method, but that's kind of ugly.
  8.  
  9. Thus, this function.  It creates an object that serves as a namespace.  It has Add, Remove, Clear, and List methods that create, remove, and work with the members you create within the namespace.  So, instead of adding a ton of variables to your Variable PSProvider, you can add only one, and just expand that one variable as much as you want.
  10.  
  11. You can even add functions, which will show up as methods on the object.  Yeah, this is the real deal.
  12. .INPUTS
  13. This function does not take input from the pipeline.
  14. .OUTPUTS
  15. This function returns an object, that you should probably store in a variable so it will be useful.
  16. .NOTES
  17. Have fun!
  18. .LINK
  19. Twitter: @XT8147
  20.    Blog: http://xt-8147.blogspot.com/
  21. .EXAMPLE
  22. $ns = New-Namespace
  23.  
  24. Creates a new namespace, and stores it in the variable $ns.  But you knew that, right?
  25. .EXAMPLE
  26. $ns.add( 'test', 5 )
  27. True
  28. C:\PS>$ns.test
  29. 5
  30.  
  31. Adds a variable named 'test' to your namespace.  It gets the value 5.  True is returned to indicate success adding the value.  If something goes horribly wrong, False will be returned instead.  I vastly prefer exit status to exceptions out the wazoo, I guess I'm not really cut out for .NET.
  32. .EXAMPLE
  33. $ns.add( 'helloworld', { write-output 'Hello, World!' } );
  34. True
  35. C:\PS>$ns.helloworld()
  36. Hello, World!
  37.  
  38. Adding functions works as expected.  Just write code in a script block.  That script block can also be parameterized as you might normally parameterize a script block.
  39. .EXAMPLE
  40. $ns.list()
  41.  
  42. Name          Type                                     Value
  43. ----          ----                                     -----
  44. test          Variable                                     5
  45. helloworld    Function    {  write-output 'Hello, World!'  }
  46.  
  47. As you can see, the list() method will tell you what everything in the namespace is, including all the values.  Function code will be shown as well.
  48. .EXAMPLE
  49. $ns.list( 'Function' )
  50.  
  51. Name          Type                                     Value
  52. ----          ----                                     -----
  53. helloworld    Function    {  write-output 'Hello, World!'  }
  54.  
  55. But wait, there's more!  You can send a type to list(), and it will return anything in the namespace that matches that wildcard.
  56.  
  57. In fact, anything in the set @( 'All', 'Variable', 'Function', 'A', 'V', 'F', 'var', 'func' ) is accepted for this parameter.
  58. .EXAMPLE
  59. $ns.list( 'All', 'hello*' )
  60.  
  61. Name          Type                                     Value
  62. ----          ----                                     -----
  63. helloworld    Function    {  write-output 'Hello, World!'  }
  64.  
  65. We're still not done with list()!  In addition to the type parameter, you can also pass a wildcard to only grab anything in the namespace whose name matches that wildcard.  Yes sir.
  66.  
  67. Note that if you want to pass a wildcard, you must pass the type parameter.  If you only want to wildcard match, that's what passing 'All' is for.
  68. .EXAMPLE
  69. $ns.remove( 'test' )
  70. True
  71.  
  72. Should you no longer require one of your namespace variables, you can just remove it.  Just like with add(), a boolean is returned.  True indicates that the variable was removed successfully, False indicates something went horribly wrong.
  73.  
  74. Also, you can't use remove() to remove any of the namespace's built-in stuff, or any of the built-in members provided by PowerShell.
  75.  
  76. You also can't pass a wildcard to this.  You'll need to know the exact name of what you want to delete.  You can still wildcard it, though, using other bits of PowerShell and just ForEach-Object'ing the result into this.  I won't hold it against you.
  77. .EXAMPLE
  78. $ns.clear()
  79.  
  80. Finally, if you don't want any of your variables or functions anymore, you can just clear the entire thing.  It's that easy.
  81. .EXAMPLE
  82. $ns._protected | Get-Member
  83.  
  84. I won't go into this too much since it's the internals of the namespace, but this exists.  Of particular interest might be $ns._protected.describe(), which returns the labelled object that $ns.list() filters for its output.  You can use this to write other code that uses the contents of the namespace in an easier manner, since the Type property contains more contextual strings instead of .NET types.  Remember that all PSCustomObjects have the PSObject property that allows for management of the object.
  85. #>
  86. [CmdletBinding()]
  87. param()
  88. new-variable -scope script -name ret -value ( new-object psobject )
  89.  
  90. # give ourselves a space to store a few relevant things
  91. $script:ret | add-member -type noteproperty -name _protected -value ( new-object psobject )
  92.  
  93. # these two properties hold the names of members that the user shouldn't be allowed to remove
  94. $script:ret._protected | add-member -type noteproperty -name methods -value @( 'Add', 'Remove', 'Clear', 'List' )
  95. $script:ret._protected | add-member -type noteproperty -name properties -value @( '_protected' )
  96.  
  97. # let's also give ourselves a nice way to group the above two properties together
  98. $script:ret._protected | add-member -type scriptproperty -name members -value { $this.methods + $this.properties }
  99.  
  100. # store a reference to $ret within $ret._protected, so methods on $ret._protected can access $ret
  101. # since getting a reference actually makes an object whose 'value' property is the object being referenced, we name the
  102. # reference itself something completely different, so we can use a scriptproperty to grab the 'value' property
  103. # and have our code look nicer
  104. $script:ret._protected | add-member -type noteproperty -name __dotnetisweirdsometimes -value ([ref]$script:ret)
  105. $script:ret._protected | add-member -type scriptproperty -name parentobject -value { $this.__dotnetisweirdsometimes.value }
  106.  
  107. # this is kinda ugly, but we need it
  108. # and it needs to be separate
  109. # returns an object that labels the object's members
  110. $script:ret._protected | add-member -type scriptmethod -name describe -value {
  111.     param()
  112.     new-variable members
  113.     # use select-object -property so we can easily iterate through all members and tag them appropriately
  114.     $members = $this.parentobject.psobject.members | select-object -property @( 'Name',
  115.         @{ name = 'Type';
  116.                     expression = {
  117.                         if( $_.membertype -eq 'NoteProperty' ) {
  118.                             if( $this.properties -contains $_.name ) { 'ManagementProperty' }
  119.                             else { 'Variable' }
  120.                         }
  121.                         elseif( $_.membertype -eq 'ScriptMethod' ) {
  122.                             if( $this.methods -contains $_.name ) { 'ManagementMethod' }
  123.                             else { 'Function' }
  124.                         }
  125.                         else { 'PSObjectBuiltin' }
  126.                     }
  127.         },
  128.         @{ name = 'Value';
  129.                     expression = {
  130.                         if( $_.membertype -eq 'ScriptMethod' ) { '{ ' + ( '{0}' -f $_.script ) + ' }' }
  131.                         else { $_.value }
  132.                     }
  133.         }
  134.     )
  135.     return $members
  136. }
  137.  
  138. # we need a way to add variables to this namespace
  139. # I prefer exit status, even though EXCEPTIONS EVERYWHERE is the .NET way...
  140. $script:ret | add-member -type scriptmethod -name Add -value {
  141.     param( [string]$name, $value )
  142.     # PowerShell transparently prevents adding a member with the name 'psobject'
  143.     # It reports success but silently fails, so let's explicitly return false
  144.     if( $name -eq 'psobject' ) {
  145.         return $false
  146.     }
  147.     # test to see if we already have a member with this name
  148.     if( $this.psobject.members | where-object { $_.name -eq $name } ) {
  149.         # we do
  150.         return $false
  151.     }
  152.     else {
  153.         # no member with this name exists, create and assign
  154.         new-variable type
  155.         # delicious runtime type identification go
  156.         switch( $value.gettype().name ) {
  157.             'ScriptBlock' {
  158.                 $type = 'ScriptMethod'
  159.             }
  160.             default {
  161.                 $type = 'NoteProperty'
  162.             }
  163.         }
  164.         $this | add-member -type $type -name $name -value $value
  165.         return $true
  166.     }
  167. }
  168.  
  169. # we also need a way to remove variables from this namespace
  170. $script:ret | add-member -type scriptmethod -name Remove -value {
  171.     param( [string]$name )
  172.     new-variable member ( $this.psobject.members | where-object { $_.name -eq $name } )
  173.     # make sure the member we're trying to remove exists
  174.     if( $member ) {
  175.         # see if we're trying to remove any of the methods for managing the namespace
  176.         if( ( $this._protected.members -notcontains $name ) ){
  177.             # nope, go ahead and remove the member so long as it's a NoteProperty or ScriptMethod
  178.             if( @( 'NoteProperty', 'ScriptMethod' ) -contains $member.membertype ) {
  179.                 $this.psobject.members.remove( $name )
  180.                 return $true
  181.             }
  182.             else {
  183.                 # member was not a NoteProperty or ScriptMethod
  184.                 # this does the job of explicitly preventing the removal of the default methods
  185.                 # even though actually trying to do so silently fails
  186.                 return $false
  187.             }
  188.         }
  189.         else {
  190.             # don't let any of the protected members be removed
  191.             return $false
  192.         }
  193.     }
  194.     else {
  195.         # trying to remove that which does not exist?
  196.         # note that trying to remove 'psobject' returns this particular false
  197.         return $false
  198.     }
  199. }
  200.  
  201. # I know, I know, mass-deletion only leads to mass-sadness, but this is a namespace for test variables we're talking about here.
  202. $script:ret | add-member -type scriptmethod -name Clear -value {
  203.     param()
  204.     # delete everything from the namespace
  205.     $this.psobject.members |
  206.         where-object { ( @( 'NoteProperty', 'ScriptMethod' ) -contains $_.membertype ) -and ( $this._protected.members -notcontains $_.name ) } |
  207.         foreach-object {
  208.             $this.psobject.members.remove( $_.name )
  209.         }
  210. }
  211.  
  212. # What the heck, a way to list what's in the namespace.  Filters out default methods on the psobject object, and the protected members.
  213. # note that if you want to pass a type, you have to pass a wildcard.  If you don't need a wildcard, pass '*' to it.
  214. $script:ret | add-member -type scriptmethod -name List -value {
  215.     param(
  216.         [Parameter( Mandatory = $false )]
  217.         [ValidateSet( 'All', 'Variable', 'Function', 'A', 'V', 'F', 'var', 'func' )]
  218.         [string]$type = 'All', # defaults to all, but can be restricted (various forms of shorthand available as seen in the ValidateSet)
  219.         [Parameter( Mandatory = $false )]
  220.         [string]$wildcard = '*' # defaults to all, but can be restricted
  221.     )
  222.     new-variable members $this._protected.describe()
  223.     # Grab what was requested from our the object we just got
  224.     switch -regex ( $type ) {
  225.         'All|A' {
  226.             $members | where-object { ( @( 'Variable', 'Function' ) -contains $_.type ) -and ( $_.name -like $wildcard ) }
  227.         }
  228.         'Variable|V|var' {
  229.             $members | where-object { ( $_.type -eq 'Variable' ) -and ( $_.name -like $wildcard ) }
  230.         }
  231.         'Function|F|func' {
  232.             $members | where-object { ( $_.type -eq 'Function' ) -and ( $_.name -like $wildcard ) }
  233.         }
  234.     }
  235. }
  236.  
  237. $script:ret
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement